• 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 = this.getFileName(parameters, inputUri);
279    let dlpFileName = decodeURIComponent(inputFileName) + '.dlp';
280    let inFileFd = getFileFd(inputUri);
281    let srcFileSize: number = await getFileSizeByUri(inputUri);
282    AppStorage.setOrCreate('hiFileSize', srcFileSize);
283    let filePath = getContext(this).filesDir + `/Share/${new Date().getTime()}/`;
284    try {
285      await fs.mkdir(filePath, true);
286    } catch (error) {
287      HiLog.error(TAG, `mkdir failed: ${JSON.stringify(error)}`);
288    }
289    let newFilePath = filePath + dlpFileName;
290    let file: fs.File | undefined;
291    let filePathUri = getFileUriByPath(newFilePath);
292    try {
293      file = fs.openSync(newFilePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
294      await dlpPermission.generateDLPFile(inFileFd, file.fd, this.dlpProperty);
295      this.showToast($r('app.string.Share_File_Encrypted_Success'));
296      let dstFileSize: number = await getFileSizeByUri(filePathUri);
297      AppStorage.setOrCreate('hiPolicySizeEnc', dstFileSize);
298      AppStorage.setOrCreate('hiCode', 201);
299      sendDlpFileCreateProperties(dlpPermission.AccountType.CLOUD_ACCOUNT); // 201: DLP_2C_FILE_CREATE_EVENT
300      this.backToPages(filePathUri, dlpFileName);
301      HiLog.info(TAG, `beginToGenerateDLPFile success`);
302    } catch (err) {
303      HiLog.error(TAG, `open temp failed: ${JSON.stringify(err)}`);
304      HiLog.info(TAG, `generateDLPFile file failed: ${JSON.stringify(err)}`);
305      storage.setOrCreate('commandSearchUserInfo', '');
306      if (err.code === Constants.SHARE_FILE_NAME_TOO_LONG) {
307        this.showToast($r('app.string.Share_File_Name_Too_Long'));
308        return;
309      }
310      this.showToast($r('app.string.Share_File_Encrypted_Failed'));
311      this.enabledFocus = true;
312      this.isConfirmButtonEnabled = true;
313    } finally {
314      if (file) {
315        fs.closeSync(file);
316      }
317    }
318  }
319
320  getFileName(parameters: Record<string, Array<string>>, inputUri: string): string {
321    let abilityPickerRecords = parameters['ability.picker.records'];
322    let srcFileMsg: FileMsg = FileUtils.getSuffixFileMsgByUri(inputUri);
323    AppStorage.setOrCreate('hiFileType', srcFileMsg.fileType);
324    let res: string = '';
325    Object.keys(abilityPickerRecords).forEach(key => {
326      this.generalType = key;
327      res = abilityPickerRecords[key][0]?.['4'];
328    });
329    if (res === undefined) {
330      res = srcFileMsg.fileName + srcFileMsg.fileType;
331    }
332    return res;
333  }
334
335  showToast(msg: Resource) {
336    promptAction.showToast({
337      message: msg,
338      duration: Constants.SHARE_SET_TIMEOUT
339    });
340  }
341
342  backToPages(filePathUri: string, dlpFileName: string) {
343    HiLog.info(TAG, `backToPages start: ${dlpFileName}`);
344    if (this.actionWant && this.actionWant.parameters) {
345      this.actionWant.parameters['ability.params.stream'] = [filePathUri];
346      let arr: Test[] = [
347        {
348          '0': 0,
349          '1': 0,
350          '4': dlpFileName
351        }
352      ];
353      let generalFile: Record<string, Test[]> = {};
354      generalFile[this.generalType] = arr;
355      this.actionWant.parameters['ability.picker.records'] = generalFile;
356      setTimeout(() => {
357        this.session!.terminateSelfWithResult({
358          resultCode: 2,
359          want: this.actionWant
360        });
361      }, Constants.SHARE_SET_TIMEOUT)
362    }
363  }
364
365  isInputValid(): boolean {
366    if (!this.isInputInvalid) {
367      return false;
368    }
369    let credentialCallBack = JSON.parse(this.isInputInvalid) as Record<string, string>;
370    HiLog.info(TAG, `credential Call Back errorCode: ${credentialCallBack.errorCode}`);
371    if (!credentialCallBack.status && Number(credentialCallBack.errorCode) === Constants.ERR_CODE_SUCCESS) {
372      HiLog.info(TAG, `credentialCallBack msg`);
373      let str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'no_user');
374      this.credentialCallBackMsg = str.length > 0 ? str : credentialCallBack.errorMsg;
375      this.phoneFormatTips = true;
376      storage.setOrCreate('commandSearchUserInfo', '');
377      return false;
378    }
379    if (!credentialCallBack.status && [
380      Constants.ERR_CODE_NETWORK_ERROR,
381      Constants.ERR_CODE_CONNECTION_FAIL,
382      Constants.ERR_CODE_CONNECTION_TIME_OUT
383    ].includes(Number(credentialCallBack.errorCode))) {
384      this.showToast($r('app.string.network_invalid'));
385      storage.setOrCreate('commandSearchUserInfo', '');
386      return false;
387    }
388    if (!credentialCallBack.status && Number(credentialCallBack.errorCode) !== Constants.ERR_CODE_SUCCESS) {
389      this.showToast($r('app.string.Share_File_Encrypted_Failed'));
390      storage.setOrCreate('commandSearchUserInfo', '');
391      return false;
392    }
393    let authUserList: AuthUserList[] = [
394      {
395        'authAccount': credentialCallBack.userIdCipher,
396        'authAccountType': 1,
397        'dlpFileAccess': 1,
398        'permExpiryTime': Date.UTC(9999, 1, 1),
399      }
400    ];
401    this.dlpProperty = {
402      'ownerAccount': this.ownerAccount,
403      'ownerAccountID': this.ownerAccountID,
404      'ownerAccountType': 1,
405      'authUserList': authUserList,
406      'contactAccount': this.ownerAccount,
407      'offlineAccess': true,
408    }
409    return true;
410  }
411
412  async checkContacts() {
413    let callerBundleName = 'com.ohos.contacts';
414    try {
415      await getAppId(callerBundleName);
416      this.contactExists = true;
417    } catch {
418      this.contactExists = false;
419    }
420  }
421
422  async getLoginStatus() {
423    HiLog.info(TAG, `get login status start.`);
424    if (await getConnectionStatus() === false) {
425      this.showToast($r('app.string.network_invalid'));
426      this.enabledFocus = true;
427      this.isConfirmButtonEnabled = this.inputValue.length > 0;
428      this.isTextInputFocus = true;
429      this.textInputGetFocus();
430      return;
431    }
432    try {
433      let accountInfo = await getOsAccountInfo();
434      if (accountInfo.distributedInfo.name === 'ohosAnonymousName' &&
435        accountInfo.distributedInfo.id === 'ohosAnonymousUid') {
436        this.showUIExtensionForAccountLogin = true;
437      } else {
438        this.isTextInputFocus = true;
439        this.textInputGetFocus();
440      }
441    } catch (err) {
442      HiLog.error(TAG, `getOsAccountInfo failed: ${JSON.stringify(err)}`);
443    }
444  }
445
446
447  onLanguageChange() {
448    let str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'add_users_hint');
449    if (str.length > 0) {
450      this.placeHolderStr = str;
451    }
452    str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'no_user');
453    if (str.length > 0) {
454      this.credentialCallBackMsg = str;
455    }
456    str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'hmos_version_label');
457    if (str.length > 0) {
458      this.osVersion = str;
459    }
460    this.getContactPersonString();
461  }
462
463  subscribeLanguageChange() {
464    emitter.on('onConfigurationUpdate', () => {
465      this.onLanguageChange();
466    })
467  }
468
469  getExternalResourceString(bundle: string, module: string, resourceName: string): string {
470    try {
471      let ctx = getContext().createModuleContext(bundle, module);
472      HiLog.info(TAG, 'getExternalResourceString get context from: ' + ctx.applicationInfo.name);
473      let str = ctx.resourceManager.getStringByNameSync(resourceName);
474      return str;
475    } catch (e) {
476      let error = e as BusinessError;
477      HiLog.error(TAG, 'getExternalResourceString error: ' + error.code + ' ' + error.message);
478      return '';
479    }
480  }
481
482  getContactPersonString() {
483    try {
484      getContext().resourceManager.getStringValue($r('app.string.Share_Contact_Person').id,
485        (error: BusinessError, value: string) => {
486          if (error === undefined || error === null) {
487            this.contactPerson = value;
488          } else {
489            HiLog.error(TAG, `error is ${JSON.stringify(error)}`);
490          }
491      });
492    } catch (error) {
493      HiLog.error(TAG, `callback getStringValue failed, error ${JSON.stringify(error)}`);
494    }
495  }
496
497  private getSettingItemPadding(): number {
498    const FONT_SIZE_SCALE_PARAM = 'persist.sys.font_scale_for_user0';
499    let fontSizeScale = Number.parseFloat(systemParameterEnhance.getSync(FONT_SIZE_SCALE_PARAM, '1'));
500    switch (fontSizeScale) {
501      case FontSizeScale.XXL1:
502        return Constants.GET_SETTING_ITEM_XXL1;
503      case FontSizeScale.XXL2:
504        return Constants.GET_SETTING_ITEM_XXL2;
505      case FontSizeScale.XXL3:
506        return Constants.GET_SETTING_ITEM_XXL3;
507      default:
508        return Constants.GET_SETTING_ITEM_DEFAULT;
509    }
510  }
511
512  clearHistoryDLPFile() {
513    let pathDir = getContext(this).filesDir + '/Share';
514    fs.listFile(pathDir).then((filenames: Array<string>) => {
515      HiLog.info(TAG, `listFile succeed`);
516      let filenamesLists = filenames.sort((a, b) => Number(a) - Number(b));
517      if (filenamesLists.length > Constants.SHARE_TEMP_SAVE_FILE_NUMBER) {
518        let deleteArray = filenamesLists.slice(0, filenamesLists.length - Constants.SHARE_TEMP_SAVE_FILE_NUMBER);
519        deleteArray.forEach((item) => {
520          fs.rmdirSync(pathDir + `/${item}`);
521        })
522      }
523    }).catch((err: BusinessError) => {
524      HiLog.error(TAG, `list file failed with error message: ${JSON.stringify(err)}`);
525    });
526  }
527
528  textInputGetFocus() {
529    setTimeout(() => {
530      try {
531        HiLog.info(TAG, `delay requestFocus start`);
532        this.getUIContext().getFocusController().requestFocus('phoneInput');
533      } catch (error) {
534        HiLog.error(TAG, `requestFocus failed. Cause: ${JSON.stringify(error)}`);
535      }
536    }, Constants.ENCRYPTION_SET_TIMEOUT_TIME);
537  }
538
539  getScrollHeight(newValue: SizeOptions) {
540    const height = newValue.height as number;
541    this.scrollHeight = height - Constants.SHARE_BUTTON_COLUMN_BOTTOM - Constants.SHARE_TITLE_HEAD_HEIGHT -
542    Constants.SHARE_TITLE_HEAD_MARGIN_TOP - Constants.SHARE_TITLE_HEAD_MARGIN_BOTTOM;
543    if (this.scrollHeight > Constants.SHARE_TEXTAREA_MAX_HEIGHT) {
544      this.scrollHeight = Constants.SHARE_TEXTAREA_MAX_HEIGHT;
545    }
546  }
547
548  aboutToAppear() {
549    HiLog.info(TAG, `aboutToAppear enter: ${this.showUIExtensionForAccountLogin}`);
550    this.isPhoneDevice = deviceInfo.deviceType === 'phone';
551    this.getLoginStatus();
552    AppStorage.setOrCreate('hiAccountType', dlpPermission.AccountType.CLOUD_ACCOUNT);
553    sendDlpManagerAccountLogin(-1);
554    this.checkContacts();
555    this.clearHistoryDLPFile();
556    this.subscribeLanguageChange();
557    let str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'add_users_hint');
558    this.placeHolderStr = str.length > 0 ? str : '';
559    str = this.getExternalResourceString(Constants.DLP_CREDMGR_BUNDLE_NAME, 'entry', 'hmos_version_label');
560    this.osVersion = str.length > 0 ? str : '';
561    this.getContactPersonString();
562  }
563
564  build() {
565    Stack() {
566      Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start }) {
567        EditableTitleBar({
568          leftIconStyle: EditableLeftIconType.Back,
569          title: $r('app.string.Share_Add_Viewable_Users'),
570          contentMargin: this.titlebarMargin,
571          menuItems: [
572            {
573              value: $r('sys.media.ohos_ic_public_cancel'),
574              isEnabled: true,
575              label: $r('app.string.ban'),
576              action: () => {
577                if (this.session !== undefined) {
578                  this.session.terminateSelfWithResult({
579                    'resultCode': 1,
580                  });
581                }
582              }
583            }
584          ],
585          isSaveIconRequired: false,
586          onCancel: () => {
587            if (this.session !== undefined) {
588              this.session.terminateSelfWithResult({
589                'resultCode': 0,
590              });
591            }
592          },
593        })
594          .constraintSize({ minHeight: Constants.SHARE_TITLE_HEAD_HEIGHT })
595          .margin({
596            top: Constants.SHARE_TITLE_HEAD_MARGIN_TOP,
597          })
598        Scroll() {
599          Column() {
600            Row() {
601              TextInput({ placeholder: this.placeHolderStr, text: this.inputValue })
602                .id('phoneInput')
603                .padding({
604                  left: SystemUtils.isRTL() ?
605                    Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_RIGHT : Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_LEFT,
606                  right: SystemUtils.isRTL() ?
607                    Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_LEFT : Constants.SHARE_TEXT_INPUT_CONTENT_PADDING_RIGHT
608                })
609                .enabled(this.enabledFocus)
610                .constraintSize({ minHeight: Constants.SHARE_TEXT_INPUT_HEIGHT })
611                .focusable(this.isTextInputFocus)
612                .direction(Direction.Ltr)
613                .textAlign(SystemUtils.isRTL() ? TextAlign.End : TextAlign.Start)
614                .type(InputType.PhoneNumber)
615                .contentType(ContentType.PHONE_COUNTRY_CODE)
616                .enterKeyType(EnterKeyType.NEW_LINE)
617                .border(this.phoneFormatTips ?
618                  { width: Constants.DIALOG_MD_OFFSET, color: $r('sys.color.ohos_id_color_warning') } : { width: 0 })
619                .onChange((value: string) => {
620                  HiLog.info(TAG, `input length: ${value.length}`);
621                  this.inputValue = value;
622                  this.phoneFormatTips = false;
623                  this.isConfirmButtonEnabled = value.length > 0;
624                })
625              if (this.contactExists) {
626                Column() {
627                  SymbolGlyph($r('sys.symbol.person_2'))
628                    .fontSize(`${Constants.SYMBOL_GLYPH_FONT_SIZE}vp`)
629                    .fontColor(this.enabledFocus ? [$r('sys.color.icon_primary')] : [$r('sys.color.icon_tertiary')])
630                }
631                .accessibilityText(this.contactPerson)
632                .enabled(this.enabledFocus)
633                .offset({
634                  x: SystemUtils.isRTL()
635                    ? Constants.SHARE_CONTACTS_GROUP_OFFSET_X_RTL : Constants.SHARE_CONTACTS_GROUP_OFFSET_X,
636                  y: Constants.SHARE_CONTACTS_GROUP_OFFSET_Y
637                })
638                .onClick(this.contactsAction)
639                .bindSheet(this.isShowSheet, this.contactsPicker(), {
640                  height: SheetSize.LARGE,
641                  dragBar: false,
642                  showClose: true,
643                  onWillDisappear: () => {
644                    this.isShowSheet = false;
645                  },
646                  backgroundColor: Color.Transparent,
647                  blurStyle: BlurStyle.COMPONENT_ULTRA_THICK
648                })
649              }
650            }
651
652            Text(this.phoneFormatTips ?
653              this.credentialCallBackMsg : $r('app.string.Share_Enter_Mobile_Number', this.osVersion))
654              .fontColor(this.phoneFormatTips ?
655              $r('sys.color.ohos_id_color_warning') : $r('sys.color.ohos_id_color_text_secondary'))
656              .fontSize($r('sys.float.ohos_id_text_size_body3'))
657              .fontWeight(FontWeight.Regular)
658              .margin({ top: Constants.ENCRYPTION_ADD_STAFF_BORDER_MARGIN_TOP })
659              .backgroundColor(Color.Transparent)
660              .width(Constants.CONTACTS_PICKER_WIDTH)
661              .padding({ left: Constants.SHARE_TITLE_HEAD_PADDING_LEFT })
662          }
663          .margin({ left: Constants.SHARE_TEXT_INPUT_MARGIN_LEFT, right: Constants.SHARE_TEXT_INPUT_MARGIN_RIGHT })
664        }
665        .align(Alignment.TopStart)
666        .constraintSize({
667          minHeight: `${this.scrollHeight}vp`
668        })
669        .padding({
670          top: Constants.SHARE_TITLE_HEAD_MARGIN_BOTTOM
671        })
672        Column() {
673          Button($r('app.string.Share_Confirms'), { type: ButtonType.Capsule, stateEffect: true })
674            .enabled(this.isConfirmButtonEnabled)
675            .backgroundColor($r('sys.color.ohos_id_color_text_primary_activated'))
676            .width(Constants.SHARE_BUTTON_WIDTH)
677            .controlSize(ControlSize.NORMAL)
678            .onClick(async () => {
679              this.beginShareEncrypt();
680            })
681        }
682        .justifyContent(FlexAlign.Center)
683        .margin({
684          left: Constants.SHARE_BUTTON_MARGIN_LEFT,
685          right: Constants.SHARE_BUTTON_MARGIN_RIGHT,
686          bottom: Constants.SHARE_BUTTON_PADDING_BOTTOM
687        })
688        .constraintSize({
689          minHeight: `${Constants.SHARE_BUTTON_COLUMN_BOTTOM}vp`
690        })
691      }
692      .width(Constants.SHARE_PAGES_COLUMN_WIDTH)
693      .height(Constants.SHARE_PAGES_COLUMN_HEIGHT)
694      if (this.showUIExtensionForAccountLogin) {
695        UIExtensionComponent({
696          bundleName: 'com.huawei.hmos.dlpcredmgr',
697          abilityName: 'DlpCredAccountAbility',
698          parameters: {
699            'ability.want.params.uiExtensionType': 'sys/commonUI'
700          }
701        })
702          .id('cloudAccountLoginUI')
703          .onRemoteReady(() => {
704            try {
705              HiLog.info(TAG, `cloudAccountLoginUI requestFocus start`);
706              this.getUIContext().getFocusController().requestFocus('cloudAccountLoginUI');
707            } catch (error) {
708              HiLog.error(TAG, `requestFocus failed. Cause: ${JSON.stringify(error)}`)
709            }
710          })
711          .onReceive((data) => {
712            HiLog.info(TAG, `data.status: ${JSON.stringify(data.status)}`);
713            let res = data.result as Record<string, string>;
714            HiLog.info(TAG, `res.code: ${JSON.stringify(res.code)}`);
715            if (data.status) {
716              this.ownerAccount = res?.uid;
717              this.ownerAccountID = res?.uid;
718              let checkCloudPhone = this.checkCloudPhone(this.inputValue);
719              HiLog.info(TAG, `checkCloudPhone: ${checkCloudPhone}`);
720              if (checkCloudPhone) {
721                this.enabledFocus = false;
722                this.isConfirmButtonEnabled = false;
723                this.connectService.connectServiceShareAbility(Constants.COMMAND_SEARCH_USER_INFO);
724              }
725            } else {
726              this.enabledFocus = true;
727              this.isConfirmButtonEnabled = this.inputValue.length > 0;
728              if (['12300001', '1001502005', '1001502009'].includes(res.code.toString())) {
729                this.showToast($r('app.string.network_invalid'));
730              }
731            }
732            this.isTextInputFocus = true;
733            this.textInputGetFocus();
734            this.showUIExtensionForAccountLogin = false;
735          })
736          .size({
737            width: Constants.SHARE_PAGES_COLUMN_WIDTH, height: Constants.SHARE_PAGES_COLUMN_HEIGHT
738          })
739      }
740    }
741    .onSizeChange((oldValue: SizeOptions, newValue: SizeOptions) => {
742      const newHeight = newValue.height as number;
743      const oldHeight = oldValue.height as number;
744      if (newHeight === oldHeight) {
745        return;
746      }
747      this.getScrollHeight(newValue);
748    })
749  }
750}