chore: initial commit
这个提交包含在:
当前提交
90bd57a69c
10
.gitignore
vendored
普通文件
10
.gitignore
vendored
普通文件
@ -0,0 +1,10 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.DS_Store
|
||||
*.class
|
||||
target/
|
||||
build/
|
||||
.gradle/
|
||||
*.iml
|
||||
.idea/
|
||||
*.log
|
||||
39
build-profile.json5
普通文件
39
build-profile.json5
普通文件
@ -0,0 +1,39 @@
|
||||
{
|
||||
"app": {
|
||||
"signingConfigs": [],
|
||||
"products": [
|
||||
{
|
||||
"name": "default",
|
||||
"signingConfig": "default",
|
||||
"compatibleSdkVersion": "5.0.0(12)",
|
||||
"runtimeOS": "HarmonyOS",
|
||||
"buildOption": {
|
||||
"strictMode": {
|
||||
"caseSensitiveCheck": true,
|
||||
"useNormalizedOHMUrl": true
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildModeSet": [
|
||||
{ "name": "debug" },
|
||||
{ "name": "release" }
|
||||
]
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"name": "xuqm-sdk",
|
||||
"srcPath": "./xuqm-sdk",
|
||||
"targets": [
|
||||
{ "name": "default", "applyToProducts": ["default"] }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "entry",
|
||||
"srcPath": "./entry",
|
||||
"targets": [
|
||||
{ "name": "default", "applyToProducts": ["default"] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
7
entry/build-profile.json5
普通文件
7
entry/build-profile.json5
普通文件
@ -0,0 +1,7 @@
|
||||
{
|
||||
"apiType": "stageMode",
|
||||
"buildOption": {},
|
||||
"targets": [
|
||||
{ "name": "default", "runtimeOS": "HarmonyOS" }
|
||||
]
|
||||
}
|
||||
6
entry/hvigorfile.ts
普通文件
6
entry/hvigorfile.ts
普通文件
@ -0,0 +1,6 @@
|
||||
import { hapTasks } from '@ohos/hvigor-ohos-plugin'
|
||||
|
||||
export default {
|
||||
system: hapTasks,
|
||||
plugins: []
|
||||
}
|
||||
11
entry/oh-package.json5
普通文件
11
entry/oh-package.json5
普通文件
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "entry",
|
||||
"version": "1.0.0",
|
||||
"description": "SDK sample app",
|
||||
"main": "",
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@xuqm/harmony-sdk": "file:../xuqm-sdk"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import UIAbility from '@ohos.app.ability.UIAbility'
|
||||
import hilog from '@ohos.hilog'
|
||||
import window from '@ohos.window'
|
||||
import { XuqmSDK } from '@xuqm/harmony-sdk'
|
||||
|
||||
export default class EntryAbility extends UIAbility {
|
||||
async onCreate(): Promise<void> {
|
||||
hilog.info(0x0000, 'EntryAbility', 'onCreate')
|
||||
await XuqmSDK.init(this.context, {
|
||||
appKey: 'YOUR_APP_KEY',
|
||||
appSecret: 'YOUR_APP_SECRET',
|
||||
apiBaseUrl: 'https://api.xuqm.com',
|
||||
imBaseUrl: 'wss://im.xuqm.com',
|
||||
debug: true,
|
||||
})
|
||||
}
|
||||
|
||||
onWindowStageCreate(windowStage: window.WindowStage): void {
|
||||
windowStage.loadContent('pages/Index', (err) => {
|
||||
if (err.code) {
|
||||
hilog.error(0x0000, 'EntryAbility', 'loadContent failed: %{public}s', JSON.stringify(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
import { XuqmSDK, ImMessage } from '@xuqm/harmony-sdk'
|
||||
import promptAction from '@ohos.promptAction'
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct Index {
|
||||
@State messages: ImMessage[] = []
|
||||
@State inputText: string = ''
|
||||
@State connected: boolean = false
|
||||
private toUserId: string = 'user_002'
|
||||
|
||||
aboutToAppear(): void {
|
||||
const im = XuqmSDK.im
|
||||
im.delegate = {
|
||||
onConnected: () => {
|
||||
this.connected = true
|
||||
promptAction.showToast({ message: 'IM 已连接' })
|
||||
},
|
||||
onDisconnected: (code, reason) => {
|
||||
this.connected = false
|
||||
console.log(`IM disconnected: ${code} ${reason}`)
|
||||
},
|
||||
onMessage: (msg) => {
|
||||
this.messages = [...this.messages, msg]
|
||||
},
|
||||
onError: (err) => {
|
||||
promptAction.showToast({ message: 'IM 错误: ' + err })
|
||||
},
|
||||
}
|
||||
im.connect()
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
XuqmSDK.im.disconnect()
|
||||
}
|
||||
|
||||
build() {
|
||||
Column({ space: 12 }) {
|
||||
Row() {
|
||||
Text('XuqmSDK 示例')
|
||||
.fontSize(20)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
Blank()
|
||||
Text(this.connected ? '● 已连接' : '○ 未连接')
|
||||
.fontSize(14)
|
||||
.fontColor(this.connected ? Color.Green : Color.Gray)
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ left: 16, right: 16, top: 16 })
|
||||
|
||||
List({ space: 8 }) {
|
||||
ForEach(this.messages, (msg: ImMessage) => {
|
||||
ListItem() {
|
||||
Column({ space: 4 }) {
|
||||
Text(msg.fromId).fontSize(12).fontColor(Color.Gray)
|
||||
Text(msg.content).fontSize(15)
|
||||
}
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.width('100%')
|
||||
.padding(10)
|
||||
.backgroundColor('#F5F5F5')
|
||||
.borderRadius(8)
|
||||
}
|
||||
})
|
||||
}
|
||||
.layoutWeight(1)
|
||||
.width('100%')
|
||||
.padding({ left: 16, right: 16 })
|
||||
|
||||
Row({ space: 8 }) {
|
||||
TextInput({ placeholder: '输入消息...', text: this.inputText })
|
||||
.layoutWeight(1)
|
||||
.onChange((val) => { this.inputText = val })
|
||||
|
||||
Button('发送')
|
||||
.onClick(() => {
|
||||
if (!this.inputText.trim()) return
|
||||
try {
|
||||
XuqmSDK.im.send({
|
||||
toId: this.toUserId,
|
||||
chatType: 'SINGLE',
|
||||
msgType: 'TEXT',
|
||||
content: this.inputText.trim(),
|
||||
})
|
||||
this.inputText = ''
|
||||
} catch (e) {
|
||||
promptAction.showToast({ message: (e as Error).message })
|
||||
}
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ left: 16, right: 16, bottom: 16 })
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor(Color.White)
|
||||
}
|
||||
}
|
||||
34
entry/src/main/module.json5
普通文件
34
entry/src/main/module.json5
普通文件
@ -0,0 +1,34 @@
|
||||
{
|
||||
"module": {
|
||||
"name": "entry",
|
||||
"type": "entry",
|
||||
"description": "$string:module_desc",
|
||||
"mainElement": "EntryAbility",
|
||||
"deviceTypes": ["phone", "tablet"],
|
||||
"deliveryWithInstall": true,
|
||||
"installationFree": false,
|
||||
"pages": "$profile:main_pages",
|
||||
"abilities": [
|
||||
{
|
||||
"name": "EntryAbility",
|
||||
"srcEntry": "./ets/entryability/EntryAbility.ets",
|
||||
"description": "$string:EntryAbility_desc",
|
||||
"icon": "$media:app_icon",
|
||||
"label": "$string:EntryAbility_label",
|
||||
"startWindowIcon": "$media:app_icon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"exported": true,
|
||||
"skills": [
|
||||
{
|
||||
"entities": ["entity.system.home"],
|
||||
"actions": ["action.system.home"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"requestPermissions": [
|
||||
{ "name": "ohos.permission.INTERNET" },
|
||||
{ "name": "ohos.permission.GET_BUNDLE_INFO" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"color": [
|
||||
{ "name": "start_window_background", "value": "#FFFFFF" }
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"string": [
|
||||
{ "name": "module_desc", "value": "XuqmSDK Sample" },
|
||||
{ "name": "EntryAbility_desc", "value": "XuqmSDK Sample App" },
|
||||
{ "name": "EntryAbility_label", "value": "XuqmSDK" }
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"src": ["pages/Index"]
|
||||
}
|
||||
6
hvigorfile.ts
普通文件
6
hvigorfile.ts
普通文件
@ -0,0 +1,6 @@
|
||||
import { appTasks } from '@ohos/hvigor-ohos-plugin'
|
||||
|
||||
export default {
|
||||
system: appTasks,
|
||||
plugins: []
|
||||
}
|
||||
7
oh-package.json5
普通文件
7
oh-package.json5
普通文件
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "xuqm-harmony-sdk-workspace",
|
||||
"version": "0.1.0",
|
||||
"description": "XuqmGroup HarmonyOS SDK workspace",
|
||||
"author": "xuqm",
|
||||
"license": "MIT"
|
||||
}
|
||||
19
xuqm-sdk/Index.ets
普通文件
19
xuqm-sdk/Index.ets
普通文件
@ -0,0 +1,19 @@
|
||||
export { XuqmSDK } from './src/main/ets/XuqmSDK'
|
||||
export { ImClient } from './src/main/ets/im/ImClient'
|
||||
export { PushSDK } from './src/main/ets/push/PushSDK'
|
||||
export { UpdateSDK } from './src/main/ets/update/UpdateSDK'
|
||||
export { SDKContext } from './src/main/ets/core/SDKContext'
|
||||
export { HttpClient } from './src/main/ets/core/HttpClient'
|
||||
export type {
|
||||
SDKConfig,
|
||||
ImMessage,
|
||||
SendMessageParams,
|
||||
AppVersionInfo,
|
||||
RnBundleInfo,
|
||||
PushTokenInfo,
|
||||
MsgType,
|
||||
ChatType,
|
||||
ApiResponse,
|
||||
} from './src/main/ets/core/Types'
|
||||
export type { ImEventDelegate, RevokeData } from './src/main/ets/im/ImClient'
|
||||
export type { AppUpdateResult, RnUpdateResult } from './src/main/ets/update/UpdateSDK'
|
||||
7
xuqm-sdk/build-profile.json5
普通文件
7
xuqm-sdk/build-profile.json5
普通文件
@ -0,0 +1,7 @@
|
||||
{
|
||||
"apiType": "stageMode",
|
||||
"buildOption": {},
|
||||
"targets": [
|
||||
{ "name": "default" }
|
||||
]
|
||||
}
|
||||
6
xuqm-sdk/hvigorfile.ts
普通文件
6
xuqm-sdk/hvigorfile.ts
普通文件
@ -0,0 +1,6 @@
|
||||
import { harTasks } from '@ohos/hvigor-ohos-plugin'
|
||||
|
||||
export default {
|
||||
system: harTasks,
|
||||
plugins: []
|
||||
}
|
||||
12
xuqm-sdk/oh-package.json5
普通文件
12
xuqm-sdk/oh-package.json5
普通文件
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@xuqm/harmony-sdk",
|
||||
"version": "0.1.0",
|
||||
"description": "XuqmGroup HarmonyOS SDK — IM, Push, Version Management",
|
||||
"main": "Index.ets",
|
||||
"author": "xuqm",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"registry": "https://ohpm.openharmony.cn/ohpm/"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
import common from '@ohos.app.ability.common'
|
||||
import type { SDKConfig } from './core/Types'
|
||||
import { SDKContext } from './core/SDKContext'
|
||||
import { ImClient } from './im/ImClient'
|
||||
import { PushSDK } from './push/PushSDK'
|
||||
import { UpdateSDK } from './update/UpdateSDK'
|
||||
|
||||
export class XuqmSDK {
|
||||
private static _imClient: ImClient | null = null
|
||||
|
||||
static async init(context: common.UIAbilityContext, config: SDKConfig): Promise<void> {
|
||||
SDKContext.init(config)
|
||||
await SDKContext.initPreferences(context)
|
||||
}
|
||||
|
||||
static async setToken(token: string | null): Promise<void> {
|
||||
await SDKContext.setToken(token)
|
||||
}
|
||||
|
||||
static getToken(): string | null {
|
||||
return SDKContext.getToken()
|
||||
}
|
||||
|
||||
static get im(): ImClient {
|
||||
if (!XuqmSDK._imClient) {
|
||||
XuqmSDK._imClient = new ImClient()
|
||||
}
|
||||
return XuqmSDK._imClient
|
||||
}
|
||||
|
||||
static get push(): typeof PushSDK {
|
||||
return PushSDK
|
||||
}
|
||||
|
||||
static get update(): typeof UpdateSDK {
|
||||
return UpdateSDK
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
import http from '@ohos.net.http'
|
||||
import type { ApiResponse } from './Types'
|
||||
import { SDKContext } from './SDKContext'
|
||||
|
||||
export class HttpClient {
|
||||
static async request<T>(method: http.RequestMethod, path: string, body?: object): Promise<T> {
|
||||
const config = SDKContext.getConfig()
|
||||
const token = SDKContext.getToken()
|
||||
const url = config.apiBaseUrl.replace(/\/$/, '') + path
|
||||
|
||||
const client = http.createHttp()
|
||||
try {
|
||||
const options: http.HttpRequestOptions = {
|
||||
method,
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { 'Authorization': `Bearer ${token}` } : {}),
|
||||
},
|
||||
extraData: body ? JSON.stringify(body) : undefined,
|
||||
connectTimeout: 15000,
|
||||
readTimeout: 15000,
|
||||
}
|
||||
|
||||
const res = await client.request(url, options)
|
||||
const json: ApiResponse<T> = JSON.parse(res.result as string) as ApiResponse<T>
|
||||
if (json.code !== 200) throw new Error(json.message)
|
||||
return json.data
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
static get<T>(path: string): Promise<T> {
|
||||
return HttpClient.request<T>(http.RequestMethod.GET, path)
|
||||
}
|
||||
|
||||
static post<T>(path: string, body?: object): Promise<T> {
|
||||
return HttpClient.request<T>(http.RequestMethod.POST, path, body)
|
||||
}
|
||||
|
||||
static put<T>(path: string, body?: object): Promise<T> {
|
||||
return HttpClient.request<T>(http.RequestMethod.PUT, path, body)
|
||||
}
|
||||
|
||||
static delete<T>(path: string): Promise<T> {
|
||||
return HttpClient.request<T>(http.RequestMethod.DELETE, path)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
import preferences from '@ohos.data.preferences'
|
||||
import type { SDKConfig } from './Types'
|
||||
|
||||
const TOKEN_KEY = 'xuqm_token'
|
||||
const PREF_NAME = 'xuqm_sdk_prefs'
|
||||
|
||||
export class SDKContext {
|
||||
private static _config: SDKConfig | null = null
|
||||
private static _token: string | null = null
|
||||
private static _pref: preferences.Preferences | null = null
|
||||
|
||||
static init(config: SDKConfig): void {
|
||||
SDKContext._config = config
|
||||
if (config.debug) {
|
||||
console.log('[XuqmSDK] init appKey=' + config.appKey)
|
||||
}
|
||||
}
|
||||
|
||||
static getConfig(): SDKConfig {
|
||||
if (!SDKContext._config) {
|
||||
throw new Error('XuqmSDK not initialized. Call XuqmSDK.init() first.')
|
||||
}
|
||||
return SDKContext._config
|
||||
}
|
||||
|
||||
static async initPreferences(context: Context): Promise<void> {
|
||||
SDKContext._pref = await preferences.getPreferences(context, PREF_NAME)
|
||||
const saved = await SDKContext._pref.get(TOKEN_KEY, '') as string
|
||||
if (saved) SDKContext._token = saved
|
||||
}
|
||||
|
||||
static async setToken(token: string | null): Promise<void> {
|
||||
SDKContext._token = token
|
||||
if (SDKContext._pref) {
|
||||
if (token) {
|
||||
await SDKContext._pref.put(TOKEN_KEY, token)
|
||||
} else {
|
||||
await SDKContext._pref.delete(TOKEN_KEY)
|
||||
}
|
||||
await SDKContext._pref.flush()
|
||||
}
|
||||
}
|
||||
|
||||
static getToken(): string | null {
|
||||
return SDKContext._token
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
export interface SDKConfig {
|
||||
appKey: string
|
||||
appSecret: string
|
||||
apiBaseUrl: string
|
||||
imBaseUrl: string
|
||||
debug: boolean
|
||||
}
|
||||
|
||||
export interface ApiResponse<T> {
|
||||
code: number
|
||||
status: string
|
||||
data: T
|
||||
message: string
|
||||
}
|
||||
|
||||
export type MsgType =
|
||||
| 'TEXT'
|
||||
| 'IMAGE'
|
||||
| 'VIDEO'
|
||||
| 'AUDIO'
|
||||
| 'FILE'
|
||||
| 'CUSTOM'
|
||||
| 'LOCATION'
|
||||
| 'NOTIFY'
|
||||
| 'RICH_TEXT'
|
||||
| 'CALL_AUDIO'
|
||||
| 'CALL_VIDEO'
|
||||
| 'REVOKED'
|
||||
| 'FORWARD'
|
||||
|
||||
export type ChatType = 'SINGLE' | 'GROUP'
|
||||
|
||||
export interface ImMessage {
|
||||
id: string
|
||||
fromId: string
|
||||
toId: string
|
||||
chatType: ChatType
|
||||
msgType: MsgType
|
||||
content: string
|
||||
extra?: string
|
||||
revoked: boolean
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface SendMessageParams {
|
||||
toId: string
|
||||
chatType: ChatType
|
||||
msgType: MsgType
|
||||
content: string
|
||||
extra?: string
|
||||
}
|
||||
|
||||
export interface AppVersionInfo {
|
||||
latestVersionCode: number
|
||||
latestVersionName: string
|
||||
downloadUrl: string
|
||||
forceUpdate: boolean
|
||||
releaseNotes: string
|
||||
}
|
||||
|
||||
export interface RnBundleInfo {
|
||||
bundleVersion: number
|
||||
downloadUrl: string
|
||||
md5: string
|
||||
forceUpdate: boolean
|
||||
}
|
||||
|
||||
export interface PushTokenInfo {
|
||||
vendor: string
|
||||
token: string
|
||||
platform: string
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
import webSocket from '@ohos.net.webSocket'
|
||||
import type { ImMessage, SendMessageParams } from '../core/Types'
|
||||
import { SDKContext } from '../core/SDKContext'
|
||||
|
||||
export interface ImEventDelegate {
|
||||
onConnected?(): void
|
||||
onDisconnected?(code: number, reason: string): void
|
||||
onMessage?(msg: ImMessage): void
|
||||
onRevoke?(data: RevokeData): void
|
||||
onError?(message: string): void
|
||||
}
|
||||
|
||||
export interface RevokeData {
|
||||
msgId: string
|
||||
operatorId: string
|
||||
}
|
||||
|
||||
const MAX_RECONNECT_DELAY = 30_000
|
||||
|
||||
export class ImClient {
|
||||
private ws: webSocket.WebSocket | null = null
|
||||
private reconnectDelay: number = 3_000
|
||||
private reconnectTimer: number | null = null
|
||||
private destroyed: boolean = false
|
||||
delegate: ImEventDelegate | null = null
|
||||
|
||||
connect(): void {
|
||||
if (this.destroyed) return
|
||||
const config = SDKContext.getConfig()
|
||||
const token = SDKContext.getToken() ?? ''
|
||||
const url = `${config.imBaseUrl}/ws/im?token=${token}`
|
||||
|
||||
this.ws = webSocket.createWebSocket()
|
||||
|
||||
this.ws.on('open', (_err: Error, _value: Object) => {
|
||||
this.reconnectDelay = 3_000
|
||||
if (config.debug) console.log('[ImClient] connected')
|
||||
this.delegate?.onConnected?.()
|
||||
})
|
||||
|
||||
this.ws.on('message', (_err: Error, value: string | ArrayBuffer) => {
|
||||
try {
|
||||
const text = typeof value === 'string' ? value : new TextDecoder().decode(value)
|
||||
const frame = JSON.parse(text) as { type: string; payload: object }
|
||||
if (frame.type === 'MESSAGE') {
|
||||
this.delegate?.onMessage?.(frame.payload as ImMessage)
|
||||
} else if (frame.type === 'REVOKE') {
|
||||
this.delegate?.onRevoke?.(frame.payload as RevokeData)
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed frames
|
||||
}
|
||||
})
|
||||
|
||||
this.ws.on('close', (_err: Error, value: webSocket.CloseResult) => {
|
||||
this.delegate?.onDisconnected?.(value.code, value.reason)
|
||||
if (!this.destroyed) this.scheduleReconnect()
|
||||
})
|
||||
|
||||
this.ws.on('error', (_err: Error) => {
|
||||
this.delegate?.onError?.(_err.message)
|
||||
})
|
||||
|
||||
this.ws.connect(url, {})
|
||||
}
|
||||
|
||||
send(params: SendMessageParams): void {
|
||||
if (!this.ws) throw new Error('WebSocket not connected')
|
||||
const frame = JSON.stringify({ destination: '/app/chat.send', payload: params })
|
||||
this.ws.send(frame, (_err: Error) => {
|
||||
if (_err) console.error('[ImClient] send error', _err.message)
|
||||
})
|
||||
}
|
||||
|
||||
revoke(msgId: string): void {
|
||||
if (!this.ws) throw new Error('WebSocket not connected')
|
||||
const frame = JSON.stringify({ destination: '/app/chat.revoke', payload: { msgId } })
|
||||
this.ws.send(frame, (_err: Error) => {
|
||||
if (_err) console.error('[ImClient] revoke error', _err.message)
|
||||
})
|
||||
}
|
||||
|
||||
disconnect(): void {
|
||||
this.destroyed = true
|
||||
if (this.reconnectTimer !== null) {
|
||||
clearTimeout(this.reconnectTimer)
|
||||
this.reconnectTimer = null
|
||||
}
|
||||
this.ws?.close((_err: Error) => {})
|
||||
this.ws = null
|
||||
}
|
||||
|
||||
private scheduleReconnect(): void {
|
||||
if (this.destroyed) return
|
||||
const delay = this.reconnectDelay
|
||||
if (SDKContext.getConfig().debug) console.log(`[ImClient] reconnect in ${delay}ms`)
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.connect()
|
||||
this.reconnectDelay = Math.min(this.reconnectDelay * 2, MAX_RECONNECT_DELAY)
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
import { HttpClient } from '../core/HttpClient'
|
||||
import type { PushTokenInfo } from '../core/Types'
|
||||
|
||||
export class PushSDK {
|
||||
/**
|
||||
* Register a push token with the server.
|
||||
* Call this after obtaining the token from HarmonyOS push service.
|
||||
* vendor should be 'HARMONY' for HarmonyOS devices.
|
||||
*/
|
||||
static async registerToken(token: string, imUserId?: string): Promise<void> {
|
||||
const body: PushTokenInfo = {
|
||||
vendor: 'HARMONY',
|
||||
token,
|
||||
platform: 'harmony',
|
||||
}
|
||||
await HttpClient.post<void>('/api/v1/push/register', {
|
||||
...body,
|
||||
imUserId: imUserId ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister push token on logout.
|
||||
*/
|
||||
static async unregisterToken(token: string): Promise<void> {
|
||||
await HttpClient.post<void>('/api/v1/push/unregister', { token, platform: 'harmony' })
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
import bundleManager from '@ohos.bundle.bundleManager'
|
||||
import request from '@ohos.request'
|
||||
import common from '@ohos.app.ability.common'
|
||||
import { HttpClient } from '../core/HttpClient'
|
||||
import type { AppVersionInfo, RnBundleInfo } from '../core/Types'
|
||||
import { SDKContext } from '../core/SDKContext'
|
||||
|
||||
export interface AppUpdateResult {
|
||||
hasUpdate: boolean
|
||||
info?: AppVersionInfo
|
||||
}
|
||||
|
||||
export interface RnUpdateResult {
|
||||
hasUpdate: boolean
|
||||
info?: RnBundleInfo
|
||||
}
|
||||
|
||||
export class UpdateSDK {
|
||||
static async checkAppUpdate(appKey: string): Promise<AppUpdateResult> {
|
||||
const bundleInfo = bundleManager.getBundleInfoForSelfSync(
|
||||
bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
|
||||
)
|
||||
const currentVersionCode = bundleInfo.versionCode
|
||||
|
||||
const data = await HttpClient.get<AppVersionInfo>(
|
||||
`/api/v1/updates/app/check?appKey=${appKey}&versionCode=${currentVersionCode}&platform=harmony`
|
||||
)
|
||||
|
||||
if (data.latestVersionCode <= currentVersionCode) {
|
||||
return { hasUpdate: false }
|
||||
}
|
||||
return { hasUpdate: true, info: data }
|
||||
}
|
||||
|
||||
static async checkRnUpdate(
|
||||
appKey: string,
|
||||
bundleName: string,
|
||||
currentBundleVersion: number
|
||||
): Promise<RnUpdateResult> {
|
||||
const data = await HttpClient.get<RnBundleInfo>(
|
||||
`/api/v1/rn/update/check?appKey=${appKey}&bundleName=${bundleName}&bundleVersion=${currentBundleVersion}`
|
||||
)
|
||||
|
||||
if (data.bundleVersion <= currentBundleVersion) {
|
||||
return { hasUpdate: false }
|
||||
}
|
||||
return { hasUpdate: true, info: data }
|
||||
}
|
||||
|
||||
static async downloadRnBundle(
|
||||
context: common.UIAbilityContext,
|
||||
downloadUrl: string,
|
||||
destFilename: string
|
||||
): Promise<string> {
|
||||
const destPath = context.cacheDir + '/' + destFilename
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
request.downloadFile(context, {
|
||||
url: downloadUrl,
|
||||
filePath: destPath,
|
||||
}, (err, task) => {
|
||||
if (err) { reject(err); return }
|
||||
task.on('complete', resolve)
|
||||
task.on('fail', (error: number) => reject(new Error(`Download failed: ${error}`)))
|
||||
})
|
||||
})
|
||||
if (SDKContext.getConfig().debug) {
|
||||
console.log('[UpdateSDK] RN bundle downloaded to', destPath)
|
||||
}
|
||||
return destPath
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"module": {
|
||||
"name": "xuqm-sdk",
|
||||
"type": "har",
|
||||
"deviceTypes": ["phone", "tablet"]
|
||||
}
|
||||
}
|
||||
正在加载...
在新工单中引用
屏蔽一个用户