From 694891c39348225b8a0062e7c67056d63bcf4df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BC=96=E7=A0=81=E5=B7=A5=E7=A8=8B=E5=B8=88?= Date: Fri, 21 Aug 2026 10:04:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(mobile-next):=20=E6=B8=85=E7=90=86=E6=82=AC?= =?UTF-8?q?=E6=B5=AE=E7=AA=97=E3=80=81=E8=AF=AD=E9=9F=B3=E6=96=87=E6=A1=88?= =?UTF-8?q?=E4=B8=8E=E4=BD=8D=E7=BD=AE=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按 B-273 / B-269 收口 Android 客户端:去掉悬浮窗申请与旧浮窗服务,通话入口统一为语音通话,删除位置发送与定位权限,保留历史位置只读展示。 Co-authored-by: Cursor Co-authored-by: multica-agent --- .../android/app/src/main/AndroidManifest.xml | 20 +- .../lib/core/controller/im_controller.dart | 26 +- mobile-next/lib/pages/chat/chat_logic.dart | 214 +++++++------ mobile-next/lib/pages/chat/chat_view.dart | 27 +- .../openim_common/lib/openim_common.dart | 1 - .../openim_common/lib/src/res/lang/en_US.dart | 5 +- .../openim_common/lib/src/res/lang/zh_CN.dart | 5 +- .../openim_common/lib/src/res/strings.dart | 23 +- .../lib/src/utils/permissions.dart | 44 +-- .../openim_common/lib/src/utils/utils.dart | 295 +++++++++++++----- .../openim_common/lib/src/widgets/button.dart | 6 +- .../src/widgets/chat/chat_location_view.dart | 141 ++++++--- .../lib/src/widgets/chat/chat_toolbox.dart | 11 +- .../src/widgets/chat/chat_webview_map.dart | 269 ---------------- .../lib/src/widgets/map_view.dart | 53 ++-- .../lib/src/widgets/rich_text_input_box.dart | 13 +- .../openim_common/lib/src/widgets/views.dart | 50 ++- mobile-next/openim_common/pubspec.yaml | 1 - .../openim_live/lib/src/live_controller.dart | 136 +++++--- mobile-next/pubspec.lock | 48 --- 20 files changed, 662 insertions(+), 726 deletions(-) delete mode 100644 mobile-next/openim_common/lib/src/widgets/chat/chat_webview_map.dart diff --git a/mobile-next/android/app/src/main/AndroidManifest.xml b/mobile-next/android/app/src/main/AndroidManifest.xml index 7420534..b3c36bd 100644 --- a/mobile-next/android/app/src/main/AndroidManifest.xml +++ b/mobile-next/android/app/src/main/AndroidManifest.xml @@ -12,10 +12,10 @@ - + + - @@ -26,14 +26,15 @@ - - - + + + + + - @@ -50,9 +51,6 @@ android:usesPermissionFlags="neverForLocation" /> - - - @@ -130,10 +128,6 @@ android:exported="false" android:stopWithTask="false" /> - - userInfo; late String atAllTag; final _rtcTokenApi = RtcTokenApi(); - final callGuard = CallSessionGuard(inviteTimeout: LiveKitCallConfig.inviteTimeout); + final callGuard = + CallSessionGuard(inviteTimeout: LiveKitCallConfig.inviteTimeout); @override void onClose() { @@ -36,7 +35,8 @@ class IMController extends GetxController with IMCallback, OpenIMLive { if (token == null || token.isEmpty) { return Future.error(StateError('登录状态已失效,请重新登录')); } - return _rtcTokenApi.getToken(room: roomID, identity: userID, authToken: token); + return _rtcTokenApi.getToken( + room: roomID, identity: userID, authToken: token); } @override @@ -54,11 +54,13 @@ class IMController extends GetxController with IMCallback, OpenIMLive { if (!FeatureFlags.livekitCall) return; final invitation = info.invitation; final roomID = invitation?.roomID ?? ''; - if (invitation?.mediaType != 'audio' || invitation?.sessionType != ConversationType.single) { + if (invitation?.mediaType != 'audio' || + invitation?.sessionType != ConversationType.single) { onTapReject(info); return; } - if (!callGuard.acceptSignaling(customType: CustomMessageType.callingInvite, roomID: roomID)) { + if (!callGuard.acceptSignaling( + customType: CustomMessageType.callingInvite, roomID: roomID)) { return; } if (isBusy || callGuard.isBusy) { @@ -149,7 +151,8 @@ class IMController extends GetxController with IMCallback, OpenIMLive { ); OpenIM.iMManager - ..setUploadLogsListener(OnUploadLogsListener(onUploadProgress: uploadLogsProgress)) + ..setUploadLogsListener( + OnUploadLogsListener(onUploadProgress: uploadLogsProgress)) ..userManager.setUserListener(OnUserListener( onSelfInfoUpdated: (u) { selfInfoUpdated(u); @@ -180,11 +183,13 @@ class IMController extends GetxController with IMCallback, OpenIMLive { customType == CustomMessageType.callingCancel || customType == CustomMessageType.callingHungup) { if (!FeatureFlags.livekitCall) return; - final signaling = SignalingInfo(invitation: InvitationInfo.fromJson(map['data'])); + final signaling = SignalingInfo( + invitation: InvitationInfo.fromJson(map['data'])); signaling.userID = signaling.invitation?.inviterUserID; final roomID = signaling.invitation?.roomID ?? ''; if (customType != CustomMessageType.callingInvite && - !callGuard.acceptSignaling(customType: customType as int, roomID: roomID)) { + !callGuard.acceptSignaling( + customType: customType as int, roomID: roomID)) { return; } @@ -236,9 +241,6 @@ class IMController extends GetxController with IMCallback, OpenIMLive { }, onSyncServerFinish: (reInstall) { imSdkStatus(IMSdkStatus.syncEnded, reInstall: reInstall ?? false); - if (Platform.isAndroid) { - Permissions.request([Permission.systemAlertWindow]); - } }, onSyncServerStart: (reInstall) { imSdkStatus(IMSdkStatus.syncStart, reInstall: reInstall ?? false); diff --git a/mobile-next/lib/pages/chat/chat_logic.dart b/mobile-next/lib/pages/chat/chat_logic.dart index 503a07e..64981d8 100644 --- a/mobile-next/lib/pages/chat/chat_logic.dart +++ b/mobile-next/lib/pages/chat/chat_logic.dart @@ -126,10 +126,13 @@ class ChatLogic extends SuperController { String get memberStr => isSingleChat ? "" : "($memberCount)"; - String? get senderName => isSingleChat ? OpenIM.iMManager.userInfo.nickname : groupMembersInfo?.nickname; + String? get senderName => isSingleChat + ? OpenIM.iMManager.userInfo.nickname + : groupMembersInfo?.nickname; bool get isAdminOrOwner => - groupMemberRoleLevel.value == GroupRoleLevel.admin || groupMemberRoleLevel.value == GroupRoleLevel.owner; + groupMemberRoleLevel.value == GroupRoleLevel.admin || + groupMemberRoleLevel.value == GroupRoleLevel.owner; final directionalUsers = [].obs; @@ -140,8 +143,10 @@ class ChatLogic extends SuperController { var isCurSingleChat = message.isSingleChat && isSingleChat && - (senderId == userID || senderId == OpenIM.iMManager.userID && receiverId == userID); - var isCurGroupChat = message.isGroupChat && isGroupChat && groupID == groupId; + (senderId == userID || + senderId == OpenIM.iMManager.userID && receiverId == userID); + var isCurGroupChat = + message.isGroupChat && isGroupChat && groupID == groupId; return isCurSingleChat || isCurGroupChat; } @@ -152,11 +157,14 @@ class ChatLogic extends SuperController { } Future> searchMediaMessage() async { - final messageList = await OpenIM.iMManager.messageManager.searchLocalMessages( - conversationID: conversationInfo.conversationID, - messageTypeList: [MessageType.picture, MessageType.video], - count: 500); - return messageList.searchResultItems?.first.messageList?.reversed.toList() ?? []; + final messageList = await OpenIM.iMManager.messageManager + .searchLocalMessages( + conversationID: conversationInfo.conversationID, + messageTypeList: [MessageType.picture, MessageType.video], + count: 500); + return messageList.searchResultItems?.first.messageList?.reversed + .toList() ?? + []; } @override @@ -185,7 +193,8 @@ class ChatLogic extends SuperController { _setSdkSyncDataListener(); conversationSub = imLogic.conversationChangedSubject.listen((value) { - final obj = value.firstWhereOrNull((e) => e.conversationID == conversationInfo.conversationID); + final obj = value.firstWhereOrNull( + (e) => e.conversationID == conversationInfo.conversationID); if (obj != null) { conversationInfo = obj; @@ -196,7 +205,8 @@ class ChatLogic extends SuperController { if (isCurrentChat(message)) { if (message.contentType == MessageType.typing) { } else { - if (!messageList.contains(message) && !scrollingCacheMessageList.contains(message)) { + if (!messageList.contains(message) && + !scrollingCacheMessageList.contains(message)) { _isReceivedMessageWhenSyncing = true; if (isShowPopMenu.value || scrollController.offset != 0) { scrollingCacheMessageList.add(message); @@ -210,7 +220,8 @@ class ChatLogic extends SuperController { }; imLogic.onRecvMessageRevoked = (RevokedInfo info) { - var message = messageList.firstWhereOrNull((e) => e.clientMsgID == info.clientMsgID); + var message = messageList + .firstWhereOrNull((e) => e.clientMsgID == info.clientMsgID); message?.notificationElem = NotificationElem(detail: jsonEncode(info)); message?.contentType = MessageType.revokeMessageNotification; @@ -273,12 +284,14 @@ class ChatLogic extends SuperController { } _putMemberInfo([info]); - final index = ownerAndAdmin.indexWhere((element) => element.userID == info.userID); + final index = ownerAndAdmin + .indexWhere((element) => element.userID == info.userID); if (info.roleLevel == GroupRoleLevel.member) { if (index > -1) { ownerAndAdmin.removeAt(index); } - } else if (info.roleLevel == GroupRoleLevel.admin || info.roleLevel == GroupRoleLevel.owner) { + } else if (info.roleLevel == GroupRoleLevel.admin || + info.roleLevel == GroupRoleLevel.owner) { if (index == -1) { ownerAndAdmin.add(info); } else { @@ -364,7 +377,8 @@ class ChatLogic extends SuperController { }; imLogic.inputStateChangedSubject.listen((value) { - if (value.conversationID == conversationInfo.conversationID && value.userID == userID) { + if (value.conversationID == conversationInfo.conversationID && + value.userID == userID) { typing.value = value.platformIDs?.isNotEmpty == true; } }); @@ -396,7 +410,8 @@ class ChatLogic extends SuperController { Future sendPicture({required String path, bool sendNow = true}) async { final file = await IMUtils.compressImageAndGetFile(File(path)); - var message = await OpenIM.iMManager.messageManager.createImageMessageFromFullPath( + var message = + await OpenIM.iMManager.messageManager.createImageMessageFromFullPath( imagePath: file!.path, ); @@ -409,7 +424,8 @@ class ChatLogic extends SuperController { } void sendVoice(int duration, String path) async { - var message = await OpenIM.iMManager.messageManager.createSoundMessageFromFullPath( + var message = + await OpenIM.iMManager.messageManager.createSoundMessageFromFullPath( soundPath: path, duration: duration, ); @@ -423,7 +439,8 @@ class ChatLogic extends SuperController { required String thumbnailPath, bool sendNow = true}) async { var d = duration > 1000.0 ? duration / 1000.0 : duration; - var message = await OpenIM.iMManager.messageManager.createVideoMessageFromFullPath( + var message = + await OpenIM.iMManager.messageManager.createVideoMessageFromFullPath( videoPath: videoPath, videoType: mimeType, duration: d.toInt(), @@ -439,24 +456,14 @@ class ChatLogic extends SuperController { } void sendFile({required String filePath, required String fileName}) async { - var message = await OpenIM.iMManager.messageManager.createFileMessageFromFullPath( + var message = + await OpenIM.iMManager.messageManager.createFileMessageFromFullPath( filePath: filePath, fileName: fileName, ); _sendMessage(message); } - void sendLocation({ - required dynamic location, - }) async { - var message = await OpenIM.iMManager.messageManager.createLocationMessage( - latitude: location['latitude'], - longitude: location['longitude'], - description: location['description'], - ); - _sendMessage(message); - } - sendForwardRemarkMsg( String content, { String? userId, @@ -481,8 +488,8 @@ class ChatLogic extends SuperController { void sendTypingMsg({bool focus = false}) async { if (isSingleChat) { - OpenIM.iMManager.conversationManager - .changeInputStates(conversationID: conversationInfo.conversationID, focus: focus); + OpenIM.iMManager.conversationManager.changeInputStates( + conversationID: conversationInfo.conversationID, focus: focus); } } @@ -544,7 +551,8 @@ class ChatLogic extends SuperController { offlinePushInfo: Config.offlinePushInfo, ) .then((value) => _sendSucceeded(message, value)) - .catchError((error, _) => _senFailed(message, groupId, userId, error, _)) + .catchError( + (error, _) => _senFailed(message, groupId, userId, error, _)) .whenComplete(() => _completed()); } @@ -557,8 +565,10 @@ class ChatLogic extends SuperController { )); } - void _senFailed(Message message, String? groupId, String? userId, error, stack) async { - Logger.print('message send failed userID: $userId groupId:$groupId, catch error :$error $stack'); + void _senFailed( + Message message, String? groupId, String? userId, error, stack) async { + Logger.print( + 'message send failed userID: $userId groupId:$groupId, catch error :$error $stack'); message.status = MessageStatus.failed; sendStatusSub.addSafely(MsgStreamEv( id: message.clientMsgID!, @@ -574,7 +584,8 @@ class ChatLogic extends SuperController { customType = CustomMessageType.deletedByFriend; } if (null != customType) { - final hintMessage = (await OpenIM.iMManager.messageManager.createFailedHintMessage(type: customType)) + final hintMessage = (await OpenIM.iMManager.messageManager + .createFailedHintMessage(type: customType)) ..status = 2 ..isRead = true; if (userId != null) { @@ -591,10 +602,15 @@ class ChatLogic extends SuperController { ); } } else { - if ((code == SDKErrorCode.userIsNotInGroup || code == SDKErrorCode.groupDisbanded) && null == groupId) { + if ((code == SDKErrorCode.userIsNotInGroup || + code == SDKErrorCode.groupDisbanded) && + null == groupId) { final status = groupInfo?.status; - final hintMessage = (await OpenIM.iMManager.messageManager.createFailedHintMessage( - type: status == 2 ? CustomMessageType.groupDisbanded : CustomMessageType.removedFromGroup)) + final hintMessage = (await OpenIM.iMManager.messageManager + .createFailedHintMessage( + type: status == 2 + ? CustomMessageType.groupDisbanded + : CustomMessageType.removedFromGroup)) ..status = 2 ..isRead = true; messageList.add(hintMessage); @@ -662,7 +678,9 @@ class ChatLogic extends SuperController { void markMessageAsRead(Message message, bool visible) async { Logger.print('markMessageAsRead: ${message.textElem?.content}, $visible'); - if (visible && message.contentType! < 1000 && message.contentType! != MessageType.voice) { + if (visible && + message.contentType! < 1000 && + message.contentType! != MessageType.voice) { var data = IMUtils.parseCustomMessage(message); if (null != data && data['viewType'] == CustomMessageType.call) { Logger.print('markMessageAsRead: call message $data'); @@ -675,11 +693,14 @@ class ChatLogic extends SuperController { _markMessageAsRead(Message message) async { if (!message.isRead! && message.sendID != OpenIM.iMManager.userID) { try { - Logger.print('mark conversation message as read:${message.clientMsgID!} ${message.isRead}'); + Logger.print( + 'mark conversation message as read:${message.clientMsgID!} ${message.isRead}'); await OpenIM.iMManager.conversationManager - .markConversationMessageAsRead(conversationID: conversationInfo.conversationID); + .markConversationMessageAsRead( + conversationID: conversationInfo.conversationID); } catch (e) { - Logger.print('failed to send group message read receipt: ${message.clientMsgID} ${message.isRead}'); + Logger.print( + 'failed to send group message read receipt: ${message.clientMsgID} ${message.isRead}'); } finally { message.isRead = true; message.hasReadTime = _timestamp; @@ -690,23 +711,23 @@ class ChatLogic extends SuperController { _clearUnreadCount() { if (conversationInfo.unreadCount > 0) { - OpenIM.iMManager.conversationManager - .markConversationMessageAsRead(conversationID: conversationInfo.conversationID); + OpenIM.iMManager.conversationManager.markConversationMessageAsRead( + conversationID: conversationInfo.conversationID); } } void _getInputState() async { if (conversationInfo.isSingleChat) { - final result = - await OpenIM.iMManager.conversationManager.getInputStates(conversationInfo.conversationID, userID!); + final result = await OpenIM.iMManager.conversationManager + .getInputStates(conversationInfo.conversationID, userID!); typing.value = result?.isNotEmpty == true; } } void _changeInputStatus(bool focus) async { if (conversationInfo.isSingleChat) { - await OpenIM.iMManager.conversationManager - .changeInputStates(conversationID: conversationInfo.conversationID, focus: focus); + await OpenIM.iMManager.conversationManager.changeInputStates( + conversationID: conversationInfo.conversationID, focus: focus); } } @@ -714,18 +735,6 @@ class ChatLogic extends SuperController { forceCloseToolbox.addSafely(true); } - void onTapLocation() async { - var location = await Get.to( - const ChatWebViewMap(host: Config.locationHost, webKey: Config.webKey, webServerKey: Config.webServerKey), - transition: Transition.cupertino, - popGesture: true, - ); - if (null != location) { - Logger.print(location); - sendLocation(location: location); - } - } - void onTapAlbum() async { final List? assets = await AssetPicker.pickAssets(Get.context!, pickerConfig: AssetPickerConfig( @@ -743,7 +752,8 @@ class ChatLogic extends SuperController { } if (entity.videoDuration > const Duration(seconds: 5 * 60)) { - IMViews.showToast(sprintf(StrRes.selectVideoLimit, [5]) + StrRes.minute); + IMViews.showToast( + sprintf(StrRes.selectVideoLimit, [5]) + StrRes.minute); return false; } return true; @@ -837,10 +847,13 @@ class ChatLogic extends SuperController { Future _handleAssets(AssetEntity? asset, {bool sendNow = true}) async { if (null != asset) { - Logger.print('--------assets type-----${asset.type} create time: ${asset.createDateTime}'); + Logger.print( + '--------assets type-----${asset.type} create time: ${asset.createDateTime}'); final originalFile = await asset.file; final originalPath = originalFile!.path; - var path = originalPath.toLowerCase().endsWith('.gif') ? originalPath : originalFile.path; + var path = originalPath.toLowerCase().endsWith('.gif') + ? originalPath + : originalFile.path; Logger.print('--------assets path-----$path'); switch (asset.type) { case AssetType.image: @@ -1041,14 +1054,16 @@ class ChatLogic extends SuperController { } void copy(Message message) { - final content = copyTextMap[message.clientMsgID] ?? message.textElem?.content; + final content = + copyTextMap[message.clientMsgID] ?? message.textElem?.content; if (null != content) { IMUtils.copy(text: content.replaceAll('\u200B', '')); } } - Message indexOfMessage(int index, {bool calculate = true}) => IMUtils.calChatTimeInterval( + Message indexOfMessage(int index, {bool calculate = true}) => + IMUtils.calChatTimeInterval( messageList, calculate: calculate, ).reversed.elementAt(index); @@ -1111,7 +1126,11 @@ class ChatLogic extends SuperController { void onDeleteEmoji() { final input = inputCtrl.text; - final regexEmoji = emojiFaces.keys.toList().join('|').replaceAll('[', '\\[').replaceAll(']', '\\]'); + final regexEmoji = emojiFaces.keys + .toList() + .join('|') + .replaceAll('[', '\\[') + .replaceAll(']', '\\]'); final list = [regexEmoji]; final pattern = '(${list.toList().join('|')})'; final emojiReg = RegExp(regexEmoji); @@ -1186,7 +1205,8 @@ class ChatLogic extends SuperController { void sendFavoritePic(int index, String url) async { var emoji = cacheLogic.favoriteList.elementAt(index); var message = await OpenIM.iMManager.messageManager.createFaceMessage( - data: json.encode({'url': emoji.url, 'width': emoji.width, 'height': emoji.height}), + data: json.encode( + {'url': emoji.url, 'width': emoji.width, 'height': emoji.height}), ); _sendMessage(message); } @@ -1227,7 +1247,8 @@ class ChatLogic extends SuperController { var diff = (end - _timestamp) ~/ 1000; if (diff > 0) { - privateMessageList.addIf(() => !privateMessageList.contains(message), message); + privateMessageList.addIf( + () => !privateMessageList.contains(message), message); } return diff < 0 ? 0 : diff; } @@ -1255,7 +1276,8 @@ class ChatLogic extends SuperController { userIDList: [OpenIM.iMManager.userID], ); groupMembersInfo = list.firstOrNull; - groupMemberRoleLevel.value = groupMembersInfo?.roleLevel ?? GroupRoleLevel.member; + groupMemberRoleLevel.value = + groupMembersInfo?.roleLevel ?? GroupRoleLevel.member; muteEndTime.value = groupMembersInfo?.muteEndTime ?? 0; if (null != groupMembersInfo) { memberUpdateInfoMap[OpenIM.iMManager.userID] = groupMembersInfo!; @@ -1266,7 +1288,8 @@ class ChatLogic extends SuperController { Future _queryOwnerAndAdmin() async { if (isGroupChat) { - ownerAndAdmin = await OpenIM.iMManager.groupManager.getGroupMemberList(groupID: groupID!, filter: 5, count: 20); + ownerAndAdmin = await OpenIM.iMManager.groupManager + .getGroupMemberList(groupID: groupID!, filter: 5, count: 20); } return; } @@ -1303,7 +1326,9 @@ class ChatLogic extends SuperController { bool get havePermissionMute => isGroupChat && - (groupInfo?.ownerUserID == OpenIM.iMManager.userID /*|| + (groupInfo?.ownerUserID == + OpenIM.iMManager + .userID /*|| groupMembersInfo?.roleLevel == 2*/ ); @@ -1315,8 +1340,10 @@ class ChatLogic extends SuperController { void _queryUserOnlineStatus() { if (isSingleChat) { - OpenIM.iMManager.userManager.subscribeUsersStatus([userID!]).then((value) { - final status = value.firstWhereOrNull((element) => element.userID == userID); + OpenIM.iMManager.userManager + .subscribeUsersStatus([userID!]).then((value) { + final status = + value.firstWhereOrNull((element) => element.userID == userID); _configUserStatusChanged(status); }); userStatusChangedSub = imLogic.userStatusChangedSubject.listen((value) { @@ -1336,8 +1363,9 @@ class ChatLogic extends SuperController { void _configUserStatusChanged(UserStatusInfo? status) { if (status != null) { onlineStatus.value = status.status == 1; - onlineStatusDesc.value = - status.status == 0 ? StrRes.offline : _onlineStatusDes(status.platformIDs!) + StrRes.online; + onlineStatusDesc.value = status.status == 0 + ? StrRes.offline + : _onlineStatusDes(status.platformIDs!) + StrRes.online; } } @@ -1496,10 +1524,12 @@ class ChatLogic extends SuperController { if (message.sendID == OpenIM.iMManager.userID) { canRevoke = true; } else { - var list = await LoadingView.singleton - .wrap(asyncFunction: () => OpenIM.iMManager.groupManager.getGroupOwnerAndAdmin(groupID: groupID!)); + var list = await LoadingView.singleton.wrap( + asyncFunction: () => OpenIM.iMManager.groupManager + .getGroupOwnerAndAdmin(groupID: groupID!)); var sender = list.firstWhereOrNull((e) => e.userID == message.sendID); - var revoker = list.firstWhereOrNull((e) => e.userID == OpenIM.iMManager.userID); + var revoker = + list.firstWhereOrNull((e) => e.userID == OpenIM.iMManager.userID); if (revoker != null && sender == null) { canRevoke = true; @@ -1531,7 +1561,8 @@ class ChatLogic extends SuperController { ), ); message.contentType = MessageType.revokeMessageNotification; - message.notificationElem = NotificationElem(detail: jsonEncode(_buildRevokeInfo(message))); + message.notificationElem = + NotificationElem(detail: jsonEncode(_buildRevokeInfo(message))); messageList.refresh(); } catch (e) { IMViews.showToast(e.toString()); @@ -1595,12 +1626,15 @@ class ChatLogic extends SuperController { if (isGroupChat) { if (groupMemberRoleLevel.value == GroupRoleLevel.owner || (groupMemberRoleLevel.value == GroupRoleLevel.admin && - ownerAndAdmin.firstWhereOrNull((element) => element.userID == message.sendID) == null)) { + ownerAndAdmin.firstWhereOrNull( + (element) => element.userID == message.sendID) == + null)) { return true; } } if (message.sendID == OpenIM.iMManager.userID) { - if (DateTime.now().millisecondsSinceEpoch - (message.sendTime ??= 0) < (1000 * 60 * 5)) { + if (DateTime.now().millisecondsSinceEpoch - (message.sendTime ??= 0) < + (1000 * 60 * 5)) { return true; } } @@ -1611,7 +1645,8 @@ class ChatLogic extends SuperController { if (message.status != MessageStatus.succeeded) { return false; } - return message.contentType == MessageType.picture || message.contentType == MessageType.customFace; + return message.contentType == MessageType.picture || + message.contentType == MessageType.customFace; } WillPopCallback? willPop() { @@ -1658,12 +1693,14 @@ class ChatLogic extends SuperController { var data = message.customElem!.data; var map = json.decode(data!); var customType = map['customType']; - return customType == CustomMessageType.deletedByFriend || customType == CustomMessageType.blockedByFriend; + return customType == CustomMessageType.deletedByFriend || + customType == CustomMessageType.blockedByFriend; } return false; } - void sendFriendVerification() => AppNavigator.startSendVerificationApplication(userID: userID); + void sendFriendVerification() => + AppNavigator.startSendVerificationApplication(userID: userID); void _setSdkSyncDataListener() { connectionSub = imLogic.imSdkStatusPublishSubject.listen((value) { @@ -1699,7 +1736,9 @@ class ChatLogic extends SuperController { } bool showBubbleBg(Message message) { - return !isNotificationType(message) && !isFailedHintMessage(message) && !isRevokeMessage(message); + return !isNotificationType(message) && + !isFailedHintMessage(message) && + !isRevokeMessage(message); } bool isRevokeMessage(Message message) { @@ -1748,7 +1787,8 @@ class ChatLogic extends SuperController { } Future _loadHistoryForSyncEnd() async { - final result = await OpenIM.iMManager.messageManager.getAdvancedHistoryMessageList( + final result = + await OpenIM.iMManager.messageManager.getAdvancedHistoryMessageList( conversationID: conversationInfo.conversationID, count: messageList.length < _pageSize ? _pageSize : messageList.length, startMsg: null, diff --git a/mobile-next/lib/pages/chat/chat_view.dart b/mobile-next/lib/pages/chat/chat_view.dart index 0cd84e2..fa505d6 100644 --- a/mobile-next/lib/pages/chat/chat_view.dart +++ b/mobile-next/lib/pages/chat/chat_view.dart @@ -86,13 +86,23 @@ class ChatPage extends StatelessWidget { onTapUserProfile: handleUserProfileTap, ); - void handleUserProfileTap(({String userID, String name, String? faceURL, String? groupID}) userProfile) { - final userInfo = UserInfo(userID: userProfile.userID, nickname: userProfile.name, faceURL: userProfile.faceURL); + void handleUserProfileTap( + ({ + String userID, + String name, + String? faceURL, + String? groupID + }) userProfile) { + final userInfo = UserInfo( + userID: userProfile.userID, + nickname: userProfile.name, + faceURL: userProfile.faceURL); logic.viewUserInfo(userInfo); } Widget? _buildMediaItem(BuildContext context, Message message) { - if (message.contentType != MessageType.picture && message.contentType != MessageType.video) { + if (message.contentType != MessageType.picture && + message.contentType != MessageType.video) { return null; } @@ -125,7 +135,8 @@ class ChatPage extends StatelessWidget { child: Hero( tag: message.clientMsgID!, child: _buildMediaContent(message), - placeholderBuilder: (BuildContext context, Size heroSize, Widget child) => child, + placeholderBuilder: + (BuildContext context, Size heroSize, Widget child) => child, ), ); } @@ -155,7 +166,8 @@ class ChatPage extends StatelessWidget { final content = data['content']; final view = ChatCallItemView(type: type, content: content); return CustomTypeInfo(view); - } else if (viewType == CustomMessageType.deletedByFriend || viewType == CustomMessageType.blockedByFriend) { + } else if (viewType == CustomMessageType.deletedByFriend || + viewType == CustomMessageType.blockedByFriend) { final view = ChatFriendRelationshipAbnormalHintView( name: logic.nickname.value, onTap: logic.sendFriendVerification, @@ -244,10 +256,11 @@ class ChatPage extends StatelessWidget { toolbox: ChatToolBox( onTapAlbum: logic.onTapAlbum, onTapCamera: logic.onTapCamera, - onTapCall: FeatureFlags.livekitCall && logic.isSingleChat ? logic.call : null, + onTapCall: FeatureFlags.livekitCall && logic.isSingleChat + ? logic.call + : null, onTapCard: logic.onTapCarte, onTapFile: logic.onTapFile, - onTapLocation: logic.onTapLocation, ), voiceRecordBar: bar, emojiView: ChatEmojiView( diff --git a/mobile-next/openim_common/lib/openim_common.dart b/mobile-next/openim_common/lib/openim_common.dart index b10ba19..4ba0d20 100644 --- a/mobile-next/openim_common/lib/openim_common.dart +++ b/mobile-next/openim_common/lib/openim_common.dart @@ -76,7 +76,6 @@ export 'src/widgets/chat/chat_voice_record_bar.dart'; export 'src/widgets/chat/chat_voice_record_layout.dart'; export 'src/widgets/chat/chat_voice_record_view.dart'; export 'src/widgets/chat/chat_voice_view.dart'; -export 'src/widgets/chat/chat_webview_map.dart'; export 'src/widgets/chat/new_message_indicator.dart'; export 'src/widgets/chat/water_mark_view.dart'; export 'src/widgets/custom_pop_up_menu.dart'; diff --git a/mobile-next/openim_common/lib/src/res/lang/en_US.dart b/mobile-next/openim_common/lib/src/res/lang/en_US.dart index b7989a0..a9673b7 100644 --- a/mobile-next/openim_common/lib/src/res/lang/en_US.dart +++ b/mobile-next/openim_common/lib/src/res/lang/en_US.dart @@ -71,6 +71,7 @@ const Map en_US = { "video": "Video", "voice": "Voice", "location": "Location", + "locationMessage": "Location", "file": "File", "carte": "Card", "emoji": "Custom Emoji", @@ -120,7 +121,7 @@ const Map en_US = { "cancel": "Cancel", "determine": "OK", "toolboxAlbum": "Album", - "toolboxCall": "Video Call", + "toolboxCall": "Voice Call", "toolboxCamera": "Camera", "toolboxCard": "Card", "toolboxFile": "File", @@ -224,7 +225,7 @@ const Map en_US = { 'position': 'Position', 'personalInfo': 'Personal Info', 'viewDynamics': 'View Dynamics', - 'audioAndVideoCall': 'Call', + 'audioAndVideoCall': 'Voice Call', 'sendMessage': 'Message', 'avatar': 'Avatar', 'name': 'Name', diff --git a/mobile-next/openim_common/lib/src/res/lang/zh_CN.dart b/mobile-next/openim_common/lib/src/res/lang/zh_CN.dart index 011e3aa..99ca674 100644 --- a/mobile-next/openim_common/lib/src/res/lang/zh_CN.dart +++ b/mobile-next/openim_common/lib/src/res/lang/zh_CN.dart @@ -71,6 +71,7 @@ const Map zh_CN = { "video": "视频", "voice": "语音", "location": "位置", + "locationMessage": "位置消息", "file": "文件", "carte": "名片", "emoji": "自定义表情", @@ -120,7 +121,7 @@ const Map zh_CN = { "cancel": "取消", "determine": "确定", "toolboxAlbum": "相册", - "toolboxCall": "视频通话", + "toolboxCall": "语音通话", "toolboxCamera": "拍摄", "toolboxCard": "名片", "toolboxFile": "文件", @@ -224,7 +225,7 @@ const Map zh_CN = { 'position': '职位', 'personalInfo': '个人资料', 'viewDynamics': '查看动态', - 'audioAndVideoCall': '音视频通话', + 'audioAndVideoCall': '语音通话', 'sendMessage': '发消息', 'avatar': '头像', 'name': '姓名', diff --git a/mobile-next/openim_common/lib/src/res/strings.dart b/mobile-next/openim_common/lib/src/res/strings.dart index 3fd375d..6f61091 100644 --- a/mobile-next/openim_common/lib/src/res/strings.dart +++ b/mobile-next/openim_common/lib/src/res/strings.dart @@ -58,7 +58,8 @@ class StrRes { static String get resendVerificationCode => 'resendVerificationCode'.tr; - static String get verificationCodeTimingReminder => 'verificationCodeTimingReminder'.tr; + static String get verificationCodeTimingReminder => + 'verificationCodeTimingReminder'.tr; static String get defaultVerificationCode => 'defaultVerificationCode'.tr; @@ -160,6 +161,8 @@ class StrRes { static String get location => 'location'.tr; + static String get locationMessage => 'locationMessage'.tr; + static String get file => 'file'.tr; static String get carte => 'carte'.tr; @@ -276,7 +279,8 @@ class StrRes { static String get releaseToSend => 'releaseToSend'.tr; - static String get releaseToSendSwipeUpToCancel => 'releaseToSendSwipeUpToCancel'.tr; + static String get releaseToSendSwipeUpToCancel => + 'releaseToSendSwipeUpToCancel'.tr; static String get liftFingerToCancelSend => 'liftFingerToCancelSend'.tr; @@ -704,7 +708,8 @@ class StrRes { static String get confirm => 'confirm'.tr; - static String get confirmTransferGroupToUser => 'confirmTransferGroupToUser'.tr; + static String get confirmTransferGroupToUser => + 'confirmTransferGroupToUser'.tr; static String get removeGroupMember => 'removeGroupMember'.tr; @@ -970,7 +975,8 @@ class StrRes { static String get confirmTheChanges => 'confirmTheChanges'.tr; - static String get invitesYouToVideoConference => 'invitesYouToVideoConference'.tr; + static String get invitesYouToVideoConference => + 'invitesYouToVideoConference'.tr; static String get over => 'over'.tr; @@ -1074,7 +1080,8 @@ class StrRes { static String get sendAnother => 'sendAnother'.tr; - static String get confirmDelTagNotificationHint => 'confirmDelTagNotificationHint'.tr; + static String get confirmDelTagNotificationHint => + 'confirmDelTagNotificationHint'.tr; static String get contentNotBlank => 'contentNotBlank'.tr; @@ -1086,11 +1093,13 @@ class StrRes { static String get groupRequestHandled => 'groupRequestHandled'.tr; - static String get burnAfterReadingDescription => 'burnAfterReadingDescription'.tr; + static String get burnAfterReadingDescription => + 'burnAfterReadingDescription'.tr; static String get periodicallyDeleteMessage => 'periodicallyDeleteMessage'.tr; - static String get periodicallyDeleteMessageDescription => 'periodicallyDeleteMessageDescription'.tr; + static String get periodicallyDeleteMessageDescription => + 'periodicallyDeleteMessageDescription'.tr; static String get nDay => 'nDay'.tr; diff --git a/mobile-next/openim_common/lib/src/utils/permissions.dart b/mobile-next/openim_common/lib/src/utils/permissions.dart index 8f31cdc..08a3bbe 100644 --- a/mobile-next/openim_common/lib/src/utils/permissions.dart +++ b/mobile-next/openim_common/lib/src/utils/permissions.dart @@ -10,10 +10,6 @@ import 'package:sprintf/sprintf.dart'; class Permissions { Permissions._(); - static Future checkSystemAlertWindow() async { - return Permission.systemAlertWindow.isGranted; - } - static Future checkStorage() async { return await Permission.storage.isGranted; } @@ -22,7 +18,8 @@ class Permissions { if (await Permission.camera.request().isGranted) { onGranted?.call(); } - if (await Permission.camera.isPermanentlyDenied || await Permission.camera.isDenied) { + if (await Permission.camera.isPermanentlyDenied || + await Permission.camera.isDenied) { _showPermissionDeniedDialog(Permission.camera.title); } } @@ -52,7 +49,8 @@ class Permissions { if (await Permission.manageExternalStorage.request().isGranted) { onGranted?.call(); } - if (await Permission.storage.isPermanentlyDenied || await Permission.storage.isDenied) { + if (await Permission.storage.isPermanentlyDenied || + await Permission.storage.isDenied) { _showPermissionDeniedDialog(Permission.storage.title); } } @@ -61,25 +59,18 @@ class Permissions { if (await Permission.microphone.request().isGranted) { onGranted?.call(); } - if (await Permission.microphone.isPermanentlyDenied || await Permission.microphone.isDenied) { + if (await Permission.microphone.isPermanentlyDenied || + await Permission.microphone.isDenied) { _showPermissionDeniedDialog(Permission.microphone.title); } } - static void location(Function()? onGranted) async { - if (await Permission.location.request().isGranted) { - onGranted?.call(); - } - if (await Permission.location.isPermanentlyDenied || await Permission.location.isDenied) { - _showPermissionDeniedDialog(Permission.location.title); - } - } - static void speech(Function()? onGranted) async { if (await Permission.speech.request().isGranted) { onGranted?.call(); } - if (await Permission.speech.isPermanentlyDenied || await Permission.speech.isDenied) { + if (await Permission.speech.isPermanentlyDenied || + await Permission.speech.isDenied) { _showPermissionDeniedDialog(Permission.speech.title); } } @@ -93,7 +84,8 @@ class Permissions { if (await Permission.photos.request().isGranted) { onGranted?.call(); } - if (await Permission.photos.isPermanentlyDenied || await Permission.photos.isDenied) { + if (await Permission.photos.isPermanentlyDenied || + await Permission.photos.isDenied) { _showPermissionDeniedDialog(Permission.photos.title); } } @@ -101,7 +93,8 @@ class Permissions { if (await Permission.photos.request().isGranted) { onGranted?.call(); } - if (await Permission.photos.isPermanentlyDenied || await Permission.photos.isDenied) { + if (await Permission.photos.isPermanentlyDenied || + await Permission.photos.isDenied) { _showPermissionDeniedDialog(Permission.photos.title); } } @@ -111,20 +104,14 @@ class Permissions { if (await Permission.notification.request().isGranted) { return true; } - if (await Permission.notification.isPermanentlyDenied || await Permission.notification.isDenied) { + if (await Permission.notification.isPermanentlyDenied || + await Permission.notification.isDenied) { _showPermissionDeniedDialog(Permission.notification.title); } return false; } - static void ignoreBatteryOptimizations(Function()? onGranted) async { - if (await Permission.ignoreBatteryOptimizations.request().isGranted) { - onGranted?.call(); - } - if (await Permission.ignoreBatteryOptimizations.isPermanentlyDenied) {} - } - static void cameraAndMicrophone(Function()? onGranted) async { final permissions = [ Permission.camera, @@ -214,7 +201,8 @@ class Permissions { } } - static Future> request(List permissions) async { + static Future> request( + List permissions) async { Map statuses = await permissions.request(); return statuses; } diff --git a/mobile-next/openim_common/lib/src/utils/utils.dart b/mobile-next/openim_common/lib/src/utils/utils.dart index ff87306..df21112 100644 --- a/mobile-next/openim_common/lib/src/utils/utils.dart +++ b/mobile-next/openim_common/lib/src/utils/utils.dart @@ -45,7 +45,8 @@ class IntervalDo { void run({required Function() fuc, int milliseconds = 0}) { DateTime now = DateTime.now(); - if (null == last || now.difference(last ?? now).inMilliseconds > milliseconds) { + if (null == last || + now.difference(last ?? now).inMilliseconds > milliseconds) { last = now; fuc(); } @@ -153,12 +154,14 @@ class IMUtils { } } - static String? emptyStrToNull(String? str) => (null != str && str.trim().isEmpty) ? null : str; + static String? emptyStrToNull(String? str) => + (null != str && str.trim().isEmpty) ? null : str; static bool isNotNullEmptyStr(String? str) => null != str && "" != str.trim(); static bool isChinaMobile(String mobile) { - RegExp exp = RegExp(r'^((13[0-9])|(14[0-9])|(15[0-9])|(16[0-9])|(17[0-9])|(18[0-9])|(19[0-9]))\d{8}$'); + RegExp exp = RegExp( + r'^((13[0-9])|(14[0-9])|(15[0-9])|(16[0-9])|(17[0-9])|(18[0-9])|(19[0-9]))\d{8}$'); return exp.hasMatch(mobile); } @@ -172,14 +175,17 @@ class IMUtils { final directory = await createTempDir(dir: 'video'); final targetPath = '$directory/$name'; - final String ffmpegCommand = '-i $path -ss 0 -vframes 1 -q:v 15 -y $targetPath'; + final String ffmpegCommand = + '-i $path -ss 0 -vframes 1 -q:v 15 -y $targetPath'; final session = await FFmpegKit.execute(ffmpegCommand); - final state = FFmpegKitConfig.sessionStateToString(await session.getState()); + final state = + FFmpegKitConfig.sessionStateToString(await session.getState()); final returnCode = await session.getReturnCode(); if (state == SessionState.failed || !ReturnCode.isSuccess(returnCode)) { - Logger().printError(info: "Command failed. Please check output for the details."); + Logger().printError( + info: "Command failed. Please check output for the details."); } session.cancel(); @@ -195,11 +201,14 @@ class IMUtils { final output = await FFprobeKit.getMediaInformation(path); final streams = output.getMediaInformation()?.getStreams(); - final isH264 = streams?.any((element) => element.getCodec()?.contains('h264') == true) ?? false; + final isH264 = streams + ?.any((element) => element.getCodec()?.contains('h264') == true) ?? + false; final size = output.getMediaInformation()?.getSize() ?? '0'; output.cancel(); - final audioStream = streams?.firstWhereOrNull((e) => e.getType()?.contains('audio') == true); + final audioStream = streams + ?.firstWhereOrNull((e) => e.getType()?.contains('audio') == true); final isAAC = audioStream?.getCodec()?.toLowerCase() != 'aac'; String ffmpegCommand = @@ -210,7 +219,8 @@ class IMUtils { if (isAAC) { return File(targetPath); } else { - ffmpegCommand = '-i $path -c:v copy -c:a aac -q:a 2 -threads 4 $targetPath'; + ffmpegCommand = + '-i $path -c:v copy -c:a aac -q:a 2 -threads 4 $targetPath'; } } @@ -220,7 +230,8 @@ class IMUtils { return File(targetPath); } else { - ffmpegCommand = '-i $path -c:v copy -c:a aac -q:a 2 -threads 4 $targetPath'; + ffmpegCommand = + '-i $path -c:v copy -c:a aac -q:a 2 -threads 4 $targetPath'; } } @@ -230,7 +241,8 @@ class IMUtils { final returnCode = await session.getReturnCode(); if (state == SessionState.failed || !ReturnCode.isSuccess(returnCode)) { - Logger().printError(info: "Command failed. Please check output for the details."); + Logger().printError( + info: "Command failed. Please check output for the details."); file.copySync(targetPath); return File(targetPath); @@ -241,7 +253,8 @@ class IMUtils { return File(targetPath); } - static Future compressImageAndGetFile(File file, {int quality = 80}) async { + static Future compressImageAndGetFile(File file, + {int quality = 80}) async { var path = file.path; var name = path.substring(path.lastIndexOf("/") + 1).toLowerCase(); @@ -360,14 +373,16 @@ class IMUtils { String? externalStorageDirPath; if (Platform.isAndroid) { try { - externalStorageDirPath = await PathProviderPlatform.instance.getDownloadsPath(); + externalStorageDirPath = + await PathProviderPlatform.instance.getDownloadsPath(); } catch (err, st) { Logger.print('failed to get downloads path: $err, $st'); final directory = await getExternalStorageDirectory(); externalStorageDirPath = directory?.path; } } else if (Platform.isIOS) { - externalStorageDirPath = (await getApplicationDocumentsDirectory()).absolute.path; + externalStorageDirPath = + (await getApplicationDocumentsDirectory()).absolute.path; } return externalStorageDirPath!; } @@ -384,7 +399,8 @@ class IMUtils { return path; } - static List calChatTimeInterval(List list, {bool calculate = true}) { + static List calChatTimeInterval(List list, + {bool calculate = true}) { if (!calculate) return list; var milliseconds = list.firstOrNull?.sendTime; if (null == milliseconds) return list; @@ -421,7 +437,9 @@ class IMUtils { final yesterday = now.subtract(Duration(days: 1)); if (isSameDay(dateTime, yesterday)) { - return isChinese ? '昨天 ${formatter.format(dateTime)}' : 'Yesterday ${formatter.format(dateTime)}'; + return isChinese + ? '昨天 ${formatter.format(dateTime)}' + : 'Yesterday ${formatter.format(dateTime)}'; } if (isSameWeek(dateTime, now)) { @@ -448,13 +466,16 @@ class IMUtils { } static bool isSameDay(DateTime date1, DateTime date2) { - return date1.year == date2.year && date1.month == date2.month && date1.day == date2.day; + return date1.year == date2.year && + date1.month == date2.month && + date1.day == date2.day; } static bool isSameWeek(DateTime date1, DateTime date2) { final weekStart = date2.subtract(Duration(days: date2.weekday - 1)); final weekEnd = weekStart.add(Duration(days: 6)); - return date1.isAfter(weekStart.subtract(Duration(days: 1))) && date1.isBefore(weekEnd.add(Duration(days: 1))); + return date1.isAfter(weekStart.subtract(Duration(days: 1))) && + date1.isBefore(weekEnd.add(Duration(days: 1))); } static String getCallTimeline(int milliseconds) { @@ -530,7 +551,8 @@ class IMUtils { return "${_combTime(days, StrRes.day)}${_combTime(hours, StrRes.hours)}${_combTime(minutes, StrRes.minute)}${_combTime(seconds, StrRes.seconds)}"; } - static String _combTime(int value, String unit) => value > 0 ? '$value$unit' : ''; + static String _combTime(int value, String unit) => + value > 0 ? '$value$unit' : ''; static String calContent({ required String content, @@ -566,9 +588,11 @@ class IMUtils { int maxLines = 1, double maxWidth = double.infinity, }) { - final TextPainter textPainter = - TextPainter(text: TextSpan(text: text, style: style), maxLines: maxLines, textDirection: TextDirection.ltr) - ..layout(minWidth: 0, maxWidth: maxWidth); + final TextPainter textPainter = TextPainter( + text: TextSpan(text: text, style: style), + maxLines: maxLines, + textDirection: TextDirection.ltr) + ..layout(minWidth: 0, maxWidth: maxWidth); return textPainter.size; } @@ -578,7 +602,10 @@ class IMUtils { int maxLines = 1, double maxWidth = double.infinity, }) => - TextPainter(text: TextSpan(text: text, style: style), maxLines: maxLines, textDirection: TextDirection.ltr) + TextPainter( + text: TextSpan(text: text, style: style), + maxLines: maxLines, + textDirection: TextDirection.ltr) ..layout(minWidth: 0, maxWidth: maxWidth); static bool isUrlValid(String? url) { @@ -600,11 +627,16 @@ class IMUtils { } static String getGroupMemberShowName(GroupMembersInfo membersInfo) { - return membersInfo.userID == OpenIM.iMManager.userID ? StrRes.you : membersInfo.nickname!; + return membersInfo.userID == OpenIM.iMManager.userID + ? StrRes.you + : membersInfo.nickname!; } static String getShowName(String? userID, String? nickname) { - return (userID == OpenIM.iMManager.userID ? OpenIM.iMManager.userInfo.nickname : nickname) ?? ''; + return (userID == OpenIM.iMManager.userID + ? OpenIM.iMManager.userInfo.nickname + : nickname) ?? + ''; } static String? parseNtf( @@ -628,7 +660,8 @@ class IMUtils { case MessageType.groupInfoSetNotification: { final ntf = GroupNotification.fromJson(map); - if (ntf.group?.notification != null && ntf.group!.notification!.isNotEmpty) { + if (ntf.group?.notification != null && + ntf.group!.notification!.isNotEmpty) { return isConversation ? ntf.group!.notification! : null; } @@ -649,8 +682,12 @@ class IMUtils { final ntf = InvitedJoinGroupNotification.fromJson(map); final label = StrRes.invitedJoinGroupNtf; - final b = ntf.invitedUserList?.map((e) => getGroupMemberShowName(e)).toList().join('、'); - text = sprintf(label, [getGroupMemberShowName(ntf.opUser!), b ?? '']); + final b = ntf.invitedUserList + ?.map((e) => getGroupMemberShowName(e)) + .toList() + .join('、'); + text = sprintf( + label, [getGroupMemberShowName(ntf.opUser!), b ?? '']); } break; case MessageType.memberKickedNotification: @@ -658,7 +695,10 @@ class IMUtils { final ntf = KickedGroupMemeberNotification.fromJson(map); final label = StrRes.kickedGroupNtf; - final b = ntf.kickedUserList!.map((e) => getGroupMemberShowName(e)).toList().join('、'); + final b = ntf.kickedUserList! + .map((e) => getGroupMemberShowName(e)) + .toList() + .join('、'); text = sprintf(label, [b, getGroupMemberShowName(ntf.opUser!)]); } break; @@ -683,7 +723,10 @@ class IMUtils { final ntf = GroupRightsTransferNoticication.fromJson(map); final label = StrRes.transferredGroupNtf; - text = sprintf(label, [getGroupMemberShowName(ntf.opUser!), getGroupMemberShowName(ntf.newGroupOwner!)]); + text = sprintf(label, [ + getGroupMemberShowName(ntf.opUser!), + getGroupMemberShowName(ntf.newGroupOwner!) + ]); } break; case MessageType.groupMemberMutedNotification: @@ -692,8 +735,11 @@ class IMUtils { final label = StrRes.muteMemberNtf; final c = ntf.mutedSeconds; - text = sprintf( - label, [getGroupMemberShowName(ntf.mutedUser!), getGroupMemberShowName(ntf.opUser!), mutedTime(c!)]); + text = sprintf(label, [ + getGroupMemberShowName(ntf.mutedUser!), + getGroupMemberShowName(ntf.opUser!), + mutedTime(c!) + ]); } break; case MessageType.groupMemberCancelMutedNotification: @@ -701,7 +747,10 @@ class IMUtils { final ntf = MuteMemberNotification.fromJson(map); final label = StrRes.muteCancelMemberNtf; - text = sprintf(label, [getGroupMemberShowName(ntf.mutedUser!), getGroupMemberShowName(ntf.opUser!)]); + text = sprintf(label, [ + getGroupMemberShowName(ntf.mutedUser!), + getGroupMemberShowName(ntf.opUser!) + ]); } break; case MessageType.groupMutedNotification: @@ -737,7 +786,8 @@ class IMUtils { break; case MessageType.groupMemberInfoChangedNotification: final ntf = GroupMemberInfoChangedNotification.fromJson(map); - text = sprintf(StrRes.memberInfoChangedNtf, [getGroupMemberShowName(ntf.opUser!)]); + text = sprintf(StrRes.memberInfoChangedNtf, + [getGroupMemberShowName(ntf.opUser!)]); break; case MessageType.groupInfoSetAnnouncementNotification: if (isConversation) { @@ -747,7 +797,8 @@ class IMUtils { break; case MessageType.groupInfoSetNameNotification: final ntf = GroupNotification.fromJson(map); - text = sprintf(StrRes.whoModifyGroupName, [getGroupMemberShowName(ntf.opUser!), ntf.group?.groupName]); + text = sprintf(StrRes.whoModifyGroupName, + [getGroupMemberShowName(ntf.opUser!), ntf.group?.groupName]); break; } } @@ -851,7 +902,8 @@ class IMUtils { switch (customType) { case CustomMessageType.call: var type = map['data']['type']; - content = '[${type == 'video' ? StrRes.callVideo : StrRes.callVoice}]'; + content = + '[${type == 'video' ? StrRes.callVideo : StrRes.callVoice}]'; break; case CustomMessageType.emoji: content = '[${StrRes.emoji}]'; @@ -924,7 +976,8 @@ class IMUtils { switch (state) { case 'beHangup': case 'hangup': - content = sprintf(StrRes.callDuration, [seconds2HMS(duration)]); + content = + sprintf(StrRes.callDuration, [seconds2HMS(duration)]); break; case 'cancel': content = StrRes.cancelled; @@ -991,8 +1044,11 @@ class IMUtils { final atUserInfos = message.atTextElem!.atUsersInfo!; for (final userID in atUserIDs) { - final groupNickname = - (newMapping[userID] ?? atUserInfos.firstWhere((e) => e.atUserID == userID).groupNickname) ?? userID; + final groupNickname = (newMapping[userID] ?? + atUserInfos + .firstWhere((e) => e.atUserID == userID) + .groupNickname) ?? + userID; mapping[userID] = getAtNickname(userID, groupNickname); } } @@ -1065,20 +1121,27 @@ class IMUtils { previewUrlPicture( [ MediaSource( - url: message.pictureElem!.sourcePicture!.url!, thumbnail: message.pictureElem!.snapshotPicture!.url!) + url: message.pictureElem!.sourcePicture!.url!, + thumbnail: message.pictureElem!.snapshotPicture!.url!) ], currentIndex: 0, ); } else { final picList = allList - .where((element) => element.contentType == MessageType.picture || element.contentType == MessageType.video) + .where((element) => + element.contentType == MessageType.picture || + element.contentType == MessageType.video) .toList(); final index = picList.indexOf(message); final urls = picList.map((e) { if (e.contentType == MessageType.picture) { - return MediaSource(url: e.pictureElem!.sourcePicture!.url!, thumbnail: e.pictureElem!.snapshotPicture!.url!); + return MediaSource( + url: e.pictureElem!.sourcePicture!.url!, + thumbnail: e.pictureElem!.snapshotPicture!.url!); } else { - return MediaSource(url: e.videoElem!.videoUrl!, thumbnail: e.videoElem!.snapshotUrl!); + return MediaSource( + url: e.videoElem!.videoUrl!, + thumbnail: e.videoElem!.snapshotUrl!); } }).toList(); previewUrlPicture(urls, currentIndex: index == -1 ? 0 : index); @@ -1106,7 +1169,8 @@ class IMUtils { const begin = Offset(0.0, 1.0); const end = Offset.zero; const curve = Curves.easeOut; - final tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); + final tween = + Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); final offsetAnimation = animation.drive(tween); return SlideTransition( @@ -1136,7 +1200,8 @@ class IMUtils { final isExitCachePath = await isExitFile(cachePath); - Logger.print('isExitSourcePath:$isExitSourcePath, isExitCachePath:$isExitCachePath, cachePath:$cachePath'); + Logger.print( + 'isExitSourcePath:$isExitSourcePath, isExitCachePath:$isExitCachePath, cachePath:$cachePath'); final isExitNetwork = isUrlValid(url); String? availablePath; @@ -1145,9 +1210,11 @@ class IMUtils { } else if (isExitCachePath) { availablePath = cachePath; } - final isAvailableFileSize = - isExitSourcePath || isExitCachePath ? (await File(availablePath!).length() == fileSize) : false; - Logger.print('previewFile isAvailableFileSize: $isAvailableFileSize isExitNetwork: $isExitNetwork'); + final isAvailableFileSize = isExitSourcePath || isExitCachePath + ? (await File(availablePath!).length() == fileSize) + : false; + Logger.print( + 'previewFile isAvailableFileSize: $isAvailableFileSize isExitNetwork: $isExitNetwork'); if (isAvailableFileSize) { String? mimeType = lookupMimeType(fileName ?? ''); if (null != mimeType && allowVideoType(mimeType)) { @@ -1159,7 +1226,9 @@ class IMUtils { previewPicture(Message() ..clientMsgID = message.clientMsgID ..contentType = MessageType.picture - ..pictureElem = PictureElem(sourcePath: availablePath, sourcePicture: PictureInfo(url: url))); + ..pictureElem = PictureElem( + sourcePath: availablePath, + sourcePicture: PictureInfo(url: url))); } else { openFileByOtherApp(availablePath); } @@ -1183,7 +1252,8 @@ class IMUtils { bool onlySave = false, ValueChanged? onOperate}) { void saveVideo(BuildContext ctx, String url, {int? length}) async { - final cachedVideoControllerService = CachedVideoControllerService(DefaultCacheManager()); + final cachedVideoControllerService = + CachedVideoControllerService(DefaultCacheManager()); final cached = await cachedVideoControllerService.getCacheFile(url); if (cached != null) { @@ -1196,7 +1266,8 @@ class IMUtils { } else { LoadingView.singleton.show(); - final downloader = MultiThreadDownloader(url: url, fileName: url.split('/').last, length: length); + final downloader = MultiThreadDownloader( + url: url, fileName: url.split('/').last, length: length); callback(EasyLoadingStatus status) { if (status == EasyLoadingStatus.dismiss) { @@ -1235,7 +1306,8 @@ class IMUtils { switch (type) { case OperateType.save: if (msg.videoElem != null) { - saveVideo(context, msg.videoElem!.videoUrl!, length: msg.videoElem!.videoSize); + saveVideo(context, msg.videoElem!.videoUrl!, + length: msg.videoElem!.videoSize); } else { final url = msg.pictureElem?.sourcePicture?.url; if (url?.isNotEmpty == true) { @@ -1254,14 +1326,18 @@ class IMUtils { final sources = message.isVideoType ? MediaSource( url: message.videoElem?.videoUrl, - thumbnail: message.videoElem!.snapshotUrl?.adjustThumbnailAbsoluteString(960) ?? '', + thumbnail: message.videoElem!.snapshotUrl + ?.adjustThumbnailAbsoluteString(960) ?? + '', file: File(message.videoElem!.videoPath!), tag: message.clientMsgID, isVideo: true, ) : MediaSource( url: message.pictureElem?.sourcePicture?.url, - thumbnail: message.pictureElem!.snapshotPicture?.url?.adjustThumbnailAbsoluteString(960) ?? '', + thumbnail: message.pictureElem!.snapshotPicture?.url + ?.adjustThumbnailAbsoluteString(960) ?? + '', file: File(message.pictureElem!.sourcePath!), tag: message.clientMsgID, ); @@ -1326,19 +1402,36 @@ class IMUtils { } static void previewLocation(Message message) { - var location = message.locationElem; - Map detail = json.decode(location!.description!); - Logger.print('previewLocation ${location.latitude} ${location.longitude}'); - Get.to( - () => MapView( + try { + final location = message.locationElem; + if (location == null || + location.latitude == null || + location.longitude == null) { + IMViews.showToast(StrRes.locationMessage); + return; + } + final data = LocationBubbleData.parse( + description: location.description ?? '', latitude: location.latitude!, longitude: location.longitude!, - address1: detail['name'], - address2: detail['addr'], - ), - transition: Transition.cupertino, - popGesture: true, - ); + fallbackTitle: StrRes.locationMessage, + ); + Logger.print( + 'previewLocation ${location.latitude} ${location.longitude}'); + Get.to( + () => MapView( + latitude: location.latitude!, + longitude: location.longitude!, + address1: data.title, + address2: data.address, + ), + transition: Transition.cupertino, + popGesture: true, + ); + } catch (e, s) { + Logger.print('previewLocation failed: $e $s'); + IMViews.showToast(StrRes.locationMessage); + } } static void previewCarteMessage( @@ -1353,7 +1446,8 @@ class IMUtils { Function(Message msg)? meetingItemClick, VoidCallback? onForward, }) async { - if (message.contentType == MessageType.picture || message.contentType == MessageType.video) { + if (message.contentType == MessageType.picture || + message.contentType == MessageType.video) { previewMediaFile( context: Get.context!, message: message, @@ -1464,15 +1558,18 @@ class IMUtils { if (mimeType == 'application/pdf') { return ImageRes.filePdf; } else if (mimeType == 'application/msword' || - mimeType == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') { + mimeType == + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') { return ImageRes.fileWord; } else if (mimeType == 'application/vnd.ms-excel' || - mimeType == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') { + mimeType == + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') { return ImageRes.fileExcel; } else if (mimeType == 'application/vnd.ms-powerpoint') { return ImageRes.filePpt; } else if (mimeType.startsWith('audio/')) { - } else if (mimeType == 'application/zip' || mimeType == 'application/x-rar-compressed') { + } else if (mimeType == 'application/zip' || + mimeType == 'application/x-rar-compressed') { return ImageRes.fileZip; } /*else if (mimeType.startsWith('audio/')) { @@ -1516,7 +1613,10 @@ class IMUtils { final checkedList = []; final values = result.values; for (final value in values) { - if (value is UserInfo || value is FriendInfo || value is UserFullInfo || value is ISUserInfo) { + if (value is UserInfo || + value is FriendInfo || + value is UserFullInfo || + value is ISUserInfo) { checkedList.add(value.userID!); } } @@ -1531,7 +1631,10 @@ class IMUtils { for (var item in checkedList) { if (item is ConversationInfo) { checkedMap[item.isSingleChat ? item.userID! : item.groupID!] = item; - } else if (item is UserInfo || item is UserFullInfo || item is ISUserInfo || item is FriendInfo) { + } else if (item is UserInfo || + item is UserFullInfo || + item is ISUserInfo || + item is FriendInfo) { checkedMap[item.userID!] = item; } else if (item is GroupInfo) { checkedMap[item.groupID] = item; @@ -1540,10 +1643,14 @@ class IMUtils { return checkedMap; } - static List> convertCheckedListToForwardObj(List checkedList) { + static List> convertCheckedListToForwardObj( + List checkedList) { final map = >[]; for (var item in checkedList) { - if (item is UserInfo || item is UserFullInfo || item is ISUserInfo || item is FriendInfo) { + if (item is UserInfo || + item is UserFullInfo || + item is ISUserInfo || + item is FriendInfo) { map.add({'nickname': item.nickname, 'faceURL': item.faceURL}); } else if (item is GroupInfo) { map.add({'nickname': item.groupName, 'faceURL': item.faceURL}); @@ -1555,7 +1662,10 @@ class IMUtils { } static String? convertCheckedToUserID(dynamic info) { - if (info is UserInfo || info is UserFullInfo || info is ISUserInfo || info is FriendInfo) { + if (info is UserInfo || + info is UserFullInfo || + info is ISUserInfo || + info is FriendInfo) { return info.userID; } else if (info is ConversationInfo) { return info.userID; @@ -1570,14 +1680,18 @@ class IMUtils { } else if (info is ConversationInfo) { return info.groupID; } - + return null; } - static List> convertCheckedListToShare(Iterable checkedList) { + static List> convertCheckedListToShare( + Iterable checkedList) { final map = >[]; for (var item in checkedList) { - if (item is UserInfo || item is UserFullInfo || item is ISUserInfo || item is FriendInfo) { + if (item is UserInfo || + item is UserFullInfo || + item is ISUserInfo || + item is FriendInfo) { map.add({'userID': item.userID, 'groupID': null}); } else if (item is GroupInfo) { map.add({'userID': null, 'groupID': item.groupID}); @@ -1612,8 +1726,10 @@ class IMUtils { return formatDateMs(ms, format: isZH ? 'yyyy年MM月dd' : 'yyyy/MM/dd'); } - static Future checkingBiometric(LocalAuthentication auth) => auth.authenticate( - localizedReason: 'Scan your fingerprint (or face or other) to authenticate.', + static Future checkingBiometric(LocalAuthentication auth) => + auth.authenticate( + localizedReason: + 'Scan your fingerprint (or face or other) to authenticate.', options: const AuthenticationOptions( biometricOnly: true, ), @@ -1636,7 +1752,8 @@ class IMUtils { goToSettingsButton: 'Go to settings', goToSettingsDescription: 'No biometric authentication is set up on your device. Please enable Touch ID or Face ID on your phone.', - lockOut: 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.', + lockOut: + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.', ), ], ); @@ -1664,11 +1781,15 @@ class IMUtils { r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d\S]{6,20}$', ).hasMatch(password); - static TextInputFormatter getPasswordFormatter() => FilteringTextInputFormatter.allow( + static TextInputFormatter getPasswordFormatter() => + FilteringTextInputFormatter.allow( RegExp(r'[a-zA-Z0-9\S]'), ); - static Future requestBackgroundPermission({required String title, required String text, bool isRetry = false}) async { + static Future requestBackgroundPermission( + {required String title, + required String text, + bool isRetry = false}) async { if (!Platform.isAndroid) { return; } @@ -1680,7 +1801,8 @@ class IMUtils { notificationTitle: title, notificationText: text, notificationImportance: AndroidNotificationImportance.normal, - notificationIcon: const AndroidResource(name: 'ic_launcher', defType: 'mipmap'), + notificationIcon: const AndroidResource( + name: 'ic_launcher', defType: 'mipmap'), shouldRequestBatteryOptimizationsOff: false)); } if (hasPermissions && !FlutterBackground.isBackgroundExecutionEnabled) { @@ -1689,7 +1811,9 @@ class IMUtils { } catch (e) { if (!isRetry) { return await Future.delayed( - const Duration(seconds: 1), () => requestBackgroundPermission(title: title, text: text, isRetry: true)); + const Duration(seconds: 1), + () => requestBackgroundPermission( + title: title, text: text, isRetry: true)); } } } @@ -1698,5 +1822,6 @@ class IMUtils { extension PlatformExt on Platform { static bool get isMobile => Platform.isIOS || Platform.isAndroid; - static bool get isDesktop => Platform.isLinux || Platform.isMacOS || Platform.isWindows; + static bool get isDesktop => + Platform.isLinux || Platform.isMacOS || Platform.isWindows; } diff --git a/mobile-next/openim_common/lib/src/widgets/button.dart b/mobile-next/openim_common/lib/src/widgets/button.dart index 5a7f57b..613f0b1 100644 --- a/mobile-next/openim_common/lib/src/widgets/button.dart +++ b/mobile-next/openim_common/lib/src/widgets/button.dart @@ -38,7 +38,9 @@ class Button extends StatelessWidget { child: Ink( height: height ?? 44.h, decoration: BoxDecoration( - color: enabled ? enabledColor ?? Styles.c_0089FF : disabledColor ?? Styles.c_0089FF_opacity50, + color: enabled + ? enabledColor ?? Styles.c_0089FF + : disabledColor ?? Styles.c_0089FF_opacity50, borderRadius: BorderRadius.circular(radius ?? 4.r), ), child: InkWell( @@ -78,7 +80,7 @@ class ImageTextButton extends StatelessWidget { final Function()? onTap; ImageTextButton.call({super.key, this.onTap}) - : icon = ImageRes.audioAndVideoCall, + : icon = ImageRes.callVoice, text = StrRes.audioAndVideoCall, color = Styles.c_FFFFFF, textStyle = null, diff --git a/mobile-next/openim_common/lib/src/widgets/chat/chat_location_view.dart b/mobile-next/openim_common/lib/src/widgets/chat/chat_location_view.dart index 8d5c7a0..8a2a09c 100644 --- a/mobile-next/openim_common/lib/src/widgets/chat/chat_location_view.dart +++ b/mobile-next/openim_common/lib/src/widgets/chat/chat_location_view.dart @@ -4,6 +4,48 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:openim_common/openim_common.dart'; +class LocationBubbleData { + const LocationBubbleData({ + required this.title, + required this.address, + this.thumbnailUrl, + }); + + final String title; + final String address; + final String? thumbnailUrl; + + static LocationBubbleData parse({ + required String description, + required double latitude, + required double longitude, + String fallbackTitle = '位置消息', + }) { + Map? map; + try { + final decoded = json.decode(description); + if (decoded is Map) { + map = decoded.map((key, value) => MapEntry(key.toString(), value)); + } + } catch (_) {} + + final name = map?['name']?.toString().trim() ?? ''; + final addr = map?['addr']?.toString().trim() ?? ''; + final url = map?['url']?.toString().trim() ?? ''; + final coord = + '${latitude.toStringAsFixed(5)}, ${longitude.toStringAsFixed(5)}'; + final raw = description.trim(); + + return LocationBubbleData( + title: name.isNotEmpty ? name : fallbackTitle, + address: addr.isNotEmpty + ? addr + : (map == null && raw.isNotEmpty ? raw : coord), + thumbnailUrl: url.isNotEmpty ? url : null, + ); + } +} + class ChatLocationView extends StatelessWidget { const ChatLocationView({ Key? key, @@ -14,53 +56,62 @@ class ChatLocationView extends StatelessWidget { final String description; final double latitude; final double longitude; - final _decoder = const JsonDecoder(); @override Widget build(BuildContext context) { - try { - final map = _decoder.convert(description); - String url = map['url'] ?? ''; - String name = map['name'] ?? ''; - String addr = map['addr'] ?? ''; - return Container( - width: locationWidth, - height: 130.h, - decoration: BoxDecoration( - color: Styles.c_FFFFFF, - border: Border.all(color: Styles.c_E8EAEF, width: 1), - borderRadius: BorderRadius.circular(6.r), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 4.verticalSpace, - Padding( - padding: EdgeInsets.symmetric(horizontal: 4.w), - child: name.toText - ..style = Styles.ts_0C1C33_14sp - ..maxLines = 1 - ..overflow = TextOverflow.ellipsis, - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 4.w), - child: addr.toText - ..style = Styles.ts_8E9AB0_12sp - ..maxLines = 1 - ..overflow = TextOverflow.ellipsis, - ), - 2.verticalSpace, - Expanded( - child: ImageUtil.networkImage( - url: url, - width: locationWidth, - fit: BoxFit.cover, - ), - ), - ], - ), - ); - } catch (e) {} - return Container(); + final data = LocationBubbleData.parse( + description: description, + latitude: latitude, + longitude: longitude, + fallbackTitle: StrRes.locationMessage, + ); + return Container( + width: locationWidth, + height: 130.h, + decoration: BoxDecoration( + color: Styles.c_FFFFFF, + border: Border.all(color: Styles.c_E8EAEF, width: 1), + borderRadius: BorderRadius.circular(6.r), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 4.verticalSpace, + Padding( + padding: EdgeInsets.symmetric(horizontal: 4.w), + child: data.title.toText + ..style = Styles.ts_0C1C33_14sp + ..maxLines = 1 + ..overflow = TextOverflow.ellipsis, + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 4.w), + child: data.address.toText + ..style = Styles.ts_8E9AB0_12sp + ..maxLines = 1 + ..overflow = TextOverflow.ellipsis, + ), + 2.verticalSpace, + Expanded( + child: data.thumbnailUrl == null + ? ColoredBox( + color: Styles.c_F0F2F6, + child: Center( + child: Icon( + Icons.location_on_outlined, + color: Styles.c_8E9AB0, + size: 28.w, + ), + ), + ) + : ImageUtil.networkImage( + url: data.thumbnailUrl!, + width: locationWidth, + fit: BoxFit.cover, + ), + ), + ], + ), + ); } } diff --git a/mobile-next/openim_common/lib/src/widgets/chat/chat_toolbox.dart b/mobile-next/openim_common/lib/src/widgets/chat/chat_toolbox.dart index 8477f18..299a93e 100644 --- a/mobile-next/openim_common/lib/src/widgets/chat/chat_toolbox.dart +++ b/mobile-next/openim_common/lib/src/widgets/chat/chat_toolbox.dart @@ -10,7 +10,6 @@ class ChatToolBox extends StatelessWidget { this.onTapCamera, this.onTapCard, this.onTapFile, - this.onTapLocation, this.onTapDirectionalMessage, }); final Function()? onTapAlbum; @@ -18,7 +17,6 @@ class ChatToolBox extends StatelessWidget { final Function()? onTapCall; final Function()? onTapFile; final Function()? onTapCard; - final Function()? onTapLocation; final VoidCallback? onTapDirectionalMessage; @override @@ -50,11 +48,6 @@ class ChatToolBox extends StatelessWidget { icon: ImageRes.toolboxCard, onTap: onTapCard, ), - ToolboxItemInfo( - text: StrRes.toolboxLocation, - icon: ImageRes.toolboxLocation, - onTap: () => Permissions.location(onTapLocation), - ), if (onTapDirectionalMessage != null) ToolboxItemInfo( text: StrRes.toolboxDirectionalMessage, @@ -63,9 +56,11 @@ class ChatToolBox extends StatelessWidget { ), ]; + final rowCount = (items.length / 4).ceil().clamp(1, 2); + return Container( color: Styles.c_F0F2F6, - height: 224.h, + height: rowCount <= 1 ? 118.h : 224.h, child: GridView.builder( itemCount: items.length, padding: EdgeInsets.only( diff --git a/mobile-next/openim_common/lib/src/widgets/chat/chat_webview_map.dart b/mobile-next/openim_common/lib/src/widgets/chat/chat_webview_map.dart deleted file mode 100644 index e441385..0000000 --- a/mobile-next/openim_common/lib/src/widgets/chat/chat_webview_map.dart +++ /dev/null @@ -1,269 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get/get.dart'; -import 'package:openim_common/openim_common.dart'; -import 'package:geolocator/geolocator.dart'; -import 'package:webview_flutter/webview_flutter.dart'; - -import 'package:webview_flutter_android/webview_flutter_android.dart'; - -import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; - -class ChatWebViewMap extends StatefulWidget { - const ChatWebViewMap({ - super.key, - required this.host, - required this.webKey, - required this.webServerKey, - this.mapThumbnailSize = "1200*600", - this.mapBackUrl = "http://callback", - this.latitude, - this.longitude, - }); - - final String host; - final String webKey; - final String webServerKey; - final String mapThumbnailSize; - final String mapBackUrl; - final double? latitude; - final double? longitude; - - @override - State createState() => _ChatWebViewMapState(); -} - -class _ChatWebViewMapState extends State { - WebViewController? _controller; - - String url = ""; - double progress = 0; - double? latitude; - double? longitude; - String? description; - - late String locationUrl; - late String thumbnailUrl; - - late String previewLocationUrl; - - late String webKey; - late String webServerKey; - late String host; - - void _configUrl() { - locationUrl = "$host?key=$webKey&serverKey=$webServerKey#/"; - previewLocationUrl = "$host?key=$webKey&serverKey=$webServerKey&location=$longitude,$latitude#/"; - } - - String getStaticMapURL(double longitude, double latitude) { - final url = - 'https://restapi.amap.com/v3/staticmap?location=$longitude,$latitude&zoom=13&size=200*200&markers=mid,,A:$longitude,$latitude&key=$webServerKey'; - - return url; - } - - bool get isPreview => widget.longitude != null && widget.latitude != null; - - @override - void initState() { - super.initState(); - host = widget.host; - webKey = widget.webKey; - webServerKey = widget.webServerKey; - - longitude = widget.longitude; - latitude = widget.latitude; - - _determinePosition().then((position) { - longitude = position.longitude; - latitude = position.latitude; - - _configUrl(); - - late final PlatformWebViewControllerCreationParams params; - if (WebViewPlatform.instance is WebKitWebViewPlatform) { - params = WebKitWebViewControllerCreationParams( - allowsInlineMediaPlayback: true, - mediaTypesRequiringUserAction: const {}, - ); - } else { - params = const PlatformWebViewControllerCreationParams(); - } - - final WebViewController controller = WebViewController.fromPlatformCreationParams(params); - - controller - ..setJavaScriptMode(JavaScriptMode.unrestricted) - ..setNavigationDelegate( - NavigationDelegate( - onProgress: (int progress) { - debugPrint('WebView is loading (progress : $progress%)'); - setState(() { - this.progress = progress / 100; - }); - }, - onPageStarted: (String url) { - debugPrint('Page started loading: $url'); - }, - onPageFinished: (String url) { - debugPrint('Page finished loading: $url'); - }, - onWebResourceError: (WebResourceError error) { - debugPrint( - 'Page resource error: code: ${error.errorCode} description: ${error.description} errorType: ${error.errorType} isForMainFrame: ${error.isForMainFrame}'); - }, - onNavigationRequest: (NavigationRequest request) { - if (request.url.startsWith('https://www.youtube.com/')) { - debugPrint('blocking navigation to ${request.url}'); - return NavigationDecision.prevent; - } - debugPrint('allowing navigation to ${request.url}'); - return NavigationDecision.navigate; - }, - onHttpError: (HttpResponseError error) { - debugPrint('Error occurred on page: ${error.response?.statusCode}'); - }, - onUrlChange: (UrlChange change) { - debugPrint('url change to ${change.url}'); - }, - onHttpAuthRequest: (HttpAuthRequest request) {}, - ), - ) - ..addJavaScriptChannel( - 'getLocaltion', - onMessageReceived: (JavaScriptMessage message) { - final value = message.message; - final params = jsonDecode(value); - final locationStr = params['location'] as String; - final locations = locationStr.split(','); - longitude = double.parse(locations[0]); - latitude = double.parse(locations[1]); - final address = params['address']; - final url = getStaticMapURL(longitude!, latitude!); - - final result = { - 'longitude': longitude, - 'latitude': latitude, - 'url': url, - 'addr': address, - 'name': '', - }; - - description = jsonEncode(result); - Logger.print('$result'); - - _confirm(); - }, - ) - ..loadRequest(Uri.parse(previewLocationUrl)); - - print('previewLocationUrl: $previewLocationUrl'); - - if (!Platform.isMacOS) { - controller.setBackgroundColor(const Color(0x80000000)); - } - - if (controller.platform is AndroidWebViewController) { - AndroidWebViewController.enableDebugging(true); - - (controller.platform as AndroidWebViewController).setMediaPlaybackRequiresUserGesture(false); - } - - setState(() { - _controller = controller; - }); - }); - } - - Future _determinePosition() async { - bool serviceEnabled; - LocationPermission permission; - - serviceEnabled = await Geolocator.isLocationServiceEnabled(); - if (!serviceEnabled) { - return Future.error('Location services are disabled.'); - } - - permission = await Geolocator.checkPermission(); - if (permission == LocationPermission.denied) { - permission = await Geolocator.requestPermission(); - if (permission == LocationPermission.denied) { - return Future.error('Location permissions are denied'); - } - } - - if (permission == LocationPermission.deniedForever) { - return Future.error('Location permissions are permanently denied, we cannot request permissions.'); - } - - return await Geolocator.getCurrentPosition(); - } - - @override - void dispose() { - super.dispose(); - } - - void _confirm() async { - if (null == latitude || null == longitude) { - await showDialog( - context: context, - builder: (_) => AlertDialog( - title: StrRes.plsSelectLocation.toText..style = Styles.ts_0C1C33_17sp_semibold, - actions: [ - GestureDetector( - onTap: () => Navigator.pop(context), - behavior: HitTestBehavior.translucent, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h), - child: StrRes.determine.toText..style = Styles.ts_0089FF_17sp_semibold, - ), - ), - ], - ), - ); - return; - } - Navigator.pop(context, { - 'latitude': latitude, - 'longitude': longitude, - 'description': description, - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.white, - appBar: TitleBar.back( - onTap: () async { - Get.back(); - }, - title: StrRes.location, - ), - body: SafeArea( - child: Stack( - children: [ - _controller == null - ? const Align( - child: CupertinoActivityIndicator(), - ) - : WebViewWidget(controller: _controller!), - progress < 1.0 - ? LinearProgressIndicator( - value: progress, - color: Colors.blue, - ) - : const SizedBox(), - ], - ), - ), - ); - } -} diff --git a/mobile-next/openim_common/lib/src/widgets/map_view.dart b/mobile-next/openim_common/lib/src/widgets/map_view.dart index fb5ff26..b23ee6b 100644 --- a/mobile-next/openim_common/lib/src/widgets/map_view.dart +++ b/mobile-next/openim_common/lib/src/widgets/map_view.dart @@ -34,7 +34,8 @@ class MapView extends StatelessWidget { ), children: [ TileLayer( - urlTemplate: 'https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', + urlTemplate: + 'https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', userAgentPackageName: '', ), MarkerLayer( @@ -88,19 +89,27 @@ class MapView extends StatelessWidget { } _openMapSheet() async { - final availableMaps = await ml.MapLauncher.installedMaps; - Get.bottomSheet( - BottomSheetView( - items: availableMaps - .map((e) => SheetItem( - label: _mapLabel(e), - onTap: () async { - _launcherMap(e); - }, - )) - .toList(), - ), - ); + try { + final availableMaps = await ml.MapLauncher.installedMaps; + if (availableMaps.isEmpty) { + IMViews.showToast(StrRes.locationMessage); + return; + } + Get.bottomSheet( + BottomSheetView( + items: availableMaps + .map((e) => SheetItem( + label: _mapLabel(e), + onTap: () async { + _launcherMap(e); + }, + )) + .toList(), + ), + ); + } catch (_) { + IMViews.showToast(StrRes.locationMessage); + } } String _mapLabel(ml.AvailableMap map) { @@ -119,11 +128,15 @@ class MapView extends StatelessWidget { } _launcherMap(ml.AvailableMap map) async { - await ml.MapLauncher.showMarker( - mapType: map.mapType, - coords: ml.Coords(latitude, longitude), - title: address1, - description: address2, - ); + try { + await ml.MapLauncher.showMarker( + mapType: map.mapType, + coords: ml.Coords(latitude, longitude), + title: address1, + description: address2, + ); + } catch (_) { + IMViews.showToast(StrRes.locationMessage); + } } } diff --git a/mobile-next/openim_common/lib/src/widgets/rich_text_input_box.dart b/mobile-next/openim_common/lib/src/widgets/rich_text_input_box.dart index 41b6329..a9cac99 100644 --- a/mobile-next/openim_common/lib/src/widgets/rich_text_input_box.dart +++ b/mobile-next/openim_common/lib/src/widgets/rich_text_input_box.dart @@ -14,11 +14,9 @@ class RichTextInputBox extends StatefulWidget { this.showCameraIcon = true, this.showCardIcon = true, this.showFileIcon = true, - this.showLocationIcon = true, this.onTapAlbum, this.onTapCard, this.onTapFile, - this.onTapLocation, this.onSend, }) : super(key: key); final TextEditingController? controller; @@ -29,12 +27,10 @@ class RichTextInputBox extends StatefulWidget { final bool showCameraIcon; final bool showFileIcon; final bool showCardIcon; - final bool showLocationIcon; final Function()? onTapAlbum; final Function()? onTapCamera; final Function()? onTapFile; final Function()? onTapCard; - final Function()? onTapLocation; final Function()? onSend; @override @@ -113,19 +109,12 @@ class _RichTextInputBoxState extends State { ..height = 22.h ..opacity = _opacity ..onTap = widget.onTapCard, - if (widget.showLocationIcon) - ImageRes.toolboxLocation1.toImage - ..width = 16.w - ..height = 22.h - ..opacity = _opacity - ..onTap = widget.onTapLocation, ], ), if (widget.showAlbumIcon || widget.showCameraIcon || widget.showCardIcon || - widget.showFileIcon || - widget.showLocationIcon) + widget.showFileIcon) 15.verticalSpace, Row( children: [ diff --git a/mobile-next/openim_common/lib/src/widgets/views.dart b/mobile-next/openim_common/lib/src/widgets/views.dart index b799d31..f07e19c 100644 --- a/mobile-next/openim_common/lib/src/widgets/views.dart +++ b/mobile-next/openim_common/lib/src/widgets/views.dart @@ -72,12 +72,6 @@ class IMViews { alignment: MainAxisAlignment.start, onTap: () => onTapSheetItem.call(0), ), - SheetItem( - label: StrRes.callVideo, - icon: ImageRes.callVideo, - alignment: MainAxisAlignment.start, - onTap: () => onTapSheetItem.call(1), - ), ], ), ); @@ -96,11 +90,6 @@ class IMViews { icon: ImageRes.callVoice, onTap: () => onTapSheetItem.call(0), ), - SheetItem( - label: StrRes.callVideo, - icon: ImageRes.callVideo, - onTap: () => onTapSheetItem.call(1), - ), ], ), ); @@ -115,7 +104,8 @@ class IMViews { List items = const [], int quality = 80}) { bool allowSendImageTypeHelper(String? mimeType) { - final result = mimeType?.contains('png') == true || mimeType?.contains('jpeg') == true; + final result = mimeType?.contains('png') == true || + mimeType?.contains('jpeg') == true; return result; } @@ -134,23 +124,25 @@ class IMViews { SheetItem( label: StrRes.toolboxAlbum, onTap: () async { - final List? assets = await AssetPicker.pickAssets(Get.context!, - pickerConfig: AssetPickerConfig( - requestType: RequestType.image, - maxAssets: 1, - selectPredicate: (_, entity, isSelected) async { - if (await allowSendImageType(entity)) { - return true; - } + final List? assets = + await AssetPicker.pickAssets(Get.context!, + pickerConfig: AssetPickerConfig( + requestType: RequestType.image, + maxAssets: 1, + selectPredicate: (_, entity, isSelected) async { + if (await allowSendImageType(entity)) { + return true; + } - IMViews.showToast(StrRes.supportsTypeHint); + IMViews.showToast(StrRes.supportsTypeHint); - return false; - })); + return false; + })); final file = await assets?.firstOrNull?.file; if (file?.path != null) { - final map = await uCropPic(file!.path, crop: crop, toUrl: toUrl, quality: quality); + final map = await uCropPic(file!.path, + crop: crop, toUrl: toUrl, quality: quality); onData?.call(map['path'], map['url']); } }, @@ -176,7 +168,8 @@ class IMViews { final file = await entity?.file; if (file?.path != null) { - final map = await uCropPic(file!.path, crop: crop, toUrl: toUrl, quality: quality); + final map = await uCropPic(file!.path, + crop: crop, toUrl: toUrl, quality: quality); onData?.call(map['path'], map['url']); } }, @@ -206,7 +199,9 @@ class IMViews { if (null != cropFile) { Logger.print('-----------crop path: ${cropFile.path}'); result = await LoadingView.singleton.wrap(asyncFunction: () async { - final image = await IMUtils.compressImageAndGetFile(File(cropFile!.path), quality: quality); + final image = await IMUtils.compressImageAndGetFile( + File(cropFile!.path), + quality: quality); return OpenIM.iMManager.uploadFile( id: putID, @@ -217,7 +212,8 @@ class IMViews { } else { Logger.print('-----------source path: $path'); result = await LoadingView.singleton.wrap(asyncFunction: () async { - final image = await IMUtils.compressImageAndGetFile(File(path), quality: quality); + final image = await IMUtils.compressImageAndGetFile(File(path), + quality: quality); return OpenIM.iMManager.uploadFile( id: putID, diff --git a/mobile-next/openim_common/pubspec.yaml b/mobile-next/openim_common/pubspec.yaml index f948224..39c1d7e 100644 --- a/mobile-next/openim_common/pubspec.yaml +++ b/mobile-next/openim_common/pubspec.yaml @@ -79,7 +79,6 @@ dependencies: flutter_map: ^6.0.1 ffmpeg_kit_flutter_full_gpl: 6.0.3 pull_to_refresh_new: ^2.0.5 - geolocator: ^12.0.0 fixnum: ^1.1.0 protobuf: ^3.0.0 intl: ^0.19.0 diff --git a/mobile-next/openim_live/lib/src/live_controller.dart b/mobile-next/openim_live/lib/src/live_controller.dart index 98dfc85..9e042dc 100644 --- a/mobile-next/openim_live/lib/src/live_controller.dart +++ b/mobile-next/openim_live/lib/src/live_controller.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/services.dart'; @@ -88,7 +87,8 @@ mixin OpenIMLive { }); } - Stream get _stream => signalingSubject.stream /*.where((event) => LiveClient.dispatchSignaling(event))*/; + Stream get _stream => signalingSubject + .stream /*.where((event) => LiveClient.dispatchSignaling(event))*/; _signalingListener() => _stream.listen( (event) async { @@ -97,15 +97,13 @@ mixin OpenIMLive { _playSound(vibrate: true); final mediaType = event.data.invitation!.mediaType; final sessionType = event.data.invitation!.sessionType; - final callType = mediaType == 'audio' ? CallType.audio : CallType.video; - final callObj = sessionType == ConversationType.single ? CallObj.single : CallObj.group; + final callType = + mediaType == 'audio' ? CallType.audio : CallType.video; + final callObj = sessionType == ConversationType.single + ? CallObj.single + : CallObj.group; - if (Platform.isAndroid && _isRunningBackground) { - _beCalledEvent = event; - if (await Permissions.checkSystemAlertWindow()) { - return; - } - } + // 不再申请/依赖悬浮窗:后台来电也走应用内通话页,切回前台可继续。 _beCalledEvent = null; OpenIMLiveClient().start( Get.overlayContext!, @@ -151,7 +149,8 @@ mixin OpenIMLive { _stopSound(); } else if (event.state == CallState.beAccepted) { _stopSound(); - } else if (event.state == CallState.otherReject || event.state == CallState.otherAccepted) { + } else if (event.state == CallState.otherReject || + event.state == CallState.otherAccepted) { _stopSound(); } else if (event.state == CallState.timeout) { insertSignalingMessageSubject.add(event); @@ -257,15 +256,19 @@ mixin OpenIMLive { onRoomDisconnected(SignalingInfo signalingInfo) {} Future onDialSingle(SignalingInfo signaling) async { - final data = {'customType': CustomMessageType.callingInvite, 'data': signaling.invitation!.toJson()}; - final message = await OpenIM.iMManager.messageManager - .createCustomMessage(data: jsonEncode(data), extension: '', description: ''); + final data = { + 'customType': CustomMessageType.callingInvite, + 'data': signaling.invitation!.toJson() + }; + final message = await OpenIM.iMManager.messageManager.createCustomMessage( + data: jsonEncode(data), extension: '', description: ''); OpenIM.iMManager.messageManager.sendMessage( message: message, offlinePushInfo: OfflinePushInfo(), userID: signaling.invitation!.inviteeUserIDList!.first, isOnlineOnly: true); - final certificate = await getRtcCertificate(signaling.invitation!.roomID!, OpenIM.iMManager.userID); + final certificate = await getRtcCertificate( + signaling.invitation!.roomID!, OpenIM.iMManager.userID); return certificate; } @@ -283,15 +286,19 @@ mixin OpenIMLive { _beCalledEvent = null; // ios bug _autoPickup = false; _stopSound(); - final data = {'customType': CustomMessageType.callingAccept, 'data': signaling.invitation!.toJson()}; - final message = await OpenIM.iMManager.messageManager - .createCustomMessage(data: jsonEncode(data), extension: '', description: ''); + final data = { + 'customType': CustomMessageType.callingAccept, + 'data': signaling.invitation!.toJson() + }; + final message = await OpenIM.iMManager.messageManager.createCustomMessage( + data: jsonEncode(data), extension: '', description: ''); OpenIM.iMManager.messageManager.sendMessage( message: message, offlinePushInfo: OfflinePushInfo(), userID: signaling.invitation!.inviterUserID, isOnlineOnly: true); - final certificate = await getRtcCertificate(signaling.invitation!.roomID!, OpenIM.iMManager.userID); + final certificate = await getRtcCertificate( + signaling.invitation!.roomID!, OpenIM.iMManager.userID); return certificate; } @@ -300,35 +307,52 @@ mixin OpenIMLive { _stopSound(); insertSignalingMessageSubject.add(CallEvent(CallState.reject, signaling)); - final data = {'customType': CustomMessageType.callingReject, 'data': signaling.invitation!.toJson()}; - final message = await OpenIM.iMManager.messageManager - .createCustomMessage(data: jsonEncode(data), extension: '', description: ''); - final recvUserID = signaling.invitation!.inviterUserID == OpenIM.iMManager.userID - ? signaling.invitation!.inviteeUserIDList!.first - : signaling.invitation!.inviterUserID; - return OpenIM.iMManager.messageManager - .sendMessage(message: message, offlinePushInfo: OfflinePushInfo(), userID: recvUserID, isOnlineOnly: true); + final data = { + 'customType': CustomMessageType.callingReject, + 'data': signaling.invitation!.toJson() + }; + final message = await OpenIM.iMManager.messageManager.createCustomMessage( + data: jsonEncode(data), extension: '', description: ''); + final recvUserID = + signaling.invitation!.inviterUserID == OpenIM.iMManager.userID + ? signaling.invitation!.inviteeUserIDList!.first + : signaling.invitation!.inviterUserID; + return OpenIM.iMManager.messageManager.sendMessage( + message: message, + offlinePushInfo: OfflinePushInfo(), + userID: recvUserID, + isOnlineOnly: true); } onTapCancel(SignalingInfo signaling) async { _stopSound(); insertSignalingMessageSubject.add(CallEvent(CallState.cancel, signaling)); - final data = {'customType': CustomMessageType.callingCancel, 'data': signaling.invitation!.toJson()}; - final message = await OpenIM.iMManager.messageManager - .createCustomMessage(data: jsonEncode(data), extension: '', description: ''); - final recvUserID = signaling.invitation!.inviterUserID == OpenIM.iMManager.userID - ? signaling.invitation!.inviteeUserIDList!.first - : signaling.invitation!.inviterUserID; - OpenIM.iMManager.messageManager - .sendMessage(message: message, offlinePushInfo: OfflinePushInfo(), userID: recvUserID, isOnlineOnly: true); + final data = { + 'customType': CustomMessageType.callingCancel, + 'data': signaling.invitation!.toJson() + }; + final message = await OpenIM.iMManager.messageManager.createCustomMessage( + data: jsonEncode(data), extension: '', description: ''); + final recvUserID = + signaling.invitation!.inviterUserID == OpenIM.iMManager.userID + ? signaling.invitation!.inviteeUserIDList!.first + : signaling.invitation!.inviterUserID; + OpenIM.iMManager.messageManager.sendMessage( + message: message, + offlinePushInfo: OfflinePushInfo(), + userID: recvUserID, + isOnlineOnly: true); return true; } onTimeoutCancelled(SignalingInfo signaling) async { - final data = {'customType': CustomMessageType.callingCancel, 'data': signaling.invitation!.toJson()}; - final message = await OpenIM.iMManager.messageManager - .createCustomMessage(data: jsonEncode(data), extension: '', description: ''); + final data = { + 'customType': CustomMessageType.callingCancel, + 'data': signaling.invitation!.toJson() + }; + final message = await OpenIM.iMManager.messageManager.createCustomMessage( + data: jsonEncode(data), extension: '', description: ''); OpenIM.iMManager.messageManager.sendMessage( message: message, @@ -349,14 +373,21 @@ mixin OpenIMLive { onTapHangup(SignalingInfo signaling, int duration, bool isPositive) async { if (isPositive) { - final data = {'customType': CustomMessageType.callingHungup, 'data': signaling.invitation!.toJson()}; - final message = await OpenIM.iMManager.messageManager - .createCustomMessage(data: jsonEncode(data), extension: '', description: ''); - final recvUserID = signaling.invitation!.inviterUserID == OpenIM.iMManager.userID - ? signaling.invitation!.inviteeUserIDList!.first - : signaling.invitation!.inviterUserID; - OpenIM.iMManager.messageManager - .sendMessage(message: message, offlinePushInfo: OfflinePushInfo(), userID: recvUserID, isOnlineOnly: true); + final data = { + 'customType': CustomMessageType.callingHungup, + 'data': signaling.invitation!.toJson() + }; + final message = await OpenIM.iMManager.messageManager.createCustomMessage( + data: jsonEncode(data), extension: '', description: ''); + final recvUserID = + signaling.invitation!.inviterUserID == OpenIM.iMManager.userID + ? signaling.invitation!.inviteeUserIDList!.first + : signaling.invitation!.inviterUserID; + OpenIM.iMManager.messageManager.sendMessage( + message: message, + offlinePushInfo: OfflinePushInfo(), + userID: recvUserID, + isOnlineOnly: true); } _stopSound(); @@ -389,7 +420,8 @@ mixin OpenIMLive { return list.firstOrNull; } - Future> onSyncGroupMemberInfo(groupID, userIDList) async { + Future> onSyncGroupMemberInfo( + groupID, userIDList) async { var list = await OpenIM.iMManager.groupManager.getGroupMembersInfo( groupID: groupID, userIDList: userIDList, @@ -412,7 +444,8 @@ mixin OpenIMLive { void _startIncomingVibrate() { _stopIncomingVibrate(); _pulseVibrate(); - _incomingVibrateTimer = Timer.periodic(Styles.callVibrate, (_) => _pulseVibrate()); + _incomingVibrateTimer = + Timer.periodic(Styles.callVibrate, (_) => _pulseVibrate()); } void _stopIncomingVibrate() { @@ -467,7 +500,8 @@ mixin OpenIMLive { receiverID = inviteeUserID; } - var msg = await OpenIM.iMManager.messageManager.insertSingleMessageToLocalStorage( + var msg = await OpenIM.iMManager.messageManager + .insertSingleMessageToLocalStorage( receiverID: inviteeUserID, senderID: inviterUserID, message: message @@ -495,7 +529,9 @@ class SignalingMessageEvent { bool get isSingleChat => sessionType == ConversationType.single; - bool get isGroupChat => sessionType == ConversationType.group || sessionType == ConversationType.superGroup; + bool get isGroupChat => + sessionType == ConversationType.group || + sessionType == ConversationType.superGroup; } extension MessageMangerExt on MessageManager { diff --git a/mobile-next/pubspec.lock b/mobile-next/pubspec.lock index 19c072f..9c0526b 100644 --- a/mobile-next/pubspec.lock +++ b/mobile-next/pubspec.lock @@ -930,54 +930,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" - geolocator: - dependency: transitive - description: - name: geolocator - sha256: "149876cc5207a0f5daf4fdd3bfcf0a0f27258b3fe95108fa084f527ad0568f1b" - url: "https://pub.dev" - source: hosted - version: "12.0.0" - geolocator_android: - dependency: transitive - description: - name: geolocator_android - sha256: "7aefc530db47d90d0580b552df3242440a10fe60814496a979aa67aa98b1fd47" - url: "https://pub.dev" - source: hosted - version: "4.6.1" - geolocator_apple: - dependency: transitive - description: - name: geolocator_apple - sha256: c4ecead17985ede9634f21500072edfcb3dba0ef7b97f8d7bc556d2d722b3ba3 - url: "https://pub.dev" - source: hosted - version: "2.3.9" - geolocator_platform_interface: - dependency: transitive - description: - name: geolocator_platform_interface - sha256: "386ce3d9cce47838355000070b1d0b13efb5bc430f8ecda7e9238c8409ace012" - url: "https://pub.dev" - source: hosted - version: "4.2.4" - geolocator_web: - dependency: transitive - description: - name: geolocator_web - sha256: "2ed69328e05cd94e7eb48bb0535f5fc0c0c44d1c4fa1e9737267484d05c29b5e" - url: "https://pub.dev" - source: hosted - version: "4.1.1" - geolocator_windows: - dependency: transitive - description: - name: geolocator_windows - sha256: "53da08937d07c24b0d9952eb57a3b474e29aae2abf9dd717f7e1230995f13f0e" - url: "https://pub.dev" - source: hosted - version: "0.2.3" gesture_password_widget: dependency: "direct main" description: