# 代理方法
小程序中部分业务是抽象定义的,这些抽象的业务通过接口的形式暴露给了外部,外部可以自行实现具体的业务逻辑。
函数签名如果是需要返回 boolean 类型表示是否需要阻止默认行为,如果需要阻止就返回
true
,否则则返回false
函数里的参数
appId
和apiServer
均为触发的小程序的信息,可以根据这两个数据来对不同的小程序来执行不同的逻辑
# 1. CapsuleHandler
胶囊按钮的代理类
// namespace IFinProxyHandlerItem
class CapsuleHandler {
/**
* 点击关闭按钮时触发
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @returns 返回 true 表示宿主自行处理,返回 false 表示宿主处理完,仍执行后续的默认操作
*/
public onCloseButtonClick(appId: string, apiServer: string): boolean
/**
* 点击更多按钮时触发
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @returns 返回 true 表示宿主自行处理,返回 false 表示宿主处理完,仍执行后续的默认操作
*/
public onMoreButtonClick(appId: string, apiServer: string): boolean
}
示例代码:
import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk'
import { promptAction } from '@kit.ArkUI'
class CapsuleHandler extends IFinProxyHandlerItem.CapsuleHandler {
onCloseButtonClick(appId: string, apiServer: string): boolean {
this.promptMsg(`${appId},关闭按钮点击了`)
return false
}
onMoreButtonClick(appId: string, apiServer: string): boolean {
this.promptMsg(`${appId},更多按钮点击了`)
return false
}
private promptMsg(message: string) {
promptAction.showToast({
message
})
}
}
FinAppProxyHandlerManager.capsuleHandler = new CapsuleHandler()
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class CapsuleHandler {
// 更多按钮的点击事件
onMoreButtonClick?: (appId: string, apiServer: string) => boolean
// 关闭按钮的点击事件
onCloseButtonClick?: (appId: string, apiServer: string) => boolean
}
示例代码
client.proxyHandlerManager.capsuleHandler.onMoreButtonClick = (appId: string, apiServer: string) => {
// do something
return false // 不阻止默认行为
}
# 2. MoreMenuHandler
更多菜单的代理类
// namespace IFinProxyHandlerItem
class MoreMenuHandler {
/**
* 当收到小程序返回的数据时触发
* @param contentInfo 小程序返回的信息
* @returns
*/
public onCustomMenuItemClickWithInfo(appId: string, apiServer: string,
contentInfo: IFinAppProxy.IMoreMenuItemContentInfo)
/**
* 当更多按钮菜单点击时出发
* @param type 菜单按钮的唯一标识
* @param currentPath 小程序当前的页面路径
* @returns
*/
public onMoreMenuItemClick(appId: string, apiServer: string, type: string, currentPath: string): boolean | void
/**
* 获取更多菜单的按钮列表
* @returns 更多菜单的按钮列表
*/
public getMoreMenuItems(): IFinAppProxy.IMoreMenuItem[]
}
示例代码:
import { FinAppProxyHandlerManager, IFinAppProxy, IFinProxyHandlerItem } from '@finclip/sdk'
import { promptAction } from '@kit.ArkUI'
class MoreMenuHandler extends IFinProxyHandlerItem.MoreMenuHandler {
onCustomMenuItemClickWithInfo(appId: string, apiServer: string,
contentInfo: IFinAppProxy.IMoreMenuItemContentInfo) {
this.promptMsg(`小程序 ${apiServer} ${appId} 的 ${contentInfo.path} 页面的菜单 ${contentInfo.menuId} 被点击了,返回的数据为${JSON.stringify(contentInfo.params)}`)
}
onMoreMenuItemClick(appId: string, apiServer: string, menuItemId: string, currentPath: string): boolean | void {
this.promptMsg(`小程序 ${apiServer} ${appId} 的 ${currentPath} 页面的菜单 ${menuItemId} 被点击了`)
}
getMoreMenuItems(): IFinAppProxy.IMoreMenuItem[] {
return [
{
label: '自定义',
icon: $r('app.media.app_icon'),
menuItemId: 'custom',
menuType: 'onMiniProgram'
}
]
}
private promptMsg(message: string) {
promptAction.showToast({
message
})
}
}
FinAppProxyHandlerManager.moreMenuHandler = new MoreMenuHandler()
// 小程序端
Page({
onCustomButtonHandler(){
return {
appInfo:'this is appInfo'
}
}
})
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class MoreMenuHandler {
/**
* @param type 菜单按钮的唯一标识
* @param currentPath 小程序当前的页面路径
* @returns
*
*/
onMoreMenuItemClick?: (appId: string, apiServer: string, type: string, currentPath: string) => void
/**
* 当 onMiniProgram 类型的按钮点击时出发,携带小程序返回的信息
* @param contentInfo 小程序返回的信息
* @returns
*/
onCustomMenuItemClickWithInfo?: (appId: string, apiServer: string,
contentInfo: IFinAppProxy.IMoreMenuItemContentInfo) => void
/**
* @returns 自定义更多菜单的按钮
*/
getMoreMenuItems: () => IMoreMenuItem[]
}
示例代码
client.proxyHandlerManager.moreMenuHandler.onMoreMenuItemClick = (appId: string, apiServer: string, type: string, currentPath: string) => {
// do something
}
client.proxyHandlerManager.moreMenuHandler.onCustomMenuItemClickWithInfo = (appId: string, apiServer: string,
contentInfo: IFinAppProxy.IMoreMenuItemContentInfo) => {
if(contentInfo.menuId === 'custom'){
// do something
}
}
client.proxyHandlerManager.moreMenuHandler.getMoreMenuItems = () => {
return [
{
label: '自定义 Btn',
icon: $r('app.media.app_icon'),
menuItemId: 'custom'
}
]
}
IMoreMenuItem
属性 | 类型 | 是否必填 | 描述 |
---|---|---|---|
label | String | 是 | 小程序 id |
icon | ResourceStr | 是 | 菜单按钮的图片资源 |
disableIcon | ResourceStr | 否 | 菜单按钮禁用的图片资源 |
darkIcon | ResourceStr | 否 | 暗黑模式菜单按钮的图片资源 |
disableDarkIcon | ResourceStr | 否 | 暗黑模式菜单按钮禁用的图片资源 |
menuItemId | String | 是 | 菜单按钮的唯一标识,即 onMoreMenuItemClick 参数的 type |
hover | Boolean | 否 | 是否开启 hover 效果 |
disable | Boolean | 否 | 是否禁用 |
IMoreMenuItemContentInfo
属性 | 类型 | 描述 |
---|---|---|
title | String | 小程序名称 |
logo | String | 小程序 logo |
description | String | 小程序描述 |
path | String | 当前页面路径 |
menuId | String | 菜单按钮的唯一标识 |
params | IMoreMenuItemParams | 由小程序返回的原始数据 |
IMoreMenuItemParams
属性 | 类型 |
---|---|
title | String |
desc | String |
path | String |
appInfo | object |
# 3. PrivacyHandler
隐私协议的代理类
// namespace IFinProxyHandlerItem
export class PrivacyHandler {
/**
* @param scope 当前请求的权限,如果为空则是在关于页面显示的隐私协议
* @returns IFinApplet.IPrivacy
*/
public getPrivacyInfo(appId: string, apiServer: string, scope?: string): IFinApplet.IPrivacy | void
}
示例代码
import { FinAppProxyHandlerManager, IFinApplet, IFinProxyHandlerItem } from '@finclip/sdk'
class PrivacyHandler extends IFinProxyHandlerItem.PrivacyHandler {
getPrivacyInfo(appId: string, apiServer: string, scope?: string): IFinApplet.IPrivacy | void {
return {
title: '自定义授权弹窗标题',
docName: '自定义隐私协议文档名称',
docUrl: 'https://www.finclip.com',
copyWriting: '自定义授权弹窗文案,隐私协议文档名称为《自定义隐私协议文档名称》'
}
}
}
FinAppProxyHandlerManager.privacyHandler = new PrivacyHandler()
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class PrivacyHandler {
/**
* @param scope 当前请求的权限,如果为空则是在关于页面显示的隐私协议
* @returns IFinApplet.IPrivacy
*/
getPrivacyInfo?: (appId: string, apiServer: string, scope?: string) => IFinApplet.IPrivacy
}
示例代码
client.proxyHandlerManager.privacyHandler.getPrivacyInfo = (appId: string, apiServer: string, scope?: string) => {
return {
title: '自定义授权弹窗标题',
docName: '自定义隐私协议文档名称',
docUrl: 'https://www.finclip.com',
copyWriting: '自定义授权弹窗文案,隐私协议文档名称为《自定义隐私协议文档名称》'
}
}
IFinApplet.IPrivacy
属性 | 类型 | 是否必填 | 描述 |
---|---|---|---|
title | String | 是 | 自定义隐私授权弹窗标题 |
copyWriting | String | 是 | 自定义隐私授权弹窗文案,内容包含 自定义隐私协议文档名称 |
docName | String | 是 | 自定义隐私协议文档名称 |
docUrl | String | 是 | 自定义隐私协议文档链接 |
# 4. ScopeHandler
自定义权限的代理类
// namespace IFinProxyHandlerItem
export class ScopeHandler {
/**
* 是否自定义小程序权限设置页
*/
public isAppletCustomizeSettingPage: boolean = false
/**
* 自定义小程序权限设置页
* @param applet
* @param scopes
* @param updateScopes 更新 scopes 的方法
* @returns boolean 是否实现了代理,如果返回 false 表示未实现代理,会走默认的逻辑
*/
public openSettingPage(applet: IFinApplet.IAppletInfo, scopes: IFinApplet.IScope[],
updateScopes: (result: IFinAppProxy.ISettingListParams) => void): boolean
/**
* 注册自定义权限
* @param appId
* @param apiServer
* @returns 自定义权限列表
*/
public getCustomScopes(appId: string, apiServer: string): IFinApplet.IScope[]
/**
* 自定义弹窗,实现方式可以参考示例代码
* @returns 弹窗 builder
*/
public getCustomView(): (() => void) | void
/**
* @param api 当初小程序触发的 API 名称
* @param context 当前 Ability 的 context
* @param scopes 权限列表,包括内置权限和自定义权限
* @param callback API 的回调函数,如果用户通过权限调用 callback 传 true,后续则会走自定义 API 的逻辑,否则则直接走 fail 逻辑
* @returns boolean 是否实现了代理,如果返回 false 表示未实现代理,会走默认的权限逻辑
*/
public onCustomApiInvoke(appId: string, apiServer: string, api: string, context: common.UIAbilityContext,
scopes: IFinApplet.IScope[], callback: (res: boolean) => void): boolean
}
示例代码
import { FinAppProxyHandlerManager, IFinApplet, IFinAppProxy, IFinProxyHandlerItem } from '@finclip/sdk'
import { promptAction } from '@kit.ArkUI'
import { ISettingRouterParams } from '../../pages/Setting'
import { getFinAppClient } from '../FinAppClient'
import { common } from '@kit.AbilityKit'
class ScopeHandler extends IFinProxyHandlerItem.ScopeHandler {
isAppletCustomizeSettingPage = true
openSettingPage(applet: IFinApplet.IAppletInfo, scopes: IFinApplet.IScope[],
updateScopes: (result: IFinAppProxy.ISettingListParams) => void): boolean {
const param: ISettingRouterParams = {
applet,
scopes
}
// 以 Navigation 的方式初始化,使用 Navigation 跳转授权管理页
getFinAppClient().getEntryInfo().routerState?.pushPath({
name: 'Setting',
param,
onPop: (info) => {
const param = info.result as ISettingRouterParams
updateScopes({
scopes: param.scopes,
applet: param.applet
})
}
})
return true
}
getCustomScopes(appId: string, apiServer: string): IFinApplet.IScope[] {
return [
{
scope: 'idCard',
scopeName: '身份证',
title: '获取身份证信息',
desc: "获取您的身份证信息用于XXXXXX",
scopeStatus: 0
}
]
}
getCustomView(): (() => void) | void {
return customView
}
onCustomApiInvoke(appId: string, apiServer: string, api: string, context: common.UIAbilityContext,
scopes: IFinApplet.IScope[], callback: (res: boolean) => void): boolean {
const scope = scopes.find(i => i.scope === 'idCard')
if (scope?.scopeStatus === 0) {
context.eventHub.emit('showCustomToast', api)
context.eventHub.on('onShowCustomToast', (res: boolean) => {
scope.scopeStatus = res ? 3 : 1
callback(res)
context.eventHub.off('onShowCustomToast')
})
} else {
callback(true)
}
return true
}
private promptMsg(message: string) {
promptAction.showToast({
message
})
}
}
@Builder
function customView() {
CustomViewComponent()
}
@Component
struct CustomViewComponent {
@State show: boolean = false
private context = getContext(this) as common.UIAbilityContext
aboutToAppear() {
this.context.eventHub.on('showCustomToast', () => {
this.show = true
})
}
handlePermission(res: boolean) {
this.context.eventHub.emit('onShowCustomToast', res)
this.show = false
}
@Builder
toast() {
Stack({ alignContent: Alignment.Bottom }) {
Row() {
}
.height('100%')
.width('100%')
.backgroundColor('rgba(0,0,0,0.5)')
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
Row() {
Button('拒绝').onClick(() => {
this.handlePermission(false)
})
Button('同意').onClick(() => {
this.handlePermission(true)
})
}
.width('100%')
}
.padding({
top: 24,
left: 18,
right: 18,
bottom: 24
})
.width('100%')
.height(241)
.backgroundColor('#F5F6F6')
.clip(true)
.borderRadius({ topLeft: 16, topRight: 16 })
.zIndex(10)
}
}
build() {
if (this.show) {
this.toast()
}
}
}
FinAppProxyHandlerManager.scopeHandler = new ScopeHandler()
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class ScopeHandler {
/**
* @param api 当初小程序触发的 API 名称
* @param context 当前 Ability 的 context * @param scopes 权限列表,包括内置权限和自定义权限
* @param callback API 的回调函数,如果用户通过权限调用 callback 传 true,后续则会走自定义 API 的逻辑,否则则直接走 fail 逻辑
* @returns
*/
onCustomApiInvoke?: (appId: string, apiServer: string, api: string, context: common.UIAbilityContext, scopes: IFinApplet.IScope[], callback: (res: boolean) => void) => void
// 自定义弹窗,实现方式可以参考示例代码
getCustomView?: () => (() => void) | void
// 注册自定义权限
getCustomScopes: (appId: string, apiServer: string) => IFinApplet.IScope[]
// 是否自定义小程序权限设置页
isAppletCustomizeSettingPage?: boolean
// 自定义小程序权限设置页
openSettingPage?: (applet: IFinApplet.IAppletInfo, scopes: IFinApplet.IScope[],
updateScopes: (result: IFinAppProxy.ISettingListParams) => void) => void
}
示例代码
client.proxyHandlerManager.privacyHandler.getPrivacyInfo = (appId: string, apiServer: string, scope?: string) => {
return {
title: '自定义授权弹窗标题',
docName: '自定义隐私协议文档名称',
docUrl: 'https://www.finclip.com',
copyWriting: '自定义授权弹窗文案,隐私协议文档名称为《自定义隐私协议文档名称》'
}
}
示例代码
4.1 自定义权限
client.proxyHandlerManager.scopeHandler.getCustomScopes = (appId: string, apiServer: string) => {
return [
{
scope: 'idCard',
scopeName: '身份证',
title: '获取身份证信息',
desc: "获取您的身份证信息用于XXXXXX",
scopeStatus: 0
}
]
}
@Builder
function customView() {
CustomViewComponent()
}
@Component
struct CustomViewComponent {
@State show: boolean = false
private context = getContext(this) as common.UIAbilityContext
aboutToAppear() {
this.context.eventHub.on('showCustomToast', () => {
this.show = true
})
}
handlePermission(res: boolean) {
this.context.eventHub.emit('onShowCustomToast', res)
this.show = false
}
@Builder
toast() {
Stack({ alignContent: Alignment.Bottom }) {
Row() {
} .height('100%')
.width('100%')
.backgroundColor('rgba(0,0,0,0.5)')
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
Row() {
Button('拒绝').onClick(() => {
this.handlePermission(false)
})
Button('同意').onClick(() => {
this.handlePermission(true)
})
}
.width('100%')
}
.padding({ top: 24, left: 18, right: 18, bottom: 24 })
.width('100%')
.height(241)
.backgroundColor('#F5F6F6')
.clip(true)
.borderRadius({ topLeft: 16, topRight: 16 })
.zIndex(10)
}
}
build() {
if (this.show) {
this.toast()
}
}}
client.proxyHandlerManager.scopeHandler.getCustomView = () => customView
const onCustomApiInvoke = (appId: string, apiServer: string, api: String, context: common.UIAbilityContext, scopes: IFinApplet.IScope[], callback: (res: boolean) => void) => {
const scope = scopes.find(i => i.scope === 'idCard')
if (scope?.scopeStatus === 0) {
context.eventHub.emit('showCustomToast', api)
context.eventHub.on('onShowCustomToast', (res: boolean) => {
scope.scopeStatus = res ? 3 : 1
callback(res)
context.eventHub.off('onShowCustomToast')
})
} else {
callback(true)
}
}
client.proxyHandlerManager.scopeHandler.onCustomApiInvoke = onCustomApiInvoke
4.2 自定义小程序权限设置页
interface ISettingRouterParams {
applet: IFinApplet.IAppletInfo,
scopes: IFinApplet.IScope[]
}
client.proxyHandlerManager.scopeHandler.isAppletCustomizeSettingPage = true
client.proxyHandlerManager.scopeHandler.openSettingPage =
(applet: IFinApplet.IAppletInfo, scopes: IFinApplet.IScope[], updateScopes) => {
const param: ISettingRouterParams = {
applet,
scopes
}
client.getEntryInfo().routerState?.pushPath({
name: 'Setting',
param,
onPop: (info) => {
const param = info.result as ISettingRouterParams
// 更新权限
updateScopes({
scopes: param.scopes,
applet: param.applet
})
}
})
}
IFinApplet.IScope
属性 | 类型 | 是否必填 | 描述 |
---|---|---|---|
scope | String | 是 | 权限标识位 |
title | String | 是 | 权限标题 |
desc | String | 是 | 权限描述 |
scopeStatus | EPermissionState | 是 | 权限状态 |
scopeName | String | 是 | 设置页描述 |
EPermissionState
属性 | 值 | 描述 |
---|---|---|
UNSET | 0 | 未设置 |
DISALLOW | 1 | 不允许 |
ALLOW_WHEN_USING | 2 | 使用小程序时 |
ALLOW | 3 | 允许 |
IFinApplet.IAppletInfo
属性 | 类型 | 是否必填 | 描述 |
---|---|---|---|
appId | string | 是 | 小程序ID |
name | string | 是 | 小程序名称 |
logo | string | 否 | 小程序logo地址 |
IFinAppProxy.ISettingListParams
属性 | 类型 | 是否必填 | 描述 |
---|---|---|---|
applet | IFinApplet.IAppletInfo | 是 | 小程序信息 |
scopes | IFinApplet.IScope[] | 是 | 权限列表 |
# 5.GrayReleaseHandler
灰度发布相关的代理类
// namespace IFinProxyHandlerItem
export class GrayReleaseHandler {
/**
* 当获取小程序信息时会调用该方法,可以自定义灰度发布的数据,sdk 会将自定义数据和内置数据合并发送,当属性名相同时,使用自定义的数据
* @param appId
* @param apiServer
* @returns 需要拼接到灰度发布配置的数据
*/
public getGrayExtension(appId: string, apiServer: string): Record<string, string>
}
示例代码
import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk'
class GrayReleaseHandler extends IFinProxyHandlerItem.GrayReleaseHandler {
getGrayExtension(appId: string, apiServer: string): Record<string, string> {
return {
'customKey': 'customValue',
'xUserId': '130000000'
}
}
}
FinAppProxyHandlerManager.grayReleaseHandler = new GrayReleaseHandler()
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class GrayReleaseHandler {
/**
* 当获取小程序信息时会调用该方法,可以自定义灰度发布的数据,sdk 会将自定义数据和内置数据合并发送,当属性名相同时,使用自定义的数据
*/
getGrayExtension?: ( appId: string, apiServer: string,) => Record<string, string>
}
示例代码:
client.proxyHandlerManager.grayReleaseHandler.getGrayExtension = ( appId: string, apiServer: string) => {
return {
customName: 'customName'
}
}
# 6.AppletLifeCycleHandler
小程序生命周期相关
// namespace IFinProxyHandlerItem
export class AppletLifeCycleHandler {
/**
* 当小程序打开时触发。
* @param appId 小程序的 ID。
* @param apiServer 与小程序关联的 API 服务器。
*/
public onOpen(appId: string, apiServer: string)
/**
* 当小程序初始化完成时触发。
* @param appId 小程序的 ID。
* @param apiServer 与小程序关联的 API 服务器。
*/
public onInitCompletion(appId: string, apiServer: string)
/**
* 当小程序关闭时触发。
* @param appId 小程序的 ID。
* @param apiServer 与小程序关联的 API 服务器。
*/
public onClose(appId: string, apiServer: string)
/**
* 当小程序发生错误时触发。
* @param appId 小程序的 ID。
* @param apiServer 与小程序关联的 API 服务器。
* @param code 错误代码。
* @param message 错误信息。
*/
public onError(appId: string, apiServer: string, code: string, message: string)
/**
* 当小程序准备就绪时触发。
* @param appId 小程序的 ID。
* @param apiServer 与小程序关联的 API 服务器。
*/
public onReady(appId: string, apiServer: string)
/**
* 当小程序显示时触发。
* @param appId 小程序的 ID。
* @param apiServer 与小程序关联的 API 服务器。
*/
public onShow(appId: string, apiServer: string)
/**
* 当小程序隐藏时触发。
* @param appId 小程序的 ID。
* @param apiServer 与小程序关联的 API 服务器。
*/
public onHide(appId: string, apiServer: string)
/**
* 当小程序销毁时触发。
* @param appId 小程序的 ID。
* @param apiServer 与小程序关联的 API 服务器。
*/
public onDestroy(appId: string, apiServer: string)
}
示例代码
import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk'
import { promptAction } from '@kit.ArkUI'
class AppletLifeCycleProxy extends IFinProxyHandlerItem.AppletLifeCycleHandler {
onOpen(appId: string, apiServer: string) {
this.promptMsg(`${appId} onOpen`)
}
onInitCompletion(appId: string, apiServer: string) {
this.promptMsg(`${appId} onInitCompletion`)
}
onClose(appId: string, apiServer: string) {
this.promptMsg(`${appId} onClose`)
}
onError(appId: string, apiServer: string, code: string, message: string) {
this.promptMsg(`${appId} onError,code:${code},message:${message}`)
}
onReady(appId: string, apiServer: string) {
this.promptMsg(`${appId} onReady`)
}
onShow(appId: string, apiServer: string) {
this.promptMsg(`${appId} onShow`)
}
onHide(appId: string, apiServer: string) {
this.promptMsg(`${appId} onHide`)
}
onDestroy(appId: string, apiServer: string) {
this.promptMsg(`${appId} onDestroy`)
}
private promptMsg(message: string) {
promptAction.showToast({
message
})
}
}
FinAppProxyHandlerManager.appletLifeCycleHandler = new AppletLifeCycleProxy()
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class AppletLifeCycleHandler {
onOpen?: (appId: string, apiServer: string) => void // 小程序打开
onInitCompletion?: (appId: string, apiServer: string) => void // 小程序初始化完成
onClose?: (appId: string, apiServer: string) => void // 小程序关闭
onError?: (appId: string, apiServer: string, code: string, message: string) => void // 小程序报错
onReady?: (appId: string, apiServer: string) => void // 小程序页面首次渲染完成
onDestroy?: (appId: string, apiServer: string) => void // 小程序销毁
}
# 7.CustomLayoutHandler
自定义布局相关的代理类
// namespace IFinProxyHandlerItem
export class CustomLayoutHandler {
/**
* 自定义分包下载错误页
* @returns 自定义分包下载错误页 builder
*/
public getDownloadSubpackageFileFailedLayout(): ((appId: string, apiServer: string, path: string,
reload: () => void) => void) | void
/**
* 自定义加载错误页
* @returns 自定义加载错误页 builder
*/
public getLoadFailedLayout(): ((appId: string, apiServer: string, title: string, label: string) => void) | void
/**
* 当页面准备完成触发,该函数结束后销毁 loading 组件
* @returns
*/
public async onPageLoadingLayoutReady(): Promise<void>
/**
* 当小程序准备完成触发,该函数结束后销毁 loading 页
* @returns
*/
public async onLoadingLayoutReady(): Promise<void>
/**
* 自定义页面 loading 组件
* @returns 自定义页面 loading 的 builder
*/
public getPageCustomLoadingLayout(): ((appId: string, apiServer: string, path: string) => void) | void
/**
* 获取自定义 loading 页的布局方法
* @returns 自定义 loading 页 builder
*/
public getCustomLoadingLayout(): (() => void) | void
/**
* 获取自定义 Toast 方法
* @returns 自定义 Toast builder
*/
public getCustomToastLayout(): (() => void) | void
/**
* 获取自定义覆盖物 builder,会渲染在小程序页面上层
* @returns 自定义覆盖物 builder
*/
public getCustomOverlayLayout(): ((appId: string, apiServer: string) => void) | void
}
示例代码
import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk'
import { CustomToast } from '../customLayout/customToast'
import { CustomOverlayLayout } from '../customLayout/customOverlayLayout'
import { DownloadSubpackageFileFailedLayout } from '../customLayout/DownloadSubpackageFileFailedLayout'
import { LoadFailedLayout } from '../customLayout/LoadFailedLayout'
import { CustomLoadingLayout, PageCustomLoadingLayout } from '../customLayout/LoadingLayout'
class CustomLayoutHandler extends IFinProxyHandlerItem.CustomLayoutHandler {
/**
* 自定义分包下载错误页
* @returns 自定义分包下载错误页 builder
*/
getDownloadSubpackageFileFailedLayout(): ((appId: string, apiServer: string, path: string,
reload: () => void) => void) | void {
return DownloadSubpackageFileFailedLayout
}
/**
* 自定义加载错误页
* @returns 自定义加载错误页 builder
*/
getLoadFailedLayout(): ((appId: string, apiServer: string, title: string, label: string) => void) | void {
return LoadFailedLayout
}
onPageLoadingLayoutReady(): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 200)
})
}
onLoadingLayoutReady(): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 200)
})
}
getPageCustomLoadingLayout(): ((appId: string, apiServer: string, path: string) => void) | void {
return PageCustomLoadingLayout
}
getCustomLoadingLayout(): (() => void) | void {
return CustomLoadingLayout
}
getCustomToastLayout(): (() => void) | void {
return CustomToast
}
getCustomOverlayLayout(): ((appId: string, apiServer: string) => void) | void {
return CustomOverlayLayout
}
}
FinAppProxyHandlerManager.customLayoutHandler = new CustomLayoutHandler()
// DownloadSubpackageFileFailedLayout.ets
@Builder
export function DownloadSubpackageFileFailedLayout(appId: string, apiServer: string, path: string,
reload: () => void) {
CustomViewComponent({
appId,
apiServer,
path,
reload
})
}
@Component
struct CustomViewComponent {
@Prop appId: string
@Prop apiServer: string
@Prop path: string
reload?: () => void
build() {
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center,
direction: FlexDirection.Column
}) {
Column() {
Text('appId: ' + this.appId)
Text('apiServer: ' + this.apiServer)
Text('path: ' + this.path)
Button('重新加载').onClick(() => {
this.reload?.()
})
}
}
.backgroundColor(Color.White)
.width('100%')
.height('100%')
}
}
// LoadFailedLayout.ets
@Builder
export function LoadFailedLayout(appId: string, apiServer: string, title: string, label: string) {
CustomViewComponent({
appId,
apiServer,
title,
label
})
}
@Component
struct CustomViewComponent {
@Prop appId: string
@Prop apiServer: string
@Prop title: string
@Prop label: string
build() {
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center,
direction: FlexDirection.Column
}) {
Column() {
Text('appId: ' + this.appId)
Text('apiServer: ' + this.apiServer)
Text('title: ' + this.title)
Text('label: ' + this.label)
}
}
.backgroundColor(Color.White)
.width('100%')
.height('100%')
}
}
// LoadingLayout.ets
@Builder
export function PageCustomLoadingLayout(appId: string, apiServer: string, path: string) {
CustomViewComponent({
appId,
apiServer,
path,
})
}
@Builder
export function CustomLoadingLayout() {
CustomViewComponent()
}
@Component
struct CustomViewComponent {
@Prop appId: string
@Prop apiServer: string
@Prop path: string
build() {
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center,
direction: FlexDirection.Column
}) {
Column() {
if (this.path) {
Text('页面自定义 loading')
Text('appId: ' + this.appId)
Text('apiServer: ' + this.apiServer)
Text('path: ' + this.path)
} else {
Text('自定义 loading 页')
}
}
}
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
.backgroundColor(this.path ? Color.Red : Color.Green)
.width('100%')
.height('100%')
}
}
// CustomToast.ets
import { IFinAppProxy } from '@finclip/sdk';
@Builder
export function CustomToast() {
CustomViewComponent()
}
@Component
struct CustomViewComponent {
@Consume toastInfo: IFinAppProxy.ICustomToastLayoutParams
build() {
this.toastWithMask()
}
@Builder
toastImage() {
if (this.toastInfo.image) {
Image(this.toastInfo.image)
.width(40)
.height(40)
} else if (this.toastInfo.icon) {
if (this.toastInfo.icon === 'loading') {
Text('loading 图案')
}
if (this.toastInfo.icon === 'success') {
Text('success 图案')
}
if (this.toastInfo.icon === 'error') {
Text('error 图案')
}
}
}
@Builder
toast() {
if (this.toastInfo.icon === 'none') {
Column() {
Text(this.toastInfo.title)
.fontSize(14)
.fontColor('rgba(255, 255, 255, 0.9)')
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(2)
.textAlign(TextAlign.Center)
}
.constraintSize({
maxWidth: 200,
minHeight: 48
})
.padding({
left: 12,
right: 12,
top: 14,
bottom: 14
})
// 加一个占位空间,让垂直居中稍微靠上
.margin({
bottom: 120
})
.backgroundColor('rgba(0, 0, 0, 0.7)')
.align(Alignment.Center)
.justifyContent(FlexAlign.Center)
.opacity(1)
.borderRadius(10)
.clip(true)
} else {
Column() {
this.toastImage()
Text(this.toastInfo.title)
.fontSize(14)
.fontColor('rgba(255, 255, 255, 0.9)')
.margin({ top: 12 })
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(2)
.textAlign(TextAlign.Center)
}
.width(130)
.height(130)
.padding({
left: 14,
right: 14
})
// 加一个占位空间,让垂直居中稍微靠上
.margin({
bottom: 120
})
.backgroundColor('rgba(0, 0, 0, 0.7)')
.align(Alignment.Center)
.justifyContent(FlexAlign.Center)
.opacity(1)
.borderRadius(10)
.clip(true)
}
}
@Builder
toastWithMask() {
Flex({
direction: FlexDirection.Column,
justifyContent: FlexAlign.Center,
alignItems: ItemAlign.Center
}) {
this.toast()
}
.width('100%')
.height('100%')
.backgroundColor('transparent')
// 稍微小于 modal 和 actionsheet
.zIndex(110)
.position({
top: 0,
left: 0
})
.hitTestBehavior(this.toastInfo.mask ? HitTestMode.Default : HitTestMode.Transparent)
}
}
// customOverlayLayout.ets
@Builder
export function CustomOverlayLayout(appId: string, apiServer: string) {
Column() {
Text('CustomLayout')
Text(appId)
Text(apiServer)
}
}
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class CustomLayoutHandler {
// 获取自定义 loading 页的布局方法
getCustomLoadingLayout?: () => (() => void) | void
// 自定义页面 loading 组件
getPageCustomLoadingLayout?: () => ((appId: string, apiServer: string, path: string) => void) | void
// 当小程序准备完成触发,该函数结束后销毁 loading 页
onLoadingLayoutReady: () => Promise<void> = async () => {}
// 当页面准备完成触发,该函数结束后销毁 loading 组件
onPageLoadingLayoutReady: () => Promise<void> = async () => {}
// 自定义分包下载错误页
getDownloadSubpackageFileFailedLayout?: () => ((appId: string, apiServer: string, path: string, reload: () => void) => void) | void
// 自定义加载错误页
getLoadFailedLayout?: () => ((appId: string, apiServer: string, title: string, label: string) => void) | void
}
示例代码1(小程序 loading)
client.proxyHandlerManager.customLayoutHandler.getCustomLoadingLayout = () => customLoading
// 如果小程序启动依赖某些异步数据才渲染,比如请求等,这时候小程序可能还是处于无数据的状态,可以通过延迟调用 resolve 的方式来延缓 loading 页销毁时机
client.proxyHandlerManager.customLayoutHandler.onLoadingLayoutReady = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 200)
})
}
@Builder
function customLoading() {
CustomViewComponent()
}
// 该组件会渲染在整个 loading 页面
@Component
struct CustomViewComponent {
@Consume layoutParams: IFinAppProxy.ICustomLoadingLayoutParams
build() {
Column() {
Image(this.layoutParams.logo)
Text(this.layoutParams.name)
}
}
}
示例代码2(页面 loading)
client.proxyHandlerManager.customLayoutHandler.getPageCustomLoadingLayout = () => customLoading
// 如果小程序启动依赖某些异步数据才渲染,比如请求等,这时候小程序页面可能还是处于无数据的状态,可以通过延迟调用 resolve 的方式来延缓 loading 页销毁时机
client.proxyHandlerManager.customLayoutHandler.onPageLoadingLayoutReady = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 200)
})
}
// 这里会传入小程序的页面的信息,开发者可以根据 path 来自定义不同的 loading 组件
@Builder
function customLoading(appId: string, apiServer: string, path: string) {
CustomViewComponent({ appId, apiServer, path })
}
@Component
struct CustomViewComponent {
@Prop appId: string
@Prop apiServer: string
@Prop path: string
build() {
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center,
direction: FlexDirection.Column
}) {
Column() {
Text('path: ' + this.path)
}
}
.backgroundColor(Color.White)
.width('100%')
.height('100%')
}
}
示例代码3(自定义分包下载错误页)
client.proxyHandlerManager.customLayoutHandler.getDownloadSubpackageFileFailedLayout = () => customLayout
// 这里会传入小程序的页面的信息
@Builder
function customLayout(appId: string, apiServer: string, path: string, reload: () => void) {
CustomViewComponent({ appId, apiServer, path, reload })
}
@Component
struct CustomViewComponent {
@Prop appId: string
@Prop apiServer: string
@Prop path: string
reload:() => void // 重新加载
build() {
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center,
direction: FlexDirection.Column
}) {
Column() {
Text('path: ' + this.path)
}.onClick(()=>{
this.reload?.()
})
}
.backgroundColor(Color.White)
.width('100%')
.height('100%')
}
}
示例代码4(自定义加载错误页)
client.proxyHandlerManager.customLayoutHandler.getLoadFailedLayout = () => customLayout
// 这里会传入小程序的页面的信息
@Builder
function customLayout(appId: string, apiServer: string, title: string, label: string) {
CustomViewComponent({ appId,
apiServer,
title,
label
})
}
@Component
struct CustomViewComponent {
@Prop appId: string
@Prop apiServer: string
@Prop title: string
@Prop label: string
build() {
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center,
direction: FlexDirection.Column
}) {
Column() {
Text('appletName: ' + this.title)
Text('label: ' + this.label)
Text('apiServer: ' + this.apiServer)
Text('appId: ' + this.appId)
}
}
.backgroundColor(Color.Green)
}
}
IFinAppProxy.ICustomLoadingLayoutParams
属性 | 类型 | 描述 |
---|---|---|
name | String | 小程序名称,当未获取到小程序详情时,默认为空 |
logo | ResourceStr | 小程序图标,当未获取到小程序详情或者小程序详情中的小程序图标字段为空会返回一个默认的图标,开发者可以自行选择是否使用 |
appId | String | 小程序 appId,当未获取到小程序详情时,默认为空 |
apiServer | String | 小程序 apiServer,当未获取到小程序详情时,默认为空 |
# 8.ButtonOpenTypeHandler
小程序中的 button 有一系列的open-type事件,有部分行为需要App来实现,所以也会通过代理方法来触发对应的事件
// namespace IFinProxyHandlerItem
export class ButtonOpenTypeHandler {
/**
* 调用获取用户信息的Api(getUserProfile) 时,会触发该代理方法。
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @returns Promise<IFinApplet.IUserProfile | void>
*
*/
public async getUserProfile(appId: string, apiServer: string): Promise<IFinApplet.IUserProfile | void>
/**
* 调用获取用户信息API(getUserInfo) 或者 点击open-type 属性为 getUserInfo 的 Button 时触发该代理事件
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @returns Promise<IFinApplet.IUserInfo | void>
*
*/
public async getUserInfo(appId: string, apiServer: string): Promise<IFinApplet.IUserInfo | void>
/**
* 用户点击转发按钮或者 点击open-type 属性为 share 的 Button 时触发该代理事件
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @param forwardInfo 转发携带的参数
* @returns Promise<void>
*
*/
public async forwardAppletWithInfo(appId: string, apiServer: string, forwardInfo: IFinAppProxy.IForwardInfo): Promise<void>
/**
* 点击open-type 属性为 getPhoneNumber 的 Button 时触发该代理事件
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @param callback 同自定义 API 的 callback,由宿主自行处理返回结果
* @returns Promise<void>
*
*/
public async getPhoneNumber(appId: string, apiServer: string, callback: IApiManager.IInvokeCallback): Promise<void>
/**
* 点击open-type 属性为 launchApp 的 Button 时触发该代理事件
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @param callback 同自定义 API 的 callback,由宿主自行处理返回结果
* @param appParameter 打开 APP 时,向 APP 传递的参数
* @returns Promise<void>
*
*/
public async launchApp(appId: string, apiServer: string, callback: IApiManager.IInvokeCallback, appParameter: string): Promise<void>
/**
* 点击open-type 属性为 feedback 的 Button 时触发该代理事件
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @returns Promise<boolean> 默认返回 false,返回 true 表示由代理实现逻辑,返回 false 表示打开更多菜单里的投诉反馈页面。
*
*/
public async feedback(appId: string, apiServer: string): Promise<boolean>
/**
* 点击open-type 属性为 chooseAvatar 的 Button 时触发该代理事件
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @param callback 同自定义 API 的 callback,由宿主自行处理返回结果
* @returns Promise<void>
*
*/
public async chooseAvatar(appId: string, apiServer: string, callback: IApiManager.IInvokeCallback): Promise<void>
/**
* 点击open-type 属性为 contact 的 Button 时触发该代理事件
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @param callback 同自定义 API 的 callback,由宿主自行处理返回结果
* @param sessionFrom 会话来源
* @param sendMessageTitle 会话内消息卡片标题
* @param sendMessagePath 会话内消息卡片点击跳转小程序路径
* @param sendMessageImg 会话内消息卡片图片
* @param showMessageCard 小程序信息
* @returns Promise<void>
*/
public async contact(appId: string, apiServer: string, callback: IApiManager.IInvokeCallback, sessionFrom: string, sendMessageTitle: string, sendMessagePath: string, sendMessageImg: string, showMessageCard: boolean): Promise<void>
}
示例代码
import { FinAppProxyHandlerManager, IFinApplet, IFinProxyHandlerItem } from '@finclip/sdk'
class ButtonOpenTypeHandler extends IFinProxyHandlerItem.ButtonOpenTypeHandler {
async getUserProfile(appId: string, apiServer: string): Promise<IFinApplet.IUserProfile | void> {
return {
nickName: "昵称",
avatarUrl: "头像地址",
gender: "性别",
province: "省份",
city: "城市",
country: "国家",
}
}
async getUserInfo(appId: string, apiServer: string): Promise<IFinApplet.IUserInfo | void> {
return {
nickName: "昵称",
avatarUrl: "头像地址",
gender: "性别",
province: "省份",
city: "城市",
country: "国家",
}
}
async forwardAppletWithInfo(appId: string, apiServer: string, forwardInfo: IFinAppProxy.IForwardInfo): Promise<void> {
this.promptMsg(JSON.stringify(forwardInfo))
}
async feedback(appId: string, apiServer: string): Promise<boolean> {
this.promptMsg(`触发了 feedback`)
return false
}
async chooseAvatar(appId: string, apiServer: string, callback: IApiManager.IInvokeCallback) {
this.promptMsg(`触发了 chooseAvatar`)
callback.onSuccess({})
}
async launchApp(appId: string, apiServer: string, callback: IApiManager.IInvokeCallback,
appParameter: string): Promise<void> {
this.promptMsg(`触发了 launchApp,appParameter${appParameter}`)
callback.onSuccess({})
}
async getPhoneNumber(appId: string, apiServer: string, callback: IApiManager.IInvokeCallback): Promise<void> {
this.promptMsg(`触发了 getPhoneNumber`)
const res: IButtonOpenTypeHandler.IGetPhoneNumberRes = {
phone: 123456789
}
callback.onSuccess(res)
}
async contact(appId: string, apiServer: string, callback: IApiManager.IInvokeCallback, sessionFrom: string,
sendMessageTitle: string, sendMessagePath: string, sendMessageImage: string, showMessageCard: boolean) {
this.promptMsg(`触发了 contact\n sessionFrom:${sessionFrom}\n sendMessageTitle:${sendMessageTitle}\n sendMessagePath:${sendMessagePath}\n sendMessageImage:${sendMessageImage}\n showMessageCard:${showMessageCard}`)
callback.onSuccess({})
}
private promptMsg(message: string) {
promptAction.showToast({
message
})
}
}
FinAppProxyHandlerManager.buttonOpenTypeHandler = new ButtonOpenTypeHandler()
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class ButtonOpenTypeHandler {
/**
* 调用获取用户信息API(getUserInfo) 或者 点击open-type属性为getUserInfo的Button时触发该代理事件
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @returns Promise<IFinApplet.IUserInfo | void>
*
*/
getUserInfo?: (appId: string, apiServer: string) => Promise<IFinApplet.IUserInfo | void>
/**
* 调用获取用户信息的Api(getUserProfile) 时,会触发该代理方法。
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @returns Promise<IFinApplet.IUserProfile | void>
*
*/
getUserProfile?: (appId: string, apiServer: string) => Promise<IFinApplet.IUserProfile | void>
}
示例代码:
client.proxyHandlerManager.buttonOpenTypeHandler.getUserInfo = async ( appId: string, apiServer: string) => {
return {
nickName: "昵称",
avatarUrl: "头像地址",
gender: "性别",
province: "省份",
city:"城市",
country: "国家",
}
}
client.proxyHandlerManager.buttonOpenTypeHandler.getUserProfile = async ( appId: string, apiServer: string) => {
return {
nickName: "昵称",
avatarUrl: "头像地址",
gender: "性别",
province: "省份",
city:"城市",
country: "国家",
}
}
IFinApplet.IUserInfo
属性 | 类型 |
---|---|
nickName | String |
avatarUrl | String |
gender | String |
province | String |
city | String |
country | String |
IFinApplet.IUserProfile
type IUserInfo = string | number | boolean | null | Serializable[] | { [key: string]: Serializable }
# 9.onAppBackPressHandler
侧滑代理
// namespace IFinProxyHandlerItem
export class onAppBackPressHandler {
/**
* 当用户侧滑时触发
* @param appId 小程序 id
* @param apiServer 小程序 apiServer
* @returns 返回 true 表示宿主自行处理,返回 false 表示宿主处理完,仍执行后续的默认操作
*/
public onBackPress(appId: string, apiServer: string): boolean | void
}
示例代码
import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk'
import { promptAction } from '@kit.ArkUI'
class onAppBackPressHandler extends IFinProxyHandlerItem.onAppBackPressHandler {
onBackPress(appId: string, apiServer: string) {
// 仅对小程序生效
// 返回 true 表示宿主自行处理
// 返回 false 表示宿主处理完,仍执行后续的默认操作
this.promptMsg(`触发了侧滑代理`)
return false
}
private promptMsg(message: string) {
promptAction.showToast({
message
})
}
}
FinAppProxyHandlerManager.onAppBackPressHandler = new onAppBackPressHandler()
# 10. LogHandler
日志的代理类
// namespace IFinProxyHandlerItem
export class LogHandler {
/**
* 是否开启控制台输出,默认 false
*/
public isConsoleLog: boolean = false;
/**
* 是否写入文件,默认 true
*/
public writeFile: boolean = true;
/**
* 设置日志文件保留时间,单位为秒(默认10天,至少为1天)
*/
public logFileAliveDuration: number = 60 * 60 * 24 * 10
/**
* 日志输出的完整的沙箱路径
* 默认路径 /data/app/el2/100/base/包名/haps/entry/files/Applet/logs
*/
public logDir?: string
/**
* 触发 log 时会执行该方法,如果想自行处理日志文件,可以将 writeFile 设置为 false,然后在该方法处理日志内容
*/
public onLog(level: IFinAppConfig.ILogLevel, log: string)
}
示例代码
import { FinAppProxyHandlerManager, IFinAppConfig, IFinProxyHandlerItem } from '@finclip/sdk';
class LogHandler extends IFinProxyHandlerItem.LogHandler {
isConsoleLog: boolean = true;
writeFile: boolean = true;
onLog(level: IFinAppConfig.ILogLevel, log: string): void {
console.log(`level:${level},log:${log}`)
}
}
FinAppProxyHandlerManager.logHandler = new LogHandler()
# 11. WatermarkHandler
水印代理类
// namespace IFinProxyHandlerItem
class WatermarkHandler {
/**
* 获取水印 builder
* @returns 水印 builder
*/
public getWatermarkLayout(): ((appId: string, apiServer: string) => void) | void {
}
}
示例代码
import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk';
@Component
struct Watermark {
@Prop appId: string
@Prop apiServer: string
watermarkWidth: number = 120;
watermarkHeight: number = 120;
watermarkText: string = this.getWatermarkText();
rotationAngle: number = -30;
fillColor: string | number | CanvasGradient | CanvasPattern = '#10000000';
font: string = '16vp';
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
draw() {
this.context.fillStyle = this.fillColor;
this.context.font = this.font;
const colCount = Math.ceil(this.context.width / this.watermarkWidth);
const rowCount = Math.ceil(this.context.height / this.watermarkHeight);
for (let col = 0; col <= colCount; col++) {
let row = 0;
for (; row <= rowCount; row++) {
const angle = this.rotationAngle * Math.PI / 180;
this.context.rotate(angle);
const positionX = this.rotationAngle > 0 ? this.watermarkHeight * Math.tan(angle) : 0;
const positionY = this.rotationAngle > 0 ? 0 : this.watermarkWidth * Math.tan(-angle);
this.context.fillText(this.watermarkText, positionX, positionY);
this.context.rotate(-angle);
this.context.translate(0, this.watermarkHeight);
}
this.context.translate(0, -this.watermarkHeight * row);
this.context.translate(this.watermarkWidth, 0);
}
}
getWatermarkText() {
return `finclip--${this.appId}`
}
build() {
Canvas(this.context)
.width('100%')
.height('100%')
.hitTestBehavior(HitTestMode.Transparent)
.onReady(() => this.draw())
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM, SafeAreaEdge.TOP])
}
}
@Builder
function watermarkBuilder(appId: string, apiServer: string) {
Watermark({
appId,
apiServer
})
}
class WatermarkHandler extends IFinProxyHandlerItem.WatermarkHandler {
public getWatermarkLayout(): ((appId: string, apiServer: string) => void) | void {
return watermarkBuilder
}
}
FinAppProxyHandlerManager.watermarkHandler = new WatermarkHandler()
# 12. AppletTraceHandler
小程序可回溯的代理类
export class AppletTraceHandler {
/**
* 请求用户授权
* @returns 返回授权结果
*/
public async requestUserAuthorization(): Promise<boolean> {
return true
}
}
示例代码
import { FinAppProxyHandlerManager, IFinAppConfig, IFinProxyHandlerItem } from '@finclip/sdk';
import { promptAction } from '@kit.ArkUI';
class AppletTraceHandler extends IFinProxyHandlerItem.AppletTraceHandler {
requestUserAuthorization(): Promise<boolean> {
return new Promise((resolve, reject)=>{
promptAction.showDialog({
title: '是否开启录制',
buttons: [
{
text: '确定',
color: '#409EFF'
},
{
text: '取消',
color: '#000000'
}
]
}).then(data => {
resolve(data.index !== 1)
})
})
}
}
FinAppProxyHandlerManager.appletTraceHandler = new AppletTraceHandler()