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