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