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 fileAccess from '@ohos.file.fileAccess' 17import Logger from '../log/Logger' 18import abilityAccessCtrl from '@ohos.abilityAccessCtrl' 19import { Permissions } from '@ohos.abilityAccessCtrl' 20import FileShare from '@ohos.fileshare' 21import wantConstant from '@ohos.app.ability.wantConstant' 22import ErrorCodeConst from '../constants/ErrorCodeConst' 23import MediaLibrary from '@ohos.multimedia.mediaLibrary' 24import { FILE_MANAGER_PREFERENCES, FILE_SUFFIX, SELECT_MODE } from '../constants/Constant' 25import StringUtil from './StringUtil' 26import { ArrayUtil } from './ArrayUtil' 27import { getPreferences } from './PreferencesUtil' 28 29const TAG = 'AbilityCommonUtil' 30 31let mediaLibrary: MediaLibrary.MediaLibrary = null 32 33/** 34 * Ability公共工具类 35 */ 36namespace AbilityCommonUtil { 37 38/** 39 * 需要用户授权的权限列表 40 */ 41 export const PERMISSION_LIST: Array<Permissions> = [ 42 "ohos.permission.MEDIA_LOCATION", 43 "ohos.permission.READ_MEDIA", 44 "ohos.permission.WRITE_MEDIA" 45 ] 46 47 /** 48 * 用来获取startAbility调用方应用uid的key 49 */ 50 export const CALLER_UID = 'ohos.aafwk.param.callerUid' 51 52 export const CALLER_BUNDLE_NAME = 'ohos.aafwk.param.callerBundleName' 53 54 /** 55 * 最大选择文件的个数 56 */ 57 export const MAX_FILE_PICK_NUM = 500 58 59 /** 60 * 后缀最大长度,包括'.' 61 */ 62 export const SUFFIX_MAX_LENGTH: number = 255; 63 64 /** 65 * 三方传入的后缀数组长度最大100 66 */ 67 export const SUFFIX_LIST_MAX_LENGTH: number = 100; 68 69 /** 70 * picker对外返回的响应码 71 */ 72 export const RESULT_CODE = { 73 SUCCESS: 0, 74 CANCEL: -1 75 } 76 77 export const ABILITY_LIST = { 78 FILE_MANAGER: 'FileManagerAbility', 79 FILE_PICKER: 'FilePickerAbility', 80 PATH_PICKER: 'PathPickerAbility' 81 } 82 /** 83 * 拉起Ability时必要的初始化操作 84 */ 85 export function init(): Promise<void[]> { 86 const fileAccessHelperPromise = createFileAccessHelper(); 87 getMediaLibrary(); 88 const getRequestPermission = requestPermission(); 89 const initData = initLastSelectPath(); 90 return Promise.all([fileAccessHelperPromise, getRequestPermission, initData]); 91 } 92 93 /** 94 * 获取FileAccessHelper,用于获取文件和操作文件 95 */ 96 export function createFileAccessHelper(): Promise<void> { 97 if (globalThis.fileAccessHelper) { 98 return Promise.resolve() 99 } 100 return new Promise(async (resolve, reject) => { 101 try { 102 let wants = await fileAccess.getFileAccessAbilityInfo() 103 globalThis.fileAcsHelper = fileAccess.createFileAccessHelper(globalThis.abilityContext, wants) 104 // 获取设备根节点信息 105 const rootIterator: fileAccess.RootIterator = await globalThis.fileAcsHelper.getRoots() 106 let rootInfoArr = [] 107 let result = rootIterator.next() 108 let isDone = result.done 109 while (!isDone) { 110 const rootInfo: fileAccess.RootInfo = result.value 111 Logger.i(TAG, 'RootInfo: ' + rootInfo.uri + ', ' + rootInfo.deviceType + ', ' + rootInfo.deviceFlags + ', ' + rootInfo.displayName+','+rootInfo.relativePath) 112 rootInfoArr.push(rootInfo) 113 result = rootIterator.next() 114 isDone = result.done 115 } 116 globalThis.rootInfoArr = rootInfoArr 117 } catch (err) { 118 Logger.e(TAG, 'createFileAccessHelper fail, error:' + JSON.stringify(err)) 119 } finally { 120 resolve() 121 } 122 }) 123 } 124 125 /** 126 * Ability初始化时,加载最新保存的路径Uri 127 */ 128 export function initLastSelectPath(): Promise<void> { 129 return new Promise((resolve, reject) => { 130 const defaultValue = FILE_MANAGER_PREFERENCES.lastSelectPath.defaultValue; 131 const lastSelectPathKey = FILE_MANAGER_PREFERENCES.lastSelectPath.key; 132 getPreferences(FILE_MANAGER_PREFERENCES.name).then(preferences => { 133 preferences.get(lastSelectPathKey, defaultValue).then((result: string) => { 134 AppStorage.SetOrCreate<string>(lastSelectPathKey, result); 135 resolve(); 136 Logger.i(TAG, 'initLastSelectPath result: ' + result); 137 }).catch((error) => { 138 AppStorage.SetOrCreate<string>(lastSelectPathKey, defaultValue); 139 Logger.e(TAG, 'initLastSelectPath preferences.get fail, error:' + JSON.stringify(error)); 140 resolve(); 141 }) 142 }).catch(err => { 143 AppStorage.SetOrCreate<string>(lastSelectPathKey, defaultValue); 144 Logger.e(TAG, 'initLastSelectPath getPreferences fail, error: ' + JSON.stringify(err)); 145 resolve(); 146 }) 147 }) 148 } 149 150 /** 151 * 申请文件管理器需用户授权的权限 152 */ 153 export function requestPermission(): Promise<void> { 154 let atManager = abilityAccessCtrl.createAtManager() 155 try { 156 return atManager.requestPermissionsFromUser(globalThis.abilityContext, PERMISSION_LIST).then((data) => { 157 if (data.authResults.some(item => item !== 0)) { 158 Logger.e(TAG, 'requestPermissionsFromUser some permission request fail, result:' + JSON.stringify(data)) 159 } else { 160 Logger.i(TAG, 'requestPermissionsFromUser success, result:' + JSON.stringify(data)) 161 } 162 }).catch((error) => { 163 Logger.e(TAG, 'requestPermissionsFromUser fail, error:' + JSON.stringify(error)) 164 }) 165 } catch (error) { 166 Logger.e(TAG, 'requestPermissionsFromUser error occurred, error:' + JSON.stringify(error)) 167 } 168 } 169 170 /** 171 * uri授权 172 * @param uriList 待授权的uri列表 173 * @param bundleName 授权应用的包名 174 */ 175 export function grantUriPermission(uriList: Array<string>, bundleName: string): Promise<boolean> { 176 return new Promise(async (resolve, reject) => { 177 Logger.i(TAG, "grantUriPermission start,grantSize = " + uriList?.length); 178 let grantSuccessCount: number = 0; 179 for (let uri of uriList) { 180 try { 181 await FileShare.grantUriPermission( 182 uri, 183 bundleName, 184 wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION | wantConstant.Flags.FLAG_AUTH_WRITE_URI_PERMISSION); 185 grantSuccessCount++; 186 } catch (error) { 187 resolve(false); 188 Logger.e(TAG, `grantUriPermission fail,grantSuccessCount:${grantSuccessCount}}, uri: ${uri}, error: ${JSON.stringify(error)}`); 189 return; 190 } 191 } 192 Logger.i(TAG, "grantUriPermission end,grantSuccessCount = " + grantSuccessCount); 193 resolve(true) 194 }) 195 196 } 197 198 /** 199 * 文件选择完成,返回uri列表 200 * @param resultCode 201 * @param result 202 * @param message 203 */ 204 export async function terminateFilePicker(result: Array<string> = [], displayNames: Array<string> = [], resultCode: number = RESULT_CODE.SUCCESS, message: string = ''): Promise<void> { 205 const bundleName = globalThis.pickerCallerBundleName 206 if (result.length && bundleName) { 207 // uri授权 208 const isSuccess = await grantUriPermission(result, bundleName); 209 if (!isSuccess) { 210 resultCode = ErrorCodeConst.PICKER.GRANT_URI_PERMISSION_FAIL, 211 result = [] 212 message = 'uri grant permission fail' 213 displayNames = [] 214 } 215 } 216 217 let abilityResult = { 218 resultCode: resultCode, 219 want: { 220 bundleName: globalThis.abilityContext.abilityInfo.bundleName, 221 abilityName: ABILITY_LIST.FILE_PICKER, 222 parameters: { 223 'select_item_list': result, 224 'file_name_list': displayNames, 225 message: message, 226 'result': result[0], 227 } 228 } 229 } 230 globalThis.abilityContext.terminateSelfWithResult(abilityResult, (error) => { 231 if (error.code) { 232 Logger.e(TAG, 'terminateFilePicker failed. Cause: ' + JSON.stringify(error)) 233 return 234 } 235 Logger.d(TAG, 'terminateFilePicker success. result: ' + JSON.stringify(abilityResult)) 236 }) 237 } 238 /** 239 * 文件创建完成,返回uri列表 240 * @param result 241 * @param resultCode 242 * @param message 243 */ 244 export async function terminatePathPicker(result: Array<string>, resultCode: number = RESULT_CODE.SUCCESS, message: string = ''): Promise<void> { 245 const bundleName = globalThis.pathCallerBundleName 246 if (result.length && bundleName) { 247 // uri授权 248 const isSuccess = await grantUriPermission(result, bundleName); 249 if (!isSuccess) { 250 resultCode = ErrorCodeConst.PICKER.GRANT_URI_PERMISSION_FAIL, 251 result = [] 252 message = 'uri grant permission fail' 253 } 254 } 255 let abilityResult = { 256 resultCode: resultCode, 257 want: { 258 bundleName: globalThis.pathAbilityContext.abilityInfo.bundleName, 259 abilityName: ABILITY_LIST.PATH_PICKER, 260 parameters: { 261 'pick_path_return': result, 262 'key_pick_select_clouddisk': false, 263 'message': message, 264 // 兼容老版本picker 265 'result': result[0] 266 } 267 } 268 } 269 globalThis.pathAbilityContext.terminateSelfWithResult(abilityResult, (error) => { 270 if (error.code) { 271 Logger.e(TAG, 'terminatePathPicker failed. Cause: ' + JSON.stringify(error)) 272 return 273 } 274 Logger.d(TAG, 'terminatePathPicker success. result: ' + JSON.stringify(abilityResult)) 275 }) 276 } 277 278 /** 279 * 获取选择文件的最大个数 280 * @param num 调用方传入的个数 281 */ 282 export function getPickFileNum(num: any): number { 283 if (typeof num === 'number') { 284 if (num > 0 && num <= MAX_FILE_PICK_NUM) { 285 return num; 286 } 287 } 288 return MAX_FILE_PICK_NUM; 289 } 290 291 /** 292 * 获取选择文件的类型列表 293 * @param keyPickType 调用方传入文件类型(兼容双框架action) 294 * @param keyPickTypeList 调用方传入文件类型列表 295 */ 296 export function getKeyPickTypeList(keyPickType, keyPickTypeList): Array<string>{ 297 let typeList =[] 298 if (keyPickType) { 299 typeList.push(keyPickType) 300 } 301 if (keyPickTypeList && keyPickTypeList.length !== 0) { 302 typeList = typeList.concat(keyPickTypeList) 303 } 304 return typeList.filter(item => item) 305 } 306 307 /** 308 * 获取选择文件Mode,默认选择文件 309 * @param keySelectMode 调用方传入文件mode 310 */ 311 export function getKeySelectMode(keySelectMode: any): number { 312 if (typeof keySelectMode === 'number') { 313 if (keySelectMode === SELECT_MODE.FILE 314 || keySelectMode === SELECT_MODE.FOLDER 315 || keySelectMode === SELECT_MODE.MIX) { 316 return keySelectMode; 317 } 318 } 319 return SELECT_MODE.FILE; 320 } 321 322 /** 323 * 获取支持的文件后缀列表 324 * @param keyFileSuffixFilter 调用方传入文件后缀列表 325 */ 326 export function getKeyFileSuffixFilter(keyFileSuffixFilter: string[]): Array<string> { 327 let suffixList = []; 328 if (!ArrayUtil.isEmpty(keyFileSuffixFilter)) { 329 let len = keyFileSuffixFilter.length; 330 let size = len > SUFFIX_LIST_MAX_LENGTH ? SUFFIX_LIST_MAX_LENGTH : len; 331 for (let index = 0; index < size; index++) { 332 const suffixStr = keyFileSuffixFilter[index]; 333 if (typeof suffixStr === 'string') { 334 const suffixArray = suffixStr.split(FILE_SUFFIX.SUFFIX_SPLIT); 335 for (let index = 0; index < suffixArray.length; index++) { 336 const suffix = suffixArray[index]; 337 if (checkFileSuffix(suffix)) { 338 suffixList.push(suffix.toUpperCase()) 339 } 340 } 341 } 342 } 343 } 344 return suffixList.filter((item, index, array) => { 345 return array.indexOf(item) === index; 346 }); 347 } 348 349 export function checkFileSuffix(fileSuffix: String): boolean { 350 return fileSuffix && fileSuffix.length <= SUFFIX_MAX_LENGTH && fileSuffix.startsWith(FILE_SUFFIX.SUFFIX_START); 351 } 352 353 /** 354 * 路径选择器获取支持的文件后缀,只支持获取第一个文件后缀 355 * @param keyFileSuffixChoices 调用方传入文件后缀列表 356 */ 357 export function getKeyFileSuffixChoices(keyFileSuffixChoices: string[]): string { 358 if (!ArrayUtil.isEmpty(keyFileSuffixChoices)) { 359 let len = keyFileSuffixChoices.length; 360 let size = len > SUFFIX_LIST_MAX_LENGTH ? SUFFIX_LIST_MAX_LENGTH : len; 361 for (let index = 0; index < size; index++) { 362 const suffixStr = keyFileSuffixChoices[index]; 363 if (typeof suffixStr === 'string') { 364 const suffixArray = suffixStr.split(FILE_SUFFIX.SUFFIX_SPLIT); 365 for (let index = 0; index < suffixArray.length; index++) { 366 const suffix = suffixArray[index]; 367 if (checkFileSuffix(suffix)) { 368 return suffix; 369 } 370 } 371 } 372 } 373 } 374 return ''; 375 } 376 377 /** 378 * 获取媒体库对象实例的统一接口 379 */ 380 export function getMediaLibrary(): MediaLibrary.MediaLibrary { 381 if (!mediaLibrary) { 382 try { 383 mediaLibrary = MediaLibrary.getMediaLibrary(globalThis.abilityContext) 384 } catch (error) { 385 Logger.e(TAG, 'getMediaLibrary fail, error:' + JSON.stringify(error)) 386 } 387 } 388 return mediaLibrary 389 } 390 391 export function releaseMediaLibrary(): void { 392 if (!mediaLibrary) { 393 try { 394 mediaLibrary.release() 395 } catch (error) { 396 Logger.e(TAG, 'releaseMediaLibrary fail, error: ' + JSON.stringify(error)) 397 } 398 } 399 } 400} 401 402export default AbilityCommonUtil 403