9afce6f6创建于 2025年5月7日历史提交
/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { BusinessError } from '@kit.BasicServicesKit';
import { picker } from '@kit.CoreFileKit';
import { window } from '@kit.ArkUI';
import { util } from '@kit.ArkTS';
import emitter from '@ohos.events.emitter';
import etswrapper from 'libetswrapper.so';
import { logger } from '../utils/Logger';

/**
 * 类型声明:处理参数为string[]的回调
 */
export interface StringArrayCbWrapper {
  call: (data: string[]) => void;
}

/**
 * 类型声明:处理参数为BusinessError类型的回调
 */
export interface CatchCbWrapper {
  call: (err: BusinessError) => void;
}

export const EVENT_ID: number = 0x1111; // 注意:仅供UI展示使用

/**
 * 封装后的documentViewPicker的Select方法,需要被注册到native侧
 * @param uiContext:调用本方法的UIContext
 * @param options:拉起picker时的options参数
 * @param thenWrapper:开发者自定义的then回调
 * @param catchWrapper:开发者自定义的catch回调
 */
function documentViewPickerSelect(uiContext: UIContext, options: picker.DocumentSelectOptions, thenWrapper:
  StringArrayCbWrapper, catchWrapper: CatchCbWrapper): void {
  // TODO:知识点:使用对应UIAbility的UIContext.runScopedTask来执行方法,确保多实例情况下,事件在正确的窗口/UIAbility内发生
  uiContext.runScopedTask(() => {
    logger.debug("enter ets select");
    logger.debug(JSON.stringify(uiContext));
    const documentViewPicker: picker.DocumentViewPicker = new picker.DocumentViewPicker();
    documentViewPicker.select(options).then((value: string[]) => {
      logger.debug("enter js select then");
      logger.debug(JSON.stringify(uiContext));
      // 注意:仅供UI展示使用:通过emitter将消息发送出去,在ui界面处接收并展示
      emitter.emit({
        eventId: EVENT_ID
      }, {
        data: {
          content: value.toString(),
          id: EVENT_ID,
          isEmpty: false
        }
      });
      thenWrapper.call(value);
    }).catch((error: BusinessError) => {
      logger.debug("enter js select catch");
      catchWrapper.call(error);
    })
  })
}

/**
 * 封装后的documentViewPicker的Save方法,需要被注册到native侧
 * @param uiContext:调用本方法的UIContext
 * @param options:拉起picker时的options参数
 * @param thenWrapper:开发者自定义的then回调
 * @param catchWrapper:开发者自定义的catch回调
 */
function documentViewPickerSave(uiContext: UIContext, options: picker.DocumentSaveOptions, thenWrapper:
  StringArrayCbWrapper, catchWrapper: CatchCbWrapper): void {
  //...
}

/**
 * TODO:知识点:步骤一:
 * 注册封装后的方法到native侧
 * 建议在EntryAbility之外调用,单次调用即可
 * example:
 * registryDocumentViewPickerFn();
 * export default class EntryAbility extends UIAbility {
 *     abilityID: string = generateAbilityID();
 *     ...
 * }
 */
export function registryDocumentViewPickerFn(): void {
  if (etswrapper !== undefined && etswrapper.registryDocumentViewPickerFn !== undefined) {
    etswrapper.registryDocumentViewPickerFn(documentViewPickerSelect, documentViewPickerSave);
  }
}

/**
 * TODO:知识点:步骤二:
 * 生成UUID作为UIAbility实例的id,需在使用时自行为EntryAbility添加id属性
 * example:
 * export default class EntryAbility extends UIAbility {
 *     abilityID: string = generateAbilityID();
 *     ...
 * }
 */
export function generateAbilityID(): string {
  return util.generateRandomUUID();
}


/**
 * TODO:知识点:步骤三:
 * 注册UIAbility的ID及其对应的UIContext,此方法应确保在loadContent成功之后执行,
 * 即EntryAbility中的onWindowStageCreate中的windowStage.loadContent回调中执行
 * @param abilityID:UIAbility的ID
 * @param windowStage:UIAbility对应的windowStage对象
 */
export function addUIContext(abilityID: string, windowClass: window.Window): void {
  const uiContext: UIContext = windowClass.getUIContext();
  // TODO:知识点:添加UIAbility的id及其对应UIContext,后续将uiContext传回ets侧,以确保多实例情况下事件在正确的窗口/UIAbility内发生
  if (etswrapper !== undefined && etswrapper.addUIContext !== undefined) {
    etswrapper.addUIContext(abilityID, uiContext);
  }
}

/**
 * TODO:步骤四:
 * 订阅相关事件,当窗口活跃时,设置对应的abilityID为全局的topAbility(表示当前正活跃的窗口),必须在windowStage.loadContent之后调用
 * @param abilityID:UIAbility的ID
 * @param windowStage:UIAbility对应的windowStage
 */
export function setTopAbilityID(abilityID: string, windowStage: window.WindowStage): void {
  windowStage.on("windowStageEvent", (data: window.WindowStageEventType) => {
    if (data === window.WindowStageEventType.ACTIVE) {
      logger.debug(`[activeAbility]${abilityID}`);
      // TODO:知识点:当窗口为Active时,设置当前UIAbility的id为活跃的id,则在Native执行方法时,需要取到对应id的UIContext
      if (etswrapper !== undefined && etswrapper.setTopAbilityID !== undefined) {
        etswrapper.setTopAbilityID(abilityID);
      }
    }
  })
}

/**
 * TODO:步骤五:
 * 移除abilityID对应的UIContext,建议在EntryAbility中的onWindowStageDestroy中执行
 * @param abilityID
 */
export function removeUIContext(abilityID: string): void {
  if (etswrapper !== undefined && etswrapper.removeUIContext !== undefined) {
    etswrapper.removeUIContext(abilityID);
  }
}

/**
 * 模拟从native侧在JS线程发起调用
 */
export function mockDocumentViewPickerSelectJSThread(): void {
  if (etswrapper !== undefined && etswrapper.mockDocumentViewPickerSelectJSThread !== undefined) {
    etswrapper.mockDocumentViewPickerSelectJSThread();
  }
}

/**
 * 模拟native侧在PThread线程发起调用
 */
export function mockDocumentViewPickerSelectPThread(): void {
  if (etswrapper !== undefined && etswrapper.mockDocumentViewPickerSelectPThread !== undefined) {
    etswrapper.mockDocumentViewPickerSelectPThread();
  }
}