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 { FilesData } from '../../databases/model/FileData'; 26 27import ctx from '@ohos.app.ability.common'; 28 29export interface abilityResultInterface { 30 want: Want, 31 resultCode: number 32}; 33 34export interface PickerStatus { 35 exceedLimit: boolean, 36 differentTypes: boolean 37} 38 39const TAG = 'FilePickerUtil'; 40 41export namespace FilePickerUtil { 42 export function returnAbilityResult(want: Want, resultCode: number, options: StartModeOptions) { 43 Logger.i(TAG, 'returnPicker start'); 44 let context = getContext() as ctx.UIAbilityContext; 45 if (options.windowType === PickerWindowType.ABILITY) { 46 let abilityResult: abilityResultInterface = { 47 want: want, 48 resultCode: resultCode 49 }; 50 Logger.i(TAG, 'terminateSelfWithResult start'); 51 context.terminateSelfWithResult(abilityResult, (error) => { 52 Logger.e(TAG, 'terminateSelfWithResult is called = ' + error.code); 53 }); 54 } else { 55 let abilityResult: ability.AbilityResult = { 56 resultCode: resultCode, 57 want: want 58 }; 59 options.session?.terminateSelfWithResult(abilityResult, (error) => { 60 Logger.e(TAG, 'closeUIExtFilePicker terminateSelfWithResult is called = ' + error?.code); 61 }); 62 } 63 } 64 65 export function getStartModeOptions(want: Want): StartModeOptions { 66 let options = new StartModeOptions(); 67 if (!want) { 68 Logger.e(TAG, 'getDocumentSelectOptions want is undefined') 69 return options; 70 } 71 options.action = want.action as string || ''; 72 options.callerAbilityName = want.parameters?.['ohos.aafwk.param.callerAbilityName'] as string || ''; 73 options.callerBundleName = want.parameters?.['ohos.aafwk.param.callerBundleName'] as string || ''; 74 options.callerUid = want?.parameters?.[AbilityCommonUtil.CALLER_UID] as number || 0; 75 options.defaultFilePathUri = want.parameters?.key_pick_dir_path as string || ''; 76 options.extType = want.parameters?.extType as string || ''; 77 options.pickerType = want.parameters?.pickerType as string || ''; 78 if (options.isOpenFileMode()) { 79 options.fileSuffixFilters = want.parameters?.key_file_suffix_filter as string[] || []; 80 options.maxSelectNumber = want.parameters?.key_pick_num as number || 1; 81 options.setSelectMode(want.parameters?.key_select_mode as number); 82 options.isAuthMode = want.parameters?.key_auth_mode as boolean || false; 83 } else if (options.isCreateFileMode()) { 84 options.setNewFileNames(want.parameters?.key_pick_file_name as string[]); 85 options.fileSuffixChoices = want.parameters?.key_file_suffix_choices as string[] || []; 86 } else { 87 Logger.e(TAG, 'getDocumentSelectOptions mode is error') 88 } 89 Logger.i(TAG, 'getDocumentOptions : ' + JSON.stringify(options)); 90 return options; 91 } 92 93 export function getStartOptionsFromStorage(): StartModeOptions { 94 let storage: LocalStorage = LocalStorage.getShared(); 95 if (!storage) { 96 Logger.i(TAG, `Storage is null`) 97 return new StartModeOptions(); 98 } 99 let options: StartModeOptions | undefined = storage.get<StartModeOptions>('startModeOptions'); 100 if (options === undefined) { 101 options = new StartModeOptions(); 102 storage.setOrCreate('startModeOptions', options); 103 } 104 return options; 105 } 106} 107 108/** 109 * 文件选择器文件状态 110 * 111 * @param item 文件对象 112 * @param checkedNum 选中数量 113 * @return 是否超限 选择类型是否不匹配 114 */ 115export function pickerStatus(item: FilesData, checkedNum: number, startModeOptions:StartModeOptions): PickerStatus { 116 let status: PickerStatus = { 117 // 选择是否超限 118 exceedLimit: checkedNum >= globalThis.filePickNum && !item.isChecked, 119 // 选择类型是否不匹配 120 differentTypes: !checkFileSelectable(item, startModeOptions) 121 }; 122 return status; 123} 124 125/** 126 * 根据文件后缀判断文件是否可选 127 * @param item 128 */ 129function checkFileSelectable(item: FilesData, startModeOptions: StartModeOptions): boolean { 130 // selectMode检查 131 let selectMode: number = startModeOptions.selectMode; 132 let isFolder = false; 133 if (ObjectUtil.hasKey(item, 'isFolder')) { 134 isFolder = item.isFolder; 135 } 136 // 文件夹模式,直接返回 137 if (selectMode === SELECT_MODE.FOLDER) { 138 return isFolder; 139 } 140 141 if (isFolder) { 142 // 混选模式下,文件夹直接返回 143 if (selectMode === SELECT_MODE.MIX) { 144 return true; 145 } 146 // 文件模式下,文件夹直接返回false 147 return false; 148 } 149 // 后缀检查 150 let keyFileSuffixFilter: string[] = startModeOptions.fileSuffixFilters; 151 if (Array.isArray(keyFileSuffixFilter) && keyFileSuffixFilter.length > 0) { 152 return checkFileSuffix(item.fileName, keyFileSuffixFilter); 153 } 154 155 // mimeType检查 156 return checkFileMimetype(item.fileName, startModeOptions); 157} 158 159/** 160 * 校验选中的文件后缀 161 * 162 * @param fileName 文件名称 163 * @param keyFileSuffixFilter 指定后缀 164 * @return 如果文件后缀满足三方指定,则返回true 165 */ 166function checkFileSuffix(fileName: string, keyFileSuffixFilter: Array<string>): boolean { 167 if (keyFileSuffixFilter) { 168 if (fileName) { 169 const suffix = FILE_SUFFIX.SUFFIX_START + FileMimeTypeUtil.getFileSuffix(fileName); 170 if (keyFileSuffixFilter.includes(suffix)) { 171 return true; 172 } 173 } 174 return false; 175 } 176 return true; 177} 178 179/** 180 * 校验选中的文件mimetype 181 * 182 * @param fileName 文件名称 183 * @return 条件满足返回true 184 */ 185function checkFileMimetype(fileName: string, startModeOptions: StartModeOptions): boolean { 186 if (!fileName) { 187 return false; 188 } 189 let keyPickTypeList: string[] = startModeOptions.phonePickerTypeList; 190 // 输入的类型全转换成小写,避免大小敏感问题 191 keyPickTypeList.forEach(item => item.toLowerCase()); 192 // 类型列表为空或包含*或*/*时,可选择所有文件 193 if (!keyPickTypeList || keyPickTypeList.length === 0 || 194 keyPickTypeList.includes('*') || keyPickTypeList.includes('*/*')) { 195 return true; 196 } 197 198 const mimeTypeObj = FileMimeTypeUtil.getFileMimeType(fileName); 199 const mimeType = mimeTypeObj.getMimeType(); 200 201 // mimeType未知不可选 202 if (!mimeType) { 203 return false; 204 } 205 206 // mimeType完全匹配 207 if (keyPickTypeList.includes(mimeType)) { 208 return true; 209 } 210 211 let fileCategory = mimeType; 212 const index = mimeType.indexOf('/'); 213 if (index > 0) { 214 fileCategory = mimeType.substring(0, index); 215 } 216 // 某一类文件 217 if (keyPickTypeList.includes(fileCategory) || keyPickTypeList.includes(`${fileCategory}/*`)) { 218 return true; 219 } 220 221 return false; 222} 223 224/** 225 * 文件选择器 选择超限提示 226 * 227 * @param isImmersion 是否沉浸式 228 */ 229export const filePickerTip = (startModeOptions: StartModeOptions) => { 230 globalThis.abilityContext.resourceManager.getPluralString($r('app.plural.filePickerTip').id, 231 startModeOptions.maxSelectNumber) 232 .then((value: string) => { 233 toast(value) 234 }) 235} 236 237 238