/* * Copyright (c) 2023-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. */ const pickerHelper = requireInternal('file.picker'); let gContext = undefined; const PhotoViewMIMETypes = { IMAGE_TYPE: 'image/*', VIDEO_TYPE: 'video/*', IMAGE_VIDEO_TYPE: '*/*', INVALID_TYPE: '' }; const DocumentSelectMode = { FILE: 0, FOLDER: 1, MIXED: 2, }; const DocumentPickerMode = { DEFAULT: 0, DOWNLOAD: 1, }; const ExtTypes = { DOWNLOAD_TYPE: 'filePicker', AUDIO_PICKER_TYPE: 'audioPicker', PHOTO_PICKER_TYPE: 'photoPicker', }; const PickerDetailType = { FILE_MGR_AUTH: 'downloadAuth', FILE_MGR_SELECT:'select', FILE_MGR_SAVE:'save', }; const ErrCode = { INVALID_ARGS: 13900020, RESULT_ERROR: 13900042, NAME_TOO_LONG: 13900030, CONTEXT_NO_EXIST: 16000011, }; const ERRCODE_MAP = new Map([ [ErrCode.INVALID_ARGS, 'Invalid argument'], [ErrCode.RESULT_ERROR, 'Unknown error'], [ErrCode.NAME_TOO_LONG, 'File name too long'], [ErrCode.CONTEXT_NO_EXIST, 'Current ability failed to obtain context'], ]); const PHOTO_VIEW_MIME_TYPE_MAP = new Map([ [PhotoViewMIMETypes.IMAGE_TYPE, 'FILTER_MEDIA_TYPE_IMAGE'], [PhotoViewMIMETypes.VIDEO_TYPE, 'FILTER_MEDIA_TYPE_VIDEO'], [PhotoViewMIMETypes.IMAGE_VIDEO_TYPE, 'FILTER_MEDIA_TYPE_ALL'], ]); const ACTION = { SELECT_ACTION: 'ohos.want.action.OPEN_FILE', SELECT_ACTION_MODAL: 'ohos.want.action.OPEN_FILE_SERVICE', SAVE_ACTION: 'ohos.want.action.CREATE_FILE', SAVE_ACTION_MODAL: 'ohos.want.action.CREATE_FILE_SERVICE', }; const CREATE_FILE_NAME_LENGTH_LIMIT = 256; const ARGS_ZERO = 0; const ARGS_ONE = 1; const ARGS_TWO = 2; const RESULT_CODE_ERROR = -1; const RESULT_CODE_OK = 0; const FILENAME_LENGTH = 3; /* * UTF-8字符编码数值对应的存储长度: * 0000 - 0x007F (eg: a~z A~Z 0~9) * 0080 - 0x07FF (eg: 希腊字母) * 0800 - 0xFFFF (eg: 中文) * 其他 (eg: 平面符号) */ function strSizeUTF8(str) { let strLen = str.length; let bytesLen = 0; let greeceLen = 2; let chineseLen = 3; let othersLen = 4; for (let i = 0; i < strLen; i++) { let charCode = str.charCodeAt(i); if (charCode <= 0x007f) { bytesLen++; } else if (charCode <= 0x07ff) { bytesLen += greeceLen; } else if (charCode <= 0xffff) { bytesLen += chineseLen; } else { bytesLen += othersLen; } } return bytesLen; } function checkArguments(args) { let checkArgumentsResult = undefined; if (args.length === ARGS_TWO && typeof args[ARGS_ONE] !== 'function') { checkArgumentsResult = getErr(ErrCode.INVALID_ARGS); } if (args.length > 0 && typeof args[ARGS_ZERO] === 'object') { let option = args[ARGS_ZERO]; if (option.maxSelectNumber !== undefined) { if (option.maxSelectNumber.toString().indexOf('.') !== -1) { checkArgumentsResult = getErr(ErrCode.INVALID_ARGS); } } if (option.newFileNames === undefined || option.newFileNames.length <= 0) { return checkArgumentsResult; } for (let i = 0; i < option.newFileNames.length; i++) { let value = option.newFileNames[i]; if (strSizeUTF8(value) >= CREATE_FILE_NAME_LENGTH_LIMIT) { console.log('[picker] checkArguments Invalid name: ' + value); checkArgumentsResult = getErr(ErrCode.NAME_TOO_LONG); } } } return checkArgumentsResult; } function getErr(errCode) { return {code: errCode, message: ERRCODE_MAP.get(errCode)}; } function parsePhotoPickerSelectOption(args) { let config = { action: 'ohos.want.action.photoPicker', type: 'multipleselect', parameters: { uri: 'multipleselect', extType: ExtTypes.PHOTO_PICKER_TYPE, }, }; if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') { let option = args[ARGS_ZERO]; if (option.maxSelectNumber && option.maxSelectNumber > 0) { let select = (option.maxSelectNumber === 1) ? 'singleselect' : 'multipleselect'; config.type = select; config.parameters.uri = select; config.parameters.maxSelectCount = option.maxSelectNumber; } if (option.MIMEType && PHOTO_VIEW_MIME_TYPE_MAP.has(option.MIMEType)) { config.parameters.filterMediaType = PHOTO_VIEW_MIME_TYPE_MAP.get(option.MIMEType); } } return config; } function anonymousPathArray(geturi) { let anonymousPathArrays = []; let anonymousPath = ''; if (geturi === undefined) { return anonymousPathArrays; } for (let i = 0; i < geturi.length; ++i) { let lastSlashIndex = geturi[i].lastIndexOf('/'); if (lastSlashIndex === -1) { anonymousPathArrays.push(geturi[i]); } else { let dirPath = geturi[i].substring(0, lastSlashIndex + 1); let fileName = geturi[i].substring(lastSlashIndex + 1); if (fileName.length <= 0) { anonymousPath = '******'; } else { let lastLetter = fileName.slice(-1); let maskedName = '******' + lastLetter; anonymousPath = dirPath + maskedName; } anonymousPathArrays.push(anonymousPath); } } return anonymousPathArrays; } function getPhotoPickerSelectResult(args) { let selectResult = { error: undefined, data: undefined, }; if (args.resultCode === 0) { let uris = args.photoUris; if (uris === undefined) { console.log('[picker] Photo uris is undefined'); uris = []; } let isOriginal = args.isOriginal; selectResult.data = new PhotoSelectResult(uris, isOriginal); } else if (args.resultCode === -1) { selectResult.data = new PhotoSelectResult([], undefined); } else { selectResult.error = getErr(ErrCode.RESULT_ERROR); } return selectResult; } async function photoPickerSelect(...args) { let checkPhotoArgsResult = checkArguments(args); if (checkPhotoArgsResult !== undefined) { console.log('[picker] Photo Invalid argument'); throw checkPhotoArgsResult; } const config = parsePhotoPickerSelectOption(args); console.log('[picker] Photo config: ' + JSON.stringify(config)); let photoSelectContext = undefined; let photoSelectWindow = undefined; try { if (this.context !== undefined) { photoSelectContext = this.context; } else { photoSelectContext = getContext(this); } } catch (getContextError) { console.error('[picker] getContext error: ' + getContextError); throw getErr(ErrCode.CONTEXT_NO_EXIST); } try { if (photoSelectContext === undefined) { console.error('[picker] photoSelectContext == undefined'); throw getErr(ErrCode.CONTEXT_NO_EXIST); } let modalSelectResult = await modalPicker(photoSelectContext, config, photoSelectWindow); console.log('[picker] photo select result: ' + JSON.stringify(modalSelectResult)); const photoSelectResult = getPhotoPickerSelectResult(modalSelectResult); console.log('[picker] photoSelectResult: ' + JSON.stringify(photoSelectResult)); if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') { return args[ARGS_ONE](photoSelectResult.error, photoSelectResult.data); } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') { return args[ARGS_ZERO](photoSelectResult.error, photoSelectResult.data); } return new Promise((resolve, reject) => { if (photoSelectResult.data !== undefined) { resolve(photoSelectResult.data); } else { reject(photoSelectResult.error); } }); } catch (error) { console.error('[picker] photo select error: ' + error); } return undefined; } function parseDocumentPickerSelectOption(args, action) { let config = { action: action, parameters: { startMode: 'choose', extType: ExtTypes.DOWNLOAD_TYPE, pickerType: PickerDetailType.FILE_MGR_SELECT, } }; if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') { let option = args[ARGS_ZERO]; config.parameters.key_select_mode = option.selectMode; console.log('[picker] parseDocumentPickerSelectOption: ' + JSON.stringify(option)); if ((option.maxSelectNumber !== undefined) && option.maxSelectNumber > 0) { config.parameters.key_pick_num = option.maxSelectNumber; } if (option.defaultFilePathUri !== undefined) { config.parameters.key_pick_dir_path = option.defaultFilePathUri; } if ((option.fileSuffixFilters !== undefined) && option.fileSuffixFilters.length > 0) { config.parameters.key_file_suffix_filter = option.fileSuffixFilters; } if (option.authMode !== undefined) { config.parameters.key_auth_mode = option.authMode; } } console.log('[picker] document select config: ' + JSON.stringify(config)); return config; } function parseAudioPickerSelectOption(args, action) { let config = { action: action, parameters: { extType: ExtTypes.AUDIO_PICKER_TYPE, } }; if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') { let option = args[ARGS_ZERO]; if ((option.maxSelectNumber !== undefined) && option.maxSelectNumber > 0) { config.parameters.key_pick_num = option.maxSelectNumber; } } console.log('[picker] audio select config: ' + JSON.stringify(config)); return config; } function getDocumentPickerSelectResult(args) { let selectResult = { error: undefined, data: undefined }; if (args === undefined || args.resultCode === undefined) { selectResult.error = getErr(ErrCode.RESULT_ERROR); console.log('[picker] document select selectResult: ' + JSON.stringify(selectResult)); return selectResult; } if (args.resultCode === RESULT_CODE_OK) { if (args.ability_params_stream) { selectResult.data = args.ability_params_stream; selectResult.error = args.resultCode; } } else if (args.resultCode === RESULT_CODE_ERROR) { selectResult.data = []; selectResult.error = args.resultCode; } console.log('[picker] document select selectResult: : errorcode is = ' + selectResult.error + ', selecturi is = ' + anonymousPathArray(selectResult.data)); return selectResult; } async function documentPickerSelect(...args) { let checkDocumentSelectArgsResult = checkArguments(args); if (checkDocumentSelectArgsResult !== undefined) { console.log('[picker] Document Select Invalid argument'); throw checkDocumentSelectArgsResult; } let documentSelectContext = undefined; let documentSelectConfig = undefined; let documentSelectResult = undefined; let selectResult = undefined; let documentSelectWindow = undefined; try { if (this.context !== undefined) { documentSelectContext = this.context; } else { documentSelectContext = getContext(this); } } catch (getContextError) { console.error('[picker] getContext error: ' + getContextError); throw getErr(ErrCode.CONTEXT_NO_EXIST); } try { if (documentSelectContext === undefined) { console.error('[picker] documentSelectContext == undefined'); throw getErr(ErrCode.CONTEXT_NO_EXIST); } if (this.window !== undefined) { documentSelectWindow = this.window; } documentSelectConfig = parseDocumentPickerSelectOption(args, ACTION.SELECT_ACTION_MODAL); console.error('[picker] DocumentSelect documentSelectConfig: ' + JSON.stringify(documentSelectConfig)); documentSelectResult = await modalPicker(documentSelectContext, documentSelectConfig, documentSelectWindow); } catch (paramError) { console.error('[picker] DocumentSelect paramError: ' + JSON.stringify(paramError)); } selectResult = getDocumentPickerSelectResult(documentSelectResult); return sendResult(args, selectResult); } function parseDocumentPickerSaveOption(args, action) { let config = { action: action, parameters: { startMode: 'save', pickerMode: DocumentPickerMode.DEFAULT, extType: ExtTypes.DOWNLOAD_TYPE, pickerType: PickerDetailType.FILE_MGR_SAVE, } }; if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') { let option = args[ARGS_ZERO]; console.log('[picker] document save option: ' + JSON.stringify(option)); if ((option.newFileNames !== undefined) && option.newFileNames.length > 0) { config.parameters.key_pick_file_name = option.newFileNames; config.parameters.saveFile = option.newFileNames[0]; } if (option.defaultFilePathUri !== undefined) { config.parameters.key_pick_dir_path = option.defaultFilePathUri; } if ((option.fileSuffixChoices !== undefined) && option.fileSuffixChoices.length > 0) { config.parameters.key_file_suffix_choices = option.fileSuffixChoices; } if (option.pickerMode === DocumentPickerMode.DOWNLOAD) { config.parameters.pickerMode = option.pickerMode; config.parameters.pickerType = PickerDetailType.FILE_MGR_AUTH; } } console.log('[picker] document save config: ' + JSON.stringify(config)); return config; } function getAudioPickerSelectResult(args) { let selectResult = { error: undefined, data: undefined }; if (args === undefined || args.resultCode === undefined) { selectResult.error = getErr(ErrCode.RESULT_ERROR); console.log('[picker] getAudioPickerSelectResult selectResult: ' + JSON.stringify(selectResult)); return selectResult; } if (args.resultCode === RESULT_CODE_OK) { if (args.uriArr) { selectResult.data = args.uriArr; selectResult.error = args.resultCode; } else { selectResult.data = []; selectResult.error = args.resultCode; } } else if (args.resultCode === RESULT_CODE_ERROR) { selectResult.data = []; selectResult.error = args.resultCode; } console.log('[picker] getAudioPickerSelectResult selectResult: errorcode is = ' + selectResult.error + ', selecturi is = ' + anonymousPathArray(selectResult.data)); return selectResult; } function getDocumentPickerSaveResult(args) { let saveResult = { error: undefined, data: undefined, suffix: -1 }; if (args === undefined || args.resultCode === undefined) { saveResult.error = getErr(ErrCode.RESULT_ERROR); console.log('[picker] getDocumentPickerSaveResult saveResult: ' + JSON.stringify(saveResult)); return saveResult; } if (args.resultCode === RESULT_CODE_OK) { if (args.ability_params_stream) { saveResult.data = args.ability_params_stream; saveResult.error = args.resultCode; if (args.userSuffixIndex >= 0) { saveResult.suffix = args.userSuffixIndex; } } } else if (args.resultCode === RESULT_CODE_ERROR) { saveResult.data = []; saveResult.error = args.resultCode; } console.log('[picker] getDocumentPickerSaveResult saveResult: errorcode is = ' + saveResult.error + ', selecturi is = ' + anonymousPathArray(saveResult.data) + ', usersavesuffix =' + saveResult.suffix); return saveResult; } function startModalPicker(context, config, window) { if (context === undefined) { throw Error('[picker] Context undefined.'); } if (config === undefined) { throw Error('[picker] Config undefined.'); } gContext = context; if (pickerHelper === undefined) { throw Error('[picker] PickerHelper undefined.'); } let helper; if (window !== undefined) { helper = pickerHelper.startModalPicker(gContext, config, window); } else { helper = pickerHelper.startModalPicker(gContext, config); } if (helper === undefined) { throw Error('[picker] Please check the parameter you entered.'); } return helper; } async function modalPicker(context, config, window) { try { console.log('[picker] Config: ' + JSON.stringify(config)); let modalResult = await startModalPicker(context, config, window); return modalResult; } catch (resultError) { console.error('[picker] Result error: ' + resultError); return undefined; } } async function documentPickerSave(...args) { let checkDocumentSaveArgsResult = checkArguments(args); if (checkDocumentSaveArgsResult !== undefined) { console.log('[picker] Document Save Invalid argument'); throw checkDocumentSaveArgsResult; } let documentSaveContext = undefined; let documentSaveConfig = undefined; let documentSaveResult = undefined; let saveResult = undefined; let documentSaveWindow = undefined; try { if (this.context !== undefined) { documentSaveContext = this.context; } else { documentSaveContext = getContext(this); } } catch (getContextError) { console.error('[picker] getContext error: ' + getContextError); throw getErr(ErrCode.CONTEXT_NO_EXIST); } if (this.window !== undefined) { documentSaveWindow = this.window; } documentSaveConfig = parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION_MODAL); console.log('[picker] document save start'); documentSaveResult = await modalPicker(documentSaveContext, documentSaveConfig, documentSaveWindow); saveResult = getDocumentPickerSaveResult(documentSaveResult); this.suffixIndex = saveResult.suffix; return sendResult(args, saveResult); } function getSelectedSuffixIndex() { console.log('[picker] Get Selected Suffix Index start'); let index = this.suffixIndex; this.suffixIndex = -1; console.log('[picker] Get Selected Suffix Index end: ' + index); return index; } async function sendResult(args, result) { try { if (result === undefined) { console.log('[picker] modal picker: result is undefined.'); return undefined; } if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') { return args[ARGS_ONE](result.error, result.data); } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') { return args[ARGS_ZERO](result.error, result.data); } return new Promise((resolve, reject) => { if (result.data !== undefined) { resolve(result.data); } else { reject(result.error); } }); } catch (resultError) { console.error('[picker] Result error: ' + resultError); } return undefined; } async function audioPickerSelect(...args) { let checkAudioArgsResult = checkArguments(args); if (checkAudioArgsResult !== undefined) { console.log('[picker] Audio Invalid argument'); throw checkAudioArgsResult; } const audioSelectConfig = parseAudioPickerSelectOption(args, ACTION.SELECT_ACTION); console.log('[picker] audio select config: ' + JSON.stringify(audioSelectConfig)); let audioSelectContext = undefined; let audipSelectWindow = undefined; try { if (this.context !== undefined) { audioSelectContext = this.context; } else { audioSelectContext = getContext(this); } } catch (getContextError) { console.error('[picker] getContext error: ' + getContextError); throw getErr(ErrCode.CONTEXT_NO_EXIST); } try { if (audioSelectContext === undefined) { console.error('[picker] audioSelectContext == undefined'); throw getErr(ErrCode.CONTEXT_NO_EXIST); } let modalSelectResult = await modalPicker(audioSelectContext, audioSelectConfig, audipSelectWindow); let saveResult = getAudioPickerSelectResult(modalSelectResult); return sendResult(args, saveResult); } catch (error) { console.error('[picker] audio select error: ' + error); } return undefined; } function PhotoSelectOptions() { this.MIMEType = PhotoViewMIMETypes.INVALID_TYPE; this.maxSelectNumber = -1; } function PhotoSelectResult(uris, isOriginalPhoto) { this.photoUris = uris; this.isOriginalPhoto = isOriginalPhoto; } function PhotoSaveOptions() { this.newFileNames = undefined; } function DocumentSelectOptions() { this.defaultFilePathUri = undefined; this.fileSuffixFilters = undefined; this.maxSelectNumber = undefined; this.selectMode = DocumentSelectMode.FILE; } function DocumentSaveOptions() { this.newFileNames = undefined; this.defaultFilePathUri = undefined; this.fileSuffixChoices = undefined; this.pickerMode = DocumentPickerMode.DEFAULT; } function AudioSelectOptions() {} function AudioSaveOptions() { this.newFileNames = undefined; } function ParseContext(args) { if (args.length > ARGS_TWO || args.length < ARGS_ZERO || typeof args[ARGS_ZERO] !== 'object') { return undefined; } return args[ARGS_ZERO]; } function parseWindow(args) { if (args.length !== ARGS_TWO) { console.log('[picker] ParseWindow: not window mode.'); return undefined; } if (args.length === ARGS_TWO && typeof args[ARGS_ONE] !== 'object') { console.log('[picker] ParseWindow: not window mode or type err.'); return undefined; } console.log('[picker] ParseWindow: window mode.'); return args[ARGS_ONE]; } function PhotoViewPicker(...args) { this.select = photoPickerSelect; this.save = documentPickerSave; this.context = ParseContext(args); } function DocumentViewPicker(...args) { this.select = documentPickerSelect; this.save = documentPickerSave; this.context = ParseContext(args); this.window = parseWindow(args); this.getSelectedIndex = getSelectedSuffixIndex; this.suffixIndex = -1; } function AudioViewPicker(...args) { this.select = audioPickerSelect; this.save = documentPickerSave; this.context = ParseContext(args); } export default { getSelectedSuffixIndex, startModalPicker, ExtTypes : ExtTypes, PickerDetailType: PickerDetailType, PhotoViewMIMETypes : PhotoViewMIMETypes, PhotoSelectOptions : PhotoSelectOptions, PhotoSelectResult : PhotoSelectResult, PhotoSaveOptions : PhotoSaveOptions, DocumentSelectMode : DocumentSelectMode, DocumentPickerMode : DocumentPickerMode, DocumentSelectOptions : DocumentSelectOptions, DocumentSaveOptions : DocumentSaveOptions, AudioSelectOptions : AudioSelectOptions, AudioSaveOptions : AudioSaveOptions, PhotoViewPicker : PhotoViewPicker, DocumentViewPicker: DocumentViewPicker, AudioViewPicker : AudioViewPicker, };