• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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