From 90bd57a69cde2d9c22c31d50cdc560bbac053a4d Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Tue, 21 Apr 2026 22:07:29 +0800 Subject: [PATCH] chore: initial commit --- .gitignore | 10 ++ build-profile.json5 | 39 +++++++ entry/build-profile.json5 | 7 ++ entry/hvigorfile.ts | 6 ++ entry/oh-package.json5 | 11 ++ .../main/ets/entryability/EntryAbility.ets | 25 +++++ entry/src/main/ets/pages/Index.ets | 98 +++++++++++++++++ entry/src/main/module.json5 | 34 ++++++ .../main/resources/base/element/color.json | 5 + .../main/resources/base/element/string.json | 7 ++ .../resources/base/profile/main_pages.json | 3 + hvigorfile.ts | 6 ++ oh-package.json5 | 7 ++ xuqm-sdk/Index.ets | 19 ++++ xuqm-sdk/build-profile.json5 | 7 ++ xuqm-sdk/hvigorfile.ts | 6 ++ xuqm-sdk/oh-package.json5 | 12 +++ xuqm-sdk/src/main/ets/XuqmSDK.ets | 38 +++++++ xuqm-sdk/src/main/ets/core/HttpClient.ets | 48 +++++++++ xuqm-sdk/src/main/ets/core/SDKContext.ets | 47 ++++++++ xuqm-sdk/src/main/ets/core/Types.ets | 72 +++++++++++++ xuqm-sdk/src/main/ets/im/ImClient.ets | 102 ++++++++++++++++++ xuqm-sdk/src/main/ets/push/PushSDK.ets | 28 +++++ xuqm-sdk/src/main/ets/update/UpdateSDK.ets | 71 ++++++++++++ xuqm-sdk/src/main/module.json5 | 7 ++ 25 files changed, 715 insertions(+) create mode 100644 .gitignore create mode 100644 build-profile.json5 create mode 100644 entry/build-profile.json5 create mode 100644 entry/hvigorfile.ts create mode 100644 entry/oh-package.json5 create mode 100644 entry/src/main/ets/entryability/EntryAbility.ets create mode 100644 entry/src/main/ets/pages/Index.ets create mode 100644 entry/src/main/module.json5 create mode 100644 entry/src/main/resources/base/element/color.json create mode 100644 entry/src/main/resources/base/element/string.json create mode 100644 entry/src/main/resources/base/profile/main_pages.json create mode 100644 hvigorfile.ts create mode 100644 oh-package.json5 create mode 100644 xuqm-sdk/Index.ets create mode 100644 xuqm-sdk/build-profile.json5 create mode 100644 xuqm-sdk/hvigorfile.ts create mode 100644 xuqm-sdk/oh-package.json5 create mode 100644 xuqm-sdk/src/main/ets/XuqmSDK.ets create mode 100644 xuqm-sdk/src/main/ets/core/HttpClient.ets create mode 100644 xuqm-sdk/src/main/ets/core/SDKContext.ets create mode 100644 xuqm-sdk/src/main/ets/core/Types.ets create mode 100644 xuqm-sdk/src/main/ets/im/ImClient.ets create mode 100644 xuqm-sdk/src/main/ets/push/PushSDK.ets create mode 100644 xuqm-sdk/src/main/ets/update/UpdateSDK.ets create mode 100644 xuqm-sdk/src/main/module.json5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10dfc90 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +node_modules/ +dist/ +.DS_Store +*.class +target/ +build/ +.gradle/ +*.iml +.idea/ +*.log diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100644 index 0000000..0274ba3 --- /dev/null +++ b/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"] } + ] + } + ] +} diff --git a/entry/build-profile.json5 b/entry/build-profile.json5 new file mode 100644 index 0000000..9d2bee6 --- /dev/null +++ b/entry/build-profile.json5 @@ -0,0 +1,7 @@ +{ + "apiType": "stageMode", + "buildOption": {}, + "targets": [ + { "name": "default", "runtimeOS": "HarmonyOS" } + ] +} diff --git a/entry/hvigorfile.ts b/entry/hvigorfile.ts new file mode 100644 index 0000000..d789d43 --- /dev/null +++ b/entry/hvigorfile.ts @@ -0,0 +1,6 @@ +import { hapTasks } from '@ohos/hvigor-ohos-plugin' + +export default { + system: hapTasks, + plugins: [] +} diff --git a/entry/oh-package.json5 b/entry/oh-package.json5 new file mode 100644 index 0000000..edcfd82 --- /dev/null +++ b/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" + } +} diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000..d536d1c --- /dev/null +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -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 { + 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)) + } + }) + } +} diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000..c04441f --- /dev/null +++ b/entry/src/main/ets/pages/Index.ets @@ -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) + } +} diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 new file mode 100644 index 0000000..18b04e4 --- /dev/null +++ b/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" } + ] + } +} diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000..2770484 --- /dev/null +++ b/entry/src/main/resources/base/element/color.json @@ -0,0 +1,5 @@ +{ + "color": [ + { "name": "start_window_background", "value": "#FFFFFF" } + ] +} diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000..87f0f14 --- /dev/null +++ b/entry/src/main/resources/base/element/string.json @@ -0,0 +1,7 @@ +{ + "string": [ + { "name": "module_desc", "value": "XuqmSDK Sample" }, + { "name": "EntryAbility_desc", "value": "XuqmSDK Sample App" }, + { "name": "EntryAbility_label", "value": "XuqmSDK" } + ] +} diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000..9140e03 --- /dev/null +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,3 @@ +{ + "src": ["pages/Index"] +} diff --git a/hvigorfile.ts b/hvigorfile.ts new file mode 100644 index 0000000..9d874ec --- /dev/null +++ b/hvigorfile.ts @@ -0,0 +1,6 @@ +import { appTasks } from '@ohos/hvigor-ohos-plugin' + +export default { + system: appTasks, + plugins: [] +} diff --git a/oh-package.json5 b/oh-package.json5 new file mode 100644 index 0000000..c2983e9 --- /dev/null +++ b/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" +} diff --git a/xuqm-sdk/Index.ets b/xuqm-sdk/Index.ets new file mode 100644 index 0000000..99e351d --- /dev/null +++ b/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' diff --git a/xuqm-sdk/build-profile.json5 b/xuqm-sdk/build-profile.json5 new file mode 100644 index 0000000..68fcfc9 --- /dev/null +++ b/xuqm-sdk/build-profile.json5 @@ -0,0 +1,7 @@ +{ + "apiType": "stageMode", + "buildOption": {}, + "targets": [ + { "name": "default" } + ] +} diff --git a/xuqm-sdk/hvigorfile.ts b/xuqm-sdk/hvigorfile.ts new file mode 100644 index 0000000..5d20485 --- /dev/null +++ b/xuqm-sdk/hvigorfile.ts @@ -0,0 +1,6 @@ +import { harTasks } from '@ohos/hvigor-ohos-plugin' + +export default { + system: harTasks, + plugins: [] +} diff --git a/xuqm-sdk/oh-package.json5 b/xuqm-sdk/oh-package.json5 new file mode 100644 index 0000000..0d1b392 --- /dev/null +++ b/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": {} +} diff --git a/xuqm-sdk/src/main/ets/XuqmSDK.ets b/xuqm-sdk/src/main/ets/XuqmSDK.ets new file mode 100644 index 0000000..a9010e9 --- /dev/null +++ b/xuqm-sdk/src/main/ets/XuqmSDK.ets @@ -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 { + SDKContext.init(config) + await SDKContext.initPreferences(context) + } + + static async setToken(token: string | null): Promise { + 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 + } +} diff --git a/xuqm-sdk/src/main/ets/core/HttpClient.ets b/xuqm-sdk/src/main/ets/core/HttpClient.ets new file mode 100644 index 0000000..60e3409 --- /dev/null +++ b/xuqm-sdk/src/main/ets/core/HttpClient.ets @@ -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(method: http.RequestMethod, path: string, body?: object): Promise { + 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 = JSON.parse(res.result as string) as ApiResponse + if (json.code !== 200) throw new Error(json.message) + return json.data + } finally { + client.destroy() + } + } + + static get(path: string): Promise { + return HttpClient.request(http.RequestMethod.GET, path) + } + + static post(path: string, body?: object): Promise { + return HttpClient.request(http.RequestMethod.POST, path, body) + } + + static put(path: string, body?: object): Promise { + return HttpClient.request(http.RequestMethod.PUT, path, body) + } + + static delete(path: string): Promise { + return HttpClient.request(http.RequestMethod.DELETE, path) + } +} diff --git a/xuqm-sdk/src/main/ets/core/SDKContext.ets b/xuqm-sdk/src/main/ets/core/SDKContext.ets new file mode 100644 index 0000000..499d9a2 --- /dev/null +++ b/xuqm-sdk/src/main/ets/core/SDKContext.ets @@ -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 { + 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 { + 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 + } +} diff --git a/xuqm-sdk/src/main/ets/core/Types.ets b/xuqm-sdk/src/main/ets/core/Types.ets new file mode 100644 index 0000000..4072b71 --- /dev/null +++ b/xuqm-sdk/src/main/ets/core/Types.ets @@ -0,0 +1,72 @@ +export interface SDKConfig { + appKey: string + appSecret: string + apiBaseUrl: string + imBaseUrl: string + debug: boolean +} + +export interface ApiResponse { + 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 +} diff --git a/xuqm-sdk/src/main/ets/im/ImClient.ets b/xuqm-sdk/src/main/ets/im/ImClient.ets new file mode 100644 index 0000000..76899f9 --- /dev/null +++ b/xuqm-sdk/src/main/ets/im/ImClient.ets @@ -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) + } +} diff --git a/xuqm-sdk/src/main/ets/push/PushSDK.ets b/xuqm-sdk/src/main/ets/push/PushSDK.ets new file mode 100644 index 0000000..37cf4bd --- /dev/null +++ b/xuqm-sdk/src/main/ets/push/PushSDK.ets @@ -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 { + const body: PushTokenInfo = { + vendor: 'HARMONY', + token, + platform: 'harmony', + } + await HttpClient.post('/api/v1/push/register', { + ...body, + imUserId: imUserId ?? null, + }) + } + + /** + * Unregister push token on logout. + */ + static async unregisterToken(token: string): Promise { + await HttpClient.post('/api/v1/push/unregister', { token, platform: 'harmony' }) + } +} diff --git a/xuqm-sdk/src/main/ets/update/UpdateSDK.ets b/xuqm-sdk/src/main/ets/update/UpdateSDK.ets new file mode 100644 index 0000000..81a9656 --- /dev/null +++ b/xuqm-sdk/src/main/ets/update/UpdateSDK.ets @@ -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 { + const bundleInfo = bundleManager.getBundleInfoForSelfSync( + bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT + ) + const currentVersionCode = bundleInfo.versionCode + + const data = await HttpClient.get( + `/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 { + const data = await HttpClient.get( + `/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 { + const destPath = context.cacheDir + '/' + destFilename + await new Promise((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 + } +} diff --git a/xuqm-sdk/src/main/module.json5 b/xuqm-sdk/src/main/module.json5 new file mode 100644 index 0000000..50d15fc --- /dev/null +++ b/xuqm-sdk/src/main/module.json5 @@ -0,0 +1,7 @@ +{ + "module": { + "name": "xuqm-sdk", + "type": "har", + "deviceTypes": ["phone", "tablet"] + } +}