docs: 添加 CLAUDE.md + 各 module README + sdk-webview JSBridge 补全
Agent 7 + Agent 8: - CLAUDE.md 项目上下文 - 各 module README (core/im/push/update/webview/log) - XWebViewStandardHandlers.kt 补全标准 JSBridge handler
这个提交包含在:
父节点
fbafc8d802
当前提交
4a18d06c63
36
CLAUDE.md
36
CLAUDE.md
@ -2,10 +2,12 @@
|
||||
|
||||
## 项目定位
|
||||
|
||||
XuqmGroup Android SDK,Gradle multi-module 项目。为集成宿主 App(如 YwxMobileApp)提供:初始化、用户认证、OTA 更新、WebView、IM、推送、证书、**日志追踪**。
|
||||
XuqmGroup Android SDK,Gradle multi-module 项目。为集成宿主 App(如 YwxMobileApp)提供:初始化、用户认证、OTA 更新、WebView、IM、推送、证书、日志追踪。
|
||||
|
||||
- Git 远端:`https://xuqinmin.com/xuqmGroup/XuqmGroup-AndroidSDK.git`
|
||||
- Nexus Maven 发布:`https://nexus.xuqinmin.com/repository/android-hosted/`
|
||||
- groupId:`com.xuqm`
|
||||
- 技术栈:Kotlin 2.3.10,AGP 9.1.0,minSdk 24,compileSdk 36,Java 21
|
||||
|
||||
## 模块结构
|
||||
|
||||
@ -59,13 +61,6 @@ XuqmSDK.logEnabled // 是否开启日志
|
||||
XuqmSDK.appKey // 当前 appKey
|
||||
```
|
||||
|
||||
## 向下兼容约束
|
||||
|
||||
- `XuqmSDK.initialize()` 现有参数不得删除或修改类型
|
||||
- `XuqmSDK.setUserInfo()` 现有 `XuqmUserInfo` 字段不得删除
|
||||
- `SdkPlatformConfig` 新增字段一律为可选(`val xxx: Type? = null`)
|
||||
- 旧服务端不返回新字段时,客户端使用合理默认值
|
||||
|
||||
## 日志 SDK(sdk-log)
|
||||
|
||||
```kotlin
|
||||
@ -86,16 +81,18 @@ XLog.defineFunnel("checkout", listOf("cart_view", "checkout_start", "payment_don
|
||||
|
||||
`logApiUrl` 由 SDK 在 init 后从平台配置自动获取,无需 App 传入。
|
||||
|
||||
## 向下兼容约束
|
||||
|
||||
- `XuqmSDK.initialize()` 现有参数不得删除或修改类型
|
||||
- `XuqmSDK.setUserInfo()` 现有 `XuqmUserInfo` 字段不得删除
|
||||
- `SdkPlatformConfig` 新增字段一律为可选(`val xxx: Type? = null`)
|
||||
- 旧服务端不返回新字段时,客户端使用合理默认值
|
||||
|
||||
## 集成验证项目
|
||||
|
||||
YwxMobileApp:`/Users/xuqinmin/Projects/TrustProjects/Pad/YwxMobileApp`
|
||||
使用 Nexus Maven 引入 SDK,开发阶段可改为 `project()` 本地引用。
|
||||
|
||||
## 任务文档
|
||||
|
||||
- Agent 7 任务:`/Users/xuqinmin/Projects/XuqmProjects/YiwangxinApp4/docs/agent-tasks/agent7-android-sdk.md`
|
||||
- Agent 8 任务:`/Users/xuqinmin/Projects/XuqmProjects/YiwangxinApp4/docs/agent-tasks/agent8-docs.md`
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
@ -103,4 +100,17 @@ YwxMobileApp:`/Users/xuqinmin/Projects/TrustProjects/Pad/YwxMobileApp`
|
||||
./gradlew :sdk-core:assembleDebug
|
||||
./gradlew :sample-app:installDebug
|
||||
./gradlew publish # 发布所有模块到 Nexus
|
||||
|
||||
# 发布单个模块
|
||||
./gradlew :sdk-core:publish -PSDK_CORE_VERSION=1.0.0
|
||||
```
|
||||
|
||||
## 发版配置
|
||||
|
||||
在 `gradle.properties` 中配置:
|
||||
```properties
|
||||
NEXUS_USER=your_username
|
||||
NEXUS_PASSWORD=your_password
|
||||
```
|
||||
|
||||
发布至 `https://nexus.xuqinmin.com/repository/android-hosted/`,版本号在各模块 `build.gradle.kts` 中维护。
|
||||
|
||||
93
sdk-core/README.md
普通文件
93
sdk-core/README.md
普通文件
@ -0,0 +1,93 @@
|
||||
# sdk-core
|
||||
|
||||
XuqmGroup Android SDK 核心模块。提供 SDK 初始化、HTTP 请求、Token 管理、文件操作、通用工具。
|
||||
|
||||
## 依赖
|
||||
|
||||
```kotlin
|
||||
implementation("com.xuqm:sdk-core:VERSION")
|
||||
```
|
||||
|
||||
## 初始化
|
||||
|
||||
### 方式 A — ContentProvider 自动初始化(推荐)
|
||||
|
||||
将 `config.xuqm` 放入 `src/main/assets/xuqm/`,SDK 在 App 启动时自动读取并初始化。
|
||||
|
||||
```kotlin
|
||||
// XuqmInitializerProvider 自动触发,无需代码
|
||||
```
|
||||
|
||||
### 方式 B — 手动初始化
|
||||
|
||||
```kotlin
|
||||
// Application.onCreate() 中:
|
||||
XuqmSDK.initialize(context, appKey = "xxx")
|
||||
XuqmSDK.initialize(context, appKey = "xxx", platformUrl = "https://xxx")
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### XuqmSDK(全局单例)
|
||||
|
||||
| API | 说明 |
|
||||
|-----|------|
|
||||
| `XuqmSDK.initialize(context, appKey, platformUrl?, logLevel?)` | 手动初始化 |
|
||||
| `XuqmSDK.awaitInitialization()` | 等待平台配置拉取(coroutine) |
|
||||
| `XuqmSDK.isInitialized()` | 检查是否已初始化 |
|
||||
| `XuqmSDK.setUserInfo(info)` | 设置用户信息,触发所有子模块登录 |
|
||||
| `XuqmSDK.setUserInfo(null)` | 登出,触发全局登出 |
|
||||
| `XuqmSDK.getUserId()` | 获取当前 userId |
|
||||
| `XuqmSDK.getUserInfo()` | 获取当前用户信息 |
|
||||
| `XuqmSDK.appKey` | 当前 appKey |
|
||||
| `XuqmSDK.platformConfig` | 平台配置(init 后可用) |
|
||||
| `XuqmSDK.logApiUrl` | 日志服务地址(从平台配置获取) |
|
||||
| `XuqmSDK.logEnabled` | 是否启用日志上报 |
|
||||
| `XuqmSDK.useLocalServiceEndpoints(ip)` | 切换本地联调环境 |
|
||||
| `XuqmSDK.tokenStore` | Token 存储(EncryptedSharedPreferences) |
|
||||
|
||||
### XuqmUserInfo
|
||||
|
||||
```kotlin
|
||||
data class XuqmUserInfo(
|
||||
val userId: String, // 必填
|
||||
val userSig: String? = null, // IM 登录凭证(可选)
|
||||
val name: String? = null,
|
||||
val phone: String? = null,
|
||||
val avatar: String? = null,
|
||||
)
|
||||
```
|
||||
|
||||
### TokenStore
|
||||
|
||||
基于 `EncryptedSharedPreferences` 持久化存储。
|
||||
|
||||
```kotlin
|
||||
XuqmSDK.tokenStore.saveToken("eyJ...")
|
||||
val token = XuqmSDK.tokenStore.getToken()
|
||||
XuqmSDK.tokenStore.clear()
|
||||
```
|
||||
|
||||
### ApiClient
|
||||
|
||||
基于 OkHttp 5 + Retrofit 3,自动附加 Bearer Token。
|
||||
|
||||
```kotlin
|
||||
val service = RetrofitFactory.create(MyApiService::class.java)
|
||||
```
|
||||
|
||||
### FileSDK
|
||||
|
||||
文件上传、下载、打开的统一入口(`com.xuqm.sdk.file`)。
|
||||
|
||||
```kotlin
|
||||
// 上传
|
||||
val result = FileSDK.upload(context, uri, onProgress = { /* 0-100 */ })
|
||||
val result = FileSDK.uploadBytes(fileName, mimeType, bytes, onProgress)
|
||||
|
||||
// 下载
|
||||
val file = FileSDK.download(context, url, fileName, destination, notificationTitle, onProgress)
|
||||
|
||||
// 打开
|
||||
FileSDK.openFile(context, file)
|
||||
```
|
||||
56
sdk-im/README.md
普通文件
56
sdk-im/README.md
普通文件
@ -0,0 +1,56 @@
|
||||
# sdk-im
|
||||
|
||||
XuqmGroup Android SDK IM 模块。提供 WebSocket 实时通信、消息收发、群组管理。
|
||||
|
||||
## 依赖
|
||||
|
||||
```kotlin
|
||||
implementation("com.xuqm:sdk-im:VERSION")
|
||||
implementation("com.xuqm:sdk-core:VERSION") // 必须
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
**无需手动初始化。** `XuqmSDK.login(userId, userSig)` 成功后自动完成 IM 登录。
|
||||
|
||||
```kotlin
|
||||
// ImClient 直接使用
|
||||
val imClient = ImClient()
|
||||
imClient.listener = object : ImEventListener {
|
||||
override fun onConnected() { }
|
||||
override fun onMessage(msg: ImMessage) { }
|
||||
override fun onRevoke(msgId: String, operatorId: String) { }
|
||||
}
|
||||
|
||||
imClient.connect()
|
||||
imClient.send(SendMessageParams(
|
||||
toId = "user_002",
|
||||
chatType = ChatType.SINGLE,
|
||||
msgType = MsgType.TEXT,
|
||||
content = "Hello!"
|
||||
))
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### ImClient
|
||||
|
||||
| API | 说明 |
|
||||
|-----|------|
|
||||
| `imClient.connect()` | 连接 WebSocket |
|
||||
| `imClient.disconnect()` | 断开连接 |
|
||||
| `imClient.send(params)` | 发送消息 |
|
||||
| `imClient.revoke(msgId)` | 撤回消息 |
|
||||
| `imClient.listener` | 事件监听器 |
|
||||
|
||||
### 消息类型
|
||||
|
||||
`TEXT` / `IMAGE` / `VIDEO` / `AUDIO` / `FILE` / `CUSTOM` / `LOCATION` / `NOTIFY` / `RICH_TEXT` / `CALL_AUDIO` / `CALL_VIDEO` / `FORWARD` / `QUOTE` / `MERGE`
|
||||
|
||||
### 自动重连
|
||||
|
||||
断线后指数退避重连,初始间隔 3 秒,最大间隔 30 秒。调用 `disconnect()` 后停止。
|
||||
|
||||
### 群聊
|
||||
|
||||
支持 `@userId` 提及,写入 `mentionedUserIds`。群组管理 API 包含:创建、解散、禁言、设置管理员、转让群主。
|
||||
@ -1,94 +1,39 @@
|
||||
# Xuqm License SDK
|
||||
# sdk-license
|
||||
|
||||
Android SDK for device license registration and verification.
|
||||
XuqmGroup Android SDK 证书授权模块。提供设备 License 验证能力。
|
||||
|
||||
## Features
|
||||
|
||||
- **Stable Device ID**: Uses `ANDROID_ID` (stable per app signing key + device + user), with UUID fallback
|
||||
- **Token Caching**: Caches valid status for configurable window (default 10 min) to reduce network calls
|
||||
- **Auto Recovery**: Automatically re-registers when token is invalid or revoked
|
||||
- **Offline Support**: Returns cached OK status when network is unavailable
|
||||
- **Encrypted License File**: Reads `assets/xuqm/license.xuqm`; the file does not expose appKey or server URL as readable text
|
||||
- **AppKey Change Detection**: Automatically clears old data when decrypted appKey changes
|
||||
|
||||
## Integration
|
||||
## 依赖
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
implementation("com.xuqm:sdk-license:0.1.0-SNAPSHOT")
|
||||
}
|
||||
implementation("com.xuqm:sdk-license:VERSION")
|
||||
implementation("com.xuqm:sdk-core:VERSION") // 必须
|
||||
```
|
||||
|
||||
## Usage
|
||||
## 使用
|
||||
|
||||
### License File
|
||||
|
||||
Download the encrypted License file from the tenant platform and place it at:
|
||||
|
||||
```text
|
||||
app/src/main/assets/xuqm/license.xuqm
|
||||
```
|
||||
|
||||
The SDK also accepts a tenant-platform downloaded file such as
|
||||
`app/src/main/assets/xuqm/MyApp.xuqmlicense`.
|
||||
|
||||
No explicit initialization code is required.
|
||||
|
||||
### Check License
|
||||
**无需独立初始化。** 内部自动等待 `XuqmSDK` 就绪。
|
||||
|
||||
```kotlin
|
||||
LicenseSDK.checkLicense { isValid ->
|
||||
if (isValid) {
|
||||
// allow app usage
|
||||
} else {
|
||||
// block app usage or show warning
|
||||
// 验证 License
|
||||
LicenseSDK.checkLicense(context, callback = object : LicenseCallback {
|
||||
override fun onResult(result: LicenseResult) {
|
||||
when (result) {
|
||||
is LicenseResult.Success -> { /* 验证通过 */ }
|
||||
is LicenseResult.Denied -> { /* 验证失败 */ }
|
||||
is LicenseResult.Error -> { /* 错误 */ }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 获取状态
|
||||
val status = LicenseSDK.getStatus()
|
||||
```
|
||||
|
||||
Optional user metadata can be uploaded during the same check. All fields are optional.
|
||||
## API
|
||||
|
||||
```kotlin
|
||||
LicenseSDK.checkLicense(
|
||||
userInfo = LicenseUserInfo(
|
||||
userId = "u_10001",
|
||||
name = "张三",
|
||||
email = "zhangsan@example.com",
|
||||
phone = "13800000000",
|
||||
),
|
||||
) { isValid ->
|
||||
// allow or block app usage
|
||||
}
|
||||
```
|
||||
|
||||
### Get Status (no network)
|
||||
|
||||
```kotlin
|
||||
val status = LicenseSDK.getStatus() // OK, DENIED, or UNKNOWN
|
||||
```
|
||||
|
||||
### Re-register (force)
|
||||
|
||||
```kotlin
|
||||
lifecycleScope.launch {
|
||||
val result = LicenseSDK.reRegister()
|
||||
}
|
||||
```
|
||||
|
||||
### Get Device ID
|
||||
|
||||
```kotlin
|
||||
val deviceId = LicenseSDK.getDeviceId()
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|-----------|---------|-------------|
|
||||
| `appKey` | from encrypted file | AppKey from tenant platform |
|
||||
| `deviceName` | `${MANUFACTURER} ${MODEL}` | Device display name |
|
||||
| `baseUrl` | `https://auth.dev.xuqinmin.com/` | License server base URL |
|
||||
|
||||
## Data Storage
|
||||
|
||||
All data (device ID, token, status) is stored in `EncryptedSharedPreferences` with AES-256 encryption.
|
||||
| API | 说明 |
|
||||
|-----|------|
|
||||
| `LicenseSDK.checkLicense(context, callback)` | 验证设备 License |
|
||||
| `LicenseSDK.getStatus()` | 获取当前 License 状态 |
|
||||
| `LicenseSDK.getDeviceId()` | 获取设备 ID |
|
||||
| `LicenseSDK.clear()` | 清除本地 License 缓存 |
|
||||
|
||||
55
sdk-log/README.md
普通文件
55
sdk-log/README.md
普通文件
@ -0,0 +1,55 @@
|
||||
# sdk-log
|
||||
|
||||
XuqmGroup Android SDK 日志模块。提供日志采集、Crash 捕获、漏斗分析能力。
|
||||
|
||||
## 依赖
|
||||
|
||||
```kotlin
|
||||
implementation("com.xuqm:sdk-log:VERSION")
|
||||
implementation("com.xuqm:sdk-core:VERSION") // 必须
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
```kotlin
|
||||
// Application.onCreate() 中(XuqmSDK.initialize 之后):
|
||||
XLog.setLogLevel(LogLevel.INFO)
|
||||
XLog.setEnvironment("production")
|
||||
XLog.startCrashCapture()
|
||||
|
||||
// 业务代码中
|
||||
XLog.event("page_view", mapOf("page" to "home"))
|
||||
XLog.captureError(exception)
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### XLog 对象
|
||||
|
||||
| API | 说明 |
|
||||
|-----|------|
|
||||
| `XLog.setLogLevel(level)` | 设置日志级别(`DEBUG` / `INFO` / `WARN` / `ERROR`) |
|
||||
| `XLog.setEnvironment(env)` | 设置环境标签 |
|
||||
| `XLog.startCrashCapture()` | 开启全局 UncaughtExceptionHandler |
|
||||
| `XLog.event(name, properties?)` | 记录自定义事件 |
|
||||
| `XLog.captureError(error, metadata?)` | 上报异常 |
|
||||
| `XLog.warn(message, metadata?)` | 记录警告 |
|
||||
| `XLog.info(message, metadata?)` | 记录信息 |
|
||||
| `XLog.defineFunnel(id, steps)` | 定义漏斗 |
|
||||
|
||||
### LogLevel
|
||||
|
||||
```kotlin
|
||||
enum class LogLevel { DEBUG, INFO, WARN, ERROR, NONE }
|
||||
```
|
||||
|
||||
### 工作原理
|
||||
|
||||
- **LogQueue**:事件进入内存队列,按批次异步上传到 `logApiUrl`
|
||||
- **CrashCapture**:注册 `Thread.UncaughtExceptionHandler`,捕获 Native Crash 并写入文件,下次启动时上传
|
||||
- **Fingerprint**:为错误生成指纹(基于 message + stack),服务端去重聚合
|
||||
- **FunnelTracker**:客户端维护漏斗进度,服务端跨 session 聚合
|
||||
|
||||
### 配置
|
||||
|
||||
`logApiUrl` 和 `logEnabled` 由 `sdk-core` 在 init 后从平台配置自动获取,无需 App 传入。
|
||||
@ -1,6 +1,5 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("maven-publish")
|
||||
}
|
||||
|
||||
@ -8,7 +7,7 @@ group = rootProject.group
|
||||
|
||||
android {
|
||||
namespace = "com.xuqm.sdk.log"
|
||||
compileSdk = 35
|
||||
compileSdk = 36
|
||||
defaultConfig { minSdk = 24 }
|
||||
publishing { singleVariant("release") { withSourcesJar() } }
|
||||
|
||||
|
||||
34
sdk-push/README.md
普通文件
34
sdk-push/README.md
普通文件
@ -0,0 +1,34 @@
|
||||
# sdk-push
|
||||
|
||||
XuqmGroup Android SDK 推送模块。支持多厂商推送(华为/小米/OPPO/vivo/荣耀/FCM/APNs)。
|
||||
|
||||
## 依赖
|
||||
|
||||
```kotlin
|
||||
implementation("com.xuqm:sdk-push:VERSION")
|
||||
implementation("com.xuqm:sdk-core:VERSION") // 必须
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
**无需手动初始化。** `XuqmSDK.login()` 成功后自动完成推送 token 注册。`logout()` 自动解绑。
|
||||
|
||||
```kotlin
|
||||
// 开启/关闭接收推送
|
||||
PushSDK.setReceivePush(context, enabled = false)
|
||||
PushSDK.setReceivePush(context, enabled = true)
|
||||
```
|
||||
|
||||
## 支持厂商
|
||||
|
||||
HUAWEI / XIAOMI / OPPO / VIVO / HONOR / FCM / APNS
|
||||
|
||||
## 与 IM 联动
|
||||
|
||||
IM 和推送服务均已开启时,IM 离线消息自动触发推送通知。
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 需要各厂商 SDK 配置(App ID / App Secret)
|
||||
- Firebase Messaging 需提供 `google-services.json`
|
||||
- FCM token 通过 `FirebaseMessagingService.onNewToken()` 自动接收
|
||||
49
sdk-update/README.md
普通文件
49
sdk-update/README.md
普通文件
@ -0,0 +1,49 @@
|
||||
# sdk-update
|
||||
|
||||
XuqmGroup Android SDK 更新模块。提供 App 版本检查和 APK 下载安装能力。
|
||||
|
||||
## 依赖
|
||||
|
||||
```kotlin
|
||||
implementation("com.xuqm:sdk-update:VERSION")
|
||||
implementation("com.xuqm:sdk-core:VERSION") // 必须
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
```kotlin
|
||||
val result = UpdateSDK.checkUpdate(
|
||||
appKey = XuqmSDK.appKey,
|
||||
platform = Platform.ANDROID,
|
||||
)
|
||||
|
||||
if (result.needsUpdate) {
|
||||
UpdateSDK.downloadAndInstall(context, result.downloadUrl)
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| API | 说明 |
|
||||
|-----|------|
|
||||
| `UpdateSDK.checkUpdate(appKey, platform)` | 检查 App 更新 |
|
||||
| `UpdateSDK.downloadAndInstall(context, url)` | 下载 APK 并调起系统安装器 |
|
||||
|
||||
### UpdateResult
|
||||
|
||||
```kotlin
|
||||
data class UpdateResult(
|
||||
val needsUpdate: Boolean,
|
||||
val versionName: String?,
|
||||
val versionCode: Int?,
|
||||
val downloadUrl: String?,
|
||||
val changeLog: String?,
|
||||
val forceUpdate: Boolean?,
|
||||
)
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
- `downloadAndInstall` 将 APK 下载到 `getExternalFilesDir(null)`,通过 `FileProvider` 触发系统安装
|
||||
- AndroidManifest 中已配置 `@xml/file_paths`(`external-files-path`)
|
||||
- 支持 WebSocket 实时推送更新通知
|
||||
69
sdk-webview/README.md
普通文件
69
sdk-webview/README.md
普通文件
@ -0,0 +1,69 @@
|
||||
# sdk-webview
|
||||
|
||||
XuqmGroup Android SDK WebView 模块。提供嵌入式 WebView 组件和独立页面,内置 JSBridge、文件选择、下载拦截。
|
||||
|
||||
## 依赖
|
||||
|
||||
```kotlin
|
||||
implementation("com.xuqm:sdk-webview:VERSION")
|
||||
implementation("com.xuqm:sdk-core:VERSION") // 必须
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
### 嵌入式组件
|
||||
|
||||
```kotlin
|
||||
import com.xuqm.sdk.webview.XWebViewView
|
||||
import com.xuqm.sdk.webview.XWebViewConfig
|
||||
|
||||
XWebViewView(
|
||||
config = XWebViewConfig(
|
||||
url = "https://example.com",
|
||||
title = "嵌入式网页",
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
### 独立页面
|
||||
|
||||
```kotlin
|
||||
import com.xuqm.sdk.webview.openXWebView
|
||||
import com.xuqm.sdk.webview.XWebViewConfig
|
||||
|
||||
openXWebView(XWebViewConfig(url = "https://example.com", title = "独立页面"))
|
||||
// navigate("xwebview") 后使用 XWebViewScreen()
|
||||
```
|
||||
|
||||
## XWebViewConfig 参数
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `url` | String | `""` | 初始加载地址 |
|
||||
| `title` | String | `""` | 页面标题 |
|
||||
| `hideToolbar` | Boolean | `false` | 隐藏顶栏 |
|
||||
| `hideStatusBar` | Boolean | `false` | 隐藏状态栏 |
|
||||
| `userAgent` | String? | `null` | 自定义 User-Agent |
|
||||
| `injectedJavaScript` | String? | `null` | 注入的 JS |
|
||||
| `jsBridgeName` | String | `"XWebViewBridge"` | JS 桥接对象名 |
|
||||
| `debugEnabled` | Boolean | `false` | 开启远程调试 |
|
||||
| `downloadDestination` | FileDownloadDestination | `Sandbox` | 下载存储目标 |
|
||||
| `downloadNotificationTitle` | String? | `null` | 通知栏下载进度 |
|
||||
| `onMessage` | (String) -> Unit? | `null` | H5 消息回调 |
|
||||
|
||||
## JSBridge 通信
|
||||
|
||||
```javascript
|
||||
// H5 → Native
|
||||
window.XWebViewBridge.postMessage(JSON.stringify({ type: 'login', token: '...' }))
|
||||
|
||||
// 监听下载进度
|
||||
window.addEventListener('__xwvDownloadProgress', (e) => { ... })
|
||||
window.addEventListener('__xwvDownloadDone', (e) => { ... })
|
||||
```
|
||||
|
||||
## 文件选择与拍照
|
||||
|
||||
- `<input type="file">` + `accept="image/*"` + `capture` → 系统相机
|
||||
- `<input type="file">` + `.docx/.xlsx` → 文件选择器
|
||||
- `getUserMedia()` WebRTC → 自动请求 CAMERA 权限
|
||||
@ -0,0 +1,82 @@
|
||||
package com.xuqm.sdk.webview
|
||||
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import com.xuqm.sdk.XuqmSDK
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
* Standard JSBridge handlers for xuqm.* messages.
|
||||
* These provide default implementations for hosts that don't use a custom protocol.
|
||||
*
|
||||
* Usage: register in XWebViewView's xwvMessageHandler or onMessage callback.
|
||||
*/
|
||||
object XWebViewStandardHandlers {
|
||||
|
||||
fun handle(message: String, webView: XWebViewController?): String {
|
||||
val json = runCatching { JSONObject(message) }.getOrNull() ?: return errorResponse("invalid JSON")
|
||||
val name = json.optString("name", "")
|
||||
return when (name) {
|
||||
"xuqm.getDeviceInfo" -> handleGetDeviceInfo()
|
||||
"xuqm.getToken" -> handleGetToken()
|
||||
"xuqm.getUserInfo" -> handleGetUserInfo()
|
||||
"xuqm.closeWebView" -> handleCloseWebView(webView)
|
||||
"xuqm.showToast" -> handleShowToast(json)
|
||||
else -> errorResponse("unknown handler: $name")
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGetDeviceInfo(): String {
|
||||
val data = JSONObject().apply {
|
||||
put("platform", "android")
|
||||
put("osVersion", Build.VERSION.RELEASE)
|
||||
put("sdkVersion", Build.VERSION.SDK_INT)
|
||||
put("manufacturer", Build.MANUFACTURER)
|
||||
put("model", Build.MODEL)
|
||||
put("brand", Build.BRAND)
|
||||
}
|
||||
return successResponse(data)
|
||||
}
|
||||
|
||||
private fun handleGetToken(): String {
|
||||
val token = XuqmSDK.currentLoginSession?.userSig ?: ""
|
||||
val data = JSONObject().apply { put("token", token) }
|
||||
return successResponse(data)
|
||||
}
|
||||
|
||||
private fun handleGetUserInfo(): String {
|
||||
val userInfo = XuqmSDK.getUserInfo()
|
||||
if (userInfo == null) return errorResponse("not logged in")
|
||||
val data = JSONObject().apply {
|
||||
put("userId", userInfo.userId ?: "")
|
||||
put("nickname", userInfo.nickname ?: "")
|
||||
put("avatar", userInfo.avatar ?: "")
|
||||
put("phone", userInfo.phone ?: "")
|
||||
}
|
||||
return successResponse(data)
|
||||
}
|
||||
|
||||
private fun handleCloseWebView(webView: XWebViewController?): String {
|
||||
webView?.let { wv ->
|
||||
if (wv.canGoBack()) wv.goBack() else wv.loadUrl("about:blank")
|
||||
}
|
||||
return successResponse(JSONObject())
|
||||
}
|
||||
|
||||
private fun handleShowToast(json: JSONObject): String {
|
||||
val message = json.optString("message", "")
|
||||
if (message.isNotBlank()) {
|
||||
val ctx = XuqmSDK.appContext
|
||||
android.os.Handler(android.os.Looper.getMainLooper()).post {
|
||||
Toast.makeText(ctx, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
return successResponse(JSONObject())
|
||||
}
|
||||
|
||||
private fun successResponse(data: JSONObject): String =
|
||||
JSONObject().apply { put("code", 0); put("data", data) }.toString()
|
||||
|
||||
private fun errorResponse(message: String): String =
|
||||
JSONObject().apply { put("code", -1); put("message", message) }.toString()
|
||||
}
|
||||
正在加载...
在新工单中引用
屏蔽一个用户