• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}