diff --git a/feature_capabilities/src/main/ets/providers/ShareProvider.ets b/feature_capabilities/src/main/ets/providers/ShareProvider.ets index e5fe545..f24d2f7 100644 --- a/feature_capabilities/src/main/ets/providers/ShareProvider.ets +++ b/feature_capabilities/src/main/ets/providers/ShareProvider.ets @@ -1,45 +1,45 @@ /** - * 分享能力(契约 §8.1/§10.1,T-M3-13)。对齐原 Android「自定义三按钮分享面板」(微信/QQ/抖音)。 + * 分享能力(契约 §8.1/§10.1)。三端统一「剪贴板+指引窗 → 系统分享」,**不依赖任何分享 SDK**。 * - * H5 调 friendsSharetypeUrlToptitleDescript(data=sharetypeBean JSON)→ 弹自定义面板(entry 容器渲染)。 - * 用户点平台后经一次性 resultEvent 回投本 Provider: - * - 微信(@tencent/wechat_open_sdk): - * type=="2" → Canvas 截图图片分享(截当前激活 Web 的 canvas.toDataURL → 落盘 → WXImageObject.uri); - * 其他 type → 网页分享(WXWebpageObject)。 - * scene:sharefriend=="1" → 好友(WXSceneSession),否则 → 朋友圈(WXSceneTimeline)。 - * 分享结果回传 H5:callHandler('sharesuccess', {success:2成功/3取消, type:1好友/2朋友圈})。 - * - QQ / 抖音(不集成对应 SDK,用 HarmonyOS 系统分享 @kit.ShareKit,让用户在系统面板选目标 app): - * type=="1" → 文本(title+description);type=="2" → 截图图片; - * type=="4"(抖音视频) → 视频(webpageUrl 为视频地址);else → 网页链接(webpageUrl)。**不回传 sharesuccess**。 + * H5 调 friendsSharetypeUrlToptitleDescript(data=sharetypeBean JSON): + * - sharefriend=="2" → 直接微信指引窗(朋友圈语义,回传 type=2)。 + * - sharefriend=="1" → 弹自定义面板(微信/QQ/抖音/取消) → 选定渠道。 + * 选定渠道后按内容类型组装并写剪贴板,再弹 ShareGuideDialog: + * - 文本(type1/4/其他):剪贴板 PLAIN_TEXT(title\ndescription,无 url); + * - 图片(type2 截图 / type3 图片链接下载,低优先):剪贴板 PIXELMAP(尽力)。 + * 指引窗动作:打开App(openLink scheme,best-effort) / 用系统分享(systemText|systemImage,可靠) / 取消。 + * 仅微信乐观回传 sharesuccess:打开/系统分享→{success:2,type};取消→{success:3,type}。QQ/抖音不回传。 * - * ⚠ 微信运行期成功需在微信开放平台注册本 HarmonyOS 应用(bundleName+指纹 绑定 AppID)。 - * 截图/系统分享拉起/微信分享 均只能真机验证。 + * 注:图片"复制剪贴板再去对方App粘贴"能否成功取决于对方是否读 PIXELMAP(鸿蒙不可保证), + * 故指引窗"用系统分享"(systemImage) 为可靠兜底;拉起 App 仅打开、不预填(需用户自行粘贴)。 */ -import { SendMessageToWXReq, WXMediaMessage, WXWebpageObject, WXImageObject, ErrCode } from '@tencent/wechat_open_sdk'; import { systemShare } from '@kit.ShareKit'; import { uniformTypeDescriptor as utd } from '@kit.ArkData'; import { fileUri } from '@kit.CoreFileKit'; -import { common } from '@kit.AbilityKit'; -import { BusinessError } from '@kit.BasicServicesKit'; +import { common, bundleManager } from '@kit.AbilityKit'; +import { BusinessError, pasteboard } from '@kit.BasicServicesKit'; +import { image } from '@kit.ImageKit'; import { util } from '@kit.ArkTS'; import { BridgeController } from 'feature_bridge'; import { InboundHandlers, OutboundHandlers, SharetypeBean, ShareSuccessResp } from 'contracts'; -import { EventBus, Logger, ShareEvents, SharePanelRequest, SharePanelResult, PhotoUploadPayload } from 'common'; -import { FileSystem } from 'platform'; +import { + EventBus, Logger, ShareEvents, + SharePanelRequest, SharePanelResult, ShareGuideRequest, ShareGuideResult, PhotoUploadPayload, +} from 'common'; +import { FileSystem, Downloader } from 'platform'; import { CapabilityContext, CapabilityProvider } from '../core/CapabilityProvider'; -import { WeChatApi } from '../wx/WeChatApi'; +import { buildShareText } from '../share/ShareText'; export class ShareProvider implements CapabilityProvider { - /** 进程内实例计数:双 Web 槽各有一个 ShareProvider 实例,用它给 resultEvent 命名空间隔离。 */ + /** 进程内实例计数:双 Web 槽各一个实例,用它给 resultEvent 命名空间隔离。 */ private static instanceCount: number = 0; readonly name: string = 'share'; private readonly log: Logger = Logger.tag('ShareProvider'); private readonly instanceId: number; private bridge: BridgeController | undefined = undefined; private ctx: CapabilityContext | undefined = undefined; - /** 截图落盘 + resultEvent 命名计数器(实例自增,避免 Math.random 串扰)。 */ + /** 落盘 + resultEvent 命名计数器(实例自增,避免 Math.random 串扰)。 */ private seq: number = 0; - /** PHOTO_UPLOAD 订阅退订函数(onDestroy 调用,防双槽实例泄漏/串扰)。 */ private photoUploadCancel: (() => void) | undefined = undefined; constructor() { @@ -53,11 +53,10 @@ export class ShareProvider implements CapabilityProvider { bridge.registerHandler(InboundHandlers.FriendsShare, (data: string, _cb: (resp: string) => void) => { this.onShare(data); }); - // 第二条链路:H5 主动 POST 截图到 LocalUploadServer → emit PHOTO_UPLOAD → 微信图片分享。 + // 第二条链路:H5 主动 POST 截图到 LocalUploadServer → emit PHOTO_UPLOAD → 微信图片指引窗。 this.photoUploadCancel = EventBus.on(ShareEvents.PHOTO_UPLOAD, (p) => this.onPhotoUpload(p)); } - /** 容器销毁:退订 PHOTO_UPLOAD(双槽各一实例,须 per-subscriber 退订,勿 offAll)。 */ onDestroy(): void { if (this.photoUploadCancel !== undefined) { this.photoUploadCancel(); @@ -65,49 +64,10 @@ export class ShareProvider implements CapabilityProvider { } } - /** - * H5 POST 截图上传分享:仅当本槽桥激活时处理(非激活槽忽略,避免双实例对同一上传重复分享)。 - * type 语义同 sharefriend:"1" 好友 → WXSceneSession/回传 type=1;其他 → 朋友圈/回传 type=2。 - * 落盘成功走微信图片分享;微信未装 reportResult(3, ...);落盘失败仅 log(无网页可降级)。 - */ - private onPhotoUpload(payload?: Object): void { - const ctx = this.ctx; - const bridge = this.bridge; - if (ctx === undefined || bridge === undefined) { - return; - } - if (!bridge.isActive()) { - return; // 非激活槽不处理,保证同一上传仅激活槽分享一次 - } - if (payload === undefined) { - return; - } - const up = payload as PhotoUploadPayload; - if (up.imageBase64 === '') { - this.log.w('photo upload empty image'); - return; - } - const friendType: number = up.type === '1' ? 1 : 2; - const wx: WeChatApi = WeChatApi.getInstance(); - if (!wx.isWXInstalled()) { - this.log.i('WeChat not installed (photo upload); report cancel'); - this.reportResult(3, friendType); - return; - } - // saveDataUrlToFile 对无 data: 前缀的纯 base64 也兼容(indexOf(',')<0 → 整串当 base64) - const filePath: string = this.saveDataUrlToFile(ctx.uiAbilityContext, up.imageBase64); - if (filePath === '') { - this.log.w('photo upload save failed'); - this.reportResult(3, friendType); - return; - } - this.wechatImageByFriend(ctx.uiAbilityContext, wx, filePath, friendType); - } + // —— 入站 —— - /** 入站:校验 bean → 发起面板请求(一对一回投)。 */ private onShare(data: string): void { - const ctx = this.ctx; - if (ctx === undefined) { + if (this.ctx === undefined) { return; } let bean: SharetypeBean; @@ -121,6 +81,11 @@ export class ShareProvider implements CapabilityProvider { this.log.w('share bean empty'); return; } + if (bean.sharefriend === '2') { + // 朋友圈语义:不弹面板,直接微信指引窗(契约 §10.1)。 + this.startShare(bean, 'wechat'); + return; + } this.seq += 1; const resultEvent: string = `share.result.${this.instanceId}.${this.seq}`; EventBus.once(resultEvent, (p) => this.onPlatformChosen(bean, p)); @@ -128,189 +93,217 @@ export class ShareProvider implements CapabilityProvider { EventBus.emit(ShareEvents.SHOW_PANEL, req); } - /** 用户在面板选定平台后回投。 */ private onPlatformChosen(bean: SharetypeBean, payload?: Object): void { const platform: string = payload !== undefined ? (payload as SharePanelResult).platform : 'cancel'; - if (platform === 'wechat') { - this.shareToWeChat(bean); - } else if (platform === 'qq') { - this.shareViaSystem(bean, 'qq'); - } else if (platform === 'douyin') { - this.shareViaSystem(bean, 'douyin'); + if (platform === 'wechat' || platform === 'qq' || platform === 'douyin') { + this.startShare(bean, platform); } - // cancel:不分享、不回传(对齐 Android 点空白关闭面板)。 + // cancel:不分享、不回传。 } - // —— 微信 —— - - /** sharefriend=="1" → 好友(回传 type=1);否则 → 朋友圈(回传 type=2)。对齐 Android scene 语义。 */ - private wechatReportType(bean: SharetypeBean): number { - return bean.sharefriend === '1' ? 1 : 2; + /** H5 POST 截图上传:仅激活槽处理,落盘后走微信图片指引窗。 */ + private onPhotoUpload(payload?: Object): void { + const ctx = this.ctx; + const bridge = this.bridge; + if (ctx === undefined || bridge === undefined || !bridge.isActive() || payload === undefined) { + return; + } + const up = payload as PhotoUploadPayload; + if (up.imageBase64 === '') { + this.log.w('photo upload empty image'); + return; + } + const bean: SharetypeBean = { + sharefriend: up.type, type: '2', sharetype: '', webpageUrl: '', title: '', description: '', + }; + const filePath: string = this.saveDataUrlToFile(ctx.uiAbilityContext, up.imageBase64); + if (filePath === '') { + this.log.w('photo upload save failed; report cancel'); + this.reportResult(3, up.type === '1' ? 1 : 2); + return; + } + this.onImageReady(bean, 'wechat', filePath); } - private shareToWeChat(bean: SharetypeBean): void { + // —— 组装 + 弹指引窗 —— + + private startShare(bean: SharetypeBean, platform: string): void { + if (bean.type === '2') { + this.startCanvasImageShare(bean, platform); + } else if (bean.type === '3') { + this.startLinkImageShare(bean, platform); + } else { + this.startTextShare(bean, platform); + } + } + + private startTextShare(bean: SharetypeBean, platform: string): void { + this.writeClipboardText(buildShareText(bean.title, bean.description)); + this.guide(bean, platform, 'text', ''); + } + + /** type2:截当前 Web canvas → 落盘 → 图片指引窗(捕获失败降级文本指引,非"图片转文本"语义)。 */ + private startCanvasImageShare(bean: SharetypeBean, platform: string): void { const ctx = this.ctx; if (ctx === undefined) { return; } - const reportType: number = this.wechatReportType(bean); - const wx: WeChatApi = WeChatApi.getInstance(); - if (!wx.isWXInstalled()) { - this.log.i('WeChat not installed; report cancel'); - this.reportResult(3, reportType); + const cap = ctx.captureCanvas; + if (cap === undefined) { + this.startTextShare(bean, platform); return; } - if (bean.type === '2') { - // Canvas 截图图片分享(失败降级网页) - const cap = ctx.captureCanvas; - if (cap === undefined) { - this.wechatWebPage(ctx.uiAbilityContext, wx, bean, reportType); - return; + cap(bean.sharetype).then((dataUrl: string) => { + const filePath: string = dataUrl !== '' ? this.saveDataUrlToFile(ctx.uiAbilityContext, dataUrl) : ''; + if (filePath !== '') { + this.onImageReady(bean, platform, filePath); + } else { + this.log.w('canvas capture empty; fallback to text guide'); + this.startTextShare(bean, platform); } - cap(bean.sharetype).then((dataUrl: string) => { - const filePath: string = dataUrl !== '' ? this.saveDataUrlToFile(ctx.uiAbilityContext, dataUrl) : ''; - if (filePath !== '') { - this.wechatImage(ctx.uiAbilityContext, wx, filePath, bean, reportType); - } else { - this.log.w('canvas capture empty; fallback to webpage share'); - this.wechatWebPage(ctx.uiAbilityContext, wx, bean, reportType); - } - }).catch((e: Error) => { - this.log.w(`captureCanvas failed: ${e.message}; fallback to webpage share`); - this.wechatWebPage(ctx.uiAbilityContext, wx, bean, reportType); - }); + }).catch((e: Error) => { + this.log.w(`captureCanvas failed: ${e.message}; fallback to text guide`); + this.startTextShare(bean, platform); + }); + } + + /** type3(低优先,需确认 H5 是否使用):从 webpageUrl 下载图片 → 落盘 → 图片指引窗(失败降级文本)。 */ + private startLinkImageShare(bean: SharetypeBean, platform: string): void { + const ctx = this.ctx; + if (ctx === undefined) { return; } - this.wechatWebPage(ctx.uiAbilityContext, wx, bean, reportType); - } - - private wechatScene(bean: SharetypeBean): number { - return this.wechatSceneByFriend(bean.sharefriend === '1' ? 1 : 2); - } - - /** friendType: 1 好友(WXSceneSession) / 2 朋友圈(WXSceneTimeline)。 */ - private wechatSceneByFriend(friendType: number): number { - return friendType === 1 ? SendMessageToWXReq.WXSceneSession : SendMessageToWXReq.WXSceneTimeline; - } - - private wechatWebPage(ctx: common.UIAbilityContext, wx: WeChatApi, bean: SharetypeBean, reportType: number): void { - const req: SendMessageToWXReq = new SendMessageToWXReq(); - req.scene = this.wechatScene(bean); - const msg: WXMediaMessage = new WXMediaMessage(); - msg.title = bean.title; - msg.description = bean.description; - const web: WXWebpageObject = new WXWebpageObject(); - web.webpageUrl = bean.webpageUrl; - msg.mediaObject = web; - req.message = msg; - wx.sendShare(ctx, req, (resp) => { - this.reportResult(resp.errCode === ErrCode.ERR_OK ? 2 : 3, reportType); + const url: string = bean.webpageUrl; + if (url === '' || !(url.startsWith('http://') || url.startsWith('https://'))) { + this.startTextShare(bean, platform); + return; + } + this.seq += 1; + const ext: string = url.toLowerCase().indexOf('.png') >= 0 ? '.png' : '.jpg'; + const savePath: string = `${ctx.uiAbilityContext.filesDir}/share/link_${this.seq}${ext}`; + Downloader.download(url, savePath).then(() => { + this.onImageReady(bean, platform, savePath); + }).catch((e: Error) => { + this.log.w(`link image download failed: ${e.message}; fallback to text guide`); + this.startTextShare(bean, platform); }); } - private wechatImage(ctx: common.UIAbilityContext, wx: WeChatApi, filePath: string, - bean: SharetypeBean, reportType: number): void { - this.wechatImageByFriend(ctx, wx, filePath, reportType); + private onImageReady(bean: SharetypeBean, platform: string, filePath: string): void { + this.writeClipboardImage(filePath); + this.guide(bean, platform, 'image', filePath); } - /** 微信图片分享(友/圈由 friendType 决定);分享面板与 H5 POST 上传两条链路共用。 - * friendType 同时作为 sharesuccess 回传的 type(1 好友 / 2 朋友圈)。 */ - private wechatImageByFriend(ctx: common.UIAbilityContext, wx: WeChatApi, filePath: string, friendType: number): void { - const req: SendMessageToWXReq = new SendMessageToWXReq(); - req.scene = this.wechatSceneByFriend(friendType); - const msg: WXMediaMessage = new WXMediaMessage(); - const img: WXImageObject = new WXImageObject(); - img.uri = fileUri.getUriFromPath(filePath); // 沙箱文件 file uri,SDK 支持 jpeg/png - msg.mediaObject = img; - req.message = msg; - wx.sendShare(ctx, req, (resp) => { - this.reportResult(resp.errCode === ErrCode.ERR_OK ? 2 : 3, friendType); - }); + private guide(bean: SharetypeBean, platform: string, kind: string, filePath: string): void { + this.seq += 1; + const resultEvent: string = `share.guide.${this.instanceId}.${this.seq}`; + EventBus.once(resultEvent, (p) => this.onGuideAction(bean, platform, kind, filePath, p)); + const req: ShareGuideRequest = { platform, contentKind: kind, resultEvent }; + EventBus.emit(ShareEvents.SHOW_GUIDE, req); } - // —— QQ / 抖音:HarmonyOS 系统分享(不回传 sharesuccess)—— - - private shareViaSystem(bean: SharetypeBean, platform: string): void { + private onGuideAction(bean: SharetypeBean, platform: string, kind: string, filePath: string, payload?: Object): void { const ctx = this.ctx; if (ctx === undefined) { return; } const uiCtx: common.UIAbilityContext = ctx.uiAbilityContext; - if (bean.type === '2') { - // 截图图片 - const cap = ctx.captureCanvas; - if (cap === undefined) { - this.systemText(uiCtx, bean, platform); - return; + const action: string = payload !== undefined ? (payload as ShareGuideResult).action : 'cancel'; + const reportType: number = bean.sharefriend === '1' ? 1 : 2; + if (action === 'open') { + this.openApp(uiCtx, platform); + if (platform === 'wechat') { + this.reportResult(2, reportType); + } + } else if (action === 'system') { + if (kind === 'image' && filePath !== '') { + this.systemImage(uiCtx, filePath, bean); + } else { + this.systemText(uiCtx, bean); + } + if (platform === 'wechat') { + this.reportResult(2, reportType); + } + } else { + // cancel + if (platform === 'wechat') { + this.reportResult(3, reportType); } - cap(bean.sharetype).then((dataUrl: string) => { - const filePath: string = dataUrl !== '' ? this.saveDataUrlToFile(uiCtx, dataUrl) : ''; - if (filePath !== '') { - this.systemImage(uiCtx, filePath, bean); - } else { - this.log.w(`[${platform}] canvas capture empty; fallback to text`); - this.systemText(uiCtx, bean, platform); - } - }).catch((e: Error) => { - this.log.w(`[${platform}] captureCanvas failed: ${e.message}; fallback to text`); - this.systemText(uiCtx, bean, platform); - }); - return; } - if (bean.type === '1') { - this.systemText(uiCtx, bean, platform); - return; - } - if (bean.type === '4') { - // 抖音视频:webpageUrl 为视频地址 - this.systemVideo(uiCtx, bean); - return; - } - // else:网页链接(title + description + webpageUrl) - this.systemLink(uiCtx, bean); } - /** 文本分享(title + description,对齐 Android 文本拼接)。 */ - private systemText(uiCtx: common.UIAbilityContext, bean: SharetypeBean, _platform: string): void { - const text: string = this.joinText(bean.title, bean.description); - const record: systemShare.SharedRecord = { utd: utd.UniformDataType.PLAIN_TEXT, content: text !== '' ? text : ' ' }; - this.showSystemShare(uiCtx, record); + // —— 拉起 App(best-effort,仅打开不预填)—— + + private schemeOf(platform: string): string { + if (platform === 'qq') { + return 'mqqapi://'; + } + if (platform === 'douyin') { + return 'snssdk1128://'; + } + return 'weixin://'; } - /** 网页链接分享。 */ - private systemLink(uiCtx: common.UIAbilityContext, bean: SharetypeBean): void { - const url: string = bean.webpageUrl !== '' ? bean.webpageUrl : ' '; + private openApp(uiCtx: common.UIAbilityContext, platform: string): void { + const link: string = this.schemeOf(platform); + let canOpen: boolean = false; + try { + canOpen = bundleManager.canOpenLink(link); + } catch (e) { + this.log.w(`canOpenLink failed: ${(e as BusinessError).message}`); + return; + } + if (!canOpen) { + this.log.i(`app not openable: ${link}`); + return; + } + uiCtx.openLink(link, { appLinkingOnly: false }).catch((e: BusinessError) => { + this.log.w(`openLink failed: code=${e.code} ${e.message}`); + }); + } + + // —— 剪贴板 —— + + private writeClipboardText(text: string): void { + try { + const data: pasteboard.PasteData = + pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text !== '' ? text : ' '); + pasteboard.getSystemPasteboard().setData(data) + .catch((e: BusinessError) => this.log.w(`clipboard text failed: ${e.code} ${e.message}`)); + } catch (e) { + this.log.w(`clipboard text init failed: ${(e as Error).message}`); + } + } + + /** 图片写剪贴板(尽力;对方能否粘贴 PIXELMAP 不可保证,systemImage 才是可靠交付)。 */ + private writeClipboardImage(filePath: string): void { + const source: image.ImageSource = image.createImageSource(filePath); + source.createPixelMap().then((pm: image.PixelMap) => { + try { + const data: pasteboard.PasteData = pasteboard.createData(pasteboard.MIMETYPE_PIXELMAP, pm); + pasteboard.getSystemPasteboard().setData(data) + .catch((e: BusinessError) => this.log.w(`clipboard image failed: ${e.code} ${e.message}`)); + } catch (e) { + this.log.w(`clipboard image init failed: ${(e as Error).message}`); + } + }).catch((e: BusinessError) => { + this.log.w(`createPixelMap failed: ${e.code} ${e.message}`); + }); + } + + // —— 系统分享(可靠兜底;文本不下发 url)—— + + private systemText(uiCtx: common.UIAbilityContext, bean: SharetypeBean): void { + const text: string = buildShareText(bean.title, bean.description); const record: systemShare.SharedRecord = { - utd: utd.UniformDataType.HYPERLINK, - content: url, - title: bean.title !== '' ? bean.title : undefined, - description: bean.description !== '' ? bean.description : undefined, + utd: utd.UniformDataType.PLAIN_TEXT, content: text !== '' ? text : ' ', }; this.showSystemShare(uiCtx, record); } - /** 视频分享(webpageUrl 为视频地址;远程地址降级为链接分享)。 */ - private systemVideo(uiCtx: common.UIAbilityContext, bean: SharetypeBean): void { - const path: string = bean.webpageUrl; - if (path !== '' && (path.startsWith('/') || path.startsWith('file://')) && FileSystem.exists(this.toLocalPath(path))) { - const local: string = this.toLocalPath(path); - const typeId: string = utd.getUniformDataTypeByFilenameExtension(this.extOf(local), utd.UniformDataType.VIDEO); - const record: systemShare.SharedRecord = { - utd: typeId, - uri: fileUri.getUriFromPath(local), - title: bean.title !== '' ? bean.title : undefined, - description: bean.description !== '' ? bean.description : undefined, - }; - this.showSystemShare(uiCtx, record); - return; - } - // 非本地视频文件:以链接形式分享地址(系统分享无法直接推送远程视频) - this.systemLink(uiCtx, bean); - } - - /** 图片分享(沙箱文件)。 */ private systemImage(uiCtx: common.UIAbilityContext, filePath: string, bean: SharetypeBean): void { - const typeId: string = utd.getUniformDataTypeByFilenameExtension(this.extOf(filePath), utd.UniformDataType.IMAGE); + const typeId: string = + utd.getUniformDataTypeByFilenameExtension(this.extOf(filePath), utd.UniformDataType.IMAGE); const record: systemShare.SharedRecord = { utd: typeId, uri: fileUri.getUriFromPath(filePath), @@ -320,7 +313,6 @@ export class ShareProvider implements CapabilityProvider { this.showSystemShare(uiCtx, record); } - /** 拉起系统分享面板(失败 log 不崩溃)。 */ private showSystemShare(uiCtx: common.UIAbilityContext, record: systemShare.SharedRecord): void { try { const data: systemShare.SharedData = new systemShare.SharedData(record); @@ -343,10 +335,7 @@ export class ShareProvider implements CapabilityProvider { this.bridge?.callHandler(OutboundHandlers.ShareSuccess, JSON.stringify(out)); } - /** - * dataURL("data:image/...;base64,XXXX")→ 解码 → 写沙箱 filesDir/share/shot_.jpg → 返回沙箱路径。 - * 失败返回空串(调用方降级)。 - */ + /** dataURL/纯base64 → 解码 → 写沙箱 filesDir/share/shot_.jpg|png → 返回路径;失败返回空串。 */ private saveDataUrlToFile(uiCtx: common.UIAbilityContext, dataUrl: string): string { try { const comma: number = dataUrl.indexOf(','); @@ -366,22 +355,6 @@ export class ShareProvider implements CapabilityProvider { } } - private joinText(title: string, description: string): string { - const parts: string[] = []; - if (title !== '') { - parts.push(title); - } - if (description !== '') { - parts.push(description); - } - return parts.join('\n\n'); - } - - /** file:// 前缀 → 本地路径。 */ - private toLocalPath(p: string): string { - return p.startsWith('file://') ? p.substring('file://'.length) : p; - } - private extOf(path: string): string { const dot: number = path.lastIndexOf('.'); const slash: number = path.lastIndexOf('/'); diff --git a/feature_capabilities/src/main/ets/wx/WeChatApi.ets b/feature_capabilities/src/main/ets/wx/WeChatApi.ets index 1113e7f..e33f195 100644 --- a/feature_capabilities/src/main/ets/wx/WeChatApi.ets +++ b/feature_capabilities/src/main/ets/wx/WeChatApi.ets @@ -1,16 +1,12 @@ /** - * 微信开放平台 SDK 封装(@tencent/wechat_open_sdk,契约 §10.1/§10.2/§13)。单例。 + * 微信开放平台 SDK 封装(@tencent/wechat_open_sdk)。单例。**仅用于登录授权**(分享已去 SDK 化)。 * - * 职责:持有 WXApi(用 AppID 创建)、发起授权/分享请求、接收微信回调并路由到对应回调。 - * AppID 为公开标识,可入客户端;**AppSecret 绝不入端**(登录换 profile 须服务端,§13 红线)。 - * - * 回调路径:微信处理完拉回宿主 App(默认 EntryAbility),EntryAbility.onNewWant/onCreate - * 把 want 交给 handleWant() → SDK 解析 → onResp 路由到 authCb/shareCb。 + * 职责:持有 WXApi(用 AppID 创建)、发起授权请求、接收微信回调并路由到 authCb。 + * AppID 为公开标识,可入客户端;AppSecret 绝不入端(登录换 profile 须服务端,§13 红线)。 */ import { common, Want } from '@kit.AbilityKit'; import { - WXAPIFactory, WXApi, WXApiEventHandler, BaseReq, BaseResp, - SendAuthResp, SendMessageToWXResp, + WXAPIFactory, WXApi, WXApiEventHandler, BaseReq, BaseResp, SendAuthResp, } from '@tencent/wechat_open_sdk'; import { Logger } from 'common'; @@ -18,7 +14,6 @@ import { Logger } from 'common'; export const WX_APP_ID: string = 'wxd2bd650e06bdfe58'; export type AuthRespCallback = (resp: SendAuthResp) => void; -export type ShareRespCallback = (resp: SendMessageToWXResp) => void; export class WeChatApi { private static readonly log: Logger = Logger.tag('WeChatApi'); @@ -26,7 +21,6 @@ export class WeChatApi { private readonly api: WXApi; private readonly handler: WXApiEventHandler; private authCb: AuthRespCallback | undefined = undefined; - private shareCb: ShareRespCallback | undefined = undefined; private constructor() { this.api = WXAPIFactory.createWXAPI(WX_APP_ID); @@ -58,12 +52,6 @@ export class WeChatApi { this.api.sendReq(context, req); } - /** 发起分享;resp 经 cb 异步回调。 */ - sendShare(context: common.UIAbilityContext, req: BaseReq, cb: ShareRespCallback): void { - this.shareCb = cb; - this.api.sendReq(context, req); - } - /** 由 EntryAbility 在 onNewWant/onCreate 调用,处理微信回调 want。 */ handleWant(want: Want): void { try { @@ -80,12 +68,6 @@ export class WeChatApi { if (cb !== undefined) { cb(resp); } - } else if (resp instanceof SendMessageToWXResp) { - const cb = this.shareCb; - this.shareCb = undefined; - if (cb !== undefined) { - cb(resp); - } } } }