From a35eba1160fab15b4382709121026365d201d22e Mon Sep 17 00:00:00 2001 From: gem Date: Thu, 20 Nov 2025 14:27:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BE=A4=E7=BB=84=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + CLAUDE.md | 195 +++++++++++++++ android/build.gradle | 21 +- .../flutter_openim_sdk/manager/IMManager.java | 63 +++++ .../FlutterOpenimSdkPlugin.kt | 35 --- example/.gitignore | 4 + example/android/app/build.gradle | 1 + .../android/app/src/main/AndroidManifest.xml | 8 + example/android/build.gradle | 46 +++- example/android/settings.gradle | 2 +- .../plugin_integration_test.dart | 17 +- example/lib/main.dart | 227 +++++++++++++++--- example/libs/README.md | 114 +++++++++ example/pubspec.lock | 145 ++++++++++- example/pubspec.yaml | 4 + example/update-local-aar.bat | 34 +++ example/update-local-aar.sh | 34 +++ .../flutter/generated_plugin_registrant.cc | 3 + .../windows/flutter/generated_plugins.cmake | 1 + ios/Classes/Module/IMManager.swift | 40 +++ ios/flutter_openim_sdk.podspec | 4 +- lib/src/enum/group_notify_filter.dart | 13 + lib/src/manager/im_manager.dart | 176 +++++++++++--- 23 files changed, 1062 insertions(+), 126 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 android/src/main/kotlin/io/openim/flutter_openim_sdk/FlutterOpenimSdkPlugin.kt create mode 100644 example/libs/README.md create mode 100644 example/update-local-aar.bat create mode 100644 example/update-local-aar.sh create mode 100644 lib/src/enum/group_notify_filter.dart diff --git a/.gitignore b/.gitignore index d442409..f5b46bb 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ build/ .idea/other.xml windows/openlib/ windows/third_party/ +.claude/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..cc3ed6a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,195 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## 项目概述 + +这是一个 Flutter 插件项目,为 OpenIM 即时通讯服务提供 Flutter SDK 封装。该 SDK 支持 Android、iOS 和 Windows 平台。 + +**核心架构:** +- Flutter 层通过 MethodChannel 与原生层通信 +- Android 原生层使用 gomobile 编译的 AAR 包与 OpenIM SDK Core (Go) 交互 +- iOS 原生层使用 gomobile 编译的 XCFramework 与 OpenIM SDK Core 交互 +- 数据传输格式为 JSON,数据存储使用 OpenIM SDK Core 内置的 SQLite + +## 常用命令 + +### 开发环境设置 +```bash +# 安装依赖 +flutter pub get + +# 运行示例应用 +cd example +flutter pub get +flutter run +``` + +### 测试 +```bash +# 运行单元测试 +flutter test + +# 运行集成测试 +cd example +flutter test integration_test/plugin_integration_test.dart +``` + +### 代码格式化 +```bash +# 格式化代码 +dart format . + +# 分析代码 +flutter analyze +``` + +### Android 构建 +```bash +# 在 example 目录中构建 Android +cd example +flutter build apk + +# 构建 Android AAR +cd android +./gradlew assembleRelease +``` + +### iOS 构建 +```bash +# 在 example 目录中构建 iOS +cd example +flutter build ios +``` + +## 核心架构设计 + +### 1. 管理器层次结构 + +`IMManager` 是核心管理器,包含以下子管理器: +- `ConversationManager`: 会话管理(lib/src/manager/im_conversation_manager.dart) +- `MessageManager`: 消息管理(lib/src/manager/im_message_manager.dart) +- `FriendshipManager`: 好友关系管理(lib/src/manager/im_friendship_manager.dart) +- `GroupManager`: 群组管理(lib/src/manager/im_group_manager.dart) +- `ChannelManager`: ��道管理(lib/src/manager/im_channel_manager.dart) +- `UserManager`: 用户管理(lib/src/manager/im_user_manager.dart) + +### 2. 双向通信机制 + +**Flutter → Native (方法调用):** +- Flutter 通过 MethodChannel 调用原生方法 +- 参数统一使用 `_buildParam()` 封装,添加 `ManagerName` 和清理空值 +- 每个请求可携带 `operationID` 用于追踪 + +**Native → Flutter (回调监听):** +- 原生层通过 MethodChannel 回调 Flutter 层 +- Flutter 在 `IMManager._addNativeCallback()` 中统一处理所有监听器回调 +- 监听器类型定义在 `lib/src/enum/listener_type.dart` + +### 3. 数据模型转换 + +所有数据模型位于 `lib/src/models/`,使用 JSON 序列化/反序列化: +- `Utils.toObj()`: JSON Map 转对象 +- `Utils.toList()`: JSON Array 转对象列表 +- 所有模型类实现 `fromJson()` 和 `toJson()` 方法 + +### 4. 监听器系统 + +监听器定义在 `lib/src/listener/`,主要包括: +- `OnConnectListener`: 连接状态监听 +- `OnAdvancedMsgListener`: 高级消息监听 +- `OnConversationListener`: 会话变化监听 +- `OnFriendshipListener`: 好友关系监听 +- `OnGroupListener`: 群组事件监听 +- `OnChannelListener`: 频道事件监听 +- `OnUserListener`: 用户信息监听 + +### 5. 原生层实现 + +**Android (Java):** +- 插件入口: `android/src/main/java/io/openim/flutter_openim_sdk/FlutterOpenimSdkPlugin.java` +- 各管理器实现: `android/src/main/java/io/openim/flutter_openim_sdk/manager/` +- 依赖的 OpenIM SDK Core AAR 包位于 `android/libs/` + +**iOS (Swift/ObjC):** +- 插件入口: `ios/Classes/SwiftFlutterOpenimSdkPlugin.swift` +- 模块化管理器: `ios/Classes/Module/` +- 工具类: `ios/Classes/Util/` + +## 开发注意事项 + +### SDK 初始化和登录流程 + +1. **必须先初始化 SDK** (`initSDK()` 或 `init()`) +2. **设置监听器** (必须在登录前完成) +3. **登录** (`login()`) +4. SDK 初始化需要提供: + - `platformID`: 平台 ID (参考 `IMPlatform` 枚举) + - `apiAddr`: OpenIM Server API ���址 + - `wsAddr`: OpenIM Server WebSocket 地址 + - `dataDir`: 数据存储目录 + +### 消息收发 + +- 创建消息使用 `MessageManager.create*Message()` 系列方法 +- 发送消息使用 `MessageManager.sendMessage()` +- 接收消息通过 `OnAdvancedMsgListener` 监听 +- 支持单聊、群聊、在线消息等多种场景 + +### 平台特定功能 + +- **Android 独有**: `setListenerForService()` - 用于后台服务监听 +- **通知可见性规则**: Android SDK 35+ 支持的通知管理功能 +- **文件上传**: 支持自定义上传监听器 (`OnUploadFileListener`) + +### 版本管理 + +- SDK 版本定义在 `lib/src/openim.dart` 中的 `OpenIM.version` +- `pubspec.yaml` 中的版本应与之保持一致 +- 使用格式: `major.minor.patch+build` (如 `3.8.3+2`) + +### 日志和调试 + +- 使用 `Logger.print()` 输出日志 (lib/src/logger.dart) +- 支持上传日志到服务器: `uploadLogs()` +- 可自定义日志级别 (1-6,默认 6 为全部输出) + +## Android 特定配置 + +### ProGuard 规则 + +项目使用 ProGuard 混淆,规则文件在 `example/android/app/proguard-rules.pro`: +```proguard +-keep class io.openim.** { *; } +-keep class open_im_sdk.** { *; } +-keep class open_im_sdk_callback.** { *; } +``` + +### Gradle 配置 + +- 使用阿里云 Maven 镜像加速依赖下载 +- 本地 Maven 仓库: `http://192.168.77.132:8081/repository/mvn2-group` (需根据实际情况修改) +- `compileSdkVersion`: 34 +- `minSdkVersion`: 21 + +## 常见任务 + +### 添加新的 API 方法 + +1. 在对应的 Manager 类(Dart)中添加方法 +2. 在 Android Manager 类(Java)中实现 +3. 在 iOS Module 类(Swift)中实现 +4. 确保参数序列化和回调处理正确 + +### 添加新的监听器 + +1. 在 `lib/src/listener/` 创建监听器类 +2. 在 `ListenerType` 枚举中添加类型 +3. 在 `IMManager._addNativeCallback()` 中添加处理逻辑 +4. 在原生层实现对应的回调触发 + +### 调试原生层交互 + +- Flutter → Native: 在 `Logger.print()` 查看方法调用日志 +- Native → Flutter: 在 `_addNativeCallback()` 设置断点查看回调数据 +- 使用 `operationID` 追踪特定操作的完整流程 diff --git a/android/build.gradle b/android/build.gradle index 82afd40..a5d8547 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -14,6 +14,8 @@ def getCurrentProjectDir() { } buildscript { + ext.kotlin_version = '1.9.24' + repositories { maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/central' } @@ -25,21 +27,26 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:7.3.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } rootProject.allprojects { repositories { - maven { - url 'http://192.168.77.132:8081/repository/mvn2-group' - allowInsecureProtocol true - } + // 本地 AAR 调试配置 - 使用 rootProject.projectDir 确保路径正确 + // maven { url 'file://' + rootProject.projectDir.absolutePath + '/local-maven' } + + // maven { + // url 'http://192.168.77.132:8081/repository/mvn2-group' + // allowInsecureProtocol true + // } google() mavenCentral() } } apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' android { namespace 'io.openim.flutter_openim_sdk' @@ -55,8 +62,12 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + kotlinOptions { + jvmTarget = '1.8' + } } dependencies { - implementation 'com.openim:sdkcore:1.0.14' + //implementation 'com.openim:sdkcore:1.0.15-local' + implementation 'com.openim:sdkcore:1.0.16' } \ No newline at end of file diff --git a/android/src/main/java/io/openim/flutter_openim_sdk/manager/IMManager.java b/android/src/main/java/io/openim/flutter_openim_sdk/manager/IMManager.java index febe22b..2c322aa 100644 --- a/android/src/main/java/io/openim/flutter_openim_sdk/manager/IMManager.java +++ b/android/src/main/java/io/openim/flutter_openim_sdk/manager/IMManager.java @@ -102,6 +102,69 @@ public class IMManager extends BaseManager { ); } + public void setNotificationVisibilityRule(MethodCall methodCall, MethodChannel.Result result) { + Open_im_sdk.setNotificationVisibilityRule( + new OnBaseListener(result, methodCall), + value(methodCall, "operationID"), + value(methodCall, "notificationType"), + value(methodCall, "visibilityType") + ); + } + + public void setNotificationVisibilityRules(MethodCall methodCall, MethodChannel.Result result) { + Open_im_sdk.setNotificationVisibilityRules( + new OnBaseListener(result, methodCall), + value(methodCall, "operationID"), + value(methodCall, "rulesJSON") + ); + } + + public void getNotificationVisibilityRule(MethodCall methodCall, MethodChannel.Result result) { + Open_im_sdk.getNotificationVisibilityRule( + new OnBaseListener(result, methodCall), + value(methodCall, "operationID"), + value(methodCall, "notificationType") + ); + } + + public void getNotificationVisibilityRules(MethodCall methodCall, MethodChannel.Result result) { + Open_im_sdk.getNotificationVisibilityRules( + new OnBaseListener(result, methodCall), + value(methodCall, "operationID") + ); + } + + public void enableNotificationVisibilityRule(MethodCall methodCall, MethodChannel.Result result) { + Open_im_sdk.enableNotificationVisibilityRule( + new OnBaseListener(result, methodCall), + value(methodCall, "operationID"), + value(methodCall, "notificationType") + ); + } + + public void disableNotificationVisibilityRule(MethodCall methodCall, MethodChannel.Result result) { + Open_im_sdk.disableNotificationVisibilityRule( + new OnBaseListener(result, methodCall), + value(methodCall, "operationID"), + value(methodCall, "notificationType") + ); + } + + public void deleteNotificationVisibilityRule(MethodCall methodCall, MethodChannel.Result result) { + Open_im_sdk.deleteNotificationVisibilityRule( + new OnBaseListener(result, methodCall), + value(methodCall, "operationID"), + value(methodCall, "notificationType") + ); + } + + public void resetNotificationVisibilityRules(MethodCall methodCall, MethodChannel.Result result) { + Open_im_sdk.resetNotificationVisibilityRules( + new OnBaseListener(result, methodCall), + value(methodCall, "operationID") + ); + } + // public void setListenerForService(MethodCall methodCall, MethodChannel.Result result) { // Open_im_sdk.setListenerForService(new OnListenerForService()); // diff --git a/android/src/main/kotlin/io/openim/flutter_openim_sdk/FlutterOpenimSdkPlugin.kt b/android/src/main/kotlin/io/openim/flutter_openim_sdk/FlutterOpenimSdkPlugin.kt deleted file mode 100644 index 308391d..0000000 --- a/android/src/main/kotlin/io/openim/flutter_openim_sdk/FlutterOpenimSdkPlugin.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.openim.flutter_openim_sdk - -import androidx.annotation.NonNull - -import io.flutter.embedding.engine.plugins.FlutterPlugin -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.MethodChannel.Result - -/** FlutterOpenimSdkPlugin */ -class FlutterOpenimSdkPlugin: FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private lateinit var channel : MethodChannel - - override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_openim_sdk") - channel.setMethodCallHandler(this) - } - - override fun onMethodCall(call: MethodCall, result: Result) { - if (call.method == "getPlatformVersion") { - result.success("Android ${android.os.Build.VERSION.RELEASE}") - } else { - result.notImplemented() - } - } - - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } -} diff --git a/example/.gitignore b/example/.gitignore index 29a3a50..c005250 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -41,3 +41,7 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +# 本地调试 AAR 文件 +/libs/*.aar +/android/local-maven/ diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 2a2d082..29ed82f 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -49,6 +49,7 @@ android { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig = signingConfigs.debug + minifyEnabled true } } } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 74a78b9..e6ebc72 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,12 @@ + + + + + + + + { + bool _isLoading = true; + bool _isLoggedIn = false; + + @override + void initState() { + super.initState(); + _init(); + } + + Future _init() async { + await _requestPermissions(); + await _initSDK(); + setState(() { + _isLoading = false; + }); + } + + Future _requestPermissions() async { + // 请求存储权限 + var status = await Permission.storage.request(); + if (status.isGranted) { + // 权限已授予,可以读写文件 + } + } + + Future _initSDK() async { + var directory = await getApplicationDocumentsDirectory(); + + final rootPath = directory.path; + OpenIM.iMManager + .initSDK( + platformID: 2, + apiAddr: 'http://192.168.77.135:10002', + wsAddr: 'ws://192.168.77.135:10001', + dataDir: '$rootPath/', + listener: OnConnectListener(onConnectSuccess: () { + // Already connected to the server + _isLoggedIn = true; + }, onConnecting: () { + // Connecting to the server, can be used for UI prompts + }, onUserTokenExpired: () { + // User token has expired, can be used for UI prompts + _isLoggedIn = false; + }, onKickedOffline: () { + // The current user is kicked offline, and the user needs to be prompted to log in again + _isLoggedIn = false; + })) + .then((value) { + if (value) { + OpenIM.iMManager.userManager.setUserListener(OnUserListener( + onSelfInfoUpdated: (userInfo) { + debugPrint('onSelfInfoUpdated: ${userInfo.toJson()}'); + }, + )); + + OpenIM.iMManager.setNotificationVisibilityRule( + notificationType: MessageType.memberQuitNotification, + visibilityType: + GroupNotifyFilter.notificationVisibleToOperatorAndAdmin); + + OpenIM.iMManager.setNotificationVisibilityRule( + notificationType: MessageType.memberKickedNotification, + visibilityType: + GroupNotifyFilter.notificationVisibleToOperatorAndAdmin); + + // success + } else { + // fail + } + }); + } + + Future _login() async { + try { + await OpenIM.iMManager + .login( + userID: "8bfe13b5eac44e87963652abb91d80d2", + token: + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiI4YmZlMTNiNWVhYzQ0ZTg3OTYzNjUyYWJiOTFkODBkMiIsIlBsYXRmb3JtSUQiOjIsImV4cCI6MTc3MTIzNzE1NywiaWF0IjoxNzYzNDYxMTUyfQ.J0WCPoGAJAV8dn8F5vfan0WPn_DOFmc4pg93V7W6VZ0") + .then((value) { + _isLoggedIn = true; + List list = []; + OpenIM.iMManager.userManager.subscribeUsersStatus(list); + //send(); + + OpenIM.iMManager.messageManager + .setAdvancedMsgListener(OnAdvancedMsgListener( + // 当消息被撤回时调用 + onRecvNewMessage: (msg) { + debugPrint('Received onRecvNewMessage: ${msg.toJson()}'); + }, + onRecvOnlineOnlyMessage: (msg) => + debugPrint('Received online-only message: ${msg.toJson()}'), + onRecvOfflineNewMessage: (msg) => + debugPrint('Received offline message: ${msg.toJson()}'), + )); + + OpenIM.iMManager.groupManager.setGroupListener(OnGroupListener( + // 当群组申请被接受时调用 + onGroupApplicationAccepted: (groupApplication) {}, + // 当群组申请被添加时调用 + onGroupApplicationAdded: (groupApplication) {}, + // 当群组申请被删除时调用 + onGroupApplicationDeleted: (groupApplication) {}, + // 当群组申请被拒绝时调用 + onGroupApplicationRejected: (groupApplication) { + debugPrint('Group application rejected: $groupApplication'); + }, + // 当群组信息发生变化时调用 + onGroupInfoChanged: (groupInfo) { + debugPrint('Group info changed: $groupInfo'); + //等待一段时间,获得会话的最后一条消息 + Future.delayed(const Duration(seconds: 2), () { + OpenIM.iMManager.conversationManager + .getOneConversation( + sourceID: groupInfo.groupID, + sessionType: ConversationType.superGroup) + .then((conversation) { + debugPrint( + 'Updated conversation last message: ${conversation.latestMsg}'); + }); + }); + }, + // 当群组成员被添加时调用 + onGroupMemberAdded: (groupMember) { + debugPrint('Group member added: $groupMember'); + }, + // 当群组成员被删除时调用 + onGroupMemberDeleted: (groupMember) { + debugPrint('Group member deleted: $groupMember'); + }, + // 当群组成员信息发生变化时调用 + onGroupMemberInfoChanged: (groupMember) { + debugPrint('Group member info changed: $groupMember'); + }, + // 当加入的群组被添加时调用 + onJoinedGroupAdded: (groupInfo) { + debugPrint('Joined group added: $groupInfo'); + }, + // 当加入的群组被删除时调用 + onJoinedGroupDeleted: (groupInfo) { + debugPrint('Joined group deleted: $groupInfo'); + }, + )); + OpenIM.iMManager.conversationManager + .getAllConversationList() + .then((value) { + print('Get all conversation list successful'); + }); + }); + } catch (error) { + _isLoggedIn = false; + } finally { + setState(() { + _isLoading = false; + }); + } + } + Future send() async { OpenIM.iMManager.messageManager .sendMessage( @@ -32,36 +195,6 @@ class _MyAppState extends State { }); } - @override - void initState() { - super.initState(); - OpenIM.iMManager - .initSDK( - platformID: 3, - apiAddr: 'http://192.168.77.135:10002', - wsAddr: 'ws://192.168.77.135:10001', - dataDir: './', - listener: OnConnectListener()) - .then((value) {}); - OpenIM.iMManager.userManager.setUserListener(OnUserListener()); - - OpenIM.iMManager - .login( - userID: "8bfe13b5eac44e87963652abb91d80d2", - token: - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiI4YmZlMTNiNWVhYzQ0ZTg3OTYzNjUyYWJiOTFkODBkMiIsIlBsYXRmb3JtSUQiOjMsImV4cCI6MTc1NDM2NDkwNCwiaWF0IjoxNzQ2NTg4ODk5fQ.575xwR9lzJo1i5Te-Ul-99odONfxbzDXW0_2Kv3nFEE") - .then((value) { - List list = []; - OpenIM.iMManager.userManager.subscribeUsersStatus(list); - send(); - OpenIM.iMManager.conversationManager - .getAllConversationList() - .then((value) { - print('Get all conversation list successful'); - }); - }).catchError((error) {}); - } - @override Widget build(BuildContext context) { return MaterialApp( @@ -69,11 +202,35 @@ class _MyAppState extends State { appBar: AppBar( title: const Text('Plugin example app'), ), - body: Column( - children: [ - TextButton(onPressed: () {}, child: const Text('login')), - ], - ), + body: _isLoading + ? const Center(child: CircularProgressIndicator()) + : _isLoggedIn + ? Column( + children: [ + TextButton( + onPressed: () { + OpenIM.iMManager.logout().then((_) { + setState(() { + _isLoggedIn = false; + }); + }); + }, + child: const Text('logout')), + ], + ) + : Center( + child: TextButton( + onPressed: () { + setState(() { + _isLoading = true; + }); + _login().then((_) { + setState(() { + _isLoading = false; + }); + }); + }, + child: const Text('login'))), ), ); } diff --git a/example/libs/README.md b/example/libs/README.md new file mode 100644 index 0000000..b211d0c --- /dev/null +++ b/example/libs/README.md @@ -0,0 +1,114 @@ +# 本地 AAR 调试配置 + +此目录用于放置本地编译的 OpenIM SDK AAR 文件,仅在 example 项目调试时使用。 + +## 快速开始 + +### 步骤 1: 准备 AAR 文件 + +将你的 AAR 文件重命名为 `sdkcore-debug.aar` 并放在本目录: + +``` +example/libs/ +└── sdkcore-debug.aar (你的本地编译 AAR 文件) +``` + +### 步骤 2: 复制到 Maven 仓库结构 + +```bash +# Linux/Mac/Git Bash +cd example +cp libs/sdkcore-debug.aar android/local-maven/com/openim/sdkcore/1.0.15-local/sdkcore-1.0.15-local.aar + +# Windows PowerShell +cd example +Copy-Item libs\sdkcore-debug.aar android\local-maven\com\openim\sdkcore\1.0.15-local\sdkcore-1.0.15-local.aar +``` + +**注意**: POM 文件已自动创建在 `android/local-maven/com/openim/sdkcore/1.0.15-local/sdkcore-1.0.15-local.pom` + +### 步骤 3: 启用本地 AAR + +编辑 `example/android/build.gradle`,取消以下两处注释: + +**1. 第 28 行 - 启用本地 Maven 仓库:** +```gradle +maven { url 'file://' + projectDir.absolutePath + '/local-maven' } +``` + +**2. 第 43-49 行 - 强制使用本地版本:** +```gradle +configurations.all { + resolutionStrategy { + force 'com.openim:sdkcore:1.0.15-local' + } +} +``` + +### 步骤 4: 清理并重新构建 + +```bash +cd example +flutter clean +flutter pub get +flutter build apk # 或 flutter run +``` + +## 更新 AAR 后快速替换 + +如果你已经配置过一次,只是更新了 AAR 文件: + +```bash +cd example +# 复制新的 AAR 文件 +cp libs/sdkcore-debug.aar android/local-maven/com/openim/sdkcore/1.0.15-local/sdkcore-1.0.15-local.aar + +# 清理并重新构建 +flutter clean && flutter pub get && flutter run +``` + +## 调试完成后恢复 + +重新注释掉 `example/android/build.gradle` 中的两处配置即可恢复使用远程 Maven 仓库的版本。 + +## 目录结构 + +``` +example/ +├── libs/ +│ ├── README.md (本文件) +│ └── sdkcore-debug.aar (你的 AAR 源文件) +└── android/ + └── local-maven/ (Git 已忽略) + └── com/openim/sdkcore/1.0.15-local/ + ├── sdkcore-1.0.15-local.aar (Maven 仓库中的 AAR) + └── sdkcore-1.0.15-local.pom (Maven 元数据) +``` + +## 注意事项 + +- ✅ 此配置只影响 example 项目,不会修改插件本身 +- ✅ 切换远程/本地版本只需注释/取消注释即可 +- ✅ `.gitignore` 已配置忽略 `android/local-maven/` 目录 +- ⚠️ 每次修改 AAR 文件后需要重新复制到 Maven 目录并运行 `flutter clean` +- ⚠️ 确保 AAR 文件名和版本号匹配 + +## 故障排查 + +### 问题 1: Kotlin 版本不兼容错误 +已修复:插件的 `android/build.gradle` 已添加 Kotlin 插件和正确的配置。 + +### 问题 2: "Zip file already contains entry" 错误 +运行彻底清理: +```bash +cd example +flutter clean +rm -rf build +cd android && ./gradlew clean +``` + +### 问题 3: 找不到 sdkcore 依赖 +确保: +1. `android/build.gradle` 中的本地 Maven 仓库已取消注释 +2. `resolutionStrategy.force` 配置已取消注释 +3. AAR 文件已正确复制到 Maven 目录结构中 diff --git a/example/pubspec.lock b/example/pubspec.lock index 56cbd94..bcb2f41 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -57,6 +57,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" flutter: dependency: "direct main" description: flutter @@ -82,6 +90,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" leak_tracker: dependency: transitive description: @@ -146,6 +159,118 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" + url: "https://pub.dev" + source: hosted + version: "2.2.15" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849" + url: "https://pub.dev" + source: hosted + version: "11.4.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc + url: "https://pub.dev" + source: hosted + version: "12.1.0" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 + url: "https://pub.dev" + source: hosted + version: "9.4.7" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + url: "https://pub.dev" + source: hosted + version: "0.1.3+5" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 + url: "https://pub.dev" + source: hosted + version: "4.3.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" sky_engine: dependency: transitive description: flutter @@ -215,6 +340,22 @@ packages: url: "https://pub.dev" source: hosted version: "14.2.5" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" sdks: - dart: ">=3.4.4 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index caccd0c..42e9c6e 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -43,6 +43,10 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.6 + permission_handler: ^11.3.1 + path_provider: ^2.1.4 + + dev_dependencies: flutter_test: diff --git a/example/update-local-aar.bat b/example/update-local-aar.bat new file mode 100644 index 0000000..64816a4 --- /dev/null +++ b/example/update-local-aar.bat @@ -0,0 +1,34 @@ +@echo off +REM ==================================== +REM 更新本地 AAR 并重新构建 +REM ==================================== + +echo [1/5] 复制 AAR 文件到 Maven 仓库... +copy /Y libs\sdkcore-debug.aar android\local-maven\com\openim\sdkcore\1.0.15-local\sdkcore-1.0.15-local.aar +if %errorlevel% neq 0 ( + echo 错误: AAR 文件复制失败,请检查 libs\sdkcore-debug.aar 是否存在 + pause + exit /b 1 +) + +echo [2/5] 清理 Flutter 缓存... +flutter clean + +echo [3/5] 删除构建目录... +if exist build rmdir /S /Q build + +echo [4/5] 清理 Gradle 缓存... +cd android +call gradlew.bat clean +cd .. + +echo [5/5] 重新获取依赖... +flutter pub get + +echo. +echo ======================================== +echo 更新完成!现在可以运行: +echo flutter run (运行应用) +echo flutter build apk (构建 APK) +echo ======================================== +pause diff --git a/example/update-local-aar.sh b/example/update-local-aar.sh new file mode 100644 index 0000000..964f925 --- /dev/null +++ b/example/update-local-aar.sh @@ -0,0 +1,34 @@ +#!/bin/bash +#################################### +# 更新本地 AAR 并重新构建 +#################################### + +set -e # 遇到错误立即退出 + +echo "[1/5] 复制 AAR 文件到 Maven 仓库..." +if [ ! -f "libs/sdkcore-debug.aar" ]; then + echo "错误: libs/sdkcore-debug.aar 不存在" + exit 1 +fi +cp libs/sdkcore-debug.aar android/local-maven/com/openim/sdkcore/1.0.15-local/sdkcore-1.0.15-local.aar + +echo "[2/5] 清理 Flutter 缓存..." +flutter clean + +echo "[3/5] 删除构建目录..." +rm -rf build + +echo "[4/5] 清理 Gradle 缓存..." +cd android +./gradlew clean +cd .. + +echo "[5/5] 重新获取依赖..." +flutter pub get + +echo "" +echo "========================================" +echo "更新完成!现在可以运行:" +echo " flutter run (运行应用)" +echo " flutter build apk (构建 APK)" +echo "========================================" diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/example/windows/flutter/generated_plugin_registrant.cc index 4193c00..83a9ce7 100644 --- a/example/windows/flutter/generated_plugin_registrant.cc +++ b/example/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,11 @@ #include "generated_plugin_registrant.h" #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { FlutterOpenimSdkPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterOpenimSdkPlugin")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); } diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake index 89b03b8..d483344 100644 --- a/example/windows/flutter/generated_plugins.cmake +++ b/example/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_openim_sdk + permission_handler_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/ios/Classes/Module/IMManager.swift b/ios/Classes/Module/IMManager.swift index 119599e..327c8a1 100644 --- a/ios/Classes/Module/IMManager.swift +++ b/ios/Classes/Module/IMManager.swift @@ -18,6 +18,14 @@ public class IMMananger: BaseServiceManager { self["updateFcmToken"] = updateFcmToken self["setAppBackgroundStatus"] = setAppBackgroundStatus self["networkStatusChanged"] = networkStatusChanged + self["setNotificationVisibilityRule"] = setNotificationVisibilityRule + self["setNotificationVisibilityRules"] = setNotificationVisibilityRules + self["getNotificationVisibilityRule"] = getNotificationVisibilityRule + self["getNotificationVisibilityRules"] = getNotificationVisibilityRules + self["enableNotificationVisibilityRule"] = enableNotificationVisibilityRule + self["disableNotificationVisibilityRule"] = disableNotificationVisibilityRule + self["deleteNotificationVisibilityRule"] = deleteNotificationVisibilityRule + self["resetNotificationVisibilityRules"] = resetNotificationVisibilityRules } fileprivate func addObservers() { @@ -107,6 +115,38 @@ public class IMMananger: BaseServiceManager { func networkStatusChanged(methodCall: FlutterMethodCall, result: @escaping FlutterResult) { Open_im_sdkNetworkStatusChanged(BaseCallback(result: result), methodCall[string: "operationID"]) } + + func setNotificationVisibilityRule(methodCall: FlutterMethodCall, result: @escaping FlutterResult) { + Open_im_sdkSetNotificationVisibilityRule(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[int32: "notificationType"], methodCall[int32: "visibilityType"]) + } + + func setNotificationVisibilityRules(methodCall: FlutterMethodCall, result: @escaping FlutterResult) { + Open_im_sdkSetNotificationVisibilityRules(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[string: "rulesJSON"]) + } + + func getNotificationVisibilityRule(methodCall: FlutterMethodCall, result: @escaping FlutterResult) { + Open_im_sdkGetNotificationVisibilityRule(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[int32: "notificationType"]) + } + + func getNotificationVisibilityRules(methodCall: FlutterMethodCall, result: @escaping FlutterResult) { + Open_im_sdkGetNotificationVisibilityRules(BaseCallback(result: result), methodCall[string: "operationID"]) + } + + func enableNotificationVisibilityRule(methodCall: FlutterMethodCall, result: @escaping FlutterResult) { + Open_im_sdkEnableNotificationVisibilityRule(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[int32: "notificationType"]) + } + + func disableNotificationVisibilityRule(methodCall: FlutterMethodCall, result: @escaping FlutterResult) { + Open_im_sdkDisableNotificationVisibilityRule(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[int32: "notificationType"]) + } + + func deleteNotificationVisibilityRule(methodCall: FlutterMethodCall, result: @escaping FlutterResult) { + Open_im_sdkDeleteNotificationVisibilityRule(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[int32: "notificationType"]) + } + + func resetNotificationVisibilityRules(methodCall: FlutterMethodCall, result: @escaping FlutterResult) { + Open_im_sdkResetNotificationVisibilityRules(BaseCallback(result: result), methodCall[string: "operationID"]) + } } public class ConnListener: NSObject, Open_im_sdk_callbackOnConnListenerProtocol { diff --git a/ios/flutter_openim_sdk.podspec b/ios/flutter_openim_sdk.podspec index 8bb385a..8eefd37 100644 --- a/ios/flutter_openim_sdk.podspec +++ b/ios/flutter_openim_sdk.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_openim_sdk' - s.version = '0.0.11' + s.version = '0.0.12' s.summary = 'A new Flutter project.' s.description = <<-DESC A new Flutter project. @@ -19,7 +19,7 @@ A new Flutter project. #s.ios.vendored_frameworks = 'frameworks/*.xcframework' #s.vendored_frameworks = 'frameworks/*.xcframework' - s.dependency 'openim_sdk_core_ios','0.11.0' + s.dependency 'openim_sdk_core_ios','0.12.0' s.static_framework = true s.library = 'resolv' diff --git a/lib/src/enum/group_notify_filter.dart b/lib/src/enum/group_notify_filter.dart new file mode 100644 index 0000000..b7f6644 --- /dev/null +++ b/lib/src/enum/group_notify_filter.dart @@ -0,0 +1,13 @@ +class GroupNotifyFilter { + static const notificationVisibleToAll = 0; // 所有人可见 - Visible to all + static const notificationVisibleToOperatorAndAdmin = + 1; // 操作者、被操作者和管理员可见 - Visible to operator, target and admin + static const notificationVisibleToAdminOnly = + 2; // 仅管理员可见 - Visible to admin only + static const notificationVisibleToOperatorOnly = + 3; // 仅操作者本人可见 - Visible to operator only + static const notificationVisibleToTargetOnly = + 4; // 仅被操作者本人可见 - Visible to target only + static const notificationVisibleToOperatorAndTarget = + 5; // 操作者和被操作者可见 - Visible to operator and target +} diff --git a/lib/src/manager/im_manager.dart b/lib/src/manager/im_manager.dart index fee0103..fe44eee 100644 --- a/lib/src/manager/im_manager.dart +++ b/lib/src/manager/im_manager.dart @@ -6,7 +6,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_openim_sdk/flutter_openim_sdk.dart'; import 'package:flutter_openim_sdk/src/logger.dart'; - class IMManager { MethodChannel _channel; late ConversationManager conversationManager; @@ -60,7 +59,7 @@ class IMManager { case 'onUserTokenExpired': _connectListener.userTokenExpired(); break; - case 'onUserTokenInvalid': + case 'onUserTokenInvalid': _connectListener.userTokenInvalid(); break; } @@ -73,7 +72,8 @@ class IMManager { userManager.listener.selfInfoUpdated(userInfo); break; case 'onUserStatusChanged': - final status = Utils.toObj(data, (map) => UserStatusInfo.fromJson(map)); + final status = + Utils.toObj(data, (map) => UserStatusInfo.fromJson(map)); userManager.listener.userStatusChanged(status); break; } @@ -82,19 +82,23 @@ class IMManager { dynamic data = call.arguments['data']; switch (type) { case 'onGroupApplicationAccepted': - final i = Utils.toObj(data, (map) => GroupApplicationInfo.fromJson(map)); + final i = Utils.toObj( + data, (map) => GroupApplicationInfo.fromJson(map)); groupManager.listener.groupApplicationAccepted(i); break; case 'onGroupApplicationAdded': - final i = Utils.toObj(data, (map) => GroupApplicationInfo.fromJson(map)); + final i = Utils.toObj( + data, (map) => GroupApplicationInfo.fromJson(map)); groupManager.listener.groupApplicationAdded(i); break; case 'onGroupApplicationDeleted': - final i = Utils.toObj(data, (map) => GroupApplicationInfo.fromJson(map)); + final i = Utils.toObj( + data, (map) => GroupApplicationInfo.fromJson(map)); groupManager.listener.groupApplicationDeleted(i); break; case 'onGroupApplicationRejected': - final i = Utils.toObj(data, (map) => GroupApplicationInfo.fromJson(map)); + final i = Utils.toObj( + data, (map) => GroupApplicationInfo.fromJson(map)); groupManager.listener.groupApplicationRejected(i); break; case 'onGroupDismissed': @@ -106,15 +110,18 @@ class IMManager { groupManager.listener.groupInfoChanged(i); break; case 'onGroupMemberAdded': - final i = Utils.toObj(data, (map) => GroupMembersInfo.fromJson(map)); + final i = + Utils.toObj(data, (map) => GroupMembersInfo.fromJson(map)); groupManager.listener.groupMemberAdded(i); break; case 'onGroupMemberDeleted': - final i = Utils.toObj(data, (map) => GroupMembersInfo.fromJson(map)); + final i = + Utils.toObj(data, (map) => GroupMembersInfo.fromJson(map)); groupManager.listener.groupMemberDeleted(i); break; case 'onGroupMemberInfoChanged': - final i = Utils.toObj(data, (map) => GroupMembersInfo.fromJson(map)); + final i = + Utils.toObj(data, (map) => GroupMembersInfo.fromJson(map)); groupManager.listener.groupMemberInfoChanged(i); break; case 'onJoinedGroupAdded': @@ -130,7 +137,6 @@ class IMManager { String type = call.arguments['type']; dynamic data = call.arguments['data']; switch (type) { - case 'onChannelDismissed': final i = Utils.toObj(data, (map) => ChannelInfo.fromJson(map)); channelManager.listener.channelDismissed(i); @@ -140,18 +146,18 @@ class IMManager { channelManager.listener.channelInfoChanged(i); break; case 'onChannelMemberAdded': - final i = Utils.toObj( - data, (map) => ChannelMembersInfo.fromJson(map)); + final i = + Utils.toObj(data, (map) => ChannelMembersInfo.fromJson(map)); channelManager.listener.channelMemberAdded(i); break; case 'onChannelMemberDeleted': - final i = Utils.toObj( - data, (map) => ChannelMembersInfo.fromJson(map)); + final i = + Utils.toObj(data, (map) => ChannelMembersInfo.fromJson(map)); channelManager.listener.channelMemberDeleted(i); break; case 'onChannelMemberInfoChanged': - final i = Utils.toObj( - data, (map) => ChannelMembersInfo.fromJson(map)); + final i = + Utils.toObj(data, (map) => ChannelMembersInfo.fromJson(map)); channelManager.listener.channelMemberInfoChanged(i); break; case 'onJoinedChannelAdded': @@ -163,7 +169,7 @@ class IMManager { channelManager.listener.joinedChannelDeleted(i); break; } - }else if (call.method == ListenerType.advancedMsgListener) { + } else if (call.method == ListenerType.advancedMsgListener) { var type = call.arguments['type']; // var id = call.arguments['data']['id']; switch (type) { @@ -184,7 +190,8 @@ class IMManager { break; case 'onRecvC2CReadReceipt': var value = call.arguments['data']['msgReceiptList']; - var list = Utils.toList(value, (map) => ReadReceiptInfo.fromJson(map)); + var list = + Utils.toList(value, (map) => ReadReceiptInfo.fromJson(map)); messageManager.msgListener.recvC2CReadReceipt(list); break; case 'onRecvNewMessage': @@ -234,19 +241,24 @@ class IMManager { conversationManager.listener.syncServerFailed(data); break; case 'onNewConversation': - var list = Utils.toList(data, (map) => ConversationInfo.fromJson(map)); + var list = + Utils.toList(data, (map) => ConversationInfo.fromJson(map)); conversationManager.listener.newConversation(list); break; case 'onConversationChanged': - var list = Utils.toList(data, (map) => ConversationInfo.fromJson(map)); + var list = + Utils.toList(data, (map) => ConversationInfo.fromJson(map)); conversationManager.listener.conversationChanged(list); break; case 'onTotalUnreadMessageCountChanged': - conversationManager.listener.totalUnreadMessageCountChanged(data ?? 0); + conversationManager.listener + .totalUnreadMessageCountChanged(data ?? 0); break; case 'onConversationUserInputStatusChanged': - final i = Utils.toObj(data, (map) => InputStatusChangedData.fromJson(map)); - conversationManager.listener.conversationUserInputStatusChanged(i); + final i = Utils.toObj( + data, (map) => InputStatusChangedData.fromJson(map)); + conversationManager.listener + .conversationUserInputStatusChanged(i); break; } } else if (call.method == ListenerType.friendListener) { @@ -267,19 +279,23 @@ class IMManager { friendshipManager.listener.friendAdded(u); break; case 'onFriendApplicationAccepted': - final u = Utils.toObj(data, (map) => FriendApplicationInfo.fromJson(map)); + final u = Utils.toObj( + data, (map) => FriendApplicationInfo.fromJson(map)); friendshipManager.listener.friendApplicationAccepted(u); break; case 'onFriendApplicationAdded': - final u = Utils.toObj(data, (map) => FriendApplicationInfo.fromJson(map)); + final u = Utils.toObj( + data, (map) => FriendApplicationInfo.fromJson(map)); friendshipManager.listener.friendApplicationAdded(u); break; case 'onFriendApplicationDeleted': - final u = Utils.toObj(data, (map) => FriendApplicationInfo.fromJson(map)); + final u = Utils.toObj( + data, (map) => FriendApplicationInfo.fromJson(map)); friendshipManager.listener.friendApplicationDeleted(u); break; case 'onFriendApplicationRejected': - final u = Utils.toObj(data, (map) => FriendApplicationInfo.fromJson(map)); + final u = Utils.toObj( + data, (map) => FriendApplicationInfo.fromJson(map)); friendshipManager.listener.friendApplicationRejected(u); break; case 'onFriendDeleted': @@ -296,7 +312,8 @@ class IMManager { String data = call.arguments['data']; switch (type) { case 'onRecvCustomBusinessMessage': - messageManager.customBusinessListener?.recvCustomBusinessMessage(data); + messageManager.customBusinessListener + ?.recvCustomBusinessMessage(data); break; } } else if (call.method == ListenerType.listenerForService) { @@ -304,19 +321,23 @@ class IMManager { String data = call.arguments['data']; switch (type) { case 'onFriendApplicationAccepted': - final u = Utils.toObj(data, (map) => FriendApplicationInfo.fromJson(map)); + final u = Utils.toObj( + data, (map) => FriendApplicationInfo.fromJson(map)); _listenerForService?.friendApplicationAccepted(u); break; case 'onFriendApplicationAdded': - final u = Utils.toObj(data, (map) => FriendApplicationInfo.fromJson(map)); + final u = Utils.toObj( + data, (map) => FriendApplicationInfo.fromJson(map)); _listenerForService?.friendApplicationAdded(u); break; case 'onGroupApplicationAccepted': - final i = Utils.toObj(data, (map) => GroupApplicationInfo.fromJson(map)); + final i = Utils.toObj( + data, (map) => GroupApplicationInfo.fromJson(map)); _listenerForService?.groupApplicationAccepted(i); break; case 'onGroupApplicationAdded': - final i = Utils.toObj(data, (map) => GroupApplicationInfo.fromJson(map)); + final i = Utils.toObj( + data, (map) => GroupApplicationInfo.fromJson(map)); _listenerForService?.groupApplicationAdded(i); break; case 'onRecvNewMessage': @@ -373,7 +394,8 @@ class IMManager { int fileSize = data['fileSize']; int streamSize = data['streamSize']; int storageSize = data['storageSize']; - _uploadFileListener?.uploadProgress(id, fileSize, streamSize, storageSize); + _uploadFileListener?.uploadProgress( + id, fileSize, streamSize, storageSize); break; case 'uploadID': String id = data['id']; @@ -385,12 +407,14 @@ class IMManager { int index = data['index']; int partSize = data['partSize']; String partHash = data['partHash']; - _uploadFileListener?.uploadPartComplete(id, index, partSize, partHash); + _uploadFileListener?.uploadPartComplete( + id, index, partSize, partHash); break; } } } catch (error, stackTrace) { - Logger.print("回调失败了。${call.method} ${call.arguments['type']} ${call.arguments['data']} $error $stackTrace"); + Logger.print( + "回调失败了。${call.method} ${call.arguments['type']} ${call.arguments['data']} $error $stackTrace"); } return Future.value(null); }); @@ -622,6 +646,86 @@ class IMManager { } } + //sdk多了这几个方法,增加一下 + Future setNotificationVisibilityRule( + {required int notificationType, + required int visibilityType, + String? operationID}) => + _channel.invokeMethod( + 'setNotificationVisibilityRule', + _buildParam({ + 'notificationType': notificationType, + 'visibilityType': visibilityType, + 'operationID': Utils.checkOperationID(operationID), + }), + ); + Future setNotificationVisibilityRules( + {required String rulesJSON, String? operationID}) => + _channel.invokeMethod( + 'setNotificationVisibilityRules', + _buildParam({ + 'rulesJSON': rulesJSON, + 'operationID': Utils.checkOperationID(operationID), + }), + ); + Future getNotificationVisibilityRule( + {required int notificationType, String? operationID}) => + _channel.invokeMethod( + 'getNotificationVisibilityRule', + _buildParam({ + 'notificationType': notificationType, + 'operationID': Utils.checkOperationID(operationID), + }), + ); + + Future> getNotificationVisibilityRules( + {String? operationID}) async { + var result = await _channel.invokeMethod>( + 'getNotificationVisibilityRules', + _buildParam({ + 'operationID': Utils.checkOperationID(operationID), + }), + ); + return result ?? {}; + } + + Future enableNotificationVisibilityRule( + {required int notificationType, String? operationID}) => + _channel.invokeMethod( + 'enableNotificationVisibilityRule', + _buildParam({ + 'notificationType': notificationType, + 'operationID': Utils.checkOperationID(operationID), + }), + ); + Future disableNotificationVisibilityRule( + {required int notificationType, String? operationID}) => + _channel.invokeMethod( + 'disableNotificationVisibilityRule', + _buildParam({ + 'notificationType': notificationType, + 'operationID': Utils.checkOperationID(operationID), + }), + ); + + Future deleteNotificationVisibilityRule( + {required int notificationType, String? operationID}) => + _channel.invokeMethod( + 'deleteNotificationVisibilityRule', + _buildParam({ + 'notificationType': notificationType, + 'operationID': Utils.checkOperationID(operationID), + }), + ); + + Future resetNotificationVisibilityRules({String? operationID}) => + _channel.invokeMethod( + 'resetNotificationVisibilityRules', + _buildParam({ + 'operationID': Utils.checkOperationID(operationID), + }), + ); + MethodChannel get channel => _channel; static Map _buildParam(Map param) {