UNPKG

@react-native-ohos/react-native-view-shot

Version:
198 lines (188 loc) 8.54 kB
/* * MIT License * * Copyright (C) 2023 Huawei Device Co., Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import { TurboModule, TurboModuleContext } from '@rnoh/react-native-openharmony/ts'; import componentSnapshot from '@ohos.arkui.componentSnapshot'; import image from '@ohos.multimedia.image'; import fs from '@ohos.file.fs'; import { Context } from '@ohos.abilityAccessCtrl'; import window from '@ohos.window'; import { util } from '@kit.ArkTS'; import { BusinessError } from '@kit.BasicServicesKit'; import Logger from './Logger'; import { Size } from '@kit.ArkUI'; type Options = { fileName?: string, width?: number, height?: number, format: "png" | "jpg" | "webm" | "raw", quality: number, result: "tmpfile" | "base64" | "data-uri" | "zip-base64", snapshotContentContainer: boolean, handleGLSurfaceViewOnAndroid: boolean, }; export class ViewShotTurboModule extends TurboModule { private context: Context = getContext(this); constructor(ctx: TurboModuleContext) { super(ctx); } captureRef(tag: number, option: Options): Promise<string> { return new Promise<string>((resolve, reject) => { const nodeId = (this.ctx && (this.ctx as any).rnInstance && (this.ctx as any).rnInstance.getNativeNodeIdByTag) ? (this.ctx as any).rnInstance.getNativeNodeIdByTag(tag) ?? (tag + '') : (tag + ''); componentSnapshot.get(nodeId).then((pixmap: image.PixelMap) => { let originImageInfo: Size = pixmap.getImageInfoSync().size; let dstX = option.width ?? originImageInfo.width; let dstY = option.height ?? originImageInfo.height; let scaleX = dstX / originImageInfo.width; let scaleY = dstY / originImageInfo.height; pixmap.scaleSync(scaleX, scaleY); if (option.result === 'base64' || option.result === 'data-uri') { this.getImageBase64(pixmap, option).then((res) => { resolve(res); }).catch((err: BusinessError) => { Logger.error(`componentSnapshot failed, message = ${err}`); reject(`componentSnapshot failed, message = ${err}`); }) } else { this.savePhotoOnDevice('ComponentSnapshot', option, pixmap).then(uri => { resolve(uri); }).catch((err: BusinessError) => { Logger.error(`componentSnapshot failed, message = ${err}`); reject(`componentSnapshot failed, message = ${err}`); }) } }).catch((err: BusinessError) => { Logger.error(`componentSnapshot failed, message = ${err}`); reject(`componentSnapshot failed, message = ${err}`); }) }) } captureScreen(option: Options): Promise<string> { return new Promise<string>(async (resolve, reject) => { window.getLastWindow(this.ctx.uiAbilityContext).then(windowClass => { windowClass.snapshot().then((pixmap) => { let originImageInfo: Size = pixmap.getImageInfoSync().size; let dstX = option.width ?? originImageInfo.width; let dstY = option.height ?? originImageInfo.height; let scaleX = dstX / originImageInfo.width; let scaleY = dstY / originImageInfo.height; pixmap.scaleSync(scaleX, scaleY); if (option.result === 'base64' || option.result === 'data-uri') { this.getImageBase64(pixmap, option).then((res) => { resolve(res); }).catch((err: BusinessError) => { Logger.error(`componentSnapshot failed, message = ${err}`); reject(`componentSnapshot failed, message = ${err}`); }) }else{ this.savePhotoOnDevice('ScreenSnapshot', option, pixmap).then(uri => { resolve(uri); }).catch((err: BusinessError) => { Logger.error(`componentSnapshot failed, message = ${err}`); reject(`componentSnapshot failed, message = ${err}`); }) } }) }).catch((err: BusinessError) => { Logger.error(`ScreenSnapshot failed, message = ${err}`); reject(`ScreenSnapshot failed, message = ${err}`); }) }) } releaseCapture(uri: string) { if (!uri.startsWith('/data')) { return; } try { fs.unlinkSync(uri); } catch (err) { Logger.error(`releaseCapture failed, message = ${err}`); } } savePhotoOnDevice(title: string, option: Options, pixmap: image.PixelMap): Promise<string> { return new Promise<string>((resolve, reject) => { let extension = option.format; let packOpts: image.PackingOption = { format: `image/${extension === 'jpg' ? 'jpeg' : 'png'}`, quality: option.quality * 100 }; let imagePacker = image.createImagePacker(); title = option.fileName ? option.fileName : title + '-' + this.getNowTime(); const path: string = this.context.tempDir + `/${title}.${extension}`; let file = fs.openSync(path, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE); imagePacker.packing(pixmap, packOpts).then(data => { try{ fs.writeSync(file.fd, data); } catch (err) { Logger.error(`componentSnapshot failed, message = ${err}`); reject(`componentSnapshot failed, message = ${err}`); } finally { fs.closeSync(file); pixmap.release().then(() => { Logger.info('Succeeded in releasing pixelmap object.'); }).catch((error: BusinessError) => { Logger.error(`Failed to release pixelmap object. code is ${error.code}, message is ${error.message}`); }) } resolve(path); }).catch((error: BusinessError) => { Logger.error(`componentSnapshot failed, message = ${error}`); reject(`componentSnapshot failed, message = ${error}`); }) }) } getNowTime(): string { let date = new Date(); let year = date.getFullYear().toString(); let month = date.getMonth() + 1; let day = date.getDay(); let hours = date.getHours().toString(); let minutes = date.getMinutes().toString(); let seconds = date.getSeconds().toString(); let milliseconds = date.getMilliseconds().toString(); return year + (month > 10 ? month : '0' + month) + (day > 10 ? day : '0' + day) + hours + minutes + seconds + milliseconds; } async getImageBase64(pixmap: image.PixelMap, options: Options): Promise<string> { const imagePackageApi: image.ImagePacker = image.createImagePacker(); let packOpts: image.PackingOption = { format: `image/${options.format === 'jpg' ? 'jpeg' : 'png'}`, quality: options.quality * 100, } const readBuffer: ArrayBuffer = await imagePackageApi.packing(pixmap, packOpts); let base64Helper = new util.Base64Helper(); let uint8Arr = new Uint8Array(readBuffer); let base64Text = base64Helper.encodeToStringSync(uint8Arr); pixmap.release().then(() => { Logger.info('Succeeded in releasing pixelmap object.'); }).catch((error: BusinessError) => { Logger.error(`Failed to release pixelmap object. code is ${error.code}, message is ${error.message}`); }) if (options.result === 'data-uri') { let dataUriStr = `data:image/${options.format};base64,${base64Text}`; return dataUriStr; } else { return base64Text; } } }