• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 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
16import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession';
17import { EditableLeftIconType } from '@ohos.arkui.advanced.EditableTitleBar';
18import { EditableTitleBar } from '@ohos.arkui.advanced.EditableTitleBar';
19import ConnectService from '../common/share/ConnectService';
20import { LengthMetrics } from '@ohos.arkui.node';
21import dlpPermission from '@ohos.dlpPermission';
22import promptAction from '@ohos.promptAction';
23import { BusinessError } from '@ohos.base';
24import Want from '@ohos.app.ability.Want';
25import fs from '@ohos.file.fs';
26import systemParameterEnhance from '@ohos.systemParameterEnhance';
27import emitter from '@ohos.events.emitter';
28import { HiLog } from '../common/HiLog';
29import Constants from '../common/constant';
30import FileUtils, { FileMsg } from '../common/FileUtils';
31import {
32  getFileUriByPath,
33  getFileFd,
34  sendDlpFileCreateProperties,
35  getFileSizeByUri,
36  sendDlpManagerAccountLogin,
37  getAppId,
38  getOsAccountInfo,
39  getConnectionStatus
40} from '../common/utils';
41import { SystemUtils } from '../common/systemUtils';
42import FontSizeScale from '../common/FontSizeScale';
43import { contact } from '@kit.ContactsKit';
44import deviceInfo from '@ohos.deviceInfo';
45
46const TAG = 'Share';
47
48class Test {
49  public '0': number = 0;
50  public '1': number = 0;
51  public '4': string = '';
52}
53
54class AuthUserList {
55  public authAccount: string = '';
56  public authAccountType: number = 0;
57  public dlpFileAccess: number = 0;
58  public permExpiryTime: number = 0;
59}
60
61let defaultDlpProperty: dlpPermission.DLPProperty = {
62  ownerAccount: '',
63  ownerAccountType: dlpPermission.AccountType.CLOUD_ACCOUNT,
64  authUserList: [],
65  contactAccount: '',
66  offlineAccess: true,
67  ownerAccountID: '',
68  everyoneAccessList: []
69};
70
71let storage = LocalStorage.getShared();
72
73@Entry(storage)
74@Component
75struct encryptedSharing {
76  static readonly GET_ACCOUNT_INFO_RESET = 'clear';
77  private connectService: ConnectService = new ConnectService(getContext(this));
78  private isPhoneDevice: boolean = true;
79  @State titlebarMargin: LocalizedMargin = {
80    start: LengthMetrics.vp(Constants.SHARE_TITLE_HEAD_MARGIN_RIGHT),
81    end: LengthMetrics.vp(Constants.SHARE_TITLE_HEAD_MARGIN_RIGHT),
82  };
83  @LocalStorageLink('commandSearchUserInfo') @Watch('beginToGenerateDLPFile') isInputInvalid: string = '';
84  @LocalStorageLink('commandGetAccountInfo') @Watch('changeAccountInfo') commandGetAccountInfo: string = '';
85  @State dlpProperty: dlpPermission.DLPProperty = defaultDlpProperty;
86  @State enabledFocus: boolean = true;
87  @State isConfirmButtonEnabled: boolean = false;
88  @State isShowSheet: boolean = false;
89  @State showUIExtensionForAccountLogin: boolean = false;
90  @State actionWant: Want | undefined = storage.get<Want>('actionWant');
91  @State inputValue: string = '';
92  @State phoneFormatTips: boolean = false;
93  @State ownerAccount: string = '';
94  @State ownerAccountID: string = '';
95  @State contactExists: boolean = true;
96  @State credentialCallBackMsg: string | Resource = '';
97  @State session: UIExtensionContentSession | undefined =
98    storage === undefined ? undefined : storage.get<UIExtensionContentSession>('session');
99  @State placeHolderStr: ResourceStr = '';
100  @State contactPerson: string = '';
101  @State recordSuccessUid: string = '';
102  @State osVersion: ResourceStr = '';
103  @State isTextInputFocus: boolean = false;
104  @State generalType: string = 'general.file';
105  @State scrollHeight: number = 188;
106
107  @Builder
108  contactsPicker() {
109    Column() {
110      UIExtensionComponent({
111        bundleName: 'com.ohos.contacts',
112        abilityName: 'ContactUiExtentionAbility',
113        parameters: {
114          'ability.want.params.uiExtensionType': 'sys/commonUI',
115          'targetUrl': 'BatchSelectContactsPage',
116          'isContactMultiSelect': false,
117        }
118      })
119        .onRelease((code) => {
120        })
121        .onResult((data) => {
122        })
123        .onReceive((data) => {
124          try {
125            let params: [] = JSON.parse((data.want as Want)?.parameters?.contactObjects as string);
126            for (let i = 0; i < params.length; i++) {
127              this.inputValue = (params[i] as Record<string, string>)?.telephone;
128            }
129          } catch (error) {
130            HiLog.error(TAG, `json parse exception, error is ${JSON.stringify(error)}`);
131          }
132          this.isShowSheet = false;
133        })
134        .width(Constants.CONTACTS_PICKER_WIDTH)
135        .height(Constants.CONTACTS_PICKER_HEIGHT)
136        .hitTestBehavior(HitTestMode.Block)
137    }
138    .width(Constants.CONTACTS_PICKER_WIDTH)
139    .height(Constants.CONTACTS_PICKER_HEIGHT)
140  }
141
142  contactsAction = () => {
143    if (this.isPhoneDevice) {
144      contact.selectContacts({ isMultiSelect: false }).then((data) => {
145        HiLog.info(TAG, 'Succeeded in selecting Contacts.');
146        if (!data || !data.length) {
147          HiLog.error(TAG, 'Contacts data is empty.');
148          return;
149        }
150        const phoneNumbers = data[0].phoneNumbers;
151        if (!phoneNumbers || !phoneNumbers.length) {
152          HiLog.error(TAG, 'Contacts phoneNumbers is empty.');
153          return;
154        }
155        const phoneNumber = phoneNumbers[0].phoneNumber;
156        if (phoneNumber) {
157          this.inputValue = phoneNumber;
158        }
159      }).catch((error: BusinessError) => {
160        HiLog.error(TAG, `Failed to select Contacts, Code: ${error.code}, message: ${error.message}`);
161      });
162    } else {
163      this.isShowSheet = !this.isShowSheet;
164    }
165  }
166
167  private async beginShareEncrypt() {
168    HiLog.info(TAG, `begin Share Encrypt start`);
169    if (this.checkCloudPhone(this.inputValue)) {
170      this.enabledFocus = false;
171      this.isConfirmButtonEnabled = false;
172      this.getAccountInfo();
173    }
174  }
175
176  async getAccountInfo() {
177    HiLog.info(TAG, `get Account Info start`);
178    if (await getConnectionStatus() === false) {
179      this.showToast($r('app.string.network_invalid'));
180      this.enabledFocus = true;
181      this.isConfirmButtonEnabled = this.inputValue.length > 0;
182      this.isTextInputFocus = true;
183      this.textInputGetFocus();
184      return;
185    }
186    try {
187      let accountInfo = await getOsAccountInfo();
188      if (accountInfo.distributedInfo.name === 'ohosAnonymousName' &&
189        accountInfo.distributedInfo.id === 'ohosAnonymousUid') {
190        this.showUIExtensionForAccountLogin = true;
191        return;
192      }
193      if (accountInfo.distributedInfo.id !== this.recordSuccessUid) {
194        HiLog.info(TAG, `COMMAND_GET_ACCOUNT_INFO start`);
195        this.connectService.connectServiceShareAbility(Constants.COMMAND_GET_ACCOUNT_INFO);
196        this.recordSuccessUid = accountInfo.distributedInfo.id;
197        return;
198      } else {
199        this.connectService.connectServiceShareAbility(Constants.COMMAND_SEARCH_USER_INFO);
200      }
201    } catch (err) {
202      HiLog.error(TAG, `getOsAccountInfo failed: ${JSON.stringify(err)}`);
203      this.showToast($r('app.string.Share_File_Encrypted_Failed'));
204      this.enabledFocus = true;
205      this.isConfirmButtonEnabled = this.inputValue.length > 0;
206    }
207  }
208
209  changeAccountInfo() {
210    if (this.commandGetAccountInfo === encryptedSharing.GET_ACCOUNT_INFO_RESET) {
211      return;
212    }
213    HiLog.info(TAG, `changeAccountInfo start`);
214    try {
215      let commandGetAccountInfoCallBack = JSON.parse(this.commandGetAccountInfo) as Record<string, object>;
216      HiLog.info(TAG, `commandGetAccountInfo Call Back errorCode: ${commandGetAccountInfoCallBack.errorCode}`);
217      let res = commandGetAccountInfoCallBack.result as Record<string, string>;
218      if (Number(commandGetAccountInfoCallBack.errorCode) === Constants.ERR_CODE_SUCCESS && res?.uid) {
219        this.ownerAccount = res?.uid;
220        this.ownerAccountID = res?.uid;
221        this.connectService.connectServiceShareAbility(Constants.COMMAND_SEARCH_USER_INFO);
222        return;
223      } else {
224        this.enabledFocus = true;
225        this.isConfirmButtonEnabled = true;
226        this.recordSuccessUid = '';
227        this.commandGetAccountInfo = encryptedSharing.GET_ACCOUNT_INFO_RESET;
228        if ([
229          Constants.ERR_CODE_NETWORK_ERROR,
230          Constants.ERR_CODE_CONNECTION_FAIL,
231          Constants.ERR_CODE_CONNECTION_TIME_OUT
232        ].includes(Number(commandGetAccountInfoCallBack.errorCode))) {
233          this.showToast($r('app.string.network_invalid'));
234        } else {
235          this.showToast($r('app.string.Share_File_Encrypted_Failed'));
236        }
237        return;
238      }
239    } catch (error) {
240      HiLog.error(TAG, `get account info failed: ${JSON.stringify(error)}`);
241      this.showToast($r('app.string.Share_File_Encrypted_Failed'));
242      this.enabledFocus = true;
243      this.isConfirmButtonEnabled = true;
244      this.recordSuccessUid = '';
245      this.commandGetAccountInfo = encryptedSharing.GET_ACCOUNT_INFO_RESET;
246      return;
247    }
248  }
249
250  checkCloudPhone(phone: string): boolean {
251    if (!phone) {
252      return false;
253    }
254    let reg = /^(?:(?:\+|00)86)?1[3456789]\d{9}$/;
255    if (!(reg.test(phone))) {
256      HiLog.info(TAG, `Please enter the phone.`);
257      this.phoneFormatTips = true;
258      this.credentialCallBackMsg = $r('app.string.Share_Tips_Phone_Format');
259      HiLog.info(TAG, `phoneFormatTips: ${this.phoneFormatTips}`);
260      return false;
261    }
262    reg = /^(0086|\+86)/;
263    let formatPhone = this.inputValue.replace(reg, '');
264    let cloudPhone = `${Constants.INTERNATIONAL_DIALING_CODE}${formatPhone}`;
265    AppStorage.setOrCreate('cloudPhone', cloudPhone);
266    return true;
267  }
268
269  async beginToGenerateDLPFile() {
270    HiLog.info(TAG, `beginToGenerateDLPFile start`);
271    if (!this.isInputValid()) {
272      this.enabledFocus = true;
273      this.isConfirmButtonEnabled = true;
274      return;
275    }
276    let parameters = this.actionWant?.parameters as Record<string, Array<string>>;
277    let inputUri: string = parameters['ability.params.stream'][0];
278    let inputFileName: string | null = this.getFileName(parameters, inputUri);
279    if (inputFileName === null) {
280      storage.setOrCreate('commandSearchUserInfo', '');
281      this.showToast($r('app.string.Share_File_Encrypted_Failed'));
282      this.enabledFocus = true;
283      this.isConfirmButtonEnabled = true;
284      return;
285    }
286    let dlpFileName: string = inputFileName + '.dlp';
287    let inFileFd = getFileFd(inputUri);
288    let srcFileSize: number = await getFileSizeByUri(inputUri);
289    AppStorage.setOrCreate('hiFileSize', srcFileSize);
290    let filePath = getContext(this).filesDir + `/Share/${new Date().getTime()}/`;
291    try {
292      await fs.mkdir(filePath, true);
293    } catch (error) {
294      HiLog.error(TAG, `mkdir failed: ${JSON.stringify(error)}`);
295    }
296    let newFilePath = filePath + dlpFileName;
297    let file: fs.File | undefined;
298    let filePathUri = getFileUriByPath(newFilePath);
299    try {
300      file = fs.openSync(newFilePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
301      await dlpPermission.generateDLPFile(inFileFd, file.fd, this.dlpProperty);
302      this.showToast($r('app.string.Share_File_Encrypted_Success'));
303      let dstFileSize: number = await getFileSizeByUri(filePathUri);
304      AppStorage.setOrCreate('hiPolicySizeEnc', dstFileSize);
305      AppStorage.setOrCreate('hiCode', 201);
306      sendDlpFileCreateProperties(dlpPermission.AccountType.CLOUD_ACCOUNT); // 201: DLP_2C_FILE_CREATE_EVENT
307      this.backToPages(filePathUri, dlpFileName);
308      HiLog.info(TAG, `beginToGenerateDLPFile success`);
309    } catch (err) {
310      HiLog.error(TAG, `open temp failed: ${JSON.stringify(err)}`);
311      HiLog.info(TAG, `generateDLPFile file failed: ${JSON.stringify(err)}`);
312      storage.setOrCreate('commandSearchUserInfo', '');
313      if (err.code === Constants.SHARE_FILE_NAME_TOO_LONG) {
314        this.showToast($r('app.string.Share_File_Name_Too_Long'));
315        return;
316      }
317      this.showToast($r('app.string.Share_File_Encrypted_Failed'));
318      this.enabledFocus = true;
319      this.isConfirmButtonEnabled = true;
320    } finally {
321      if (file) {
322        fs.closeSync(file);
323      }
324    }
325  }
326
327  getFileName(parameters: Record<string, Array<string>>, inputUri: string): string | null {
328    let abilityPickerRecords = parameters['ability.picker.records'];
329    let srcFileMsg: FileMsg = FileUtils.getSuffixFileMsgByUri(inputUri);
330    AppStorage.setOrCreate('hiFileType', srcFileMsg.fileType);
331    let res: string = '';
332    Object.keys(abilityPickerRecords).forEach(key => {
333      this.generalType = key;
334      res = abilityPickerRecords[key][0]?.['4'];
335    });
336    if (res === undefined) {
337      try {
338        res = decodeURIComponent(srcFileMsg.fileName + srcFileMsg.fileType);
339      } catch (error) {
340        HiLog.error(TAG, `getFileName failed: ${error?.message}`);
341        return null;
342      }
343    }
344    return res;
345  }
346
347  showToast(msg: Resource) {
348    promptAction.showToast({
349      message: msg,
350      duration: Constants.SHARE_SET_TIMEOUT
351    });
352  }
353
354  backToPages(filePathUri: string, dlpFileName: string) {
355    HiLog.info(TAG, `backToPages start: ${dlpFileName}`);
356    if (this.actionWant && this.actionWant.parameters) {
357      this.actionWant.parameters['ability.params.stream'] = [filePathUri];
358      let arr: Test[] = [
359        {
360          '0': 0,
361          '1': 0,
362          '4': dlpFileName
363        }
364      ];
365      let generalFile: Record<string, Test[]> = {};
366      generalFile[this.generalType] = arr;
367      this.actionWant.parameters['ability.picker.records'] = generalFile;
368      setTimeout(() => {
369        this.session!.terminateSelfWithResult({
370          resultCode: 2,
371          want: this.actionWant
372        });
373      }, Constants.SHARE_SET_TIMEOUT)
374    }
375  }
376
377  isInputValid(): boolean {
378    if (!this.isInputInvalid) {
379      return false;
380    }
381    let credentialCallBack = JSON.parse(this.isInputInvalid) as Record<string, string>;
382    HiLog.info(TAG, `credential Call Back errorCode: ${credentialCallBack.errorCode}`);
383    if (!credentialCallBack.status && Number(credentialCallBack.errorCode) === Constants.ERR_CODE_SUCCESS) {
384      HiLog.info(TAG, `credentialCallBack msg`);
385      let str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'no_user');
386      this.credentialCallBackMsg = str.length > 0 ? str : credentialCallBack.errorMsg;
387      this.phoneFormatTips = true;
388      storage.setOrCreate('commandSearchUserInfo', '');
389      return false;
390    }
391    if (!credentialCallBack.status && [
392      Constants.ERR_CODE_NETWORK_ERROR,
393      Constants.ERR_CODE_CONNECTION_FAIL,
394      Constants.ERR_CODE_CONNECTION_TIME_OUT
395    ].includes(Number(credentialCallBack.errorCode))) {
396      this.showToast($r('app.string.network_invalid'));
397      storage.setOrCreate('commandSearchUserInfo', '');
398      return false;
399    }
400    if (!credentialCallBack.status && Number(credentialCallBack.errorCode) !== Constants.ERR_CODE_SUCCESS) {
401      this.showToast($r('app.string.Share_File_Encrypted_Failed'));
402      storage.setOrCreate('commandSearchUserInfo', '');
403      return false;
404    }
405    let authUserList: AuthUserList[] = [
406      {
407        'authAccount': credentialCallBack.userIdCipher,
408        'authAccountType': 1,
409        'dlpFileAccess': 1,
410        'permExpiryTime': Date.UTC(9999, 1, 1),
411      }
412    ];
413    this.dlpProperty = {
414      'ownerAccount': this.ownerAccount,
415      'ownerAccountID': this.ownerAccountID,
416      'ownerAccountType': 1,
417      'authUserList': authUserList,
418      'contactAccount': this.ownerAccount,
419      'offlineAccess': true,
420    }
421    return true;
422  }
423
424  async checkContacts() {
425    let callerBundleName = 'com.ohos.contacts';
426    try {
427      await getAppId(callerBundleName);
428      this.contactExists = true;
429    } catch {
430      this.contactExists = false;
431    }
432  }
433
434  async getLoginStatus() {
435    HiLog.info(TAG, `get login status start.`);
436    if (await getConnectionStatus() === false) {
437      this.showToast($r('app.string.network_invalid'));
438      this.enabledFocus = true;
439      this.isConfirmButtonEnabled = this.inputValue.length > 0;
440      this.isTextInputFocus = true;
441      this.textInputGetFocus();
442      return;
443    }
444    try {
445      let accountInfo = await getOsAccountInfo();
446      if (accountInfo.distributedInfo.name === 'ohosAnonymousName' &&
447        accountInfo.distributedInfo.id === 'ohosAnonymousUid') {
448        this.showUIExtensionForAccountLogin = true;
449      } else {
450        this.isTextInputFocus = true;
451        this.textInputGetFocus();
452      }
453    } catch (err) {
454      HiLog.error(TAG, `getOsAccountInfo failed: ${JSON.stringify(err)}`);
455    }
456  }
457
458
459  onLanguageChange() {
460    let str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'add_users_hint');
461    if (str.length > 0) {
462      this.placeHolderStr = str;
463    }
464    str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'no_user');
465    if (str.length > 0) {
466      this.credentialCallBackMsg = str;
467    }
468    str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'hmos_version_label');
469    if (str.length > 0) {
470      this.osVersion = str;
471    }
472    this.getContactPersonString();
473  }
474
475  subscribeLanguageChange() {
476    emitter.on('onConfigurationUpdate', () => {
477      this.onLanguageChange();
478    })
479  }
480
481  getExternalResourceString(bundle: string, module: string, resourceName: string): string {
482    try {
483      let ctx = getContext().createModuleContext(bundle, module);
484      HiLog.info(TAG, 'getExternalResourceString get context from: ' + ctx.applicationInfo.name);
485      let str = ctx.resourceManager.getStringByNameSync(resourceName);
486      return str;
487    } catch (e) {
488      let error = e as BusinessError;
489      HiLog.error(TAG, 'getExternalResourceString error: ' + error.code + ' ' + error.message);
490      return '';
491    }
492  }
493
494  getContactPersonString() {
495    try {
496      getContext().resourceManager.getStringValue($r('app.string.Share_Contact_Person').id,
497        (error: BusinessError, value: string) => {
498          if (error === undefined || error === null) {
499            this.contactPerson = value;
500          } else {
501            HiLog.error(TAG, `error is ${JSON.stringify(error)}`);
502          }
503      });
504    } catch (error) {
505      HiLog.error(TAG, `callback getStringValue failed, error ${JSON.stringify(error)}`);
506    }
507  }
508
509  private getSettingItemPadding(): number {
510    const FONT_SIZE_SCALE_PARAM = 'persist.sys.font_scale_for_user0';
511    let fontSizeScale = Number.parseFloat(systemParameterEnhance.getSync(FONT_SIZE_SCALE_PARAM, '1'));
512    switch (fontSizeScale) {
513      case FontSizeScale.XXL1:
514        return Constants.GET_SETTING_ITEM_XXL1;
515      case FontSizeScale.XXL2:
516        return Constants.GET_SETTING_ITEM_XXL2;
517      case FontSizeScale.XXL3:
518        return Constants.GET_SETTING_ITEM_XXL3;
519      default:
520        return Constants.GET_SETTING_ITEM_DEFAULT;
521    }
522  }
523
524  clearHistoryDLPFile() {
525    let pathDir = getContext(this).filesDir + '/Share';
526    fs.listFile(pathDir).then((filenames: Array<string>) => {
527      HiLog.info(TAG, `listFile succeed`);
528      let filenamesLists = filenames.sort((a, b) => Number(a) - Number(b));
529      if (filenamesLists.length > Constants.SHARE_TEMP_SAVE_FILE_NUMBER) {
530        let deleteArray = filenamesLists.slice(0, filenamesLists.length - Constants.SHARE_TEMP_SAVE_FILE_NUMBER);
531        deleteArray.forEach((item) => {
532          fs.rmdirSync(pathDir + `/${item}`);
533        })
534      }
535    }).catch((err: BusinessError) => {
536      HiLog.error(TAG, `list file failed with error message: ${JSON.stringify(err)}`);
537    });
538  }
539
540  textInputGetFocus() {
541    setTimeout(() => {
542      try {
543        HiLog.info(TAG, `delay requestFocus start`);
544        this.getUIContext().getFocusController().requestFocus('phoneInput');
545      } catch (error) {
546        HiLog.error(TAG, `requestFocus failed. Cause: ${JSON.stringify(error)}`);
547      }
548    }, Constants.ENCRYPTION_SET_TIMEOUT_TIME);
549  }
550
551  getScrollHeight(newValue: SizeOptions) {
552    const height = newValue.height as number;
553    this.scrollHeight = height - Constants.SHARE_BUTTON_COLUMN_BOTTOM - Constants.SHARE_TITLE_HEAD_HEIGHT -
554    Constants.SHARE_TITLE_HEAD_MARGIN_TOP - Constants.SHARE_TITLE_HEAD_MARGIN_BOTTOM;
555    if (this.scrollHeight > Constants.SHARE_TEXTAREA_MAX_HEIGHT) {
556      this.scrollHeight = Constants.SHARE_TEXTAREA_MAX_HEIGHT;
557    }
558  }
559
560  aboutToAppear() {
561    HiLog.info(TAG, `aboutToAppear enter: ${this.showUIExtensionForAccountLogin}`);
562    this.isPhoneDevice = deviceInfo.deviceType === 'phone';
563    this.getLoginStatus();
564    AppStorage.setOrCreate('hiAccountType', dlpPermission.AccountType.CLOUD_ACCOUNT);
565    sendDlpManagerAccountLogin(-1);
566    this.checkContacts();
567    this.clearHistoryDLPFile();
568    this.subscribeLanguageChange();
569    let str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'add_users_hint');
570    this.placeHolderStr = str.length > 0 ? str : '';
571    str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'hmos_version_label');
572    this.osVersion = str.length > 0 ? str : '';
573    this.getContactPersonString();
574  }
575
576  build() {
577    Stack() {
578      Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start }) {
579        EditableTitleBar({
580          leftIconStyle: EditableLeftIconType.Back,
581          title: $r('app.string.Share_Add_Viewable_Users'),
582          contentMargin: this.titlebarMargin,
583          menuItems: [
584            {
585              value: $r('sys.media.ohos_ic_public_cancel'),
586              isEnabled: true,
587              label: $r('app.string.ban'),
588              action: () => {
589                if (this.session !== undefined) {
590                  this.session.terminateSelfWithResult({
591                    'resultCode': 1,
592                  });
593                }
594              }
595            }
596          ],
597          isSaveIconRequired: false,
598          onCancel: () => {
599            if (this.session !== undefined) {
600              this.session.terminateSelfWithResult({
601                'resultCode': 0,
602              });
603            }
604          },
605        })
606          .constraintSize({ minHeight: Constants.SHARE_TITLE_HEAD_HEIGHT })
607          .margin({
608            top: Constants.SHARE_TITLE_HEAD_MARGIN_TOP,
609          })
610        Scroll() {
611          Column() {
612            Row() {
613              TextInput({ placeholder: this.placeHolderStr, text: this.inputValue })
614                .id('phoneInput')
615                .padding({
616                  left: SystemUtils.isRTL() ?
617                    Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_RIGHT : Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_LEFT,
618                  right: SystemUtils.isRTL() ?
619                    Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_LEFT : Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_RIGHT
620                })
621                .enabled(this.enabledFocus)
622                .constraintSize({ minHeight: Constants.SHARE_TEXT_INPUT_HEIGHT })
623                .focusable(this.isTextInputFocus)
624                .defaultFocus(false)
625                .direction(SystemUtils.isRTL() ? Direction.Rtl : Direction.Ltr)
626                .type(InputType.PhoneNumber)
627                .contentType(ContentType.PHONE_COUNTRY_CODE)
628                .enterKeyType(EnterKeyType.NEW_LINE)
629                .border(this.phoneFormatTips ?
630                  { width: Constants.DIALOG_MD_OFFSET, color: $r('sys.color.ohos_id_color_warning') } : { width: 0 })
631                .onChange((value: string) => {
632                  HiLog.info(TAG, `input length: ${value.length}`);
633                  this.inputValue = value;
634                  this.phoneFormatTips = false;
635                  this.isConfirmButtonEnabled = value.length > 0;
636                })
637              if (this.contactExists) {
638                Column() {
639                  SymbolGlyph($r('sys.symbol.person_2'))
640                    .fontSize(`${Constants.SYMBOL_GLYPH_FONT_SIZE}vp`)
641                    .fontColor(this.enabledFocus ? [$r('sys.color.icon_primary')] : [$r('sys.color.icon_tertiary')])
642                }
643                .accessibilityText(this.contactPerson)
644                .enabled(this.enabledFocus)
645                .offset({
646                  x: SystemUtils.isRTL()
647                    ? Constants.SHARE_CONTACTS_GROUP_OFFSET_X_RTL : Constants.SHARE_CONTACTS_GROUP_OFFSET_X,
648                  y: Constants.SHARE_CONTACTS_GROUP_OFFSET_Y
649                })
650                .onClick(this.contactsAction)
651                .bindSheet(this.isShowSheet, this.contactsPicker(), {
652                  height: SheetSize.LARGE,
653                  dragBar: false,
654                  showClose: true,
655                  onWillDisappear: () => {
656                    this.isShowSheet = false;
657                  },
658                  backgroundColor: Color.Transparent,
659                  blurStyle: BlurStyle.COMPONENT_ULTRA_THICK
660                })
661              }
662            }
663
664            Text(this.phoneFormatTips ?
665              this.credentialCallBackMsg : $r('app.string.Share_Enter_Mobile_Number', this.osVersion))
666              .fontColor(this.phoneFormatTips ?
667              $r('sys.color.ohos_id_color_warning') : $r('sys.color.ohos_id_color_text_secondary'))
668              .fontSize($r('sys.float.ohos_id_text_size_body3'))
669              .fontWeight(FontWeight.Regular)
670              .margin({ top: Constants.ENCRYPTION_ADD_STAFF_BORDER_MARGIN_TOP })
671              .backgroundColor(Color.Transparent)
672              .width(Constants.CONTACTS_PICKER_WIDTH)
673              .padding({ left: Constants.SHARE_TITLE_HEAD_PADDING_LEFT })
674          }
675          .margin({ left: Constants.SHARE_TEXT_INPUT_MARGIN_LEFT, right: Constants.SHARE_TEXT_INPUT_MARGIN_RIGHT })
676        }
677        .align(Alignment.TopStart)
678        .constraintSize({
679          minHeight: `${this.scrollHeight}vp`
680        })
681        .padding({
682          top: Constants.SHARE_TITLE_HEAD_MARGIN_BOTTOM
683        })
684        Column() {
685          Button($r('app.string.Share_Confirms'), { type: ButtonType.Capsule, stateEffect: true })
686            .enabled(this.isConfirmButtonEnabled)
687            .backgroundColor($r('sys.color.ohos_id_color_text_primary_activated'))
688            .width(Constants.SHARE_BUTTON_WIDTH)
689            .controlSize(ControlSize.NORMAL)
690            .onClick(async () => {
691              this.beginShareEncrypt();
692            })
693        }
694        .justifyContent(FlexAlign.Center)
695        .margin({
696          left: Constants.SHARE_BUTTON_MARGIN_LEFT,
697          right: Constants.SHARE_BUTTON_MARGIN_RIGHT,
698          bottom: Constants.SHARE_BUTTON_PADDING_BOTTOM
699        })
700        .constraintSize({
701          minHeight: `${Constants.SHARE_BUTTON_COLUMN_BOTTOM}vp`
702        })
703      }
704      .width(Constants.SHARE_PAGES_COLUMN_WIDTH)
705      .height(Constants.SHARE_PAGES_COLUMN_HEIGHT)
706      if (this.showUIExtensionForAccountLogin) {
707        UIExtensionComponent({
708          bundleName: 'com.huawei.hmos.dlpcredmgr',
709          abilityName: 'DlpCredAccountAbility',
710          parameters: {
711            'ability.want.params.uiExtensionType': 'sys/commonUI'
712          }
713        })
714          .id('cloudAccountLoginUI')
715          .onRemoteReady(() => {
716            try {
717              HiLog.info(TAG, `cloudAccountLoginUI requestFocus start`);
718              this.getUIContext().getFocusController().requestFocus('cloudAccountLoginUI');
719            } catch (error) {
720              HiLog.error(TAG, `requestFocus failed. Cause: ${JSON.stringify(error)}`)
721            }
722          })
723          .onReceive((data) => {
724            HiLog.info(TAG, `data.status: ${JSON.stringify(data.status)}`);
725            let res = data.result as Record<string, string>;
726            HiLog.info(TAG, `res.code: ${JSON.stringify(res.code)}`);
727            if (data.status) {
728              this.ownerAccount = res?.uid;
729              this.ownerAccountID = res?.uid;
730              let checkCloudPhone = this.checkCloudPhone(this.inputValue);
731              HiLog.info(TAG, `checkCloudPhone: ${checkCloudPhone}`);
732              if (checkCloudPhone) {
733                this.enabledFocus = false;
734                this.isConfirmButtonEnabled = false;
735                this.connectService.connectServiceShareAbility(Constants.COMMAND_SEARCH_USER_INFO);
736              }
737            } else {
738              this.enabledFocus = true;
739              this.isConfirmButtonEnabled = this.inputValue.length > 0;
740              if (['12300001', '1001502005', '1001502009'].includes(res.code.toString())) {
741                this.showToast($r('app.string.network_invalid'));
742              }
743            }
744            this.isTextInputFocus = true;
745            this.textInputGetFocus();
746            this.showUIExtensionForAccountLogin = false;
747          })
748          .size({
749            width: Constants.SHARE_PAGES_COLUMN_WIDTH, height: Constants.SHARE_PAGES_COLUMN_HEIGHT
750          })
751      }
752    }
753    .onSizeChange((oldValue: SizeOptions, newValue: SizeOptions) => {
754      const newHeight = newValue.height as number;
755      const oldHeight = oldValue.height as number;
756      if (newHeight === oldHeight) {
757        return;
758      }
759      this.getScrollHeight(newValue);
760    })
761  }
762}