docs(sdk): 添加 Android SDK 文档和 API 设计规范
- 新增 Android SDK 使用文档,包含模块结构、集成方式和快速开始指南 - 添加 SDK API 重设计规范,统一初始化和登录接口设计 - 补充安全设计规范,完善 UserSig 鉴权和敏感数据处理方案 - 创建平台 REST API 规范,定义服务端到服务端的调用接口 - 添加离线推送架构设计,集成各大厂商推送服务与 IM 联动方案
这个提交包含在:
父节点
5c97b669ae
当前提交
b3d510ddae
2
.hvigor/cache/file-cache.json
vendored
2
.hvigor/cache/file-cache.json
vendored
文件差异因一行或多行过长而隐藏
2
.hvigor/cache/task-cache.json
vendored
2
.hvigor/cache/task-cache.json
vendored
文件差异因一行或多行过长而隐藏
@ -7,7 +7,6 @@
|
|||||||
"INCLUDE_IN_BUILD": true,
|
"INCLUDE_IN_BUILD": true,
|
||||||
"IS_COMMAND_LINE_ENTRY_MODULE": false,
|
"IS_COMMAND_LINE_ENTRY_MODULE": false,
|
||||||
"MODULE_TYPE": "har",
|
"MODULE_TYPE": "har",
|
||||||
"IS_BYTE_CODE_HAR": true,
|
|
||||||
"IS_INCREMENTAL_MODULE": true
|
"IS_INCREMENTAL_MODULE": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -17,15 +16,14 @@
|
|||||||
"IS_COMMAND_LINE_ENTRY_MODULE": false,
|
"IS_COMMAND_LINE_ENTRY_MODULE": false,
|
||||||
"MODULE_TYPE": "entry",
|
"MODULE_TYPE": "entry",
|
||||||
"INCREMENTAL_TASKS": {
|
"INCREMENTAL_TASKS": {
|
||||||
"COMPILE_ARKTS": false
|
"COMPILE_ARKTS": true
|
||||||
},
|
},
|
||||||
"IS_INCREMENTAL_MODULE": false
|
"IS_INCREMENTAL_MODULE": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"NATIVE_COMPILER": "Default",
|
"NATIVE_COMPILER": "Default",
|
||||||
"IS_FULL_BUILD": true,
|
"IS_FULL_BUILD": false,
|
||||||
"BUILD_MODE": "debug",
|
"BUILD_MODE": "debug"
|
||||||
"USE_NORMALIZED_OHMURL": true
|
|
||||||
},
|
},
|
||||||
"HVIGOR": {
|
"HVIGOR": {
|
||||||
"IS_INCREMENTAL": true,
|
"IS_INCREMENTAL": true,
|
||||||
@ -34,50 +32,20 @@
|
|||||||
"IS_HVIGORFILE_TYPE_CHECK": false,
|
"IS_HVIGORFILE_TYPE_CHECK": false,
|
||||||
"TASK_TIME": {
|
"TASK_TIME": {
|
||||||
"923fe53966c6cd9343e11af776cd4b05be315ea4b200b02e4d5dfb0f929b73bf": {
|
"923fe53966c6cd9343e11af776cd4b05be315ea4b200b02e4d5dfb0f929b73bf": {
|
||||||
"clean": 6854084,
|
"CreateModuleInfo": 591083,
|
||||||
"PreBuild": 57877208,
|
"PreCheckSyscap": 186208,
|
||||||
"CreateModuleInfo": 313958,
|
"ProcessIntegratedHsp": 333500,
|
||||||
"GenerateMetadata": 1059542,
|
"SyscapTransform": 12419000,
|
||||||
"PreCheckSyscap": 113666,
|
"ProcessStartupConfig": 799500,
|
||||||
"GeneratePkgContextInfo": 4513000,
|
"ConfigureCmake": 57125,
|
||||||
"ProcessIntegratedHsp": 210250,
|
"BuildNativeWithCmake": 53875,
|
||||||
"SyscapTransform": 1379833,
|
"BuildNativeWithNinja": 125916,
|
||||||
"ProcessRouterMap": 1444334,
|
"BuildJS": 765334
|
||||||
"ProcessShareConfig": 693041,
|
|
||||||
"ProcessStartupConfig": 484167,
|
|
||||||
"CreateBuildProfile": 407542,
|
|
||||||
"MergeProfile": 1007333,
|
|
||||||
"GenerateLoaderJson": 3654458,
|
|
||||||
"ConfigureCmake": 76708,
|
|
||||||
"MakePackInfo": 1146667,
|
|
||||||
"ProcessProfile": 64121000,
|
|
||||||
"BuildNativeWithCmake": 118208,
|
|
||||||
"ProcessResource": 1388167,
|
|
||||||
"CompileResource": 172291834,
|
|
||||||
"BuildNativeWithNinja": 288083,
|
|
||||||
"BuildJS": 653791,
|
|
||||||
"ProcessLibs": 2090750,
|
|
||||||
"DoNativeStrip": 806875,
|
|
||||||
"CacheNativeLibs": 255417750,
|
|
||||||
"CompileArkTS": 2806613791,
|
|
||||||
"GeneratePkgModuleJson": 794250,
|
|
||||||
"ProcessCompiledResources": 138375,
|
|
||||||
"PackageHap": 221881709,
|
|
||||||
"PackingCheck": 2139958,
|
|
||||||
"SignHap": 820869459,
|
|
||||||
"CollectDebugSymbol": 420750,
|
|
||||||
"assembleHap": 78875
|
|
||||||
},
|
},
|
||||||
"77aabe6c19463543339f337db9c84e4d10fd2f56ea0aedaf85a0214d59e93ec4": {
|
"77aabe6c19463543339f337db9c84e4d10fd2f56ea0aedaf85a0214d59e93ec4": {
|
||||||
"clean": 1606375,
|
"ConfigureCmake": 68125,
|
||||||
"PreBuild": 20062292,
|
"BuildNativeWithCmake": 139875,
|
||||||
"CreateHarBuildProfile": 780417,
|
"BuildNativeWithNinja": 437542
|
||||||
"ConfigureCmake": 129750,
|
|
||||||
"MergeProfile": 1277375,
|
|
||||||
"BuildNativeWithCmake": 71500,
|
|
||||||
"BuildNativeWithNinja": 441083,
|
|
||||||
"ProcessLibs": 2313208,
|
|
||||||
"DoNativeStrip": 1523833
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"APIS": [
|
"APIS": [
|
||||||
@ -88,7 +56,13 @@
|
|||||||
"ENABLE_CPP_FUNCTION_LEVEL_INCREMENTAL": false
|
"ENABLE_CPP_FUNCTION_LEVEL_INCREMENTAL": false
|
||||||
},
|
},
|
||||||
"CONFIG_PROPERTIES": {},
|
"CONFIG_PROPERTIES": {},
|
||||||
"BUILD_ID": "202604290016471500",
|
"BUILD_ID": "202604291536118220",
|
||||||
"TOTAL_TIME": 5208027167
|
"ERROR_MESSAGE": [
|
||||||
|
{
|
||||||
|
"CODE": "10505001",
|
||||||
|
"TIMESTAMP": "1777448175246"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TOTAL_TIME": 3424986416
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
@ -76,8 +76,6 @@ export default class EntryAbility extends UIAbility {
|
|||||||
await XuqmSDK.init(this.context, {
|
await XuqmSDK.init(this.context, {
|
||||||
appKey: 'ak_your_app_key',
|
appKey: 'ak_your_app_key',
|
||||||
appSecret: 'as_your_app_secret',
|
appSecret: 'as_your_app_secret',
|
||||||
apiBaseUrl: 'https://api.xuqm.com',
|
|
||||||
imBaseUrl: 'wss://im.xuqm.com',
|
|
||||||
debug: true,
|
debug: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -195,13 +193,16 @@ import { XuqmSDK } from '@xuqm/harmony-sdk'
|
|||||||
const result = await XuqmSDK.update.checkAppUpdate('ak_xxx')
|
const result = await XuqmSDK.update.checkAppUpdate('ak_xxx')
|
||||||
|
|
||||||
if (result.hasUpdate && result.info) {
|
if (result.hasUpdate && result.info) {
|
||||||
const { latestVersionName, downloadUrl, forceUpdate } = result.info
|
const { latestVersionName, marketUrl, forceUpdate } = result.info
|
||||||
console.log(`发现新版本 ${latestVersionName}`)
|
console.log(`发现新版本 ${latestVersionName}`)
|
||||||
// platform 'harmony': 通常引导用户跳转应用市场
|
if (marketUrl) {
|
||||||
|
await XuqmSDK.update.openAppMarket(getContext(this), marketUrl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`checkAppUpdate` 内部通过 `bundleManager.getBundleInfoForSelfSync` 获取当前 `versionCode`。
|
`checkAppUpdate` 内部通过 `bundleManager.getBundleInfoForSelfSync` 获取当前 `versionCode`。
|
||||||
|
Harmony 版本不提供本地安装包下载,更新只跳转应用市场。
|
||||||
|
|
||||||
### 检查 RN Bundle 热更新
|
### 检查 RN Bundle 热更新
|
||||||
|
|
||||||
|
|||||||
@ -9,8 +9,6 @@ export default class EntryAbility extends UIAbility {
|
|||||||
await XuqmSDK.init(this.context, {
|
await XuqmSDK.init(this.context, {
|
||||||
appKey: 'YOUR_APP_KEY',
|
appKey: 'YOUR_APP_KEY',
|
||||||
appSecret: 'YOUR_APP_SECRET',
|
appSecret: 'YOUR_APP_SECRET',
|
||||||
apiBaseUrl: 'https://api.xuqm.com',
|
|
||||||
imBaseUrl: 'wss://im.xuqm.com',
|
|
||||||
debug: true,
|
debug: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,13 @@
|
|||||||
* Config: xuqm.properties in the module or project root
|
* Config: xuqm.properties in the module or project root
|
||||||
* ---
|
* ---
|
||||||
* xuqm.serverUrl=https://update.dev.xuqinmin.com
|
* xuqm.serverUrl=https://update.dev.xuqinmin.com
|
||||||
* xuqm.appId=your-app-id
|
* xuqm.appKey=your-app-key
|
||||||
* xuqm.apiToken=your-api-token
|
* xuqm.apiToken=your-api-token
|
||||||
* xuqm.storeTargets=HUAWEI # optional: HUAWEI,HONOR,...
|
|
||||||
* xuqm.autoPublishAfterReview=false
|
* xuqm.autoPublishAfterReview=false
|
||||||
|
* xuqm.publishImmediately=false
|
||||||
* xuqm.scheduledPublishAt= # optional ISO datetime
|
* xuqm.scheduledPublishAt= # optional ISO datetime
|
||||||
* xuqm.webhookUrl= # optional
|
* xuqm.webhookUrl= # optional
|
||||||
|
* xuqm.marketUrl= # Harmony app market URL
|
||||||
* ---
|
* ---
|
||||||
*
|
*
|
||||||
* Version is read from AppScope/app.json5 (standard HarmonyOS project layout).
|
* Version is read from AppScope/app.json5 (standard HarmonyOS project layout).
|
||||||
@ -62,6 +63,16 @@ fun readHarmonyVersion(projectDir: File): HarmonyVersion {
|
|||||||
return HarmonyVersion(name, code)
|
return HarmonyVersion(name, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun readHarmonyBundleName(projectDir: File): String {
|
||||||
|
val json5 = listOf(
|
||||||
|
File(projectDir, "AppScope/app.json5"),
|
||||||
|
File(projectDir.parentFile, "AppScope/app.json5"),
|
||||||
|
).firstOrNull { it.exists() } ?: throw GradleException("AppScope/app.json5 not found")
|
||||||
|
val content = json5.readText()
|
||||||
|
return Regex(""""bundleName"\s*:\s*"([^"]+)"""").find(content)?.groupValues?.get(1)
|
||||||
|
?: throw GradleException("bundleName not found in app.json5")
|
||||||
|
}
|
||||||
|
|
||||||
// ── HTTP helpers ──────────────────────────────────────────────────────────
|
// ── HTTP helpers ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
fun httpGet(url: String, token: String): String {
|
fun httpGet(url: String, token: String): String {
|
||||||
@ -98,6 +109,22 @@ fun httpMultipartPost(url: String, token: String, parts: Map<String, Any>): Stri
|
|||||||
fun parseJson(json: String, key: String) =
|
fun parseJson(json: String, key: String) =
|
||||||
Regex(""""$key"\s*:\s*"([^"]+)"""").find(json)?.groupValues?.get(1)
|
Regex(""""$key"\s*:\s*"([^"]+)"""").find(json)?.groupValues?.get(1)
|
||||||
|
|
||||||
|
fun promptLine(message: String): String? {
|
||||||
|
val console = System.console() ?: return null
|
||||||
|
print(message)
|
||||||
|
return console.readLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun promptYesNo(message: String, default: Boolean = false): Boolean {
|
||||||
|
val answer = promptLine(message + if (default) " [Y/n]: " else " [y/N]: ")?.trim().orEmpty()
|
||||||
|
return when {
|
||||||
|
answer.isBlank() -> default
|
||||||
|
answer.equals("y", ignoreCase = true) || answer.equals("yes", ignoreCase = true) -> true
|
||||||
|
answer.equals("n", ignoreCase = true) || answer.equals("no", ignoreCase = true) -> false
|
||||||
|
else -> default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Task ──────────────────────────────────────────────────────────────────
|
// ── Task ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
tasks.register("xuqmRelease") {
|
tasks.register("xuqmRelease") {
|
||||||
@ -110,19 +137,28 @@ tasks.register("xuqmRelease") {
|
|||||||
doLast {
|
doLast {
|
||||||
val cfg = loadXuqmCfg(projectDir)
|
val cfg = loadXuqmCfg(projectDir)
|
||||||
val serverUrl = cfg.getProperty("xuqm.serverUrl") ?: throw GradleException("xuqm.serverUrl missing")
|
val serverUrl = cfg.getProperty("xuqm.serverUrl") ?: throw GradleException("xuqm.serverUrl missing")
|
||||||
val appId = cfg.getProperty("xuqm.appId") ?: throw GradleException("xuqm.appId missing")
|
val appKey = cfg.getProperty("xuqm.appKey") ?: throw GradleException("xuqm.appKey missing")
|
||||||
val apiToken = cfg.getProperty("xuqm.apiToken") ?: throw GradleException("xuqm.apiToken missing")
|
val apiToken = cfg.getProperty("xuqm.apiToken") ?: throw GradleException("xuqm.apiToken missing")
|
||||||
val storeTargets = cfg.getProperty("xuqm.storeTargets", "")
|
|
||||||
val autoPublish = cfg.getProperty("xuqm.autoPublishAfterReview", "false").toBoolean()
|
val autoPublish = cfg.getProperty("xuqm.autoPublishAfterReview", "false").toBoolean()
|
||||||
val scheduledAt = cfg.getProperty("xuqm.scheduledPublishAt", "")
|
val publishImmediately = cfg.getProperty("xuqm.publishImmediately", "false").toBoolean()
|
||||||
|
var scheduledAt = cfg.getProperty("xuqm.scheduledPublishAt", "")
|
||||||
val webhookUrl = cfg.getProperty("xuqm.webhookUrl", "")
|
val webhookUrl = cfg.getProperty("xuqm.webhookUrl", "")
|
||||||
|
val marketUrl = cfg.getProperty("xuqm.marketUrl", "")
|
||||||
|
var publishMode = cfg.getProperty("xuqm.publishMode", "").trim().uppercase()
|
||||||
|
if (publishMode.isBlank() && !publishImmediately && scheduledAt.isBlank() && !autoPublish && System.console() != null) {
|
||||||
|
publishMode = promptReleaseMode()
|
||||||
|
if (publishMode == "SCHEDULED" && scheduledAt.isBlank()) {
|
||||||
|
scheduledAt = promptLine("Scheduled publish time (ISO datetime): ")?.trim().orEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── 1. Read local version ──────────────────────────────────────────
|
// ── 1. Read local version ──────────────────────────────────────────
|
||||||
val (versionName, versionCode) = readHarmonyVersion(projectDir)
|
val (versionName, versionCode) = readHarmonyVersion(projectDir)
|
||||||
println("[xuqm] Local version: $versionName ($versionCode)")
|
val bundleName = readHarmonyBundleName(projectDir)
|
||||||
|
println("[xuqm] Local version: $versionName ($versionCode), appKey: $appKey")
|
||||||
|
|
||||||
// ── 2. Check server ────────────────────────────────────────────────
|
// ── 2. Check server ────────────────────────────────────────────────
|
||||||
val listResp = httpGet("$serverUrl/api/v1/updates/app/list?appId=$appId&platform=ANDROID", apiToken)
|
val listResp = httpGet("$serverUrl/api/v1/updates/app/list?appId=$appKey&platform=HARMONY", apiToken)
|
||||||
val serverCode = Regex(""""versionCode"\s*:\s*(\d+)""").findAll(listResp)
|
val serverCode = Regex(""""versionCode"\s*:\s*(\d+)""").findAll(listResp)
|
||||||
.mapNotNull { it.groupValues[1].toIntOrNull() }.maxOrNull() ?: 0
|
.mapNotNull { it.groupValues[1].toIntOrNull() }.maxOrNull() ?: 0
|
||||||
println("[xuqm] Server latest versionCode: $serverCode")
|
println("[xuqm] Server latest versionCode: $serverCode")
|
||||||
@ -135,47 +171,45 @@ tasks.register("xuqmRelease") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── 3. Locate HAP ──────────────────────────────────────────────────
|
// ── 3. Locate HAP ──────────────────────────────────────────────────
|
||||||
val hapDir = File(projectDir, "entry/build/default/outputs/default")
|
// ── 4. Upload release metadata to update service ──────────────────
|
||||||
val hapFile = hapDir.listFiles { f -> f.extension == "hap" }?.firstOrNull()
|
if (marketUrl.isBlank()) {
|
||||||
?: throw GradleException("HAP not found in ${hapDir.absolutePath}")
|
throw GradleException("xuqm.marketUrl missing for Harmony release")
|
||||||
println("[xuqm] HAP: ${hapFile.absolutePath}")
|
}
|
||||||
|
|
||||||
// ── 4. Upload ──────────────────────────────────────────────────────
|
|
||||||
val parts = mutableMapOf<String, Any>(
|
val parts = mutableMapOf<String, Any>(
|
||||||
"appId" to appId, "platform" to "ANDROID",
|
"appId" to appKey, "platform" to "HARMONY",
|
||||||
"versionName" to versionName, "versionCode" to versionCode,
|
"versionName" to versionName, "versionCode" to versionCode,
|
||||||
"forceUpdate" to "false", "autoPublishAfterReview" to autoPublish.toString(),
|
"forceUpdate" to "false", "autoPublishAfterReview" to autoPublish.toString(),
|
||||||
"apkFile" to hapFile,
|
"packageName" to bundleName,
|
||||||
|
"marketUrl" to marketUrl,
|
||||||
)
|
)
|
||||||
if (storeTargets.isNotBlank()) parts["storeSubmitTargets"] = "[\"${storeTargets.split(",").joinToString("\",\"")}\"]"
|
|
||||||
if (scheduledAt.isNotBlank()) parts["scheduledPublishAt"] = scheduledAt
|
if (scheduledAt.isNotBlank()) parts["scheduledPublishAt"] = scheduledAt
|
||||||
if (webhookUrl.isNotBlank()) parts["webhookUrl"] = webhookUrl
|
if (webhookUrl.isNotBlank()) parts["webhookUrl"] = webhookUrl
|
||||||
|
if (publishMode == "NOW") parts["publishImmediately"] = "true"
|
||||||
|
|
||||||
println("[xuqm] Uploading HAP...")
|
println("[xuqm] Uploading Harmony release metadata...")
|
||||||
val uploadResp = httpMultipartPost("$serverUrl/api/v1/updates/app/upload", apiToken, parts)
|
val uploadResp = httpMultipartPost("$serverUrl/api/v1/updates/app/upload", apiToken, parts)
|
||||||
val versionId = parseJson(uploadResp, "id")
|
val versionId = parseJson(uploadResp, "id")
|
||||||
?: throw GradleException("[xuqm] Upload failed:\n$uploadResp")
|
?: throw GradleException("[xuqm] Upload failed:\n$uploadResp")
|
||||||
println("[xuqm] Uploaded, version ID: $versionId")
|
println("[xuqm] Uploaded, version ID: $versionId")
|
||||||
|
|
||||||
// ── 5. Trigger store submission ────────────────────────────────────
|
if (publishImmediately || publishMode == "NOW") {
|
||||||
if (storeTargets.isNotBlank()) {
|
println("[xuqm] Published immediately in update service.")
|
||||||
println("[xuqm] Triggering store submission: $storeTargets")
|
} else if (publishMode == "SCHEDULED" || scheduledAt.isNotBlank()) {
|
||||||
val body = """{"storeTypes":[${storeTargets.split(",").joinToString(",") { "\"$it\"" }}]}"""
|
println("[xuqm] Will auto-publish at: ${scheduledAt.ifBlank { "(use update service scheduled publish config)" }}")
|
||||||
|
} else if (autoPublish) {
|
||||||
|
println("[xuqm] Will auto-publish after store reviews pass.")
|
||||||
|
} else if (System.console() != null && promptYesNo("[xuqm] Publish now?", false)) {
|
||||||
val client = HttpClient.newHttpClient()
|
val client = HttpClient.newHttpClient()
|
||||||
val req = HttpRequest.newBuilder(URI.create("$serverUrl/api/v1/updates/store/app/$versionId/execute-submit"))
|
val req = HttpRequest.newBuilder(URI.create("$serverUrl/api/v1/updates/app/$versionId/publish"))
|
||||||
.header("Authorization", "Bearer $apiToken")
|
.header("Authorization", "Bearer $apiToken")
|
||||||
.header("Content-Type", "application/json")
|
.POST(HttpRequest.BodyPublishers.noBody())
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(body)).build()
|
.build()
|
||||||
val storeResp = client.send(req, HttpResponse.BodyHandlers.ofString())
|
val publishResp = client.send(req, HttpResponse.BodyHandlers.ofString())
|
||||||
println("[xuqm] Store submission HTTP ${storeResp.statusCode()}")
|
println("[xuqm] Publish HTTP ${publishResp.statusCode()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
println("[xuqm] Done.")
|
println("[xuqm] Done.")
|
||||||
if (scheduledAt.isNotBlank()) {
|
if (!publishImmediately && publishMode != "NOW" && scheduledAt.isBlank() && !autoPublish) {
|
||||||
println("[xuqm] Will publish at: $scheduledAt")
|
|
||||||
} else if (autoPublish) {
|
|
||||||
println("[xuqm] Will auto-publish after store reviews pass.")
|
|
||||||
} else {
|
|
||||||
println("[xuqm] Publish manually: POST $serverUrl/api/v1/updates/app/$versionId/publish")
|
println("[xuqm] Publish manually: POST $serverUrl/api/v1/updates/app/$versionId/publish")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
export const DEFAULT_API_BASE_URL = 'https://dev.xuqinmin.com'
|
||||||
|
export const DEFAULT_IM_WS_URL = 'wss://dev.xuqinmin.com/ws/im'
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import http from '@ohos.net.http'
|
import http from '@ohos.net.http'
|
||||||
import type { ApiResponse, HttpHeaders } from './Types'
|
import type { ApiResponse, HttpHeaders } from './Types'
|
||||||
import { SDKContext } from './SDKContext'
|
import { SDKContext } from './SDKContext'
|
||||||
|
import { DEFAULT_API_BASE_URL } from './Endpoints'
|
||||||
|
|
||||||
export class HttpClient {
|
export class HttpClient {
|
||||||
static async request<T>(
|
static async request<T>(
|
||||||
@ -11,7 +12,7 @@ export class HttpClient {
|
|||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const config = SDKContext.getConfig()
|
const config = SDKContext.getConfig()
|
||||||
const token = SDKContext.getToken()
|
const token = SDKContext.getToken()
|
||||||
const url = config.apiBaseUrl.replace(/\/$/, '') + path + (query ? '?' + query : '')
|
const url = DEFAULT_API_BASE_URL.replace(/\/$/, '') + path + (query ? '?' + query : '')
|
||||||
|
|
||||||
const client = http.createHttp()
|
const client = http.createHttp()
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
export interface SDKConfig {
|
export interface SDKConfig {
|
||||||
appKey: string
|
appKey: string
|
||||||
appSecret: string
|
appSecret: string
|
||||||
apiBaseUrl: string
|
|
||||||
imBaseUrl: string
|
|
||||||
debug: boolean
|
debug: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +143,8 @@ export interface AppVersionInfo {
|
|||||||
latestVersionCode: number
|
latestVersionCode: number
|
||||||
latestVersionName: string
|
latestVersionName: string
|
||||||
downloadUrl: string
|
downloadUrl: string
|
||||||
|
marketUrl?: string
|
||||||
|
appStoreUrl?: string
|
||||||
forceUpdate: boolean
|
forceUpdate: boolean
|
||||||
releaseNotes: string
|
releaseNotes: string
|
||||||
}
|
}
|
||||||
@ -154,6 +154,8 @@ export interface RnBundleInfo {
|
|||||||
downloadUrl: string
|
downloadUrl: string
|
||||||
md5: string
|
md5: string
|
||||||
forceUpdate: boolean
|
forceUpdate: boolean
|
||||||
|
packageName?: string
|
||||||
|
packageMatched?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PushTokenInfo {
|
export interface PushTokenInfo {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import webSocket from '@ohos.net.webSocket'
|
import webSocket from '@ohos.net.webSocket'
|
||||||
import { HttpClient } from '../core/HttpClient'
|
import { HttpClient } from '../core/HttpClient'
|
||||||
import { SDKContext } from '../core/SDKContext'
|
import { SDKContext } from '../core/SDKContext'
|
||||||
|
import { DEFAULT_IM_WS_URL } from '../core/Endpoints'
|
||||||
import type {
|
import type {
|
||||||
ChatType,
|
ChatType,
|
||||||
ConversationData,
|
ConversationData,
|
||||||
@ -129,7 +130,7 @@ export class ImClient {
|
|||||||
if (this.destroyed) return
|
if (this.destroyed) return
|
||||||
const config = SDKContext.getConfig()
|
const config = SDKContext.getConfig()
|
||||||
const token = SDKContext.getToken() ?? ''
|
const token = SDKContext.getToken() ?? ''
|
||||||
const url = config.imBaseUrl.replace(/\/$/, '') + '/ws/im?token=' + encodeURIComponent(token)
|
const url = DEFAULT_IM_WS_URL.replace(/\/$/, '') + '?token=' + encodeURIComponent(token)
|
||||||
|
|
||||||
this.ws = webSocket.createWebSocket()
|
this.ws = webSocket.createWebSocket()
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import bundleManager from '@ohos.bundle.bundleManager'
|
import bundleManager from '@ohos.bundle.bundleManager'
|
||||||
import request from '@ohos.request'
|
|
||||||
import common from '@ohos.app.ability.common'
|
import common from '@ohos.app.ability.common'
|
||||||
|
import request from '@ohos.request'
|
||||||
|
import type { BusinessError } from '@ohos.base'
|
||||||
import { HttpClient } from '../core/HttpClient'
|
import { HttpClient } from '../core/HttpClient'
|
||||||
import type { AppVersionInfo, RnBundleInfo } from '../core/Types'
|
import type { AppVersionInfo, RnBundleInfo } from '../core/Types'
|
||||||
import { SDKContext } from '../core/SDKContext'
|
import { SDKContext } from '../core/SDKContext'
|
||||||
@ -23,7 +24,7 @@ export class UpdateSDK {
|
|||||||
const currentVersionCode = bundleInfo.versionCode
|
const currentVersionCode = bundleInfo.versionCode
|
||||||
|
|
||||||
const data = await HttpClient.get<AppVersionInfo>(
|
const data = await HttpClient.get<AppVersionInfo>(
|
||||||
`/api/v1/updates/app/check?appKey=${appKey}&versionCode=${currentVersionCode}&platform=harmony`
|
`/api/v1/updates/app/check?appKey=${appKey}&versionCode=${currentVersionCode}&platform=HARMONY`
|
||||||
)
|
)
|
||||||
|
|
||||||
if (data.latestVersionCode <= currentVersionCode) {
|
if (data.latestVersionCode <= currentVersionCode) {
|
||||||
@ -32,18 +33,29 @@ export class UpdateSDK {
|
|||||||
return { hasUpdate: true, info: data }
|
return { hasUpdate: true, info: data }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async openAppMarket(context: common.UIAbilityContext, url?: string): Promise<void> {
|
||||||
|
if (!url) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await context.openLink(url, { appLinkingOnly: false })
|
||||||
|
}
|
||||||
|
|
||||||
static async checkRnUpdate(
|
static async checkRnUpdate(
|
||||||
appKey: string,
|
appKey: string,
|
||||||
bundleName: string,
|
bundleName: string,
|
||||||
currentBundleVersion: number
|
currentBundleVersion: number,
|
||||||
|
packageName?: string
|
||||||
): Promise<RnUpdateResult> {
|
): Promise<RnUpdateResult> {
|
||||||
const data = await HttpClient.get<RnBundleInfo>(
|
const data = await HttpClient.get<RnBundleInfo>(
|
||||||
`/api/v1/rn/update/check?appKey=${appKey}&bundleName=${bundleName}&bundleVersion=${currentBundleVersion}`
|
`/api/v1/rn/update/check?appKey=${appKey}&bundleName=${bundleName}&bundleVersion=${currentBundleVersion}${packageName ? `&packageName=${encodeURIComponent(packageName)}` : ''}`
|
||||||
)
|
)
|
||||||
|
|
||||||
if (data.bundleVersion <= currentBundleVersion) {
|
if (data.bundleVersion <= currentBundleVersion) {
|
||||||
return { hasUpdate: false }
|
return { hasUpdate: false }
|
||||||
}
|
}
|
||||||
|
if (packageName && data.packageMatched === false) {
|
||||||
|
return { hasUpdate: false, info: data }
|
||||||
|
}
|
||||||
return { hasUpdate: true, info: data }
|
return { hasUpdate: true, info: data }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +69,7 @@ export class UpdateSDK {
|
|||||||
request.downloadFile(context, {
|
request.downloadFile(context, {
|
||||||
url: downloadUrl,
|
url: downloadUrl,
|
||||||
filePath: destPath,
|
filePath: destPath,
|
||||||
}, (err, task) => {
|
}, (err: BusinessError | null, task: request.DownloadTask) => {
|
||||||
if (err) { reject(err); return }
|
if (err) { reject(err); return }
|
||||||
task.on('complete', resolve)
|
task.on('complete', resolve)
|
||||||
task.on('fail', (error: number) => reject(new Error(`Download failed: ${error}`)))
|
task.on('fail', (error: number) => reject(new Error(`Download failed: ${error}`)))
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户