1/* 2 * Copyright (c) 2024-2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import AccountAssociationService from '../service/AccountAssociationService'; 17import { BusinessError } from '@ohos.base'; 18import Constants from '../common/constant'; 19import { common, wantConstant } from '@kit.AbilityKit'; 20import { contact } from '@kit.ContactsKit'; 21import CredConnectService from '../rpc/CredConnectService'; 22import deviceInfo from '@ohos.deviceInfo'; 23import dlpPermission from '@ohos.dlpPermission'; 24import { EditableLeftIconType } from '@ohos.arkui.advanced.EditableTitleBar'; 25import { EditableTitleBar } from '@ohos.arkui.advanced.EditableTitleBar'; 26import emitter from '@ohos.events.emitter'; 27import EncryptSharingHelper from '../component/helper/EncryptSharingHelper'; 28import { EncryptSharingShowCodeEnum, EncryptSharingTerminateCode } from '../common/enum/EncryptSharingShowCodeEnum'; 29import FileUtils, { FileMsg } from '../common/FileUtils/FileUtils'; 30import fs from '@ohos.file.fs'; 31import { 32 getAppId, 33 getConnectionStatus, 34 getFileSizeByUriSync, 35 getFileUriByPath, 36 getOsAccountInfo, 37 sendDlpFileCreateProperties, 38 sendDlpManagerAccountLogin, 39 showToast 40} from '../common/FileUtils/utils'; 41import { HiLog } from '../common/HiLog'; 42import { LengthMetrics } from '@ohos.arkui.node'; 43import { SystemUtils } from '../common/systemUtils'; 44import { systemDateTime } from '@kit.BasicServicesKit'; 45import taskpool from '@ohos.taskpool'; 46import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; 47import Want from '@ohos.app.ability.Want'; 48import FileUtil from '../common/external/FileUtil'; 49 50const TAG = 'encryptedSharing'; 51 52class Test { 53 public '0': number = 0; 54 public '1': number = 0; 55 public '4': string = ''; 56} 57 58class AuthUserList { 59 public authAccount: string = ''; 60 public authAccountType: number = 0; 61 public dlpFileAccess: number = 0; 62 public permExpiryTime: number = 0; 63} 64 65interface GenerateDlpFileResult { 66 inFileFd: number | undefined; 67 dlpFileFd: number | undefined; 68 error: string | undefined; 69} 70 71let defaultDlpProperty: dlpPermission.DLPProperty = { 72 ownerAccount: '', 73 ownerAccountType: dlpPermission.AccountType.CLOUD_ACCOUNT, 74 authUserList: [], 75 contactAccount: '', 76 offlineAccess: true, 77 ownerAccountID: '', 78 everyoneAccessList: [] 79}; 80 81@Entry 82@Component 83struct encryptedSharing { 84 static readonly GET_ACCOUNT_INFO_RESET = 'clear'; 85 private context: common.UIExtensionContext = this.getUIContext().getHostContext() as common.UIExtensionContext; 86 private connectService: CredConnectService = new CredConnectService(this.context); 87 private storage = this.getUIContext().getSharedLocalStorage() as LocalStorage; 88 private session: UIExtensionContentSession | undefined = 89 this.storage === undefined ? undefined : this.storage.get<UIExtensionContentSession>('session'); 90 private actionWant: Want | undefined = this.storage.get<Want>('actionWant'); 91 private inputUriArray: string[] = 92 this.storage === undefined ? [] : this.storage.get<string[]>('inputUriArray') as string[]; 93 private isPhoneDevice: boolean = true; 94 private dlpProperty: dlpPermission.DLPProperty = defaultDlpProperty; 95 private isFromContactsPicker: boolean = false; 96 private tempInputValue: string = ''; 97 private allAssociationList: string[] = []; 98 @State titlebarMargin: LocalizedMargin = { 99 start: LengthMetrics.vp(Constants.SHARE_TITLE_HEAD_MARGIN_RIGHT), 100 end: LengthMetrics.vp(Constants.SHARE_TITLE_HEAD_MARGIN_RIGHT), 101 }; 102 @StorageLink('commandSearchUserInfo') @Watch('beginToGenerateDLPFile') phoneNumSearchResult: string = ''; 103 @StorageLink('commandGetAccountInfo') @Watch('checkGetAccountInfoResult') getAccountInfoResult: string = ''; 104 @State isTextInputEnabled: boolean = false; 105 @State isConfirmButtonEnabled: boolean = false; 106 @State isShowSheet: boolean = false; 107 @State showUIExtensionForAccountLogin: boolean = false; 108 @State inputValue: string = ''; 109 @State phoneFormatTips: boolean = false; 110 @State ownerAccount: string = ''; 111 @State ownerAccountID: string = ''; 112 @State contactExists: boolean = true; 113 @State credentialCallBackMsg: string | Resource = ''; 114 @State placeHolderStr: ResourceStr = ''; 115 @State contactPerson: string = ''; 116 @State recordSuccessUid: string = ''; 117 @State osVersion: ResourceStr = ''; 118 @State generalType: string = 'general.file'; 119 @State scrollHeight: number = 188; 120 @State showAssociationList: string[][] = []; 121 122 async getAuthorizedAccount() { 123 const list = await AccountAssociationService.getInstance().getAuthorizedAccount(); 124 if (!list || !list.length) { 125 HiLog.error(TAG, 'Cannot get association account list.'); 126 this.allAssociationList = []; 127 return; 128 } 129 this.allAssociationList = list; 130 } 131 132 getShowAccountAssociationList(value: string) { 133 this.showAssociationList = []; 134 if (value.length < Constants.SHARE_ASSOCIATION_MIN || value.length >= Constants.SHARE_ASSOCIATION_MAX) { 135 return; 136 } 137 if (!this.allAssociationList || !this.allAssociationList.length) { 138 return; 139 } 140 const filterList = this.allAssociationList.filter(item => item.includes(value)); 141 filterList.forEach(item => { 142 let index = 0; 143 const result: string[] = []; 144 while (index < item.length) { 145 const nextIndex = item.indexOf(this.inputValue, index); 146 if (nextIndex === -1) { 147 result.push(item.slice(index)); 148 break; 149 } else { 150 if (nextIndex !== index) { 151 result.push(item.slice(index, nextIndex)); 152 } 153 result.push(item.slice(nextIndex, nextIndex + this.inputValue.length)); 154 index = nextIndex + this.inputValue.length; 155 } 156 } 157 this.showAssociationList.push(result); 158 }) 159 } 160 161 async setAuthorizedAccount(authorizedAccount: string) { 162 await AccountAssociationService.getInstance().setAuthorizedAccount(authorizedAccount); 163 this.getAuthorizedAccount(); 164 } 165 166 associationItemAction(phone: string[]) { 167 this.inputValue = phone.join(''); 168 this.showAssociationList = []; 169 } 170 171 @Builder 172 AssociationList() { 173 List() { 174 ForEach(this.showAssociationList, (phone: string[]) => { 175 ListItem() { 176 Text() { 177 ForEach(phone, (item: string) => { 178 Span(item).fontWeight(item === this.inputValue ? FontWeight.Bold : FontWeight.Normal) 179 }) 180 } 181 .width(Constants.SHARE_ASSOCIATION_LIST_WIDTH) 182 .height(Constants.SHARE_ASSOCIATION_LIST_HEIGHT) 183 .textAlign(TextAlign.Start) 184 .backgroundColor($r('sys.color.ohos_id_color_background')) 185 } 186 .onClick(() => { 187 this.associationItemAction(phone); 188 }) 189 }, (item: string) => item) 190 } 191 .listDirection(Axis.Vertical) 192 .backgroundColor($r('sys.color.ohos_id_color_background')) 193 .width(Constants.SHARE_ASSOCIATION_LIST_WIDTH) 194 .height(Constants.SHARE_ASSOCIATION_LIST_HEIGHT) 195 .padding({ 196 top: Constants.SHARE_ASSOCIATION_LIST_PADDING, 197 bottom: Constants.SHARE_ASSOCIATION_LIST_PADDING 198 }) 199 } 200 201 @Builder 202 contactsPicker() { 203 Column() { 204 UIExtensionComponent({ 205 bundleName: 'com.ohos.contacts', 206 abilityName: 'ContactUiExtentionAbility', 207 parameters: { 208 'ability.want.params.uiExtensionType': 'sys/commonUI', 209 'targetUrl': 'BatchSelectContactsPage', 210 'isContactMultiSelect': false, 211 } 212 }) 213 .onReceive((data) => { 214 try { 215 let params: [] = JSON.parse((data.want as Want)?.parameters?.contactObjects as string); 216 for (let i = 0; i < params.length; i++) { 217 this.inputValue = (params[i] as Record<string, string>)?.telephone; 218 } 219 this.isShowSheet = false; 220 } catch (error) { 221 HiLog.wrapError(TAG, error, 'json parse exception'); 222 } 223 }) 224 .width(Constants.CONTACTS_PICKER_WIDTH) 225 .height(Constants.CONTACTS_PICKER_HEIGHT) 226 .hitTestBehavior(HitTestMode.Block) 227 } 228 .width(Constants.CONTACTS_PICKER_WIDTH) 229 .height(Constants.CONTACTS_PICKER_HEIGHT) 230 } 231 232 contactsAction = () => { 233 if (this.isPhoneDevice) { 234 contact.selectContacts({ isMultiSelect: false }).then((data) => { 235 HiLog.info(TAG, 'success in selecting Contacts.'); 236 if (!data || !data.length) { 237 HiLog.error(TAG, 'Contacts data is empty.'); 238 return; 239 } 240 const phoneNumbers = data[0].phoneNumbers; 241 if (!phoneNumbers || !phoneNumbers.length) { 242 HiLog.error(TAG, 'Contacts phoneNumbers is empty.'); 243 return; 244 } 245 const phoneNumber = phoneNumbers[0].phoneNumber; 246 if (phoneNumber) { 247 this.inputValue = phoneNumber; 248 this.isFromContactsPicker = true; 249 this.tempInputValue = phoneNumber; 250 } 251 }).catch((error: BusinessError) => { 252 HiLog.wrapError(TAG, error, 'Failed to select Contacts'); 253 }); 254 } else { 255 this.isShowSheet = !this.isShowSheet; 256 } 257 } 258 259 private beginShareEncrypt() { 260 HiLog.info(TAG, 'begin Share Encrypt start'); 261 if (this.isPhoneNumber(this.inputValue)) { 262 this.isTextInputEnabled = false; 263 this.isConfirmButtonEnabled = false; 264 this.getLocalAccountInfo(); 265 } 266 } 267 268 checkGetAccountInfoResult() { 269 if (this.getAccountInfoResult === encryptedSharing.GET_ACCOUNT_INFO_RESET) { 270 return; 271 } 272 HiLog.info(TAG, 'checkGetAccountInfoResult start'); 273 try { 274 let commandGetAccountInfoCallBack = JSON.parse(this.getAccountInfoResult) as Record<string, object>; 275 HiLog.info(TAG, `getAccountInfoResult Call Back errorCode: ${commandGetAccountInfoCallBack.errorCode}`); 276 let res = commandGetAccountInfoCallBack.result as Record<string, string>; 277 const errcode = commandGetAccountInfoCallBack.errorCode ?? -1; 278 if (Number(errcode) === Constants.ERR_CODE_SUCCESS && res?.uid) { 279 this.ownerAccount = res?.uid; 280 this.ownerAccountID = res?.uid; 281 this.connectService.connectServiceShareAbility(Constants.COMMAND_SEARCH_USER_INFO); 282 return; 283 } else { 284 this.enableComponents(); 285 this.recordSuccessUid = ''; 286 this.getAccountInfoResult = encryptedSharing.GET_ACCOUNT_INFO_RESET; 287 if ([ 288 Constants.ERR_CODE_NETWORK_ERROR, 289 Constants.ERR_CODE_CONNECTION_FAIL, 290 Constants.ERR_CODE_CONNECTION_TIME_OUT 291 ].includes(Number(commandGetAccountInfoCallBack.errorCode))) { 292 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.NETWORK_ERROR)); 293 } else { 294 showToast(this.getUIContext(), 295 EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.ENCRYPT_FAIL_ERROR)); 296 } 297 return; 298 } 299 } catch (error) { 300 HiLog.wrapError(TAG, error, 'get account info failed'); 301 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.ENCRYPT_FAIL_ERROR)); 302 this.enableComponents(); 303 this.recordSuccessUid = ''; 304 this.getAccountInfoResult = encryptedSharing.GET_ACCOUNT_INFO_RESET; 305 return; 306 } 307 } 308 309 isPhoneNumber(phoneNum: string): boolean { 310 if (!phoneNum) { 311 return false; 312 } 313 let reg = /^(?:(?:\+|00)86)?1[3456789]\d{9}$/; 314 if (!(reg.test(phoneNum))) { 315 HiLog.info(TAG, 'Please enter the phoneNum.'); 316 this.phoneFormatTips = true; 317 this.credentialCallBackMsg = EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.VALID_PHONE_FORMAT_TIP); 318 HiLog.info(TAG, `phoneFormatTips: ${this.phoneFormatTips}`); 319 return false; 320 } 321 reg = /^(0086|\+86)/; 322 let formatPhone = this.inputValue.replace(reg, ''); 323 let cloudPhone = `${Constants.INTERNATIONAL_DIALING_CODE}${formatPhone}`; 324 this.connectService.setCloudPhone(cloudPhone); 325 return true; 326 } 327 328 checkTaskResults(results: GenerateDlpFileResult[]): boolean { 329 if (!results || results.length !== this.inputUriArray.length) { 330 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.ENCRYPT_FAIL_ERROR)); 331 this.enableComponents(); 332 return false; 333 } 334 for (const res of results) { 335 if (res && res.error === undefined) { 336 continue; 337 } 338 const errRes = JSON.parse(res?.error ?? '') as BusinessError; 339 AppStorage.setOrCreate('commandSearchUserInfo', ''); 340 if (errRes && errRes.code === Constants.SHARE_FILE_NAME_TOO_LONG) { 341 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.FILE_NAME_TOO_LONG)); 342 this.enableComponents(); 343 return false; 344 } 345 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.ENCRYPT_FAIL_ERROR)); 346 this.enableComponents(); 347 return false; 348 } 349 return true; 350 } 351 352 async processTaskResults(results: GenerateDlpFileResult[], dlpFilePathArray: string[], 353 dlpFileNameArray: string[]): Promise<void> { 354 try { 355 if (!this.checkTaskResults(results)) { 356 HiLog.error(TAG, 'Check task results failed.'); 357 return; 358 } 359 360 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.SUCCESS)); 361 let dstFileSizeArray: number[] = []; 362 let filePathUriArray: string[] = []; 363 for (const dlpFilePath of dlpFilePathArray) { 364 let filePathUri: string = getFileUriByPath(dlpFilePath); 365 let dstFileSize: number = getFileSizeByUriSync(filePathUri); 366 filePathUriArray.push(filePathUri); 367 dstFileSizeArray.push(dstFileSize); 368 } 369 AppStorage.setOrCreate('hiPolicySizeEncArray', dstFileSizeArray); 370 AppStorage.setOrCreate('hiCode', 201); 371 sendDlpFileCreateProperties(dlpPermission.AccountType.CLOUD_ACCOUNT); // 201: DLP_2C_FILE_CREATE_EVENT 372 if (!this.isFromContactsPicker) { 373 await this.setAuthorizedAccount(this.inputValue); 374 } 375 this.backToPages(filePathUriArray, dlpFileNameArray); 376 HiLog.info(TAG, 'beginToGenerateDLPFile success'); 377 } finally { 378 for (const res of results) { 379 if (!res) { 380 continue; 381 } 382 if (res.inFileFd !== -1) { 383 FileUtil.closeSync(res.inFileFd); 384 } 385 if (res.dlpFileFd !== -1) { 386 FileUtil.closeSync(res.dlpFileFd); 387 } 388 } 389 } 390 } 391 392 async beginToGenerateDLPFile() { 393 HiLog.info(TAG, 'beginToGenerateDLPFile start'); 394 if (!this.isRegisteredPhoneNum()) { 395 this.enableComponents(); 396 return; 397 } 398 399 let filePath = this.context.filesDir + `/Share/${systemDateTime.getUptime(systemDateTime.TimeType.ACTIVE, false)}/`; 400 try { 401 await fs.mkdir(filePath, true); 402 } catch (error) { 403 HiLog.wrapError(TAG, error, 'mkdir failed'); 404 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.ENCRYPT_FAIL_ERROR)); 405 this.enableComponents(); 406 return; 407 } 408 409 let dlpFilePathArray: string[] = []; 410 let dlpFileNameArray: string[] = []; 411 let taskGroup: taskpool.TaskGroup = new taskpool.TaskGroup(); 412 for (const inputUri of this.inputUriArray) { 413 let srcFileMsg: FileMsg = FileUtils.getSuffixFileMsgByUri(inputUri); 414 let dlpFileName: string = srcFileMsg.fileName + srcFileMsg.fileType + Constants.DLP_FILE_SUFFIX; 415 let dlpFilePath = filePath + dlpFileName; 416 417 let task: taskpool.Task = 418 new taskpool.Task(generateDlpFile, dlpFilePath, inputUri, this.dlpProperty); 419 taskGroup.addTask(task); 420 dlpFilePathArray.push(dlpFilePath); 421 dlpFileNameArray.push(dlpFileName); 422 } 423 let taskResults: GenerateDlpFileResult[] = await taskpool.execute(taskGroup) as GenerateDlpFileResult[]; 424 await this.processTaskResults(taskResults, dlpFilePathArray, dlpFileNameArray); 425 } 426 427 backToPages(filePathUriArray: string[], dlpFileNameArray: string[]) { 428 HiLog.info(TAG, `backToPages start: ${dlpFileNameArray}`); 429 if (this.actionWant && this.actionWant.parameters) { 430 this.actionWant.flags = wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION; 431 this.actionWant.parameters[Constants.PARAMS_STREAM] = filePathUriArray; 432 let generatedFiles: Test[] = []; 433 for (const dlpFileName of dlpFileNameArray) { 434 let fileItem: Test = { 435 '0': 0, 436 '1': 0, 437 '4': dlpFileName 438 }; 439 generatedFiles.push(fileItem); 440 } 441 442 let generalFile: Record<string, Test[]> = {}; 443 generalFile[this.generalType] = generatedFiles; 444 this.actionWant.parameters['ability.picker.records'] = generalFile; 445 setTimeout(() => { 446 try { 447 this.session!.terminateSelfWithResult({ 448 resultCode: EncryptSharingTerminateCode.SUCCESS, 449 want: this.actionWant 450 }); 451 } catch (error) { 452 HiLog.wrapError(TAG, error, 'terminateSelfWithResult failed'); 453 } 454 }, Constants.SHARE_SET_TIMEOUT); 455 } 456 } 457 458 isRegisteredPhoneNum(): boolean { 459 if (!this.phoneNumSearchResult) { 460 return false; 461 } 462 let credentialCallBack: Record<string, string> = {}; 463 try { 464 credentialCallBack = JSON.parse(this.phoneNumSearchResult) as Record<string, string>; 465 } catch (error) { 466 HiLog.error(TAG, `json parse exception, error is ${JSON.stringify(error)}`); 467 return false; 468 } 469 HiLog.info(TAG, `credential Call Back errorCode: ${credentialCallBack.errorCode}`); 470 if (!credentialCallBack.status && Number(credentialCallBack.errorCode) === Constants.ERR_CODE_SUCCESS) { 471 HiLog.info(TAG, 'credentialCallBack msg'); 472 let str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'no_user'); 473 this.credentialCallBackMsg = str.length > 0 ? str : credentialCallBack.errorMsg; 474 this.phoneFormatTips = true; 475 AppStorage.setOrCreate('commandSearchUserInfo', ''); 476 return false; 477 } 478 if (!credentialCallBack.status && [ 479 Constants.ERR_CODE_NETWORK_ERROR, 480 Constants.ERR_CODE_CONNECTION_FAIL, 481 Constants.ERR_CODE_CONNECTION_TIME_OUT 482 ].includes(Number(credentialCallBack.errorCode))) { 483 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.NETWORK_ERROR)); 484 AppStorage.setOrCreate('commandSearchUserInfo', ''); 485 return false; 486 } 487 if (!credentialCallBack.status && Number(credentialCallBack.errorCode) !== Constants.ERR_CODE_SUCCESS) { 488 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.ENCRYPT_FAIL_ERROR)); 489 AppStorage.setOrCreate('commandSearchUserInfo', ''); 490 return false; 491 } 492 let authUserList: AuthUserList[] = [{ 493 'authAccount': credentialCallBack.userIdCipher, 494 'authAccountType': 1, 495 'dlpFileAccess': 1, 496 'permExpiryTime': Date.UTC(9999, 1, 1), 497 }]; 498 this.dlpProperty = { 499 'ownerAccount': this.ownerAccount, 500 'ownerAccountID': this.ownerAccountID, 501 'ownerAccountType': 1, 502 'authUserList': authUserList, 503 'contactAccount': this.ownerAccount, 504 'offlineAccess': true, 505 } 506 return true; 507 } 508 509 async checkContacts() { 510 let callerBundleName = 'com.ohos.contacts'; 511 try { 512 await getAppId(callerBundleName); 513 this.contactExists = true; 514 } catch { 515 this.contactExists = false; 516 } 517 } 518 519 async getLoginStatus() { 520 HiLog.info(TAG, 'get login status start.'); 521 if (!(await getConnectionStatus())) { 522 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.NETWORK_ERROR)); 523 this.enableComponents(); 524 return; 525 } 526 try { 527 let accountInfo = await getOsAccountInfo(); 528 if (accountInfo.distributedInfo.name === 'ohosAnonymousName' && 529 accountInfo.distributedInfo.id === 'ohosAnonymousUid') { 530 this.showUIExtensionForAccountLogin = true; 531 } else { 532 this.enableComponents(); 533 } 534 } catch (err) { 535 this.enableComponents(); 536 HiLog.wrapError(TAG, err, 'getOsAccountInfo failed'); 537 } 538 } 539 540 async getLocalAccountInfo() { 541 HiLog.info(TAG, 'get Account Info start'); 542 if (!(await getConnectionStatus())) { 543 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.NETWORK_ERROR)); 544 this.enableComponents(); 545 return; 546 } 547 try { 548 let accountInfo = await getOsAccountInfo(); 549 if (accountInfo.distributedInfo.name === 'ohosAnonymousName' && 550 accountInfo.distributedInfo.id === 'ohosAnonymousUid') { 551 this.showUIExtensionForAccountLogin = true; 552 return; 553 } 554 if (accountInfo.distributedInfo.id !== this.recordSuccessUid) { 555 HiLog.info(TAG, 'COMMAND_GET_ACCOUNT_INFO start'); 556 this.connectService.connectServiceShareAbility(Constants.COMMAND_GET_ACCOUNT_INFO); 557 this.recordSuccessUid = accountInfo.distributedInfo.id; 558 return; 559 } else { 560 this.connectService.connectServiceShareAbility(Constants.COMMAND_SEARCH_USER_INFO); 561 } 562 } catch (error) { 563 HiLog.wrapError(TAG, error, 'getOsAccountInfo failed'); 564 showToast(this.getUIContext(), EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.ENCRYPT_FAIL_ERROR)); 565 this.enableComponents(); 566 } 567 } 568 569 onLanguageChange() { 570 let str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'add_users_hint'); 571 if (str.length > 0) { 572 this.placeHolderStr = str; 573 } 574 str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'no_user'); 575 if (str.length > 0) { 576 this.credentialCallBackMsg = str; 577 } 578 str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'hmos_version_label'); 579 if (str.length > 0) { 580 this.osVersion = str; 581 } 582 this.getContactPersonString(); 583 } 584 585 subscribeLanguageChange() { 586 emitter.on('onConfigurationUpdate', () => { 587 this.onLanguageChange(); 588 }) 589 } 590 591 getExternalResourceString(bundle: string, module: string, resourceName: string): string { 592 try { 593 let ctx = this.context.createModuleContext(bundle, module); 594 HiLog.info(TAG, 'getExternalResourceString get context from: ' + ctx.applicationInfo.name); 595 let str = ctx.resourceManager.getStringByNameSync(resourceName); 596 return str; 597 } catch (e) { 598 let error = e as BusinessError; 599 HiLog.error(TAG, 'getExternalResourceString error: ' + error.code + ' ' + error.message); 600 return ''; 601 } 602 } 603 604 getContactPersonString() { 605 try { 606 this.context.resourceManager.getStringValue($r('app.string.Share_Contact_Person').id, 607 (error: BusinessError, value: string) => { 608 if (error === undefined || error === null) { 609 this.contactPerson = value; 610 } else { 611 HiLog.wrapError(TAG, error, 'getStringValue failed'); 612 } 613 }); 614 } catch (error) { 615 HiLog.wrapError(TAG, error, 'callback getStringValue failed failed'); 616 } 617 } 618 619 clearHistoryDLPFile() { 620 let pathDir = this.context.filesDir + '/Share'; 621 fs.listFile(pathDir).then((filenames: Array<string>) => { 622 HiLog.info(TAG, 'listFile success when judge time'); 623 let realTime = Number(systemDateTime.getUptime(systemDateTime.TimeType.ACTIVE, false)); 624 filenames.forEach((item) => { 625 let fileTime = Number(item); 626 if (realTime < fileTime || realTime - fileTime >= Constants.CLEAN_DLP_FILE_IN_CACHE_TIMEOUT) { 627 FileUtil.rmdirSync(pathDir + `/${item}`); 628 } 629 }) 630 }).catch((err: BusinessError) => { 631 HiLog.wrapError(TAG, err, 'list file failed when judge time with error message'); 632 }); 633 fs.listFile(pathDir).then((filenames: Array<string>) => { 634 HiLog.info(TAG, 'listFile success when judge num'); 635 let filenamesLists = filenames.sort((a, b) => Number(a) - Number(b)); 636 if (filenamesLists.length > Constants.SHARE_TEMP_SAVE_FILE_NUMBER) { 637 let deleteArray = filenamesLists.slice(0, filenamesLists.length - Constants.SHARE_TEMP_SAVE_FILE_NUMBER); 638 deleteArray.forEach((item) => { 639 FileUtil.rmdirSync(pathDir + `/${item}`); 640 }) 641 } 642 }).catch((err: BusinessError) => { 643 HiLog.wrapError(TAG, err, 'list file failed'); 644 }); 645 } 646 647 enableComponents() { 648 this.isTextInputEnabled = true; 649 this.isConfirmButtonEnabled = this.inputValue.length > 0; 650 this.textInputGetFocus(); 651 } 652 653 textInputGetFocus() { 654 setTimeout(() => { 655 try { 656 HiLog.info(TAG, 'delay requestFocus start'); 657 this.getUIContext().getFocusController().requestFocus('phoneInput'); 658 } catch (error) { 659 HiLog.wrapError(TAG, error, 'requestFocus failed'); 660 } 661 }, Constants.ENCRYPTION_SET_TIMEOUT_TIME); 662 } 663 664 getScrollHeight(newValue: SizeOptions) { 665 const height = newValue.height as number; 666 this.scrollHeight = height - Constants.SHARE_BUTTON_COLUMN_BOTTOM - Constants.SHARE_TITLE_HEAD_HEIGHT - 667 Constants.SHARE_TITLE_HEAD_MARGIN_TOP - Constants.SHARE_TITLE_HEAD_MARGIN_BOTTOM; 668 if (this.scrollHeight > Constants.SHARE_TEXTAREA_MAX_HEIGHT) { 669 this.scrollHeight = Constants.SHARE_TEXTAREA_MAX_HEIGHT; 670 } 671 } 672 673 aboutToAppear() { 674 HiLog.info(TAG, `aboutToAppear enter: ${this.showUIExtensionForAccountLogin}`); 675 this.isPhoneDevice = deviceInfo.deviceType === 'phone'; 676 this.getLoginStatus(); 677 AppStorage.setOrCreate('hiAccountType', dlpPermission.AccountType.CLOUD_ACCOUNT); 678 sendDlpManagerAccountLogin(-1); 679 this.checkContacts(); 680 this.getAuthorizedAccount(); 681 this.clearHistoryDLPFile(); 682 this.subscribeLanguageChange(); 683 let str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'add_users_hint'); 684 this.placeHolderStr = str.length > 0 ? str : ''; 685 str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'hmos_version_label'); 686 this.osVersion = str.length > 0 ? str : ''; 687 this.getContactPersonString(); 688 } 689 690 build() { 691 Stack() { 692 Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start }) { 693 EditableTitleBar({ 694 leftIconStyle: EditableLeftIconType.Back, 695 title: $r('app.string.Share_Add_Viewable_Users'), 696 contentMargin: this.titlebarMargin, 697 menuItems: [ 698 { 699 value: $r('sys.media.ohos_ic_public_cancel'), 700 isEnabled: true, 701 label: $r('app.string.ban'), 702 action: () => { 703 if (this.session !== undefined) { 704 try { 705 this.session.terminateSelfWithResult({ 706 'resultCode': EncryptSharingTerminateCode.CANCEL, 707 }); 708 } catch (error) { 709 HiLog.wrapError(TAG, error, 'terminateSelfWithResult exception'); 710 } 711 } 712 } 713 } 714 ], 715 isSaveIconRequired: false, 716 onCancel: () => { 717 if (this.session !== undefined) { 718 try { 719 this.session.terminateSelfWithResult({ 720 'resultCode': EncryptSharingTerminateCode.BACK, 721 }); 722 } catch (error) { 723 HiLog.wrapError(TAG, error, 'terminateSelfWithResult exception'); 724 } 725 } 726 }, 727 }) 728 .constraintSize({ minHeight: Constants.SHARE_TITLE_HEAD_HEIGHT }) 729 .margin({ 730 top: Constants.SHARE_TITLE_HEAD_MARGIN_TOP, 731 }) 732 Scroll() { 733 Column() { 734 Row() { 735 TextInput({ placeholder: this.placeHolderStr, text: this.inputValue }) 736 .id('phoneInput') 737 .padding({ 738 left: SystemUtils.isRTL() ? 739 Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_RIGHT : Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_LEFT, 740 right: SystemUtils.isRTL() ? 741 Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_LEFT : Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_RIGHT 742 }) 743 .enabled(this.isTextInputEnabled) 744 .constraintSize({ minHeight: Constants.SHARE_TEXT_INPUT_HEIGHT }) 745 .focusable(this.isTextInputEnabled) 746 .defaultFocus(false) 747 .direction(Direction.Ltr) 748 .textAlign(SystemUtils.isRTL() ? TextAlign.End : TextAlign.Start) 749 .type(InputType.PhoneNumber) 750 .enterKeyType(EnterKeyType.Next) 751 .onSubmit(() => { 752 this.beginShareEncrypt(); 753 }) 754 .border(this.phoneFormatTips ? 755 { width: Constants.DIALOG_MD_OFFSET, color: $r('sys.color.ohos_id_color_warning') } : { width: 0 }) 756 .onChange((value: string) => { 757 HiLog.info(TAG, `input length: ${value.length}`); 758 this.inputValue = value; 759 this.getShowAccountAssociationList(value); 760 this.phoneFormatTips = false; 761 this.isConfirmButtonEnabled = value.length > 0; 762 if (this.tempInputValue !== this.inputValue) { 763 this.tempInputValue = ''; 764 this.isFromContactsPicker = false; 765 } 766 }) 767 if (this.contactExists) { 768 Column() { 769 SymbolGlyph($r('sys.symbol.person_2')) 770 .fontSize(`${Constants.SYMBOL_GLYPH_FONT_SIZE}vp`) 771 .fontColor(this.isTextInputEnabled ? [$r('sys.color.icon_primary')] : 772 [$r('sys.color.icon_tertiary')]) 773 } 774 .accessibilityText(this.contactPerson) 775 .enabled(this.isTextInputEnabled) 776 .offset({ 777 x: SystemUtils.isRTL() 778 ? Constants.SHARE_CONTACTS_GROUP_OFFSET_X_RTL : Constants.SHARE_CONTACTS_GROUP_OFFSET_X, 779 y: Constants.SHARE_CONTACTS_GROUP_OFFSET_Y 780 }) 781 .onClick(this.contactsAction) 782 .bindSheet(this.isShowSheet, this.contactsPicker(), { 783 height: SheetSize.LARGE, 784 dragBar: false, 785 showClose: true, 786 onWillDisappear: () => { 787 this.isShowSheet = false; 788 }, 789 backgroundColor: Color.Transparent, 790 blurStyle: BlurStyle.COMPONENT_ULTRA_THICK 791 }) 792 } 793 } 794 795 Stack({ alignContent: Alignment.TopStart }) { 796 Text(this.phoneFormatTips ? 797 this.credentialCallBackMsg : $r('app.string.Share_Enter_Mobile_Number', this.osVersion)) 798 .fontColor(this.phoneFormatTips ? 799 $r('sys.color.ohos_id_color_warning') : $r('sys.color.ohos_id_color_text_secondary')) 800 .fontSize($r('sys.float.ohos_id_text_size_body3')) 801 .fontWeight(FontWeight.Regular) 802 .margin({ top: Constants.ENCRYPTION_ADD_STAFF_BORDER_MARGIN_TOP }) 803 if (this.showAssociationList.length) { 804 this.AssociationList(); 805 } 806 } 807 .backgroundColor(Color.Transparent) 808 .width(Constants.CONTACTS_PICKER_WIDTH) 809 .padding({ left: Constants.SHARE_TITLE_HEAD_PADDING_LEFT }) 810 } 811 .margin({ left: Constants.SHARE_TEXT_INPUT_MARGIN_LEFT, right: Constants.SHARE_TEXT_INPUT_MARGIN_RIGHT }) 812 } 813 .align(Alignment.TopStart) 814 .constraintSize({ 815 minHeight: `${this.scrollHeight}vp` 816 }) 817 .padding({ 818 top: Constants.SHARE_TITLE_HEAD_MARGIN_BOTTOM 819 }) 820 821 Column() { 822 Button($r('app.string.Share_Confirms'), { type: ButtonType.Capsule, stateEffect: true }) 823 .enabled(this.isConfirmButtonEnabled) 824 .backgroundColor($r('sys.color.ohos_id_color_text_primary_activated')) 825 .width(Constants.SHARE_BUTTON_WIDTH) 826 .controlSize(ControlSize.NORMAL) 827 .onClick(() => { 828 this.beginShareEncrypt(); 829 }) 830 } 831 .justifyContent(FlexAlign.Center) 832 .margin({ 833 left: Constants.SHARE_BUTTON_MARGIN_LEFT, 834 right: Constants.SHARE_BUTTON_MARGIN_RIGHT, 835 bottom: Constants.SHARE_BUTTON_PADDING_BOTTOM 836 }) 837 .constraintSize({ 838 minHeight: `${Constants.SHARE_BUTTON_COLUMN_BOTTOM}vp` 839 }) 840 } 841 .width(Constants.SHARE_PAGES_COLUMN_WIDTH) 842 .height(Constants.SHARE_PAGES_COLUMN_HEIGHT) 843 844 if (this.showUIExtensionForAccountLogin) { 845 UIExtensionComponent({ 846 bundleName: Constants.DLP_CREDMGR_BUNDLE_NAME, 847 abilityName: Constants.DLP_CREDMGR_ACCOUNT_ABILITY_NAME, 848 parameters: { 849 'ability.want.params.uiExtensionType': 'sys/commonUI' 850 } 851 }) 852 .id('cloudAccountLoginUI') 853 .onRemoteReady(() => { 854 try { 855 HiLog.info(TAG, 'cloudAccountLoginUI requestFocus start'); 856 this.getUIContext().getFocusController().requestFocus('cloudAccountLoginUI'); 857 } catch (error) { 858 HiLog.wrapError(TAG, error, 'requestFocus failed'); 859 } 860 }) 861 .onReceive((data) => { 862 HiLog.info(TAG, `data.status: ${JSON.stringify(data.status)}`); 863 let res = data.result as Record<string, string>; 864 HiLog.info(TAG, `res.code: ${JSON.stringify(res.code)}`); 865 if (data.status) { 866 this.ownerAccount = res?.uid; 867 this.ownerAccountID = res?.uid; 868 let isPhoneNumber = this.isPhoneNumber(this.inputValue); 869 HiLog.info(TAG, `isPhoneNumber: ${isPhoneNumber}`); 870 if (isPhoneNumber) { 871 this.isTextInputEnabled = false; 872 this.isConfirmButtonEnabled = false; 873 this.connectService.connectServiceShareAbility(Constants.COMMAND_SEARCH_USER_INFO); 874 } else { 875 this.enableComponents(); 876 } 877 } else { 878 this.enableComponents(); 879 if (['12300001', '1001502005', '1001502009'].includes(res.code.toString())) { 880 showToast(this.getUIContext(), 881 EncryptSharingHelper.getShowErr(EncryptSharingShowCodeEnum.NETWORK_ERROR)); 882 } 883 } 884 this.showUIExtensionForAccountLogin = false; 885 }) 886 .size({ 887 width: Constants.SHARE_PAGES_COLUMN_WIDTH, height: Constants.SHARE_PAGES_COLUMN_HEIGHT 888 }) 889 } 890 } 891 .onSizeChange((oldValue: SizeOptions, newValue: SizeOptions) => { 892 const newHeight = newValue.height as number; 893 const oldHeight = oldValue.height as number; 894 if (newHeight === oldHeight) { 895 return; 896 } 897 this.getScrollHeight(newValue); 898 }) 899 } 900} 901 902@Concurrent 903async function generateDlpFile(newFilePath: string, inputUri: string, 904 dlpProperty: dlpPermission.DLPProperty): Promise<GenerateDlpFileResult> { 905 const TAG = 'GenerateDlpFile'; 906 let error: BusinessError | undefined = undefined; 907 let inFile: fs.File | undefined = undefined; 908 let newFile: fs.File | undefined = undefined; 909 let result: GenerateDlpFileResult = { 910 inFileFd: -1, 911 dlpFileFd: -1, 912 error: undefined 913 } 914 915 try { 916 inFile = fs.openSync(inputUri, fs.OpenMode.READ_ONLY); 917 newFile = fs.openSync(newFilePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE); 918 await dlpPermission.generateDLPFile(inFile.fd, newFile.fd, dlpProperty); 919 } catch (err) { 920 HiLog.wrapError(TAG, error, 'generateDlpFile failed'); 921 error = err; 922 if (newFile?.fd && newFile?.fd >= Constants.NORMAL_FD_MIN) { 923 fs.closeSync(newFile.fd); 924 fs.unlinkSync(newFilePath); 925 } 926 } finally { 927 result = { 928 inFileFd: inFile?.fd ?? Constants.ERR_CODE_OPEN_FILE_ERROR, 929 dlpFileFd: (newFile?.fd === undefined || error === undefined) ? Constants.ERR_CODE_OPEN_FILE_ERROR : newFile?.fd, 930 error: error === undefined ? undefined : JSON.stringify(error) 931 } 932 } 933 return result; 934}