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'); 19const rpc = requireInternal('rpc'); 20 21const ARGS_ZERO = 0; 22const ARGS_ONE = 1; 23const ARGS_TWO = 2; 24const ARGS_THREE = 3; 25 26const WRITE_PERMISSION = 'ohos.permission.WRITE_IMAGEVIDEO'; 27const ACROSS_ACCOUNTS_PERMISSION = 'ohos.permission.INTERACT_ACROSS_LOCAL_ACCOUNTS'; 28 29const PERMISSION_DENIED = 13900012; 30const ERR_CODE_PARAMERTER_INVALID = 13900020; 31const ERR_CODE_OHOS_PERMISSION_DENIED = 201; 32const ERR_CODE_OHOS_PARAMERTER_INVALID = 401; 33const REQUEST_CODE_SUCCESS = 0; 34const PERMISSION_STATE_ERROR = -1; 35const ERROR_MSG_WRITE_PERMISSION = 'not have ohos.permission.WRITE_IMAGEVIDEO'; 36const ERROR_MSG_ACROSS_ACCOUNTS_PERMISSION = 'not have ohos.permission.INTERACT_ACROSS_LOCAL_ACCOUNTS'; 37const ERROR_MSG_USER_DENY = 'user deny'; 38const ERROR_MSG_PARAMERTER_INVALID = 'input parmaeter invalid'; 39const ERROR_MSG_INNER_FAIL = 'System inner fail'; 40const ERROR_MSG_OHOS_INNER_FAIL = 'Internal system error'; 41 42const SECONDS_OF_ONE_DAY = 24 * 60 * 60; 43const DELAY_MILLSECONDS = 33; 44const RETRY_COUNTER = 10; 45const RPC_TOKEN_RECENT_PHOTO_INFO = 'rpcRecentPhotoInfoServiceAbility'; 46const RPC_MSGID_RECENT_PHOTO_INFO = 1; 47 48const MAX_DELETE_NUMBER = 300; 49const MIN_DELETE_NUMBER = 1; 50const MAX_CONFIRM_NUMBER = 100; 51const MIN_CONFIRM_NUMBER = 1; 52 53let gContext = undefined; 54 55class BusinessError extends Error { 56 constructor(msg, code) { 57 super(msg); 58 this.code = code || PERMISSION_DENIED; 59 } 60} 61 62function checkArrayAndSize(array, minSize, maxSize) { 63 // check whether input is array 64 if (!Array.isArray(array)) { 65 console.error('photoAccessHelper invalid, array is null.'); 66 return false; 67 } 68 69 // check whether array length is valid 70 let len = array.length; 71 if ((len < minSize) || (len > maxSize)) { 72 console.error('photoAccessHelper invalid, array size invalid.'); 73 return false; 74 } 75 76 return true; 77} 78 79function checkIsUriValid(uri, isAppUri) { 80 if (!uri) { 81 console.error('photoAccessHelper invalid, uri is null.'); 82 return false; 83 } 84 85 if (typeof uri !== 'string') { 86 console.error('photoAccessHelper invalid, uri type is not string.'); 87 return false; 88 } 89 90 // media library uri starts with 'file://media/Photo/', createDeleteReques delete media library resource should check 91 if (!isAppUri) { 92 return uri.includes('file://media/Photo/'); 93 } 94 95 // showAssetsCreationDialog store third part application resource to media library, no need to check it 96 return true; 97} 98 99function checkParams(uriList, asyncCallback) { 100 if (arguments.length > ARGS_TWO) { 101 return false; 102 } 103 if (!checkArrayAndSize(uriList, MIN_DELETE_NUMBER, MAX_DELETE_NUMBER)) { 104 return false; 105 } 106 if (asyncCallback && typeof asyncCallback !== 'function') { 107 return false; 108 } 109 for (let uri of uriList) { 110 if (!checkIsUriValid(uri, false)) { 111 console.info(`photoAccessHelper invalid uri: ${uri}`); 112 return false; 113 } 114 } 115 return true; 116} 117function errorResult(rej, asyncCallback) { 118 if (asyncCallback) { 119 return asyncCallback(rej); 120 } 121 return new Promise((resolve, reject) => { 122 reject(rej); 123 }); 124} 125 126function getAbilityResource(bundleInfo) { 127 console.info('getAbilityResource enter.'); 128 let labelId = 0; 129 for (let hapInfo of bundleInfo.hapModulesInfo) { 130 if (hapInfo.type === bundleManager.ModuleType.ENTRY) { 131 labelId = getLabelId(hapInfo); 132 } 133 } 134 return labelId; 135} 136 137function getLabelId(hapInfo) { 138 let labelId = 0; 139 for (let abilityInfo of hapInfo.abilitiesInfo) { 140 let abilitiesInfoName = ''; 141 if (abilityInfo.name.includes('.')) { 142 let abilitiesInfoLength = abilityInfo.name.split('.').length; 143 abilitiesInfoName = abilityInfo.name.split('.')[abilitiesInfoLength - 1]; 144 } else { 145 abilitiesInfoName = abilityInfo.name; 146 } 147 if (abilitiesInfoName === hapInfo.mainElementName) { 148 labelId = abilityInfo.labelId; 149 } 150 } 151 return labelId; 152} 153 154async function getAppName() { 155 let appName = ''; 156 try { 157 const flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_ABILITY | bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_HAP_MODULE; 158 const bundleInfo = await bundleManager.getBundleInfoForSelf(flags); 159 console.info(`photoAccessHelper bundleInfo: ${JSON.stringify(bundleInfo)}`); 160 if (bundleInfo === undefined || bundleInfo.hapModulesInfo === undefined || bundleInfo.hapModulesInfo.length === 0) { 161 return appName; 162 } 163 const labelId = getAbilityResource(bundleInfo); 164 const resourceMgr = gContext.resourceManager; 165 appName = await resourceMgr.getStringValue(labelId); 166 console.info(`photoAccessHelper appName: ${appName}`); 167 } catch (error) { 168 console.info(`photoAccessHelper error: ${JSON.stringify(error)}`); 169 } 170 171 return appName; 172} 173 174async function createPhotoDeleteRequestParamsOk(uriList, asyncCallback) { 175 let flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION; 176 let { reqPermissionDetails, permissionGrantStates } = await bundleManager.getBundleInfoForSelf(flags); 177 let permissionIndex = -1; 178 for (let i = 0; i < reqPermissionDetails.length; i++) { 179 if (reqPermissionDetails[i].name === WRITE_PERMISSION) { 180 permissionIndex = i; 181 } 182 } 183 if (permissionIndex < 0 || permissionGrantStates[permissionIndex] === PERMISSION_STATE_ERROR) { 184 console.info('photoAccessHelper permission error'); 185 return errorResult(new BusinessError(ERROR_MSG_WRITE_PERMISSION), asyncCallback); 186 } 187 const appName = await getAppName(); 188 if (appName.length === 0) { 189 console.info(`photoAccessHelper appName not found`); 190 return errorResult(new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_PARAMERTER_INVALID), asyncCallback); 191 } 192 try { 193 if (asyncCallback) { 194 return photoAccessHelper.createDeleteRequest(getContext(this), appName, uriList, result => { 195 if (result.result === REQUEST_CODE_SUCCESS) { 196 asyncCallback(); 197 } else if (result.result == PERMISSION_DENIED) { 198 asyncCallback(new BusinessError(ERROR_MSG_USER_DENY)); 199 } else { 200 asyncCallback(new BusinessError(ERROR_MSG_INNER_FAIL, result.result)); 201 } 202 }); 203 } else { 204 return new Promise((resolve, reject) => { 205 photoAccessHelper.createDeleteRequest(getContext(this), appName, uriList, result => { 206 if (result.result === REQUEST_CODE_SUCCESS) { 207 resolve(); 208 } else if (result.result == PERMISSION_DENIED) { 209 reject(new BusinessError(ERROR_MSG_USER_DENY)); 210 } else { 211 reject(new BusinessError(ERROR_MSG_INNER_FAIL, result.result)); 212 } 213 }); 214 }); 215 } 216 } catch (error) { 217 return errorResult(new BusinessError(error.message, error.code), asyncCallback); 218 } 219} 220 221function createDeleteRequest(...params) { 222 if (!checkParams(...params)) { 223 throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_PARAMERTER_INVALID); 224 } 225 return createPhotoDeleteRequestParamsOk(...params); 226} 227 228function checkIsPhotoCreationConfigValid(config) { 229 if (!config) { 230 console.error('photoAccessHelper invalid, config is null.'); 231 return false; 232 } 233 234 // check whether input is a object 235 if (typeof config !== 'object') { 236 console.error('photoAccessHelper invalid, config type is not object.'); 237 return false; 238 } 239 240 // check whether title is string if exsit 241 if ((config.title) && (typeof config.title !== 'string')) { 242 console.error('photoAccessHelper invalid, config.title type is not string.'); 243 return false; 244 } 245 246 // check whether fileNameExtension is string 247 if (!config.fileNameExtension) { 248 console.error('photoAccessHelper invalid, config.fileNameExtension is null.'); 249 return false; 250 } 251 if (typeof config.fileNameExtension !== 'string') { 252 console.error('photoAccessHelper invalid, config.fileNameExtension type is not string.'); 253 return false; 254 } 255 256 // check whether photoType is number 257 if (!config.photoType) { 258 console.error('photoAccessHelper invalid, config.photoType is null.'); 259 return false; 260 } 261 if (typeof config.photoType !== 'number') { 262 console.error('photoAccessHelper invalid, config.photoType type is not number.'); 263 return false; 264 } 265 266 // check whether subtype is number if exsit 267 if ((config.subtype) && (typeof config.subtype !== 'number')) { 268 console.error('photoAccessHelper invalid, config.subtype type is not number.'); 269 return false; 270 } 271 272 return true; 273} 274 275function checkConfirmBoxParams(srcFileUris, photoCreationConfigs) { 276 // check param number 277 if (arguments.length > ARGS_TWO) { 278 return false; 279 } 280 281 // check whether input array is valid 282 if (!checkArrayAndSize(srcFileUris, MIN_CONFIRM_NUMBER, MAX_CONFIRM_NUMBER)) { 283 return false; 284 } 285 if (!checkArrayAndSize(photoCreationConfigs, MIN_CONFIRM_NUMBER, MAX_CONFIRM_NUMBER)) { 286 return false; 287 } 288 if (srcFileUris.length !== photoCreationConfigs.length) { 289 return false; 290 } 291 292 // check whether srcFileUris element is valid 293 for (let srcFileUri of srcFileUris) { 294 if (!checkIsUriValid(srcFileUri, true)) { 295 console.error('photoAccessHelper invalid uri: ${srcFileUri}.'); 296 return false; 297 } 298 } 299 300 // check whether photoCreationConfigs element is valid 301 for (let photoCreateConfig of photoCreationConfigs) { 302 if (!checkIsPhotoCreationConfigValid(photoCreateConfig)) { 303 return false; 304 } 305 } 306 307 return true; 308} 309 310function getBundleInfo() { 311 let flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_ABILITY | // for appName 312 bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_HAP_MODULE | // for appName 313 bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO | // for appId 314 bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION; // for appInfo 315 let bundleInfo = bundleManager.getBundleInfoForSelfSync(flags); 316 if (((bundleInfo === undefined) || (bundleInfo.name === undefined)) || 317 ((bundleInfo.hapModulesInfo === undefined) || (bundleInfo.hapModulesInfo.length === 0)) || 318 ((bundleInfo.signatureInfo === undefined) || (bundleInfo.signatureInfo.appId === undefined)) || 319 ((bundleInfo.appInfo === undefined) || (bundleInfo.appInfo.labelId === 0))) { 320 console.error('photoAccessHelper failed to get bundle info.'); 321 return undefined; 322 } 323 324 return bundleInfo; 325} 326 327function showAssetsCreationDialogResult(result, reject, resolve) { 328 if (result.result !== REQUEST_CODE_SUCCESS) { 329 reject(new BusinessError(ERROR_MSG_OHOS_INNER_FAIL, result.result)); 330 } 331 332 if (result.data === undefined) { 333 result.data = []; 334 } 335 336 resolve(result.data); 337} 338 339async function showAssetsCreationDialogParamsOk(srcFileUris, photoCreationConfigs) { 340 let bundleInfo = getBundleInfo(); 341 if (bundleInfo === undefined) { 342 return new Promise((resolve, reject) => { 343 reject(new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID)); 344 }); 345 } 346 347 // get bundleName and appId and appName 348 let bundleName = bundleInfo.name; 349 let appId = bundleInfo.signatureInfo.appId; 350 console.info('photoAccessHelper bundleName is ' + bundleName + '.'); 351 console.info('photoAccessHelper appId is ' + appId + '.'); 352 353 let labelId = bundleInfo.appInfo.labelId; 354 console.info('photoAccessHelper labelId is ' + appId + '.'); 355 let appName = ''; 356 357 try { 358 let modeleName = ''; 359 for (let hapInfo of bundleInfo.hapModulesInfo) { 360 if (labelId === hapInfo.labelId) { 361 modeleName = hapInfo.name; 362 } 363 } 364 console.info('photoAccessHelper modeleName is ' + modeleName + '.'); 365 appName = await gContext.createModuleContext(modeleName).resourceManager.getStringValue(labelId); 366 console.info('photoAccessHelper appName is ' + appName + '.'); 367 // only promise type 368 return new Promise((resolve, reject) => { 369 photoAccessHelper.showAssetsCreationDialog(getContext(this), srcFileUris, photoCreationConfigs, bundleName, 370 appName, appId, result => { 371 showAssetsCreationDialogResult(result, reject, resolve); 372 }); 373 }); 374 } catch (error) { 375 return errorResult(new BusinessError(error.message, error.code), null); 376 } 377} 378 379function showAssetsCreationDialog(...params) { 380 if (!checkConfirmBoxParams(...params)) { 381 throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID); 382 } 383 return showAssetsCreationDialogParamsOk(...params); 384} 385 386async function requestPhotoUrisReadPermission(srcFileUris) { 387 console.info('requestPhotoUrisReadPermission enter'); 388 389 //check whether srcFileUris is valid 390 if (srcFileUris === undefined || srcFileUris.length < MIN_CONFIRM_NUMBER) { 391 console.error('photoAccessHelper invalid, array size invalid.'); 392 return false; 393 } 394 for (let srcFileUri of srcFileUris) { 395 if (!checkIsUriValid(srcFileUri, true)) { 396 console.error('photoAccesshelper invalid uri : ${srcFileUri}.'); 397 return false; 398 } 399 } 400 401 let context = gContext; 402 if (context === undefined) { 403 console.info('photoAccessHelper gContet undefined'); 404 context = getContext(this); 405 } 406 407 let bundleInfo = getBundleInfo(); 408 if (bundleInfo === undefined) { 409 return new Promise((resolve, reject) => { 410 reject(new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID)); 411 }); 412 } 413 let labelId = bundleInfo.appInfo.labelId; 414 console.info('photoAccessHelper labelId is ' + labelId + '.'); 415 let appName = ''; 416 417 try { 418 let moduleName = ''; 419 for (let hapInfo of bundleInfo.hapModulesInfo) { 420 if (labelId === hapInfo.labelId) { 421 moduleName = hapInfo.name; 422 } 423 } 424 console.info('photoAccessHelper moduleName is ' + moduleName + '.'); 425 appName = await gContext.createModuleContext(moduleName).resourceManager.getStringValue(labelId); 426 console.info('photoAccessHelper appName is ' + appName + '.'); 427 return new Promise((resolve, reject) => { 428 photoAccessHelper.requestPhotoUrisReadPermission(context, srcFileUris, appName, result => { 429 showAssetsCreationDialogResult(result, reject, resolve); 430 }); 431 }); 432 } catch (error) { 433 console.error('requestPhotoUrisReadPermission catch error.'); 434 return errorResult(new BusinessError(ERROR_MSG_INNER_FAIL, error.code), null); 435 } 436} 437 438async function createAssetWithShortTermPermissionOk(photoCreationConfig) { 439 let bundleInfo = getBundleInfo(); 440 if (bundleInfo === undefined) { 441 return new Promise((resolve, reject) => { 442 reject(new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID)); 443 }); 444 } 445 446 let bundleName = bundleInfo.name; 447 let appId = bundleInfo.signatureInfo.appId; 448 console.info('photoAccessHelper bundleName is ' + bundleName + '.'); 449 console.info('photoAccessHelper appId is ' + appId + '.'); 450 451 let labelId = bundleInfo.appInfo.labelId; 452 console.info('photoAccessHelper labelId is ' + appId + '.'); 453 let appName = ''; 454 let tokenId = bundleInfo.appInfo.accessTokenId; 455 console.info('photoAccessHelper tokenId is ' + tokenId + '.'); 456 457 try { 458 let modeleName = ''; 459 for (let hapInfo of bundleInfo.hapModulesInfo) { 460 if (labelId === hapInfo.labelId) { 461 modeleName = hapInfo.name; 462 } 463 } 464 console.info('photoAccessHelper modeleName is ' + modeleName + '.'); 465 appName = await gContext.createModuleContext(modeleName).resourceManager.getStringValue(labelId); 466 console.info('photoAccessHelper appName is ' + appName + '.'); 467 468 if (photoAccessHelper.checkShortTermPermission()) { 469 let photoCreationConfigs = [photoCreationConfig]; 470 let desFileUris = await getPhotoAccessHelper(getContext(this)).createAssetsHasPermission(bundleName, appName, tokenId, 471 photoCreationConfigs); 472 return new Promise((resolve, reject) => { 473 resolve(desFileUris[0]); 474 }); 475 } 476 return new Promise((resolve, reject) => { 477 photoAccessHelper.createAssetWithShortTermPermission(getContext(this), photoCreationConfig, bundleName, appName, 478 appId, result => { 479 showAssetsCreationDialogResult(result, reject, resolve); 480 }); 481 }); 482 } catch (error) { 483 return errorResult(new BusinessError(ERROR_MSG_INNER_FAIL, error.code), null); 484 } 485} 486 487function createAssetWithShortTermPermission(photoCreationConfig) { 488 if (!checkIsPhotoCreationConfigValid(photoCreationConfig)) { 489 throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID); 490 } 491 return createAssetWithShortTermPermissionOk(photoCreationConfig); 492} 493 494function getPhotoPickerComponentDefaultAlbumName() { 495 let bundleInfo = getBundleInfo(); 496 if (bundleInfo === undefined) { 497 return new Promise((resolve, reject) => { 498 reject(new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID)); 499 }); 500 } 501 502 try { 503 return new Promise((resolve, reject) => { 504 photoAccessHelper.getPhotoPickerComponentDefaultAlbumName(getContext(this), result => { 505 showAssetsCreationDialogResult(result, reject, resolve); 506 }); 507 }); 508 } catch (error) { 509 return errorResult(new BusinessError(ERROR_MSG_INNER_FAIL, error.code), null); 510 } 511} 512 513async function delayFunc() { 514 return new Promise(resolve => setTimeout(resolve, DELAY_MILLSECONDS)); 515} 516 517function convertMIMETypeToFilterType(e) { 518 let o; 519 if (e === PhotoViewMIMETypes.IMAGE_TYPE) { 520 o = PHOTO_VIEW_MIME_TYPE_MAP.get(e); 521 } else if (e === PhotoViewMIMETypes.VIDEO_TYPE) { 522 o = PHOTO_VIEW_MIME_TYPE_MAP.get(e); 523 } else if (e === PhotoViewMIMETypes.MOVING_PHOTO_IMAGE_TYPE) { 524 o = PHOTO_VIEW_MIME_TYPE_MAP.get(e); 525 } else { 526 o = PHOTO_VIEW_MIME_TYPE_MAP.get(PhotoViewMIMETypes.IMAGE_VIDEO_TYPE); 527 } 528 console.info('convertMIMETypeToFilterType: ' + JSON.stringify(o)); 529 return o; 530} 531 532function checkIsRecentPhotoOptionValid(option) { 533 if (!option) { 534 console.error('photoAccessHelper invalid, option is null.'); 535 return false; 536 } 537 // check whether input type is object 538 if (typeof option !== 'object') { 539 console.error('photoAccessHelper invalid, option type is not object.'); 540 return false; 541 } 542 // check whether period is number when it exist 543 if (!option.period) { 544 option.period = SECONDS_OF_ONE_DAY; 545 } 546 if (typeof option.period !== 'number') { 547 console.error('photoAccessHelper invalid, option.period type is not number.'); 548 return false; 549 } 550 if (option.period <= 0 || option.period > SECONDS_OF_ONE_DAY) { 551 option.period = SECONDS_OF_ONE_DAY; 552 } 553 // check whether MIMEType is PhotoViewMIMETypes 554 if (!option.MIMEType) { 555 option.MIMEType = PhotoViewMIMETypes.IMAGE_VIDEO_TYPE; 556 } 557 if (typeof option.MIMEType !== 'string') { 558 console.error('photoAccessHelper invalid, option.MIMEType is not string.'); 559 return false; 560 } 561 // convert MIMEType string 562 option.MIMEType = convertMIMETypeToFilterType(option.MIMEType); 563 const PhotoSource = { 564 ALL: 0, 565 CAMERA: 1, 566 SCREENSHOT: 2 567 }; 568 // check whether photoSource valid 569 if (!option.photoSource) { 570 option.photoSource = PhotoSource.ALL; 571 } 572 if (typeof option.photoSource !== 'number') { 573 console.error('photoAccessHelper invalid, option.photoSource is not number'); 574 return false; 575 } 576 if (option.photoSource < PhotoSource.ALL || option.photoSource > PhotoSource.SCREENSHOT) { 577 option.photoSource = PhotoSource.ALL; 578 } 579 return true; 580} 581 582async function rpcGetRecentPhotoInfoGetProxy() { 583 let proxy = undefined; 584 let want = { 585 'bundleName': 'com.ohos.photos', 586 'abilityName': 'RecentPhotoInfoAbility' 587 }; 588 let connect = { 589 onConnect: (elementName, remoteProxy) => { 590 console.info('RpcClient: js onConnect called'); 591 proxy = remoteProxy; 592 }, 593 onDisconnect: (elementName) => { 594 console.info('RpcClient: onDisconnect'); 595 }, 596 onFailed: () => { 597 console.info('RpcClient: onFailed'); 598 } 599 }; 600 let context = gContext; 601 if (context === undefined) { 602 console.info('photoAccessHelper gContext undefined.'); 603 context = getContext(this); 604 } 605 try { 606 let connectId = context.connectServiceExtensionAbility(want, connect); 607 let retryConter = RETRY_COUNTER; 608 while (proxy === undefined) { 609 retryConter -= 1; 610 if (retryConter < 0) { 611 break; 612 } 613 await delayFunc(); 614 } 615 } catch (error) { 616 console.error('rpcGetRecentPhotoInfo Error: ' + error); 617 } 618 return proxy; 619} 620 621async function rpcGetRecentPhotoInfo(recentPhotoOption) { 622 let proxy = await rpcGetRecentPhotoInfoGetProxy(); 623 if (proxy === undefined) { 624 console.error('rpcGetRecentPhotoInfo proxy is undefined'); 625 return undefined; 626 } 627 let option = new rpc.MessageOption(); 628 let data = rpc.MessageSequence.create(); 629 let reply = rpc.MessageSequence.create(); 630 try { // rpc Request 631 data.writeInterfaceToken(RPC_TOKEN_RECENT_PHOTO_INFO); 632 data.writeInt(recentPhotoOption.period); 633 data.writeString(recentPhotoOption.MIMEType); 634 data.writeInt(recentPhotoOption.photoSource); 635 let result = await proxy.sendMessageRequest(RPC_MSGID_RECENT_PHOTO_INFO, data, reply, option); 636 if (result.errCode !== 0) { 637 console.error('rpcGetRecentPhotoInfo sendMessageRequest failed, errCode: ' + result.errCode); 638 return undefined; 639 } 640 let dateTaken = result.reply.readLong(); 641 let identifier = result.reply.readString(); 642 console.info('rpcGetRecentPhotoInfo sendMessageRequest succ, result: ' + dateTaken); 643 return {'dateTaken': dateTaken, 'identifier': identifier}; 644 } catch (err) { 645 console.error('rpcGetRecentPhotoInfo sendMessageRequest failed: ' + err); 646 return undefined; 647 } finally { 648 data.reclaim(); 649 reply.reclaim(); 650 } 651} 652 653async function getRecentPhotoInfoOk(recentPhotoOption) { 654 try { 655 console.info('getRecentPhotoInfoOk enter'); 656 let photoInfo = rpcGetRecentPhotoInfo(recentPhotoOption); 657 if (!photoInfo) { 658 photoInfo = { dateTaken: -1, identifier: '-1' }; 659 } 660 if (photoInfo.dateTaken === 0) { 661 photoInfo.identifier = '0'; 662 } 663 console.info('recentPhotoInfo result: ' + photoInfo.identifier); 664 return new Promise((resolve, reject) => { 665 resolve(photoInfo); 666 }); 667 } catch (error) { 668 return errorResult(new BusinessError(ERROR_MSG_INNER_FAIL, error.code), null); 669 } 670} 671 672function getRecentPhotoInfo(recentPhotoOption) { 673 if (!checkIsRecentPhotoOptionValid(recentPhotoOption)) { 674 throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID); 675 } 676 return getRecentPhotoInfoOk(recentPhotoOption); 677} 678 679function getPhotoAccessHelper(context, userId = -1) { 680 if (context === undefined) { 681 console.log('photoAccessHelper gContext undefined'); 682 throw Error('photoAccessHelper gContext undefined'); 683 } 684 gContext = context; 685 let helper = photoAccessHelper.getPhotoAccessHelper(gContext, userId); 686 if (helper !== undefined && helper.constructor.prototype.createDeleteRequest === undefined) { 687 console.log('photoAccessHelper getPhotoAccessHelper inner add createDeleteRequest and showAssetsCreationDialog'); 688 helper.constructor.prototype.createDeleteRequest = createDeleteRequest; 689 helper.constructor.prototype.showAssetsCreationDialog = showAssetsCreationDialog; 690 helper.constructor.prototype.createAssetWithShortTermPermission = createAssetWithShortTermPermission; 691 helper.constructor.prototype.requestPhotoUrisReadPermission = requestPhotoUrisReadPermission; 692 helper.constructor.prototype.getPhotoPickerComponentDefaultAlbumName = getPhotoPickerComponentDefaultAlbumName; 693 helper.constructor.prototype.getRecentPhotoInfo = getRecentPhotoInfo; 694 } 695 return helper; 696} 697 698function startPhotoPicker(context, config) { 699 if (context === undefined) { 700 console.log('photoAccessHelper gContext undefined'); 701 throw Error('photoAccessHelper gContext undefined'); 702 } 703 if (config === undefined) { 704 console.log('photoAccessHelper config undefined'); 705 throw Error('photoAccessHelper config undefined'); 706 } 707 gContext = context; 708 let helper = photoAccessHelper.startPhotoPicker(gContext, config); 709 if (helper !== undefined) { 710 console.log('photoAccessHelper startPhotoPicker inner add createDeleteRequest'); 711 helper.createDeleteRequest = createDeleteRequest; 712 } 713 return helper; 714} 715 716function getPhotoAccessHelperAsync(context, asyncCallback) { 717 if (context === undefined) { 718 console.log('photoAccessHelper gContext undefined'); 719 throw Error('photoAccessHelper gContext undefined'); 720 } 721 gContext = context; 722 if (arguments.length === 1) { 723 return photoAccessHelper.getPhotoAccessHelperAsync(gContext) 724 .then((helper) => { 725 if (helper !== undefined) { 726 console.log('photoAccessHelper getPhotoAccessHelperAsync inner add createDeleteRequest' + 727 ' and showAssetsCreationDialog'); 728 helper.createDeleteRequest = createDeleteRequest; 729 helper.showAssetsCreationDialog = showAssetsCreationDialog; 730 helper.createAssetWithShortTermPermission = createAssetWithShortTermPermission; 731 helper.requestPhotoUrisReadPermission = requestPhotoUrisReadPermission; 732 helper.getPhotoPickerComponentDefaultAlbumName = getPhotoPickerComponentDefaultAlbumName; 733 helper.getRecentPhotoInfo = getRecentPhotoInfo; 734 } 735 return helper; 736 }) 737 .catch((err) => { 738 console.log('photoAccessHelper getPhotoAccessHelperAsync err ' + err); 739 throw Error(err); 740 }); 741 } else if (arguments.length === ARGS_TWO && typeof asyncCallback === 'function') { 742 photoAccessHelper.getPhotoAccessHelperAsync(gContext, (err, helper) => { 743 console.log('photoAccessHelper getPhotoAccessHelperAsync callback ' + err); 744 if (err) { 745 asyncCallback(err); 746 } else { 747 if (helper !== undefined) { 748 console.log('photoAccessHelper getPhotoAccessHelperAsync callback add createDeleteRequest' + 749 ' and showAssetsCreationDialog'); 750 helper.createDeleteRequest = createDeleteRequest; 751 helper.showAssetsCreationDialog = showAssetsCreationDialog; 752 helper.createAssetWithShortTermPermission = createAssetWithShortTermPermission; 753 helper.requestPhotoUrisReadPermission = requestPhotoUrisReadPermission; 754 helper.getPhotoPickerComponentDefaultAlbumName = getPhotoPickerComponentDefaultAlbumName; 755 helper.getRecentPhotoInfo = getRecentPhotoInfo; 756 } 757 asyncCallback(err, helper); 758 } 759 }); 760 } else { 761 console.log('photoAccessHelper getPhotoAccessHelperAsync param invalid'); 762 throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID); 763 } 764 return undefined; 765} 766 767const RecommendationType = { 768 // Indicates that QR code or barcode photos can be recommended 769 QR_OR_BAR_CODE: 1, 770 771 // Indicates that QR code photos can be recommended 772 QR_CODE: 2, 773 774 // Indicates that barcode photos can be recommended 775 BAR_CODE: 3, 776 777 // Indicates that QR code or barcode photos can be recommended 778 ID_CARD: 4, 779 780 // Indicates that profile picture photos can be recommended 781 PROFILE_PICTURE: 5, 782 783 // Indicates that passport photos can be recommended 784 PASSPORT: 6, 785 786 // Indicates that bank card photos can be recommended 787 BANK_CARD: 7, 788 789 // Indicates that driver license photos can be recommended 790 DRIVER_LICENSE: 8, 791 792 // Indicates that driving license photos can be recommended 793 DRIVING_LICENSE: 9, 794 795 // Indicates that featured single portrait photos can be recommended 796 FEATURED_SINGLE_PORTRAIT: 10 797}; 798 799const PhotoViewMIMETypes = { 800 IMAGE_TYPE: 'image/*', 801 VIDEO_TYPE: 'video/*', 802 IMAGE_VIDEO_TYPE: '*/*', 803 MOVING_PHOTO_IMAGE_TYPE: 'image/movingPhoto', 804 JPEG_IMAGE_TYPE: 'image/jpeg', 805 GIF_IMAGE_TYPE: 'image/gif', 806 PNG_IMAGE_TYPE: 'image/png', 807 HEIC_IMAGE_TYPE: 'image/heic', 808 HEIF_IMAGE_TYPE: 'image/heif', 809 BMP_IMAGE_TYPE: 'image/bmp', 810 WEBP_IMAGE_TYPE: 'image/webp', 811 AVIF_IMAGE_TYPE: 'image/avif', 812 MP4_VIDEO_TYPE: 'video/mp4', 813 MOV_VIDEO_TYPE: 'video/quicktime', 814 INVALID_TYPE: '' 815}; 816 817const FilterOperator = { 818 INVALID_OPERATOR: -1, 819 EQUAL_TO: 0, 820 NOT_EQUAL_TO: 1, 821 MORE_THAN: 2, 822 LESS_THAN: 3, 823 MORE_THAN_OR_EQUAL_TO: 4, 824 LESS_THAN_OR_EQUAL_TO: 5, 825 BETWEEN: 6, 826}; 827 828const SingleSelectionMode = { 829 BROWSER_MODE: 0, 830 SELECT_MODE: 1, 831 BROWSER_AND_SELECT_MODE: 2, 832}; 833 834const ErrCode = { 835 INVALID_ARGS: 13900020, 836 RESULT_ERROR: 13900042, 837 CONTEXT_NO_EXIST: 16000011, 838}; 839 840const CompleteButtonText = { 841 TEXT_DONE: 0, 842 TEXT_SEND: 1, 843 TEXT_ADD: 2, 844}; 845 846const ERRCODE_MAP = new Map([ 847 [ErrCode.INVALID_ARGS, 'Invalid argument'], 848 [ErrCode.RESULT_ERROR, 'Unknown error'], 849 [ErrCode.CONTEXT_NO_EXIST, 'Current ability failed to obtain context'], 850]); 851 852const PHOTO_VIEW_MIME_TYPE_MAP = new Map([ 853 [PhotoViewMIMETypes.IMAGE_TYPE, 'FILTER_MEDIA_TYPE_IMAGE'], 854 [PhotoViewMIMETypes.VIDEO_TYPE, 'FILTER_MEDIA_TYPE_VIDEO'], 855 [PhotoViewMIMETypes.IMAGE_VIDEO_TYPE, 'FILTER_MEDIA_TYPE_ALL'], 856 [PhotoViewMIMETypes.MOVING_PHOTO_IMAGE_TYPE, 'FILTER_MEDIA_TYPE_IMAGE_MOVING_PHOTO'], 857 [PhotoViewMIMETypes.JPEG_IMAGE_TYPE, 'JPEG_IMAGE_TYPE'], 858 [PhotoViewMIMETypes.GIF_IMAGE_TYPE, 'GIF_IMAGE_TYPE'], 859 [PhotoViewMIMETypes.PNG_IMAGE_TYPE, 'PNG_IMAGE_TYPE'], 860 [PhotoViewMIMETypes.HEIC_IMAGE_TYPE, 'HEIC_IMAGE_TYPE'], 861 [PhotoViewMIMETypes.HEIF_IMAGE_TYPE, 'HEIF_IMAGE_TYPE'], 862 [PhotoViewMIMETypes.BMP_IMAGE_TYPE, 'BMP_IMAGE_TYPE'], 863 [PhotoViewMIMETypes.WEBP_IMAGE_TYPE, 'WEBP_IMAGE_TYPE'], 864 [PhotoViewMIMETypes.AVIF_IMAGE_TYPE, 'AVIF_IMAGE_TYPE'], 865 [PhotoViewMIMETypes.MP4_VIDEO_TYPE, 'MP4_VIDEO_TYPE'], 866 [PhotoViewMIMETypes.MOV_VIDEO_TYPE, 'MOV_VIDEO_TYPE'], 867]); 868 869function checkArguments(args) { 870 let checkArgumentsResult = undefined; 871 872 if (args.length === ARGS_TWO && typeof args[ARGS_ONE] !== 'function') { 873 checkArgumentsResult = getErr(ErrCode.INVALID_ARGS); 874 } 875 876 if (args.length > 0 && typeof args[ARGS_ZERO] === 'object') { 877 let option = args[ARGS_ZERO]; 878 if (option.maxSelectNumber !== undefined) { 879 if (option.maxSelectNumber.toString().indexOf('.') !== -1) { 880 checkArgumentsResult = getErr(ErrCode.INVALID_ARGS); 881 } 882 } 883 } 884 885 return checkArgumentsResult; 886} 887 888function getErr(errCode) { 889 return { code: errCode, message: ERRCODE_MAP.get(errCode) }; 890} 891 892function parsePhotoPickerSelectOption(args) { 893 let config = { 894 action: 'ohos.want.action.photoPicker', 895 type: 'multipleselect', 896 parameters: { 897 uri: 'multipleselect', 898 }, 899 }; 900 901 if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') { 902 let option = args[ARGS_ZERO]; 903 if (option.maxSelectNumber && option.maxSelectNumber > 0) { 904 let select = (option.maxSelectNumber === 1) ? 'singleselect' : 'multipleselect'; 905 config.type = select; 906 config.parameters.uri = select; 907 config.parameters.maxSelectCount = option.maxSelectNumber; 908 } 909 if (option.MIMEType && PHOTO_VIEW_MIME_TYPE_MAP.has(option.MIMEType)) { 910 config.parameters.filterMediaType = PHOTO_VIEW_MIME_TYPE_MAP.get(option.MIMEType); 911 } 912 config.parameters.isSearchSupported = option.isSearchSupported === undefined || option.isSearchSupported; 913 config.parameters.isPhotoTakingSupported = option.isPhotoTakingSupported === undefined || option.isPhotoTakingSupported; 914 config.parameters.isEditSupported = option.isEditSupported === undefined || option.isEditSupported; 915 config.parameters.recommendationOptions = option.recommendationOptions; 916 config.parameters.preselectedUris = option.preselectedUris; 917 config.parameters.isPreviewForSingleSelectionSupported = option.isPreviewForSingleSelectionSupported; 918 config.parameters.singleSelectionMode = option.singleSelectionMode; 919 config.parameters.isOriginalSupported = option.isOriginalSupported; 920 config.parameters.subWindowName = option.subWindowName; 921 config.parameters.themeColor = option.themeColor; 922 config.parameters.completeButtonText = option.completeButtonText; 923 config.parameters.userId = option.userId; 924 config.parameters.mimeTypeFilter = parseMimeTypeFilter(option.mimeTypeFilter); 925 config.parameters.fileSizeFilter = option.fileSizeFilter; 926 config.parameters.videoDurationFilter = option.videoDurationFilter; 927 config.parameters.photoViewMimeTypeFileSizeFilters = option.photoViewMimeTypeFileSizeFilters; 928 config.parameters.combinedMediaTypeFilter = option.combinedMediaTypeFilter; 929 config.parameters.isPc = deviceinfo.deviceType === '2in1'; 930 } 931 932 return config; 933} 934 935function parseMimeTypeFilter(filter) { 936 if (!filter) { 937 return undefined; 938 } 939 let o = {}; 940 o.mimeTypeArray = []; 941 if (filter.mimeTypeArray) { 942 for (let mimeType of filter.mimeTypeArray) { 943 if (PHOTO_VIEW_MIME_TYPE_MAP.has(mimeType)) { 944 o.mimeTypeArray.push(PHOTO_VIEW_MIME_TYPE_MAP.get(mimeType)); 945 } else { 946 o.mimeTypeArray.push(mimeType); 947 } 948 } 949 } 950 return o; 951} 952 953function getPhotoPickerSelectResult(args) { 954 let selectResult = { 955 error: undefined, 956 data: undefined, 957 }; 958 959 if (args.resultCode === 0) { 960 let uris = args.uris; 961 let isOrigin = args.isOrigin; 962 selectResult.data = new PhotoSelectResult(uris, isOrigin); 963 } else if (args.resultCode === -1) { 964 selectResult.data = new PhotoSelectResult([], undefined); 965 } else { 966 selectResult.error = getErr(ErrCode.RESULT_ERROR); 967 } 968 969 return selectResult; 970} 971 972async function photoPickerSelect(...args) { 973 let checkArgsResult = checkArguments(args); 974 if (checkArgsResult !== undefined) { 975 console.log('[picker] Invalid argument'); 976 throw checkArgsResult; 977 } 978 979 const config = parsePhotoPickerSelectOption(args); 980 console.log('[picker] config: ' + encrypt(JSON.stringify(config))); 981 if (config.parameters.userId && config.parameters.userId > 0) { 982 let check = await checkInteractAcrossLocalAccounts(); 983 if (!check) { 984 console.log('[picker] error: ' + ERROR_MSG_ACROSS_ACCOUNTS_PERMISSION); 985 return undefined; 986 } 987 } 988 989 let context = undefined; 990 try { 991 context = getContext(this); 992 } catch (getContextError) { 993 console.error('[picker] getContext error: ' + getContextError); 994 throw getErr(ErrCode.CONTEXT_NO_EXIST); 995 } 996 try { 997 if (context === undefined) { 998 throw getErr(ErrCode.CONTEXT_NO_EXIST); 999 } 1000 let result = await startPhotoPicker(context, config); 1001 console.log('[picker] result: ' + encrypt(JSON.stringify(result))); 1002 const selectResult = getPhotoPickerSelectResult(result); 1003 console.log('[picker] selectResult: ' + encrypt(JSON.stringify(selectResult))); 1004 if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') { 1005 return args[ARGS_ONE](selectResult.error, selectResult.data); 1006 } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') { 1007 return args[ARGS_ZERO](selectResult.error, selectResult.data); 1008 } 1009 return new Promise((resolve, reject) => { 1010 if (selectResult.data !== undefined) { 1011 resolve(selectResult.data); 1012 } else { 1013 reject(selectResult.error); 1014 } 1015 }); 1016 } catch (error) { 1017 console.error('[picker] error: ' + JSON.stringify(error)); 1018 } 1019 return undefined; 1020} 1021 1022async function checkInteractAcrossLocalAccounts() { 1023 let flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION; 1024 let { reqPermissionDetails, permissionGrantStates } = await bundleManager.getBundleInfoForSelf(flags); 1025 let permissionIndex = -1; 1026 for (let i = 0; i < reqPermissionDetails.length; i++) { 1027 if (reqPermissionDetails[i].name === ACROSS_ACCOUNTS_PERMISSION) { 1028 permissionIndex = i; 1029 } 1030 } 1031 if (permissionIndex < 0 || permissionGrantStates[permissionIndex] === PERMISSION_STATE_ERROR) { 1032 return false; 1033 } else { 1034 return true; 1035 } 1036} 1037 1038function MimeTypeFilter() { 1039 this.mimeTypeArray = []; 1040} 1041 1042function FileSizeFilter() { 1043 this.filterOperator = -1; 1044 this.fileSize = -1; 1045} 1046 1047function VideoDurationFilter() { 1048 this.filterOperator = -1; 1049 this.videoDuration = -1; 1050} 1051 1052function FileSizeFilterArray() { 1053 this.photoViewMimeTypeFileSizeFilters = []; 1054} 1055 1056function BaseSelectOptions() { 1057 this.MIMEType = PhotoViewMIMETypes.INVALID_TYPE; 1058 this.maxSelectNumber = -1; 1059 this.isSearchSupported = true; 1060 this.isPhotoTakingSupported = true; 1061 this.isPreviewForSingleSelectionSupported = true; 1062 this.singleSelectionMode = SingleSelectionMode.BROWSER_MODE; 1063} 1064 1065function PhotoSelectOptions() { 1066 this.MIMEType = PhotoViewMIMETypes.INVALID_TYPE; 1067 this.maxSelectNumber = -1; 1068 this.isSearchSupported = true; 1069 this.isPhotoTakingSupported = true; 1070 this.isEditSupported = true; 1071 this.isOriginalSupported = false; 1072 this.completeButtonText = CompleteButtonText.TEXT_DONE; 1073 this.userId = -1; 1074} 1075 1076function PhotoSelectResult(uris, isOriginalPhoto) { 1077 this.photoUris = uris; 1078 this.isOriginalPhoto = isOriginalPhoto; 1079} 1080 1081function PhotoViewPicker() { 1082 this.select = photoPickerSelect; 1083} 1084 1085function RecommendationOptions() { 1086} 1087 1088function encrypt(data) { 1089 if (!data || data?.indexOf('file:///data/storage/') !== -1) { 1090 return ''; 1091 } 1092 return data.replace(/(\/\w+)\./g, '/******.'); 1093} 1094 1095class MediaAssetChangeRequest extends photoAccessHelper.MediaAssetChangeRequest { 1096 static deleteAssets(context, assets, asyncCallback) { 1097 if (arguments.length > ARGS_THREE || arguments.length < ARGS_TWO) { 1098 throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_OHOS_PARAMERTER_INVALID); 1099 } 1100 1101 try { 1102 if (asyncCallback) { 1103 return super.deleteAssets(context, result => { 1104 if (result.result === REQUEST_CODE_SUCCESS) { 1105 asyncCallback(); 1106 } else if (result.result === PERMISSION_DENIED) { 1107 asyncCallback(new BusinessError(ERROR_MSG_USER_DENY, ERR_CODE_OHOS_PERMISSION_DENIED)); 1108 } else { 1109 asyncCallback(new BusinessError(ERROR_MSG_INNER_FAIL, result.result)); 1110 } 1111 }, assets, asyncCallback); 1112 } 1113 1114 return new Promise((resolve, reject) => { 1115 super.deleteAssets(context, result => { 1116 if (result.result === REQUEST_CODE_SUCCESS) { 1117 resolve(); 1118 } else if (result.result === PERMISSION_DENIED) { 1119 reject(new BusinessError(ERROR_MSG_USER_DENY, ERR_CODE_OHOS_PERMISSION_DENIED)); 1120 } else { 1121 reject(new BusinessError(ERROR_MSG_INNER_FAIL, result.result)); 1122 } 1123 }, assets, (err) => { 1124 if (err) { 1125 reject(err); 1126 } else { 1127 resolve(); 1128 } 1129 }); 1130 }); 1131 } catch (error) { 1132 return errorResult(new BusinessError(error.message, error.code), asyncCallback); 1133 } 1134 } 1135} 1136 1137export default { 1138 getPhotoAccessHelper, 1139 startPhotoPicker, 1140 getPhotoAccessHelperAsync, 1141 PhotoType: photoAccessHelper.PhotoType, 1142 ThumbnailType: photoAccessHelper.ThumbnailType, 1143 PhotoCreationConfig: photoAccessHelper.PhotoCreationConfig, 1144 PhotoKeys: photoAccessHelper.PhotoKeys, 1145 AlbumKeys: photoAccessHelper.AlbumKeys, 1146 AlbumType: photoAccessHelper.AlbumType, 1147 AlbumSubtype: photoAccessHelper.AlbumSubtype, 1148 AnalysisAlbum: photoAccessHelper.AnalysisAlbum, 1149 HighlightAlbum: photoAccessHelper.HighlightAlbum, 1150 PositionType: photoAccessHelper.PositionType, 1151 PhotoSubtype: photoAccessHelper.PhotoSubtype, 1152 PhotoPermissionType: photoAccessHelper.PhotoPermissionType, 1153 HideSensitiveType: photoAccessHelper.HideSensitiveType, 1154 NotifyType: photoAccessHelper.NotifyType, 1155 DefaultChangeUri: photoAccessHelper.DefaultChangeUri, 1156 HiddenPhotosDisplayMode: photoAccessHelper.HiddenPhotosDisplayMode, 1157 AnalysisType: photoAccessHelper.AnalysisType, 1158 HighlightAlbumInfoType: photoAccessHelper.HighlightAlbumInfoType, 1159 HighlightUserActionType: photoAccessHelper.HighlightUserActionType, 1160 RequestPhotoType: photoAccessHelper.RequestPhotoType, 1161 PhotoViewMIMETypes: PhotoViewMIMETypes, 1162 SingleSelectionMode: SingleSelectionMode, 1163 MimeTypeFilter: MimeTypeFilter, 1164 FileSizeFilter: FileSizeFilter, 1165 VideoDurationFilter: VideoDurationFilter, 1166 PhotoViewMimeTypeFileSizeFilters: FileSizeFilterArray, 1167 FilterOperator: FilterOperator, 1168 DeliveryMode: photoAccessHelper.DeliveryMode, 1169 SourceMode: photoAccessHelper.SourceMode, 1170 AuthorizationMode: photoAccessHelper.AuthorizationMode, 1171 CompatibleMode: photoAccessHelper.CompatibleMode, 1172 BaseSelectOptions: BaseSelectOptions, 1173 PhotoSelectOptions: PhotoSelectOptions, 1174 PhotoSelectResult: PhotoSelectResult, 1175 PhotoViewPicker: PhotoViewPicker, 1176 RecommendationType: RecommendationType, 1177 RecommendationOptions: RecommendationOptions, 1178 ResourceType: photoAccessHelper.ResourceType, 1179 MediaAssetEditData: photoAccessHelper.MediaAssetEditData, 1180 MediaAssetChangeRequest: MediaAssetChangeRequest, 1181 MediaAssetsChangeRequest: photoAccessHelper.MediaAssetsChangeRequest, 1182 MediaAlbumChangeRequest: photoAccessHelper.MediaAlbumChangeRequest, 1183 MediaAnalysisAlbumChangeRequest: photoAccessHelper.MediaAnalysisAlbumChangeRequest, 1184 MediaAssetManager: photoAccessHelper.MediaAssetManager, 1185 MovingPhoto: photoAccessHelper.MovingPhoto, 1186 MovingPhotoEffectMode: photoAccessHelper.MovingPhotoEffectMode, 1187 CompleteButtonText: CompleteButtonText, 1188 ImageFileType: photoAccessHelper.ImageFileType, 1189 CloudEnhancement: photoAccessHelper.CloudEnhancement, 1190 CloudEnhancementTaskStage: photoAccessHelper.CloudEnhancementTaskStage, 1191 CloudEnhancementState: photoAccessHelper.CloudEnhancementState, 1192 CloudEnhancementTaskState: photoAccessHelper.CloudEnhancementTaskState, 1193 WatermarkType: photoAccessHelper.WatermarkType, 1194 VideoEnhancementType: photoAccessHelper.VideoEnhancementType, 1195 CloudMediaAssetManager: photoAccessHelper.CloudMediaAssetManager, 1196 CloudMediaDownloadType: photoAccessHelper.CloudMediaDownloadType, 1197 CloudMediaRetainType: photoAccessHelper.CloudMediaRetainType, 1198 CloudMediaAssetTaskStatus: photoAccessHelper.CloudMediaAssetTaskStatus, 1199 CloudMediaTaskPauseCause: photoAccessHelper.CloudMediaTaskPauseCause, 1200 CloudMediaAssetStatus: photoAccessHelper.CloudMediaAssetStatus, 1201 PhotoAssetCustomRecordManager: photoAccessHelper.PhotoAssetCustomRecordManager, 1202 PhotoAssetCustomRecord: photoAccessHelper.PhotoAssetCustomRecord, 1203 NotifyChangeType: photoAccessHelper.NotifyChangeType, 1204 ThumbnailChangeStatus: photoAccessHelper.ThumbnailChangeStatus, 1205 StrongAssociationType: photoAccessHelper.StrongAssociationType, 1206}; 1207