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