UNPKG

@react-native-oh-tpl/react-native-file-viewer

Version:
218 lines (189 loc) 7.55 kB
/* * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved * Use of this source code is governed by a MIT license that can be * found in the LICENSE file. */ import { TurboModule, TurboModuleContext } from '@rnoh/react-native-openharmony/ts'; import { TM } from '@rnoh/react-native-openharmony/generated/ts'; import { BusinessError } from '@kit.BasicServicesKit'; import { filePreview } from '@kit.PreviewKit'; import fileUri from '@ohos.file.fileuri'; import { Context, common, Want } from '@kit.AbilityKit'; import wantConstant from '@ohos.app.ability.wantConstant'; import fs from '@ohos.file.fs'; import mime from 'mime'; import EventEmitter from './EventMitter'; import Hilog from './Logger'; import type { ISubscribe } from './EventMitter'; import { fileShare } from '@kit.CoreFileKit'; type EmitID = ISubscribe['id']; interface RNFileViewerOptions { displayName?: string; showAppsSuggestions?: boolean; showOpenWithDialog?: boolean; } // 派发事件名称 const OPEN_EVENT: string = 'RNFileViewerDidOpen'; const DISMISS_EVENT: string = 'RNFileViewerDidDismiss'; const eventEmitter = new EventEmitter(); export class RNFileViewerTurboModule extends TurboModule implements TM.FileViewerNativeModule.Spec { constructor(protected ctx: TurboModuleContext) { super(ctx); this.ctx = ctx; } open(filepath: string, currentId: number, options: RNFileViewerOptions | string = {}): Promise<void> { const _options = typeof options === 'string' ? { displayName: options } : options; return new Promise((resolve, reject) => { const openSubscription = eventEmitter.$on( OPEN_EVENT, (id: EmitID, error: string) => { if (id === openSubscription.id) { openSubscription.$off(); this.ctx.rnInstance.emitDeviceEvent(OPEN_EVENT, { id: currentId }); return error ? reject(new Error(error)) : resolve(); } }, ); const dismissSubscription = eventEmitter.$on( DISMISS_EVENT, (id: EmitID) => { if (id === dismissSubscription.id) { dismissSubscription.$off(); this.ctx.rnInstance.emitDeviceEvent(DISMISS_EVENT, { id: currentId }); } }, ); // 判断文件是否存在 fs.stat(filepath) .then((stat: fs.Stat) => { this.OpenFile(filepath, this.ctx, _options); resolve(); }) .catch((err: BusinessError) => { Hilog.error('get file info failed with error message: ' + err.message + ', error code: ' + err.code); reject('get file info failed with error code: ' + err.code); throw new Error('err.code:' + err.code); }); }); } private async OpenFile(uri: string, ctx: TurboModuleContext, options: RNFileViewerOptions): Promise<void> { const { showOpenWithDialog, showAppsSuggestions, displayName } = options; const uiContext: Context = ctx.uiAbilityContext; // 获取前提信息 const fUri: fileUri.FileUri = new fileUri.FileUri(uri); const filename: string = fUri.name; const fileExt: string = filename.substring(filename.lastIndexOf('.')); const fileMimeType: string = mime.getType(fileExt); const context = uiContext as common.UIAbilityContext; // UIAbilityContext // 是否支持直接预览 const canPreviewFlag: boolean = await filePreview.canPreview( uiContext, uri, ); // 非多种预览方式,直接进行预览 if (canPreviewFlag && !showOpenWithDialog) { const showFileName = displayName ? displayName : filename.split('.')[0]; // action: 'ohos.want.action.viewData' this.OpenByFilePreview(ctx, uri, fileMimeType, showFileName); return; } let want: Want = { type: fileMimeType, uri: 'file://' + uri, flags: wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION, }; // 跳转应用商店 if (showAppsSuggestions) { want = { uri: `store://appgallery.huawei.com/app`, }; } try { context.startAbilityForResult( want, (err: BusinessError, result: common.AbilityResult) => { if (err.code) { // 处理业务逻辑错误 Hilog.error(`startAbilityForResult failed, code is ${err.code}`); throw new Error('err.code:' + err.code); } eventEmitter.$emit(OPEN_EVENT); // 关闭ability if (result.resultCode === -1) { eventEmitter.$emit(DISMISS_EVENT); } }, ); } catch (error) { Hilog.error(`Failed to startAbility. message: ${error}`); eventEmitter.$emit(OPEN_EVENT, error); } } // filePreview能够展示自定义name,无关闭回调 private OpenByFilePreview( ctx: TurboModuleContext, uri: string, fileMimeType: string, filename: string, ): void { const uiContext: Context = ctx.uiAbilityContext; // 计算打开窗口大小及居中位置 const screenInfo = ctx.getDisplayMetrics().screenPhysicalPixels; const screenWidth = screenInfo.width / screenInfo.scale; const screenHeight = screenInfo.height / screenInfo.scale; const offsetX = screenInfo.width > screenWidth ? (screenInfo.width - screenWidth) / 2 : 0; const offsetY = screenInfo.height > screenHeight ? (screenInfo.height - screenHeight) / 2 : 0; if (!canIUse('SystemCapability.FileManagement.AppFileService.FolderAuthorization')) { console.error('this api is not supported on this device'); return; } let policyInfo: fileShare.PolicyInfo = { uri: fileUri.getUriFromPath(uri), operationMode: fileShare.OperationMode.READ_MODE, }; let policies: Array<fileShare.PolicyInfo> = [policyInfo]; fileShare.persistPermission(policies).then(() => { console.info("persistPermission successfully"); // 调用OS filePreview 进行预览 const displayInfo: filePreview.DisplayInfo = { x: offsetX, y: offsetY, width: screenWidth, height: screenHeight, }; const fileInfo: filePreview.PreviewInfo = { title: filename, uri: fileUri.getUriFromPath(uri), mimeType: fileMimeType, }; // 打开预览窗口 filePreview .openPreview(uiContext, fileInfo, displayInfo) .then(() => { eventEmitter.$emit(OPEN_EVENT); }) .catch((err: BusinessError) => { Hilog.error(`Failed to openPreview, err.code = ${err.code}`); eventEmitter.$emit(OPEN_EVENT, err); throw new Error('err.code:' + err.code); }); }).catch((err: BusinessError<Array<fileShare.PolicyErrorResult>>) => { console.error("persistPermission failed with error message: " + err.message + ", error code: " + err.code); if (err.code == 13900001 && err.data) { for (let i = 0; i < err.data.length; i++) { console.error("error code : " + JSON.stringify(err.data[i].code)); console.error("error uri : " + JSON.stringify(err.data[i].uri)); console.error("error reason : " + JSON.stringify(err.data[i].message)); } } }); } public addListener(eventType: string): void { } public removeListeners(count: number): void { } }