• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023-2024 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
16const photoAccessHelper = requireNapi('file.photoAccessHelperNative');
17const bundleManager = requireNapi('bundle.bundleManager');
18const deviceinfo = requireInternal('deviceInfo');
19
20const ARGS_ZERO = 0;
21const ARGS_ONE = 1;
22const ARGS_TWO = 2;
23const ARGS_THREE = 3;
24
25const WRITE_PERMISSION = 'ohos.permission.WRITE_IMAGEVIDEO';
26const ACROSS_ACCOUNTS_PERMISSION = 'ohos.permission.INTERACT_ACROSS_LOCAL_ACCOUNTS';
27
28const PERMISSION_DENIED = 13900012;
29const ERR_CODE_PARAMERTER_INVALID = 13900020;
30const ERR_CODE_OHOS_PERMISSION_DENIED = 201;
31const ERR_CODE_OHOS_PARAMERTER_INVALID = 401;
32const REQUEST_CODE_SUCCESS = 0;
33const PERMISSION_STATE_ERROR = -1;
34const ERROR_MSG_WRITE_PERMISSION = 'not have ohos.permission.WRITE_IMAGEVIDEO';
35const ERROR_MSG_ACROSS_ACCOUNTS_PERMISSION = 'not have ohos.permission.INTERACT_ACROSS_LOCAL_ACCOUNTS';
36const ERROR_MSG_USER_DENY = 'user deny';
37const ERROR_MSG_PARAMERTER_INVALID = 'input parmaeter invalid';
38const ERROR_MSG_INNER_FAIL = 'System inner fail';
39const ERROR_MSG_OHOS_INNER_FAIL = 'Internal system error';
40
41const MAX_DELETE_NUMBER = 300;
42const MIN_DELETE_NUMBER = 1;
43const MAX_CONFIRM_NUMBER = 100;
44const MIN_CONFIRM_NUMBER = 1;
45
46let gContext = undefined;
47
48class BusinessError extends Error {
49  constructor(msg, code) {
50    super(msg);
51    this.code = code || PERMISSION_DENIED;
52  }
53}
54
55function checkArrayAndSize(array, minSize, maxSize) {
56  // check whether input is array
57  if (!Array.isArray(array)) {
58    console.error('photoAccessHelper invalid, array is null.');
59    return false;
60  }
61
62  // check whether array length is valid
63  let len = array.length;
64  if ((len < minSize) || (len > maxSize)) {
65    console.error('photoAccessHelper invalid, array size invalid.');
66    return false;
67  }
68
69  return true;
70}
71
72function checkIsUriValid(uri, isAppUri) {
73  if (!uri) {
74    console.error('photoAccessHelper invalid, uri is null.');
75    return false;
76  }
77
78  if (typeof uri !== 'string') {
79    console.error('photoAccessHelper invalid, uri type is not string.');
80    return false;
81  }
82
83  // media library uri starts with 'file://media/Photo/', createDeleteReques delete media library resource should check
84  if (!isAppUri) {
85    return uri.includes('file://media/Photo/');
86  }
87
88  // showAssetsCreationDialog store third part application resource to media library, no need to check it
89  return true;
90}
91
92function checkParams(uriList, asyncCallback) {
93  if (arguments.length > ARGS_TWO) {
94    return false;
95  }
96  if (!checkArrayAndSize(uriList, MIN_DELETE_NUMBER, MAX_DELETE_NUMBER)) {
97    return false;
98  }
99  if (asyncCallback && typeof asyncCallback !== 'function') {
100    return false;
101  }
102  for (let uri of uriList) {
103    if (!checkIsUriValid(uri, false)) {
104      console.info(`photoAccessHelper invalid uri: ${uri}`);
105      return false;
106    }
107  }
108  return true;
109}
110function errorResult(rej, asyncCallback) {
111  if (asyncCallback) {
112    return asyncCallback(rej);
113  }
114  return new Promise((resolve, reject) => {
115    reject(rej);
116  });
117}
118
119function getAbilityResource(bundleInfo) {
120  console.info('getAbilityResource enter.');
121  let labelId = 0;
122  for (let hapInfo of bundleInfo.hapModulesInfo) {
123    if (hapInfo.type === bundleManager.ModuleType.ENTRY) {
124      labelId = getLabelId(hapInfo);
125    }
126  }
127  return labelId;
128}
129
130function getLabelId(hapInfo) {
131  let labelId = 0;
132  for (let abilityInfo of hapInfo.abilitiesInfo) {
133    let abilitiesInfoName = '';
134    if (abilityInfo.name.includes('.')) {
135      let abilitiesInfoLength = abilityInfo.name.split('.').length;
136      abilitiesInfoName = abilityInfo.name.split('.')[abilitiesInfoLength - 1];
137    } else {
138      abilitiesInfoName = abilityInfo.name;
139    }
140    if (abilitiesInfoName === hapInfo.mainElementName) {
141      labelId = abilityInfo.labelId;
142    }
143  }
144  return labelId;
145}
146
147async function getAppName() {
148  let appName = '';
149  try {
150    const flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_ABILITY | bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_HAP_MODULE;
151    const bundleInfo = await bundleManager.getBundleInfoForSelf(flags);
152    console.info(`photoAccessHelper bundleInfo: ${JSON.stringify(bundleInfo)}`);
153    if (bundleInfo === undefined || bundleInfo.hapModulesInfo === undefined || bundleInfo.hapModulesInfo.length === 0) {
154      return appName;
155    }
156    const labelId = getAbilityResource(bundleInfo);
157    const resourceMgr = gContext.resourceManager;
158    appName = await resourceMgr.getStringValue(labelId);
159    console.info(`photoAccessHelper appName: ${appName}`);
160  } catch (error) {
161    console.info(`photoAccessHelper error: ${JSON.stringify(error)}`);
162  }
163
164  return appName;
165}
166
167async function createPhotoDeleteRequestParamsOk(uriList, asyncCallback) {
168  let flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION;
169  let { reqPermissionDetails, permissionGrantStates } = await bundleManager.getBundleInfoForSelf(flags);
170  let permissionIndex = -1;
171  for (let i = 0; i < reqPermissionDetails.length; i++) {
172    if (reqPermissionDetails[i].name === WRITE_PERMISSION) {
173      permissionIndex = i;
174    }
175  }
176  if (permissionIndex < 0 || permissionGrantStates[permissionIndex] === PERMISSION_STATE_ERROR) {
177    console.info('photoAccessHelper permission error');
178    return errorResult(new BusinessError(ERROR_MSG_WRITE_PERMISSION), asyncCallback);
179  }
180  const appName = await getAppName();
181  if (appName.length === 0) {
182    console.info(`photoAccessHelper appName not found`);
183    return errorResult(new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_PARAMERTER_INVALID), asyncCallback);
184  }
185  try {
186    if (asyncCallback) {
187      return photoAccessHelper.createDeleteRequest(getContext(this), appName, uriList, result => {
188        if (result.result === REQUEST_CODE_SUCCESS) {
189          asyncCallback();
190        } else if (result.result == PERMISSION_DENIED) {
191          asyncCallback(new BusinessError(ERROR_MSG_USER_DENY));
192        } else {
193          asyncCallback(new BusinessError(ERROR_MSG_INNER_FAIL, result.result));
194        }
195      });
196    } else {
197      return new Promise((resolve, reject) => {
198        photoAccessHelper.createDeleteRequest(getContext(this), appName, uriList, result => {
199          if (result.result === REQUEST_CODE_SUCCESS) {
200            resolve();
201          } else if (result.result == PERMISSION_DENIED) {
202            reject(new BusinessError(ERROR_MSG_USER_DENY));
203          } else {
204            reject(new BusinessError(ERROR_MSG_INNER_FAIL, result.result));
205          }
206        });
207      });
208    }
209  } catch (error) {
210    return errorResult(new BusinessError(error.message, error.code), asyncCallback);
211  }
212}
213
214function createDeleteRequest(...params) {
215  if (!checkParams(...params)) {
216    throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_PARAMERTER_INVALID);
217  }
218  return createPhotoDeleteRequestParamsOk(...params);
219}
220
221function checkIsPhotoCreationConfigValid(config) {
222  if (!config) {
223    console.error('photoAccessHelper invalid, config is null.');
224    return false;
225  }
226
227  // check whether input is a object
228  if (typeof config !== 'object') {
229    console.error('photoAccessHelper invalid, config type is not object.');
230    return false;
231  }
232
233  // check whether title is string if exsit
234  if ((config.title) && (typeof config.title !== 'string')) {
235    console.error('photoAccessHelper invalid, config.title type is not string.');
236    return false;
237  }
238
239  // check whether fileNameExtension is string
240  if (!config.fileNameExtension) {
241    console.error('photoAccessHelper invalid, config.fileNameExtension is null.');
242    return false;
243  }
244  if (typeof config.fileNameExtension !== 'string') {
245    console.error('photoAccessHelper invalid, config.fileNameExtension type is not string.');
246    return false;
247  }
248
249  // check whether photoType is number
250  if (!config.photoType) {
251    console.error('photoAccessHelper invalid, config.photoType is null.');
252    return false;
253  }
254  if (typeof config.photoType !== 'number') {
255    console.error('photoAccessHelper invalid, config.photoType type is not number.');
256    return false;
257  }
258
259  // check whether subtype is number if exsit
260  if ((config.subtype) && (typeof config.subtype !== 'number')) {
261    console.error('photoAccessHelper invalid, config.subtype type is not number.');
262    return false;
263  }
264
265  return true;
266}
267
268function checkConfirmBoxParams(srcFileUris, photoCreationConfigs) {
269  // check param number
270  if (arguments.length > ARGS_TWO) {
271    return false;
272  }
273
274  // check whether input array is valid
275  if (!checkArrayAndSize(srcFileUris, MIN_CONFIRM_NUMBER, MAX_CONFIRM_NUMBER)) {
276    return false;
277  }
278  if (!checkArrayAndSize(photoCreationConfigs, MIN_CONFIRM_NUMBER, MAX_CONFIRM_NUMBER)) {
279    return false;
280  }
281  if (srcFileUris.length !== photoCreationConfigs.length) {
282    return false;
283  }
284
285  // check whether srcFileUris element is valid
286  for (let srcFileUri of srcFileUris) {
287    if (!checkIsUriValid(srcFileUri, true)) {
288      console.error('photoAccessHelper invalid uri: ${srcFileUri}.');
289      return false;
290    }
291  }
292
293  // check whether photoCreationConfigs element is valid
294  for (let photoCreateConfig of photoCreationConfigs) {
295    if (!checkIsPhotoCreationConfigValid(photoCreateConfig)) {
296      return false;
297    }
298  }
299
300  return true;
301}
302
303function getBundleInfo() {
304  let flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_ABILITY | // for appName
305    bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_HAP_MODULE | // for appName
306    bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO | // for appId
307    bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION; // for appInfo
308  let bundleInfo = bundleManager.getBundleInfoForSelfSync(flags);
309  if (((bundleInfo === undefined) || (bundleInfo.name === undefined)) ||
310      ((bundleInfo.hapModulesInfo === undefined) || (bundleInfo.hapModulesInfo.length === 0)) ||
311      ((bundleInfo.signatureInfo === undefined) || (bundleInfo.signatureInfo.appId === undefined)) ||
312    ((bundleInfo.appInfo === undefined) || (bundleInfo.appInfo.labelId === 0))) {
313    console.error('photoAccessHelper failed to get bundle info.');
314    return undefined;
315  }
316
317  return bundleInfo;
318}
319
320function showAssetsCreationDialogResult(result, reject, resolve) {
321  if (result.result !== REQUEST_CODE_SUCCESS) {
322    reject(new BusinessError(ERROR_MSG_OHOS_INNER_FAIL, result.result));
323  }
324
325  if (result.data === undefined) {
326    result.data = [];
327  }
328
329  resolve(result.data);
330}
331
332async function showAssetsCreationDialogParamsOk(srcFileUris, photoCreationConfigs) {
333  let bundleInfo = getBundleInfo();
334  if (bundleInfo === undefined) {
335    return new Promise((resolve, reject) => {
336      reject(new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID));
337    });
338  }
339
340  // get bundleName and appId and appName
341  let bundleName = bundleInfo.name;
342  let appId = bundleInfo.signatureInfo.appId;
343  console.info('photoAccessHelper bundleName is ' + bundleName + '.');
344  console.info('photoAccessHelper appId is ' + appId + '.');
345
346  let labelId = bundleInfo.appInfo.labelId;
347  console.info('photoAccessHelper labelId is ' + appId + '.');
348  let appName = '';
349
350  try {
351    let modeleName = '';
352    for (let hapInfo of bundleInfo.hapModulesInfo) {
353      if (labelId === hapInfo.labelId) {
354        modeleName = hapInfo.name;
355      }
356    }
357    console.info('photoAccessHelper modeleName is ' + modeleName + '.');
358    appName = await gContext.createModuleContext(modeleName).resourceManager.getStringValue(labelId);
359    console.info('photoAccessHelper appName is ' + appName + '.');
360    // only promise type
361    return new Promise((resolve, reject) => {
362      photoAccessHelper.showAssetsCreationDialog(getContext(this), srcFileUris, photoCreationConfigs, bundleName,
363        appName, appId, result => {
364          showAssetsCreationDialogResult(result, reject, resolve);
365      });
366    });
367  } catch (error) {
368    return errorResult(new BusinessError(error.message, error.code), null);
369  }
370}
371
372function showAssetsCreationDialog(...params) {
373  if (!checkConfirmBoxParams(...params)) {
374    throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID);
375  }
376  return showAssetsCreationDialogParamsOk(...params);
377}
378
379async function requestPhotoUrisReadPermission(srcFileUris) {
380  console.info('requestPhotoUrisReadPermission enter');
381
382  //check whether srcFileUris is valid
383  if (srcFileUris === undefined || srcFileUris.length < MIN_CONFIRM_NUMBER) {
384    console.error('photoAccessHelper invalid, array size invalid.');
385    return false;
386  }
387  for (let srcFileUri of srcFileUris) {
388    if (!checkIsUriValid(srcFileUri, true)) {
389      console.error('photoAccesshelper invalid uri : ${srcFileUri}.');
390      return false;
391    }
392  }
393
394  let context = gContext;
395  if (context === undefined) {
396    console.info('photoAccessHelper gContet undefined');
397    context = getContext(this);
398  }
399
400  let bundleInfo = getBundleInfo();
401  if (bundleInfo === undefined) {
402    return new Promise((resolve, reject) => {
403      reject(new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID));
404    });
405  }
406  let labelId = bundleInfo.appInfo.labelId;
407  console.info('photoAccessHelper labelId is ' + labelId + '.');
408  let appName = '';
409
410  try {
411    let moduleName = '';
412    for (let hapInfo of bundleInfo.hapModulesInfo) {
413      if (labelId === hapInfo.labelId) {
414        moduleName = hapInfo.name;
415      }
416    }
417    console.info('photoAccessHelper moduleName is ' + moduleName + '.');
418    appName = await gContext.createModuleContext(moduleName).resourceManager.getStringValue(labelId);
419    console.info('photoAccessHelper appName is ' + appName + '.');
420    return new Promise((resolve, reject) => {
421      photoAccessHelper.requestPhotoUrisReadPermission(context, srcFileUris, appName, result => {
422        showAssetsCreationDialogResult(result, reject, resolve);
423      });
424    });
425  } catch (error) {
426    console.error('requestPhotoUrisReadPermission catch error.');
427    return errorResult(new BusinessError(ERROR_MSG_INNER_FAIL, error.code), null);
428  }
429}
430
431async function createAssetWithShortTermPermissionOk(photoCreationConfig) {
432  let bundleInfo = getBundleInfo();
433  if (bundleInfo === undefined) {
434    return new Promise((resolve, reject) => {
435      reject(new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID));
436    });
437  }
438
439  let bundleName = bundleInfo.name;
440  let appId = bundleInfo.signatureInfo.appId;
441  console.info('photoAccessHelper bundleName is ' + bundleName + '.');
442  console.info('photoAccessHelper appId is ' + appId + '.');
443
444  let labelId = bundleInfo.appInfo.labelId;
445  console.info('photoAccessHelper labelId is ' + appId + '.');
446  let appName = '';
447
448  try {
449    let modeleName = '';
450    for (let hapInfo of bundleInfo.hapModulesInfo) {
451      if (labelId === hapInfo.labelId) {
452        modeleName = hapInfo.name;
453      }
454    }
455    console.info('photoAccessHelper modeleName is ' + modeleName + '.');
456    appName = await gContext.createModuleContext(modeleName).resourceManager.getStringValue(labelId);
457    console.info('photoAccessHelper appName is ' + appName + '.');
458
459    if (photoAccessHelper.checkShortTermPermission()) {
460      let photoCreationConfigs = [photoCreationConfig];
461      let desFileUris = await getPhotoAccessHelper(getContext(this)).createAssetsHasPermission(bundleName, appName, appId,
462        photoCreationConfigs);
463      return new Promise((resolve, reject) => {
464        resolve(desFileUris[0]);
465      });
466    }
467    return new Promise((resolve, reject) => {
468      photoAccessHelper.createAssetWithShortTermPermission(getContext(this), photoCreationConfig, bundleName, appName,
469        appId, result => {
470          showAssetsCreationDialogResult(result, reject, resolve);
471        });
472    });
473  } catch (error) {
474    return errorResult(new BusinessError(ERROR_MSG_INNER_FAIL, error.code), null);
475  }
476}
477
478function createAssetWithShortTermPermission(photoCreationConfig) {
479  if (!checkIsPhotoCreationConfigValid(photoCreationConfig)) {
480    throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID);
481  }
482  return createAssetWithShortTermPermissionOk(photoCreationConfig);
483}
484
485function getPhotoAccessHelper(context, userId = -1) {
486  if (context === undefined) {
487    console.log('photoAccessHelper gContext undefined');
488    throw Error('photoAccessHelper gContext undefined');
489  }
490  gContext = context;
491  let helper = photoAccessHelper.getPhotoAccessHelper(gContext, userId);
492  if (helper !== undefined && helper.constructor.prototype.createDeleteRequest === undefined) {
493    console.log('photoAccessHelper getPhotoAccessHelper inner add createDeleteRequest and showAssetsCreationDialog');
494    helper.constructor.prototype.createDeleteRequest = createDeleteRequest;
495    helper.constructor.prototype.showAssetsCreationDialog = showAssetsCreationDialog;
496    helper.constructor.prototype.createAssetWithShortTermPermission = createAssetWithShortTermPermission;
497    helper.constructor.prototype.requestPhotoUrisReadPermission = requestPhotoUrisReadPermission;
498  }
499  return helper;
500}
501
502function startPhotoPicker(context, config) {
503  if (context === undefined) {
504    console.log('photoAccessHelper gContext undefined');
505    throw Error('photoAccessHelper gContext undefined');
506  }
507  if (config === undefined) {
508    console.log('photoAccessHelper config undefined');
509    throw Error('photoAccessHelper config undefined');
510  }
511  gContext = context;
512  let helper = photoAccessHelper.startPhotoPicker(gContext, config);
513  if (helper !== undefined) {
514    console.log('photoAccessHelper startPhotoPicker inner add createDeleteRequest');
515    helper.createDeleteRequest = createDeleteRequest;
516  }
517  return helper;
518}
519
520function getPhotoAccessHelperAsync(context, asyncCallback) {
521  if (context === undefined) {
522    console.log('photoAccessHelper gContext undefined');
523    throw Error('photoAccessHelper gContext undefined');
524  }
525  gContext = context;
526  if (arguments.length === 1) {
527    return photoAccessHelper.getPhotoAccessHelperAsync(gContext)
528      .then((helper) => {
529        if (helper !== undefined) {
530          console.log('photoAccessHelper getPhotoAccessHelperAsync inner add createDeleteRequest' +
531            ' and showAssetsCreationDialog');
532          helper.createDeleteRequest = createDeleteRequest;
533          helper.showAssetsCreationDialog = showAssetsCreationDialog;
534          helper.createAssetWithShortTermPermission = createAssetWithShortTermPermission;
535          helper.requestPhotoUrisReadPermission = requestPhotoUrisReadPermission;
536        }
537        return helper;
538      })
539      .catch((err) => {
540        console.log('photoAccessHelper getPhotoAccessHelperAsync err ' + err);
541        throw Error(err);
542      });
543  } else if (arguments.length === ARGS_TWO && typeof asyncCallback === 'function') {
544    photoAccessHelper.getPhotoAccessHelperAsync(gContext, (err, helper) => {
545      console.log('photoAccessHelper getPhotoAccessHelperAsync callback ' + err);
546      if (err) {
547        asyncCallback(err);
548      } else {
549        if (helper !== undefined) {
550          console.log('photoAccessHelper getPhotoAccessHelperAsync callback add createDeleteRequest' +
551            ' and showAssetsCreationDialog');
552          helper.createDeleteRequest = createDeleteRequest;
553          helper.showAssetsCreationDialog = showAssetsCreationDialog;
554          helper.createAssetWithShortTermPermission = createAssetWithShortTermPermission;
555          helper.requestPhotoUrisReadPermission = requestPhotoUrisReadPermission;
556        }
557        asyncCallback(err, helper);
558      }
559    });
560  } else {
561    console.log('photoAccessHelper getPhotoAccessHelperAsync param invalid');
562    throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID);
563  }
564  return undefined;
565}
566
567const RecommendationType = {
568  // Indicates that QR code or barcode photos can be recommended
569  QR_OR_BAR_CODE: 1,
570
571  // Indicates that QR code photos can be recommended
572  QR_CODE: 2,
573
574  // Indicates that barcode photos can be recommended
575  BAR_CODE: 3,
576
577  // Indicates that QR code or barcode photos can be recommended
578  ID_CARD: 4,
579
580  // Indicates that profile picture photos can be recommended
581  PROFILE_PICTURE: 5,
582
583  // Indicates that passport photos can be recommended
584  PASSPORT: 6,
585
586  // Indicates that bank card photos can be recommended
587  BANK_CARD: 7,
588
589  // Indicates that driver license photos can be recommended
590  DRIVER_LICENSE: 8,
591
592  // Indicates that driving license photos can be recommended
593  DRIVING_LICENSE: 9,
594
595  // Indicates that featured single portrait photos can be recommended
596  FEATURED_SINGLE_PORTRAIT: 10
597};
598
599const PhotoViewMIMETypes = {
600  IMAGE_TYPE: 'image/*',
601  VIDEO_TYPE: 'video/*',
602  IMAGE_VIDEO_TYPE: '*/*',
603  MOVING_PHOTO_IMAGE_TYPE: 'image/movingPhoto',
604  INVALID_TYPE: ''
605};
606
607const SingleSelectionMode = {
608  BROWSER_MODE: 0,
609  SELECT_MODE: 1,
610  BROWSER_AND_SELECT_MODE: 2,
611};
612
613const ErrCode = {
614  INVALID_ARGS: 13900020,
615  RESULT_ERROR: 13900042,
616  CONTEXT_NO_EXIST: 16000011,
617};
618
619const CompleteButtonText = {
620  TEXT_DONE: 0,
621  TEXT_SEND: 1,
622  TEXT_ADD: 2,
623};
624
625const ERRCODE_MAP = new Map([
626  [ErrCode.INVALID_ARGS, 'Invalid argument'],
627  [ErrCode.RESULT_ERROR, 'Unknown error'],
628  [ErrCode.CONTEXT_NO_EXIST, 'Current ability failed to obtain context'],
629]);
630
631const PHOTO_VIEW_MIME_TYPE_MAP = new Map([
632  [PhotoViewMIMETypes.IMAGE_TYPE, 'FILTER_MEDIA_TYPE_IMAGE'],
633  [PhotoViewMIMETypes.VIDEO_TYPE, 'FILTER_MEDIA_TYPE_VIDEO'],
634  [PhotoViewMIMETypes.IMAGE_VIDEO_TYPE, 'FILTER_MEDIA_TYPE_ALL'],
635  [PhotoViewMIMETypes.MOVING_PHOTO_IMAGE_TYPE, 'FILTER_MEDIA_TYPE_IMAGE_MOVING_PHOTO'],
636]);
637
638function checkArguments(args) {
639  let checkArgumentsResult = undefined;
640
641  if (args.length === ARGS_TWO && typeof args[ARGS_ONE] !== 'function') {
642    checkArgumentsResult = getErr(ErrCode.INVALID_ARGS);
643  }
644
645  if (args.length > 0 && typeof args[ARGS_ZERO] === 'object') {
646    let option = args[ARGS_ZERO];
647    if (option.maxSelectNumber !== undefined) {
648      if (option.maxSelectNumber.toString().indexOf('.') !== -1) {
649        checkArgumentsResult = getErr(ErrCode.INVALID_ARGS);
650      }
651    }
652  }
653
654  return checkArgumentsResult;
655}
656
657function getErr(errCode) {
658  return { code: errCode, message: ERRCODE_MAP.get(errCode) };
659}
660
661function parsePhotoPickerSelectOption(args) {
662  let config = {
663    action: 'ohos.want.action.photoPicker',
664    type: 'multipleselect',
665    parameters: {
666      uri: 'multipleselect',
667    },
668  };
669
670  if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') {
671    let option = args[ARGS_ZERO];
672    if (option.maxSelectNumber && option.maxSelectNumber > 0) {
673      let select = (option.maxSelectNumber === 1) ? 'singleselect' : 'multipleselect';
674      config.type = select;
675      config.parameters.uri = select;
676      config.parameters.maxSelectCount = option.maxSelectNumber;
677    }
678    if (option.MIMEType && PHOTO_VIEW_MIME_TYPE_MAP.has(option.MIMEType)) {
679      config.parameters.filterMediaType = PHOTO_VIEW_MIME_TYPE_MAP.get(option.MIMEType);
680    }
681    config.parameters.isSearchSupported = option.isSearchSupported === undefined || option.isSearchSupported;
682    config.parameters.isPhotoTakingSupported = option.isPhotoTakingSupported === undefined || option.isPhotoTakingSupported;
683    config.parameters.isEditSupported = option.isEditSupported === undefined || option.isEditSupported;
684    config.parameters.recommendationOptions = option.recommendationOptions;
685    config.parameters.preselectedUris = option.preselectedUris;
686    config.parameters.isPreviewForSingleSelectionSupported = option.isPreviewForSingleSelectionSupported;
687    config.parameters.singleSelectionMode = option.singleSelectionMode;
688    config.parameters.isOriginalSupported = option.isOriginalSupported;
689    config.parameters.subWindowName = option.subWindowName;
690    config.parameters.themeColor = option.themeColor;
691    config.parameters.completeButtonText = option.completeButtonText;
692    config.parameters.userId = option.userId;
693    config.parameters.isPc = deviceinfo.deviceType === '2in1';
694  }
695
696  return config;
697}
698
699function getPhotoPickerSelectResult(args) {
700  let selectResult = {
701    error: undefined,
702    data: undefined,
703  };
704
705  if (args.resultCode === 0) {
706    let uris = args.uris;
707    let isOrigin = args.isOrigin;
708    selectResult.data = new PhotoSelectResult(uris, isOrigin);
709  } else if (args.resultCode === -1) {
710    selectResult.data = new PhotoSelectResult([], undefined);
711  } else {
712    selectResult.error = getErr(ErrCode.RESULT_ERROR);
713  }
714
715  return selectResult;
716}
717
718async function photoPickerSelect(...args) {
719  let checkArgsResult = checkArguments(args);
720  if (checkArgsResult !== undefined) {
721    console.log('[picker] Invalid argument');
722    throw checkArgsResult;
723  }
724
725  const config = parsePhotoPickerSelectOption(args);
726  console.log('[picker] config: ' + encrypt(JSON.stringify(config)));
727  if (config.parameters.userId && config.parameters.userId > 0) {
728    let check = await checkInteractAcrossLocalAccounts();
729    if (!check) {
730      console.log('[picker] error: ' + ERROR_MSG_ACROSS_ACCOUNTS_PERMISSION);
731      return undefined;
732    }
733  }
734
735  let context = undefined;
736  try {
737    context = getContext(this);
738  } catch (getContextError) {
739    console.error('[picker] getContext error: ' + getContextError);
740    throw getErr(ErrCode.CONTEXT_NO_EXIST);
741  }
742  try {
743    if (context === undefined) {
744      throw getErr(ErrCode.CONTEXT_NO_EXIST);
745    }
746    let result = await startPhotoPicker(context, config);
747    console.log('[picker] result: ' + encrypt(JSON.stringify(result)));
748    const selectResult = getPhotoPickerSelectResult(result);
749    console.log('[picker] selectResult: ' + encrypt(JSON.stringify(selectResult)));
750    if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') {
751      return args[ARGS_ONE](selectResult.error, selectResult.data);
752    } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') {
753      return args[ARGS_ZERO](selectResult.error, selectResult.data);
754    }
755    return new Promise((resolve, reject) => {
756      if (selectResult.data !== undefined) {
757        resolve(selectResult.data);
758      } else {
759        reject(selectResult.error);
760      }
761    });
762  } catch (error) {
763    console.error('[picker] error: ' + JSON.stringify(error));
764  }
765  return undefined;
766}
767
768async function checkInteractAcrossLocalAccounts() {
769  let flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION;
770  let { reqPermissionDetails, permissionGrantStates } = await bundleManager.getBundleInfoForSelf(flags);
771  let permissionIndex = -1;
772  for (let i = 0; i < reqPermissionDetails.length; i++) {
773    if (reqPermissionDetails[i].name === ACROSS_ACCOUNTS_PERMISSION) {
774      permissionIndex = i;
775    }
776  }
777  if (permissionIndex < 0 || permissionGrantStates[permissionIndex] === PERMISSION_STATE_ERROR) {
778    return false;
779  } else {
780    return true;
781  }
782}
783
784function BaseSelectOptions() {
785  this.MIMEType = PhotoViewMIMETypes.INVALID_TYPE;
786  this.maxSelectNumber = -1;
787  this.isSearchSupported = true;
788  this.isPhotoTakingSupported = true;
789  this.isPreviewForSingleSelectionSupported = true;
790  this.singleSelectionMode = SingleSelectionMode.BROWSER_MODE;
791}
792
793function PhotoSelectOptions() {
794  this.MIMEType = PhotoViewMIMETypes.INVALID_TYPE;
795  this.maxSelectNumber = -1;
796  this.isSearchSupported = true;
797  this.isPhotoTakingSupported = true;
798  this.isEditSupported = true;
799  this.isOriginalSupported = false;
800  this.completeButtonText = CompleteButtonText.TEXT_DONE;
801  this.userId = -1;
802}
803
804function PhotoSelectResult(uris, isOriginalPhoto) {
805  this.photoUris = uris;
806  this.isOriginalPhoto = isOriginalPhoto;
807}
808
809function PhotoViewPicker() {
810  this.select = photoPickerSelect;
811}
812
813function RecommendationOptions() {
814}
815
816function encrypt(data) {
817  if (!data || data?.indexOf('file:///data/storage/') !== -1) {
818    return '';
819  }
820  return data.replace(/(\/\w+)\./g, '/******.');
821}
822
823class MediaAssetChangeRequest extends photoAccessHelper.MediaAssetChangeRequest {
824  static deleteAssets(context, assets, asyncCallback) {
825    if (arguments.length > ARGS_THREE || arguments.length < ARGS_TWO) {
826      throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID);
827    }
828
829    try {
830      if (asyncCallback) {
831        return super.deleteAssets(context, result => {
832          if (result.result === REQUEST_CODE_SUCCESS) {
833            asyncCallback();
834          } else if (result.result === PERMISSION_DENIED) {
835            asyncCallback(new BusinessError(ERROR_MSG_USER_DENY, ERR_CODE_OHOS_PERMISSION_DENIED));
836          } else {
837            asyncCallback(new BusinessError(ERROR_MSG_INNER_FAIL, result.result));
838          }
839        }, assets, asyncCallback);
840      }
841
842      return new Promise((resolve, reject) => {
843        super.deleteAssets(context, result => {
844          if (result.result === REQUEST_CODE_SUCCESS) {
845            resolve();
846          } else if (result.result === PERMISSION_DENIED) {
847            reject(new BusinessError(ERROR_MSG_USER_DENY, ERR_CODE_OHOS_PERMISSION_DENIED));
848          } else {
849            reject(new BusinessError(ERROR_MSG_INNER_FAIL, result.result));
850          }
851        }, assets, (err) => {
852          if (err) {
853            reject(err);
854          } else {
855            resolve();
856          }
857        });
858      });
859    } catch (error) {
860      return errorResult(new BusinessError(error.message, error.code), asyncCallback);
861    }
862  }
863}
864
865export default {
866  getPhotoAccessHelper,
867  startPhotoPicker,
868  getPhotoAccessHelperAsync,
869  PhotoType: photoAccessHelper.PhotoType,
870  ThumbnailType: photoAccessHelper.ThumbnailType,
871  PhotoCreationConfig: photoAccessHelper.PhotoCreationConfig,
872  PhotoKeys: photoAccessHelper.PhotoKeys,
873  AlbumKeys: photoAccessHelper.AlbumKeys,
874  AlbumType: photoAccessHelper.AlbumType,
875  AlbumSubtype: photoAccessHelper.AlbumSubtype,
876  AnalysisAlbum: photoAccessHelper.AnalysisAlbum,
877  HighlightAlbum: photoAccessHelper.HighlightAlbum,
878  PositionType: photoAccessHelper.PositionType,
879  PhotoSubtype: photoAccessHelper.PhotoSubtype,
880  PhotoPermissionType: photoAccessHelper.PhotoPermissionType,
881  HideSensitiveType: photoAccessHelper.HideSensitiveType,
882  NotifyType: photoAccessHelper.NotifyType,
883  DefaultChangeUri: photoAccessHelper.DefaultChangeUri,
884  HiddenPhotosDisplayMode: photoAccessHelper.HiddenPhotosDisplayMode,
885  AnalysisType: photoAccessHelper.AnalysisType,
886  HighlightAlbumInfoType: photoAccessHelper.HighlightAlbumInfoType,
887  HighlightUserActionType: photoAccessHelper.HighlightUserActionType,
888  RequestPhotoType: photoAccessHelper.RequestPhotoType,
889  PhotoViewMIMETypes: PhotoViewMIMETypes,
890  SingleSelectionMode: SingleSelectionMode,
891  DeliveryMode: photoAccessHelper.DeliveryMode,
892  SourceMode: photoAccessHelper.SourceMode,
893  AuthorizationMode: photoAccessHelper.AuthorizationMode,
894  CompatibleMode: photoAccessHelper.CompatibleMode,
895  BaseSelectOptions: BaseSelectOptions,
896  PhotoSelectOptions: PhotoSelectOptions,
897  PhotoSelectResult: PhotoSelectResult,
898  PhotoViewPicker: PhotoViewPicker,
899  RecommendationType: RecommendationType,
900  RecommendationOptions: RecommendationOptions,
901  ResourceType: photoAccessHelper.ResourceType,
902  MediaAssetEditData: photoAccessHelper.MediaAssetEditData,
903  MediaAssetChangeRequest: MediaAssetChangeRequest,
904  MediaAssetsChangeRequest: photoAccessHelper.MediaAssetsChangeRequest,
905  MediaAlbumChangeRequest: photoAccessHelper.MediaAlbumChangeRequest,
906  MediaAnalysisAlbumChangeRequest: photoAccessHelper.MediaAnalysisAlbumChangeRequest,
907  MediaAssetManager: photoAccessHelper.MediaAssetManager,
908  MovingPhoto: photoAccessHelper.MovingPhoto,
909  MovingPhotoEffectMode: photoAccessHelper.MovingPhotoEffectMode,
910  CompleteButtonText: CompleteButtonText,
911  ImageFileType: photoAccessHelper.ImageFileType,
912  CloudEnhancement: photoAccessHelper.CloudEnhancement,
913  CloudEnhancementTaskStage: photoAccessHelper.CloudEnhancementTaskStage,
914  CloudEnhancementState: photoAccessHelper.CloudEnhancementState,
915  CloudEnhancementTaskState: photoAccessHelper.CloudEnhancementTaskState,
916  WatermarkType: photoAccessHelper.WatermarkType,
917  VideoEnhancementType: photoAccessHelper.VideoEnhancementType,
918  CloudMediaAssetManager: photoAccessHelper.CloudMediaAssetManager,
919  CloudMediaDownloadType: photoAccessHelper.CloudMediaDownloadType,
920  CloudMediaRetainType: photoAccessHelper.CloudMediaRetainType,
921  CloudMediaAssetTaskStatus: photoAccessHelper.CloudMediaAssetTaskStatus,
922  CloudMediaTaskPauseCause: photoAccessHelper.CloudMediaTaskPauseCause,
923  CloudMediaAssetStatus: photoAccessHelper.CloudMediaAssetStatus,
924};
925