UNPKG

@react-native-oh-tpl/camera-roll

Version:
412 lines (392 loc) 16.7 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 { TM } from '@rnoh/react-native-openharmony/generated/ts'; import { Album, PhotoThumbnail, GetPhotosParams, PhotoIdentifier, GetAlbumsParams, PhotoIdentifiersPage, PhotoConvertionOptions, PhotoThumbnailOptions, SaveToCameraRollOptions, SaveToCameraRollOptionsTypeMenu } from './CameraRollParamTypes'; import photoAccessHelper from '@ohos.file.photoAccessHelper'; import dataSharePredicates from '@ohos.data.dataSharePredicates'; import util from '@ohos.util'; import image from '@ohos.multimedia.image'; import { request } from '@kit.BasicServicesKit'; import fs from '@ohos.file.fs'; import { Context } from '@kit.AbilityKit'; import { fileUri } from '@kit.CoreFileKit'; import Logger from './Logger'; const ASSET_TYPE_PHOTOS = 'Photos'; const ASSET_TYPE_VIDEOS = 'Videos'; const ASSET_TYPE_ALL = 'All'; const ResourceType = 'internet'; export class CameraRollTurboModule extends TurboModule implements TM.RNCCameraRoll.Spec { private phAccessHelper: photoAccessHelper.PhotoAccessHelper; private context: Context = getContext(this); constructor(ctx: TurboModuleContext) { super(ctx); this.phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.ctx.uiAbilityContext); } async saveToDevice(saveUri: string, options: SaveToCameraRollOptions, resourceType: string): Promise<PhotoIdentifier> { let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [ { title: saveUri.substring(saveUri.lastIndexOf('/') + 1, saveUri.lastIndexOf('.')), fileNameExtension: saveUri.substring(saveUri.lastIndexOf('.') + 1), photoType: options.type === SaveToCameraRollOptionsTypeMenu.photo ? photoAccessHelper.PhotoType.IMAGE : photoAccessHelper.PhotoType.VIDEO, subtype: photoAccessHelper.PhotoSubtype.DEFAULT } ]; let _saveUri = saveUri; if (!saveUri.startsWith('file://')) { _saveUri = fileUri.getUriFromPath(saveUri) } let saveUris: string[] = await this.phAccessHelper.showAssetsCreationDialog([_saveUri], photoCreationConfigs); if (saveUris.length) { try{ let file = fs.openSync(_saveUri, fs.OpenMode.READ_ONLY); let stat = fs.statSync(file.fd); let buffer = new ArrayBuffer(stat.size); fs.readSync(file.fd, buffer); let media_file = fs.openSync(saveUris[0], fs.OpenMode.WRITE_ONLY); fs.writeSync(media_file.fd, buffer); fs.closeSync(file); if (resourceType) { fs.unlinkSync(saveUri); } fs.closeSync(media_file); let result: PhotoIdentifier = { node: { id: '', type: options.type ?? '', subTypes: 'PhotoPanorama', sourceType: 'UserLibrary', group_name: [], image: { filename: photoCreationConfigs[0].title ?? null, filepath: null, extension: photoCreationConfigs[0].fileNameExtension ?? null, uri: saveUris[0], height: 0, width: 0, fileSize: stat.size, playableDuration: 0, orientation: null }, timestamp: 0, modificationTimestamp: 0, location: null } }; return result; }catch(e){ Logger.error(`saveToDevice error: ${e}`); } } else { if (resourceType) { fs.unlinkSync(saveUri); } } } saveToCameraRoll(uri: string, options: SaveToCameraRollOptions): Promise<PhotoIdentifier> { return new Promise<PhotoIdentifier>(async (resolve, reject) => { if (!uri) { Logger.error(`Incorrect path.${uri}`); reject('Incorrect path.'); } if (uri.startsWith('http') || uri.startsWith('https')) { try { let sandBoxPath: string = await this.DownloadResources(uri); if (sandBoxPath) { let saveData: PhotoIdentifier = await this.saveToDevice(sandBoxPath, options, ResourceType); resolve(saveData); } } catch (err) { reject(err); } } else { try { let saveData: PhotoIdentifier = await this.saveToDevice(uri, options, ''); resolve(saveData); } catch (err) { Logger.error(`Failed to save, errorCode: ${err.code}`); reject(err); } } }) } getPhotos(params: GetPhotosParams): Promise<PhotoIdentifiersPage> { return new Promise<PhotoIdentifiersPage>((resolve, reject) => { let first = params.first; let after = params.after; let groupTypes = params.groupTypes; let groupName = params.groupName; let includeSharedAlbums = params.includeSharedAlbums; let assetType = params.assetType; let fromTime = params.fromTime; let toTime = params.toTime; let mimeTypes = params.mimeTypes; let include = params.include; let queryBegin = parseInt(this.isEmpty(after) ? '0' : after); let predicates = new dataSharePredicates.DataSharePredicates(); predicates.in('media_type', mimeTypes); predicates.limit(first + 1, queryBegin); predicates.orderByDesc('date_added').orderByDesc('date_modified'); let fetchOptions: photoAccessHelper.FetchOptions = { fetchColumns: [ photoAccessHelper.PhotoKeys.URI, photoAccessHelper.PhotoKeys.PHOTO_TYPE, photoAccessHelper.PhotoKeys.DISPLAY_NAME, photoAccessHelper.PhotoKeys.SIZE, photoAccessHelper.PhotoKeys.DATE_ADDED, photoAccessHelper.PhotoKeys.DATE_MODIFIED, photoAccessHelper.PhotoKeys.DURATION, photoAccessHelper.PhotoKeys.WIDTH, photoAccessHelper.PhotoKeys.HEIGHT, photoAccessHelper.PhotoKeys.ORIENTATION, photoAccessHelper.PhotoKeys.DATE_TAKEN, photoAccessHelper.PhotoKeys.FAVORITE, photoAccessHelper.PhotoKeys.TITLE], predicates: predicates }; this.phAccessHelper.getAssets(fetchOptions).then((fetchResult) => { if (fetchResult !== undefined) { fetchResult.getAllObjects().then((photoAssets) => { if (photoAssets !== undefined && photoAssets.length != 0) { let has_next_page: boolean = first < photoAssets.length; let pageInfo: { has_next_page: boolean; start_cursor?: string; end_cursor?: string; } = { has_next_page: has_next_page, end_cursor: has_next_page ? (queryBegin + first).toString() : '' } let photos: Array<PhotoIdentifier> = new Array<PhotoIdentifier>(); photoAssets.forEach(photoAsset => { let type: string = photoAsset.photoType == 1 ? 'IMAGE' : 'VIDEO' let photo: PhotoIdentifier = { node: { id: '', type: type, subTypes: 'PhotoPanorama', sourceType: 'UserLibrary', group_name: [], image: { filename: photoAsset.displayName, filepath: null, extension: null, uri: photoAsset.uri, height: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.HEIGHT).toString()), width: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.WIDTH).toString()), fileSize: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.SIZE).toString()), playableDuration: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.DURATION).toString()), orientation: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.ORIENTATION).toString()) }, timestamp: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.DATE_ADDED).toString()), modificationTimestamp: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.DATE_MODIFIED) .toString()), location: null } } photos.push(photo); }) let resulPage: PhotoIdentifiersPage = { edges: photos, page_info: pageInfo, } resolve(resulPage); } }) } }).catch(err => { Logger.error('getPhotos failed with err: ' + err); }) }) } getAlbums(params: GetAlbumsParams): Promise<Album[]> { return new Promise<Album[]>((resolve, reject) => { let albumSubtype; if (ASSET_TYPE_PHOTOS == params.assetType) { albumSubtype = photoAccessHelper.AlbumSubtype.USER_GENERIC; } else if (ASSET_TYPE_VIDEOS == params.assetType) { albumSubtype = photoAccessHelper.AlbumSubtype.VIDEO; } else if (ASSET_TYPE_ALL == params.assetType) { albumSubtype = photoAccessHelper.AlbumSubtype.ANY; } this.phAccessHelper.getAlbums(photoAccessHelper.AlbumType.USER, albumSubtype).then(result => { let resultAlbums: Album[] = new Array(); result.getAllObjects().then(albums => { albums.forEach(album => { resultAlbums.push({ title: album.albumName, count: album.count, id: '', type: 'All' }) }) resolve(resultAlbums); }) }).catch((err: Error) => { Logger.error('getAlbums failed with err: ' + err); reject(`Could not get media, ${JSON.stringify(err)}`) }) }) } deletePhotos(photoUris: Array<string>): Promise<void> { // this.phAccessHelper.deleteAssets 为系统接口 return new Promise<void>((resolve, reject) => { }) } getPhotoByInternalID(internalID: string, options: PhotoConvertionOptions): Promise<PhotoIdentifier> { // internalID IOS特有 return new Promise<PhotoIdentifier>((resolve, reject) => { this.getPhotoByUri(internalID).then(photoAsset => { let type: string = photoAsset.photoType == 1 ? 'IMAGE' : 'VIDEO' let photo: PhotoIdentifier = { node: { id: '', type: type, subTypes: 'PhotoPanorama', sourceType: 'UserLibrary', group_name: [], image: { filename: photoAsset.displayName, filepath: null, extension: null, uri: photoAsset.uri, height: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.HEIGHT).toString()), width: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.WIDTH).toString()), fileSize: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.SIZE).toString()), playableDuration: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.DURATION).toString()), orientation: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.ORIENTATION).toString()), }, timestamp: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.DATE_ADDED).toString()), modificationTimestamp: parseInt(photoAsset.get(photoAccessHelper.PhotoKeys.DATE_MODIFIED).toString()), location: null } } resolve(photo); }) }) } getPhotoThumbnail(internalID: string, options: PhotoThumbnailOptions): Promise<PhotoThumbnail> { // internalID IOS特有 return new Promise<PhotoThumbnail>((resolve, reject) => { this.getPhotoByUri(internalID).then(photoAsset => { photoAsset.getThumbnail({ height: options.targetSize.height, width: options.targetSize.width }).then(pixMap => { var base64 = new util.Base64Helper(); let packOpts: image.PackingOption = { format: 'image/jpeg', quality: options.quality * 100 } let imagePackerApi = image.createImagePacker(); imagePackerApi.packing(pixMap, packOpts).then(arrayBuffer => { base64.encodeToString(new Uint8Array(arrayBuffer)).then(base64String => { resolve({ thumbnailBase64: base64String }); }) }) }) }) }) } isEmpty(str: string): boolean { if (str === null || str === undefined || str.trim().length === 0) { return true; } return false; } getPhotoByUri(uri: string): Promise<photoAccessHelper.PhotoAsset> { return new Promise<photoAccessHelper.PhotoAsset>((resolve, reject) => { let predicates = new dataSharePredicates.DataSharePredicates(); predicates.equalTo('uri', uri); let fetchOptions: photoAccessHelper.FetchOptions = { fetchColumns: [ photoAccessHelper.PhotoKeys.URI, photoAccessHelper.PhotoKeys.PHOTO_TYPE, photoAccessHelper.PhotoKeys.DISPLAY_NAME, photoAccessHelper.PhotoKeys.SIZE, photoAccessHelper.PhotoKeys.DATE_ADDED, photoAccessHelper.PhotoKeys.DATE_MODIFIED, photoAccessHelper.PhotoKeys.DURATION, photoAccessHelper.PhotoKeys.WIDTH, photoAccessHelper.PhotoKeys.HEIGHT, photoAccessHelper.PhotoKeys.ORIENTATION, photoAccessHelper.PhotoKeys.DATE_TAKEN, photoAccessHelper.PhotoKeys.FAVORITE, photoAccessHelper.PhotoKeys.TITLE], predicates: predicates }; this.phAccessHelper.getAssets(fetchOptions).then((fetchResult) => { fetchResult.getFirstObject().then(photoAsset => { resolve(photoAsset); }) }) }) } DownloadResources(saveUri): Promise<string> { return new Promise<string>(async (resolve, reject) => { try { let downloadTask: request.DownloadTask = await request.downloadFile(this.context, { url: saveUri }); downloadTask.on('complete', async () => { try { let downloadInfo: request.DownloadInfo = await downloadTask.getTaskInfo(); resolve(downloadInfo.filePath); } catch (err) { Logger.error(`Failed to get downloadInfo. Code: ${err.code}, message: ${err.message}`); } finally { downloadTask.off('complete'); } }) } catch (err) { Logger.error(`Failed to request the download. Code: ${err.code}, message: ${err.message}`); reject(err); } }) } addListener(eventName: string): void { } removeListeners(count: number): void { } 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; } }