1/* 2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { toast } from '../../base/utils/Common'; 17import { FileMimeTypeUtil } from '../../base/utils/FileMimeTypeUtil'; 18import { FILE_SUFFIX, SELECT_MODE } from '../constants/Constant'; 19import ObjectUtil from './ObjectUtil'; 20import { ability, Want } from '@kit.AbilityKit'; 21import Logger from '../log/Logger'; 22import { PickerWindowType } from '../constants/FilePickerItems'; 23import { StartModeOptions } from '../model/StartModeOptions'; 24import AbilityCommonUtil from './AbilityCommonUtil'; 25import ctx from '@ohos.app.ability.common'; 26 27interface abilityResultInterface { 28 want: Want, 29 resultCode: number 30}; 31 32const TAG = 'FilePickerUtil'; 33 34export namespace FilePickerUtil { 35 export function returnAbilityResult(want: Want, resultCode: number, options: StartModeOptions) { 36 Logger.i(TAG, 'returnPicker start'); 37 let context = getContext() as ctx.UIAbilityContext; 38 if (options.windowType === PickerWindowType.ABILITY) { 39 let abilityResult: abilityResultInterface = { 40 want: want, 41 resultCode: resultCode 42 }; 43 Logger.i(TAG, 'terminateSelfWithResult start'); 44 context.terminateSelfWithResult(abilityResult, (error) => { 45 Logger.e(TAG, 'terminateSelfWithResult is called = ' + error.code); 46 }); 47 } else { 48 let abilityResult: ability.AbilityResult = { 49 resultCode: resultCode, 50 want: want 51 }; 52 options.session?.terminateSelfWithResult(abilityResult, (error) => { 53 Logger.e(TAG, 'closeUIExtFilePicker terminateSelfWithResult is called = ' + error?.code); 54 }); 55 } 56 } 57 58 export function getStartModeOptions(want: Want): StartModeOptions { 59 let options = new StartModeOptions(); 60 if (!want) { 61 Logger.e(TAG, 'getDocumentSelectOptions want is undefined') 62 return options; 63 } 64 options.action = want.action as string || ''; 65 options.callerAbilityName = want.parameters?.['ohos.aafwk.param.callerAbilityName'] as string || ''; 66 options.callerBundleName = want.parameters?.['ohos.aafwk.param.callerBundleName'] as string || ''; 67 options.callerUid = want?.parameters?.[AbilityCommonUtil.CALLER_UID] as number || 0; 68 options.defaultFilePathUri = want.parameters?.key_pick_dir_path as string || ''; 69 options.extType = want.parameters?.extType as string || ''; 70 options.pickerType = want.parameters?.pickerType as string || ''; 71 if (options.isOpenFileMode()) { 72 options.fileSuffixFilters = want.parameters?.key_file_suffix_filter as string[] || []; 73 options.maxSelectNumber = want.parameters?.key_pick_num as number || 1; 74 options.setSelectMode(want.parameters?.key_select_mode as number); 75 options.isAuthMode = want.parameters?.key_auth_mode as boolean || false; 76 } else if (options.isCreateFileMode()) { 77 options.setNewFileNames(want.parameters?.key_pick_file_name as string[]); 78 options.fileSuffixChoices = want.parameters?.key_file_suffix_choices as string[] || []; 79 } else { 80 Logger.e(TAG, 'getDocumentSelectOptions mode is error') 81 } 82 Logger.i(TAG, 'getDocumentOptions : ' + JSON.stringify(options)); 83 return options; 84 } 85 86 export function getStartOptionsFromStorage(): StartModeOptions { 87 let storage: LocalStorage = LocalStorage.getShared(); 88 if (!storage) { 89 Logger.i(TAG, `Storage is null`) 90 return new StartModeOptions(); 91 } 92 let options: StartModeOptions | undefined = storage.get<StartModeOptions>('startModeOptions'); 93 if (options === undefined) { 94 options = new StartModeOptions(); 95 storage.setOrCreate('startModeOptions', options); 96 } 97 return options; 98 } 99} 100 101/** 102 * 文件选择器文件状态 103 * 104 * @param item 文件对象 105 * @param checkedNum 选中数量 106 * @return 是否超限 选择类型是否不匹配 107 */ 108export function pickerStatus(item, checkedNum, startModeOptions:StartModeOptions) { 109 return { 110 // 选择是否超限 111 exceedLimit: checkedNum >= globalThis.filePickNum && !item.isChecked, 112 // 选择类型是否不匹配 113 differentTypes: !checkFileSelectable(item, startModeOptions) 114 }; 115} 116 117/** 118 * 根据文件后缀判断文件是否可选 119 * @param item 120 */ 121function checkFileSelectable(item, startModeOptions: StartModeOptions): boolean { 122 // selectMode检查 123 let selectMode: number = startModeOptions.selectMode; 124 let isFolder = false; 125 if (ObjectUtil.hasKey(item, 'isFolder')) { 126 isFolder = item.isFolder; 127 } 128 // 文件夹模式,直接返回 129 if (selectMode === SELECT_MODE.FOLDER) { 130 return isFolder; 131 } 132 133 if (isFolder) { 134 // 混选模式下,文件夹直接返回 135 if (selectMode === SELECT_MODE.MIX) { 136 return true; 137 } 138 // 文件模式下,文件夹直接返回false 139 return false; 140 } 141 // 后缀检查 142 let keyFileSuffixFilter: string[] = startModeOptions.fileSuffixFilters; 143 if (Array.isArray(keyFileSuffixFilter) && keyFileSuffixFilter.length > 0) { 144 return checkFileSuffix(item.fileName, keyFileSuffixFilter); 145 } 146 147 // mimeType检查 148 return checkFileMimetype(item.fileName, startModeOptions); 149} 150 151/** 152 * 校验选中的文件后缀 153 * 154 * @param fileName 文件名称 155 * @param keyFileSuffixFilter 指定后缀 156 * @return 如果文件后缀满足三方指定,则返回true 157 */ 158function checkFileSuffix(fileName: string, keyFileSuffixFilter: Array<string>): boolean { 159 if (keyFileSuffixFilter) { 160 if (fileName) { 161 const suffix = FILE_SUFFIX.SUFFIX_START + FileMimeTypeUtil.getFileSuffix(fileName); 162 if (keyFileSuffixFilter.includes(suffix)) { 163 return true; 164 } 165 } 166 return false; 167 } 168 return true; 169} 170 171/** 172 * 校验选中的文件mimetype 173 * 174 * @param fileName 文件名称 175 * @return 条件满足返回true 176 */ 177function checkFileMimetype(fileName: string, startModeOptions: StartModeOptions): boolean { 178 if (!fileName) { 179 return false; 180 } 181 let keyPickTypeList: string[] = startModeOptions.phonePickerTypeList; 182 // 输入的类型全转换成小写,避免大小敏感问题 183 keyPickTypeList.forEach(item => item.toLowerCase()); 184 // 类型列表为空或包含*或*/*时,可选择所有文件 185 if (!keyPickTypeList || keyPickTypeList.length === 0 || 186 keyPickTypeList.includes('*') || keyPickTypeList.includes('*/*')) { 187 return true; 188 } 189 190 const mimeTypeObj = FileMimeTypeUtil.getFileMimeType(fileName); 191 const mimeType = mimeTypeObj.getMimeType(); 192 193 // mimeType未知不可选 194 if (!mimeType) { 195 return false; 196 } 197 198 // mimeType完全匹配 199 if (keyPickTypeList.includes(mimeType)) { 200 return true; 201 } 202 203 let fileCategory = mimeType; 204 const index = mimeType.indexOf('/'); 205 if (index > 0) { 206 fileCategory = mimeType.substring(0, index); 207 } 208 // 某一类文件 209 if (keyPickTypeList.includes(fileCategory) || keyPickTypeList.includes(`${fileCategory}/*`)) { 210 return true; 211 } 212 213 return false; 214} 215 216/** 217 * 文件选择器 选择超限提示 218 * 219 * @param isImmersion 是否沉浸式 220 */ 221export const filePickerTip = (startModeOptions: StartModeOptions) => { 222 globalThis.abilityContext.resourceManager.getPluralString($r('app.plural.filePickerTip').id, 223 startModeOptions.maxSelectNumber) 224 .then((value) => { 225 toast(value) 226 }) 227} 228 229 230