199da06d0Sopenharmony_ci/**
299da06d0Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
399da06d0Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
499da06d0Sopenharmony_ci * you may not use this file except in compliance with the License.
599da06d0Sopenharmony_ci * You may obtain a copy of the License at
699da06d0Sopenharmony_ci *
799da06d0Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
899da06d0Sopenharmony_ci *
999da06d0Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1099da06d0Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1199da06d0Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1299da06d0Sopenharmony_ci * See the License for the specific language governing permissions and
1399da06d0Sopenharmony_ci * limitations under the License.
1499da06d0Sopenharmony_ci */
1599da06d0Sopenharmony_ci
1699da06d0Sopenharmony_ciimport { addAirPlaneModeListener, removeAirPlaneModeListener, turnOffAirPlaneMode } from '../common/utils/AirplaneMode';
1799da06d0Sopenharmony_ciimport ConfirmDialogEx from './dialog/ConfirmDialogEx';
1899da06d0Sopenharmony_ciimport mediaQuery from '@ohos.mediaquery';
1999da06d0Sopenharmony_ciimport wantAgent from '@ohos.wantAgent';
2099da06d0Sopenharmony_ciimport ContactCard from '../common/components/ContactCard';
2199da06d0Sopenharmony_ciimport FuncBtnGroup from '../common/components/FuncBtnGroup';
2299da06d0Sopenharmony_ciimport BottomBtn from '../common/components/BottomBtn';
2399da06d0Sopenharmony_ciimport Keyboard from '../common/components/Keyboard';
2499da06d0Sopenharmony_ciimport IncomingCom from '../common/components/IncomingCom';
2599da06d0Sopenharmony_ciimport CallStateConst from '../common/constant/CallStateConst';
2699da06d0Sopenharmony_ciimport LogUtils from '../common/utils/LogUtils';
2799da06d0Sopenharmony_ciimport byTrace from '@ohos.bytrace'
2899da06d0Sopenharmony_ciimport NotificationManager from '../model/NotificationManager';
2999da06d0Sopenharmony_ciimport CallManager from '../model/CallManager';
3099da06d0Sopenharmony_ciimport backgroundTaskManager from '@ohos.backgroundTaskManager';
3199da06d0Sopenharmony_ciimport * as Constants from '../common/utils/Constants';
3299da06d0Sopenharmony_ciimport CallDataManager from '../model/CallDataManager';
3399da06d0Sopenharmony_ciimport MultiContactCard from '../common/components/MultiContactCard';
3499da06d0Sopenharmony_ciimport DefaultCallData from '../common/struct/TypeUtils'
3599da06d0Sopenharmony_ciimport CallListStruct from '../common/struct/CallListStruct'
3699da06d0Sopenharmony_ciimport CallTimeListStruct from '../common/struct/CallTimeListStruct'
3799da06d0Sopenharmony_ci
3899da06d0Sopenharmony_ciconst AIRPLANE_MODE = 1;
3999da06d0Sopenharmony_ciconst TAG = 'Index';
4099da06d0Sopenharmony_ci
4199da06d0Sopenharmony_ci/**
4299da06d0Sopenharmony_ci * @file: Main interface
4399da06d0Sopenharmony_ci */
4499da06d0Sopenharmony_ci@Entry
4599da06d0Sopenharmony_ci@Component
4699da06d0Sopenharmony_cistruct Index {
4799da06d0Sopenharmony_ci  @State callData: DefaultCallData = new DefaultCallData();
4899da06d0Sopenharmony_ci  @State isShowKeyboard: boolean = false;
4999da06d0Sopenharmony_ci  @State callList: Array<CallListStruct> = [];
5099da06d0Sopenharmony_ci  @State callTimeList: Array<CallTimeListStruct> = [];
5199da06d0Sopenharmony_ci  private notificationManager: NotificationManager;
5299da06d0Sopenharmony_ci  private mCallDataManager: CallDataManager;
5399da06d0Sopenharmony_ci  @State curBp: string = 'md';
5499da06d0Sopenharmony_ci  @State incomingData: DefaultCallData = new DefaultCallData();
5599da06d0Sopenharmony_ci  @State mHangup: boolean = false;
5699da06d0Sopenharmony_ci  private smListener: mediaQuery.MediaQueryListener;
5799da06d0Sopenharmony_ci  private mdListener: mediaQuery.MediaQueryListener;
5899da06d0Sopenharmony_ci  private lgListener: mediaQuery.MediaQueryListener;
5999da06d0Sopenharmony_ci  private isNeedCallAgain: boolean = false;
6099da06d0Sopenharmony_ci
6199da06d0Sopenharmony_ci  airplaneModeDialogController: CustomDialogController = new CustomDialogController({
6299da06d0Sopenharmony_ci    //Components shared by the TIP for creating or updating a contact by mistake and the TIP for deleting a contact
6399da06d0Sopenharmony_ci    builder: ConfirmDialogEx({
6499da06d0Sopenharmony_ci      cancel: () => {
6599da06d0Sopenharmony_ci        this.airplaneModeDialogController.close();
6699da06d0Sopenharmony_ci        LogUtils.i(TAG, 'dial airplaneModeDialogController cancel');
6799da06d0Sopenharmony_ci        this.mCallDataManager.clearCall(this.callData);
6899da06d0Sopenharmony_ci      },
6999da06d0Sopenharmony_ci      confirm: () => {
7099da06d0Sopenharmony_ci        LogUtils.i(TAG, 'dial airplaneModeDialogController confirm');
7199da06d0Sopenharmony_ci        this.airplaneModeDialogController.close();
7299da06d0Sopenharmony_ci        this.isNeedCallAgain = true;
7399da06d0Sopenharmony_ci        turnOffAirPlaneMode();
7499da06d0Sopenharmony_ci      },
7599da06d0Sopenharmony_ci      title: $r('app.string.turn_off_airplane_and_calls'),
7699da06d0Sopenharmony_ci      cancelText: $r('app.string.cancel'),
7799da06d0Sopenharmony_ci      confirmText: $r('app.string.shutDown')
7899da06d0Sopenharmony_ci    }),
7999da06d0Sopenharmony_ci    autoCancel: false,
8099da06d0Sopenharmony_ci    alignment: DialogAlignment.Bottom,
8199da06d0Sopenharmony_ci    offset: { dx: 0, dy: -16 }
8299da06d0Sopenharmony_ci  });
8399da06d0Sopenharmony_ci
8499da06d0Sopenharmony_ci  isBreakpointSM = (mediaQueryResult) => {
8599da06d0Sopenharmony_ci    if (mediaQueryResult.matches) {
8699da06d0Sopenharmony_ci      this.curBp = 'sm'
8799da06d0Sopenharmony_ci      AppStorage.SetOrCreate('curBp', this.curBp)
8899da06d0Sopenharmony_ci    }
8999da06d0Sopenharmony_ci  }
9099da06d0Sopenharmony_ci  isBreakpointMD = (mediaQueryResult) => {
9199da06d0Sopenharmony_ci    if (mediaQueryResult.matches) {
9299da06d0Sopenharmony_ci      this.curBp = 'md'
9399da06d0Sopenharmony_ci      AppStorage.SetOrCreate('curBp', this.curBp)
9499da06d0Sopenharmony_ci    }
9599da06d0Sopenharmony_ci  }
9699da06d0Sopenharmony_ci  isBreakpointLG = (mediaQueryResult) => {
9799da06d0Sopenharmony_ci    if (mediaQueryResult.matches) {
9899da06d0Sopenharmony_ci      this.curBp = 'lg'
9999da06d0Sopenharmony_ci      AppStorage.SetOrCreate('curBp', this.curBp)
10099da06d0Sopenharmony_ci    }
10199da06d0Sopenharmony_ci  }
10299da06d0Sopenharmony_ci
10399da06d0Sopenharmony_ci  aboutToAppear(): void {
10499da06d0Sopenharmony_ci    LogUtils.i(TAG, 'aboutToAppear')
10599da06d0Sopenharmony_ci    byTrace.startTrace('aboutToAppear', 0);
10699da06d0Sopenharmony_ci    this.notificationManager = new NotificationManager();
10799da06d0Sopenharmony_ci    CallManager.getInstance()?.init(this);
10899da06d0Sopenharmony_ci    AppStorage.SetOrCreate<NotificationManager>('notificationManager', this.notificationManager);
10999da06d0Sopenharmony_ci    this.mCallDataManager = CallDataManager.getInstance();
11099da06d0Sopenharmony_ci    byTrace.finishTrace('aboutToAppear', 0);
11199da06d0Sopenharmony_ci    this.smListener = mediaQuery.matchMediaSync('(320vp<width<=520vp)');
11299da06d0Sopenharmony_ci    this.smListener?.on('change', this.isBreakpointSM);
11399da06d0Sopenharmony_ci    this.mdListener = mediaQuery.matchMediaSync('(520vp<width<=840vp)');
11499da06d0Sopenharmony_ci    this.mdListener?.on('change', this.isBreakpointMD);
11599da06d0Sopenharmony_ci    this.lgListener = mediaQuery.matchMediaSync('(840vp<width)');
11699da06d0Sopenharmony_ci    this.lgListener?.on('change', this.isBreakpointLG);
11799da06d0Sopenharmony_ci    this.initAirPlaneMode();
11899da06d0Sopenharmony_ci  }
11999da06d0Sopenharmony_ci
12099da06d0Sopenharmony_ci  onPageShow() {
12199da06d0Sopenharmony_ci    LogUtils.i(TAG, 'onPageShow');
12299da06d0Sopenharmony_ci    byTrace.startTrace('onPageShow', 1);
12399da06d0Sopenharmony_ci    globalThis.appInactiveState = false;
12499da06d0Sopenharmony_ci    this.stopBackgroundRunning();
12599da06d0Sopenharmony_ci    this.notificationManager?.cancelNotification();
12699da06d0Sopenharmony_ci    this.notificationManager?.sendCapsuleNotification(this.incomingData ? this.callData : this.incomingData, false);
12799da06d0Sopenharmony_ci    byTrace.finishTrace('onPageShow', 1);
12899da06d0Sopenharmony_ci    LogUtils.i(TAG, 'onPageShow end');
12999da06d0Sopenharmony_ci  }
13099da06d0Sopenharmony_ci
13199da06d0Sopenharmony_ci  initAirPlaneMode() {
13299da06d0Sopenharmony_ci    LogUtils.i(TAG, 'initAirPlaneMode');
13399da06d0Sopenharmony_ci    try {
13499da06d0Sopenharmony_ci      addAirPlaneModeListener((data) => {
13599da06d0Sopenharmony_ci        LogUtils.i(TAG, 'initAirPlaneMode callback');
13699da06d0Sopenharmony_ci        if (data == AIRPLANE_MODE) {
13799da06d0Sopenharmony_ci          this.airplaneModeDialogController.open();
13899da06d0Sopenharmony_ci          AppStorage.SetOrCreate('AirplaneMode', true);
13999da06d0Sopenharmony_ci        } else if (this.isNeedCallAgain) {
14099da06d0Sopenharmony_ci          this.isNeedCallAgain = false;
14199da06d0Sopenharmony_ci          AppStorage.SetOrCreate('AirplaneMode', false);
14299da06d0Sopenharmony_ci          this.mCallDataManager.clearCall(this.callData);
14399da06d0Sopenharmony_ci        }
14499da06d0Sopenharmony_ci      });
14599da06d0Sopenharmony_ci    } catch (err) {
14699da06d0Sopenharmony_ci      LogUtils.e(TAG, `initAirPlaneMode err = ${JSON.stringify(err)}`);
14799da06d0Sopenharmony_ci    }
14899da06d0Sopenharmony_ci  }
14999da06d0Sopenharmony_ci
15099da06d0Sopenharmony_ci  onPageHide() {
15199da06d0Sopenharmony_ci    LogUtils.i(TAG, 'onPageHide');
15299da06d0Sopenharmony_ci    if (!this.mHangup) {
15399da06d0Sopenharmony_ci      globalThis.appInactiveState = true;
15499da06d0Sopenharmony_ci      this.updateNotification();
15599da06d0Sopenharmony_ci    }
15699da06d0Sopenharmony_ci    LogUtils.i(TAG, 'onPageHide end');
15799da06d0Sopenharmony_ci  }
15899da06d0Sopenharmony_ci
15999da06d0Sopenharmony_ci  updateNotification() {
16099da06d0Sopenharmony_ci    const {callState, callId} = this.callData;
16199da06d0Sopenharmony_ci    if (callState !== CallStateConst.callStateObj.CALL_STATUS_DISCONNECTED && callId) {
16299da06d0Sopenharmony_ci      if (this.mCallDataManager.hasAliveCall()) {
16399da06d0Sopenharmony_ci        this.startBackgroundRunning();
16499da06d0Sopenharmony_ci      }
16599da06d0Sopenharmony_ci      this.notificationManager?.sendNotification(this.incomingData ? this.callData : this.incomingData);
16699da06d0Sopenharmony_ci      this.notificationManager?.sendCapsuleNotification(this.incomingData ? this.callData : this.incomingData, true);
16799da06d0Sopenharmony_ci    }
16899da06d0Sopenharmony_ci  }
16999da06d0Sopenharmony_ci
17099da06d0Sopenharmony_ci  aboutToDisappear() {
17199da06d0Sopenharmony_ci    LogUtils.i(TAG, 'aboutToDisappear');
17299da06d0Sopenharmony_ci    this.stopBackgroundRunning();
17399da06d0Sopenharmony_ci    this.smListener?.off('change', this.isBreakpointSM);
17499da06d0Sopenharmony_ci    this.mdListener?.off('change', this.isBreakpointMD);
17599da06d0Sopenharmony_ci    this.lgListener?.off('change', this.isBreakpointLG);
17699da06d0Sopenharmony_ci    removeAirPlaneModeListener();
17799da06d0Sopenharmony_ci    this.airplaneModeDialogController = null;
17899da06d0Sopenharmony_ci  }
17999da06d0Sopenharmony_ci
18099da06d0Sopenharmony_ci  onBackPress() {
18199da06d0Sopenharmony_ci    LogUtils.i(TAG, 'onBackPress');
18299da06d0Sopenharmony_ci  }
18399da06d0Sopenharmony_ci
18499da06d0Sopenharmony_ci  private stopBackgroundRunning() {
18599da06d0Sopenharmony_ci    backgroundTaskManager.stopBackgroundRunning(globalThis.calluiAbilityContext).then(() => {
18699da06d0Sopenharmony_ci      LogUtils.i(TAG, 'stopBackgroundRunning succeeded');
18799da06d0Sopenharmony_ci    }).catch((err) => {
18899da06d0Sopenharmony_ci      LogUtils.i(TAG, 'stopBackgroundRunning failed');
18999da06d0Sopenharmony_ci    });
19099da06d0Sopenharmony_ci  }
19199da06d0Sopenharmony_ci
19299da06d0Sopenharmony_ci  private startBackgroundRunning() {
19399da06d0Sopenharmony_ci    let wantAgentInfo = {
19499da06d0Sopenharmony_ci      wants: [{
19599da06d0Sopenharmony_ci        bundleName: Constants.CALL_BUNDLE_NAME,
19699da06d0Sopenharmony_ci        abilityName: Constants.CALL_ABILITY_NAME,
19799da06d0Sopenharmony_ci      }],
19899da06d0Sopenharmony_ci      requestCode: 0,
19999da06d0Sopenharmony_ci      operationType: wantAgent.OperationType.START_ABILITY,
20099da06d0Sopenharmony_ci      wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
20199da06d0Sopenharmony_ci    };
20299da06d0Sopenharmony_ci    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
20399da06d0Sopenharmony_ci      backgroundTaskManager.startBackgroundRunning(globalThis.calluiAbilityContext,
20499da06d0Sopenharmony_ci        backgroundTaskManager.BackgroundMode.VOIP, wantAgentObj).then(() => {
20599da06d0Sopenharmony_ci        LogUtils.i(TAG, 'startBackgroundRunning succeeded')
20699da06d0Sopenharmony_ci      }).catch((err) => {
20799da06d0Sopenharmony_ci        LogUtils.i(TAG, 'startBackgroundRunning failed cause:' + JSON.stringify(err))
20899da06d0Sopenharmony_ci      });
20999da06d0Sopenharmony_ci    });
21099da06d0Sopenharmony_ci  }
21199da06d0Sopenharmony_ci
21299da06d0Sopenharmony_ci  /**
21399da06d0Sopenharmony_ci   * method to control the display of DTMF keyboard
21499da06d0Sopenharmony_ci   *
21599da06d0Sopenharmony_ci   * parent component pass by value child component
21699da06d0Sopenharmony_ci   */
21799da06d0Sopenharmony_ci  public showKeyboard() {
21899da06d0Sopenharmony_ci    this.isShowKeyboard = !this.isShowKeyboard;
21999da06d0Sopenharmony_ci  }
22099da06d0Sopenharmony_ci
22199da06d0Sopenharmony_ci  /**
22299da06d0Sopenharmony_ci   * Call status
22399da06d0Sopenharmony_ci   *
22499da06d0Sopenharmony_ci   * @return {number} -  callState
22599da06d0Sopenharmony_ci   */
22699da06d0Sopenharmony_ci  private callState() {
22799da06d0Sopenharmony_ci    LogUtils.i(TAG, 'callState : ' + this.callData.callState)
22899da06d0Sopenharmony_ci    return this.callData.callState;
22999da06d0Sopenharmony_ci  }
23099da06d0Sopenharmony_ci
23199da06d0Sopenharmony_ci  /**
23299da06d0Sopenharmony_ci   * is Triple Call
23399da06d0Sopenharmony_ci   *
23499da06d0Sopenharmony_ci   * @return {boolean} - isTripleCall
23599da06d0Sopenharmony_ci   */
23699da06d0Sopenharmony_ci  private isTripleCall() {
23799da06d0Sopenharmony_ci    return (this.callData.callState === CallStateConst.callStateObj.CALL_STATUS_WAITING ||
23899da06d0Sopenharmony_ci      this.callData.callState === CallStateConst.callStateObj.CALL_STATUS_INCOMING) &&
23999da06d0Sopenharmony_ci      this.callList.length === 3;
24099da06d0Sopenharmony_ci  }
24199da06d0Sopenharmony_ci
24299da06d0Sopenharmony_ci  pageTransition() {
24399da06d0Sopenharmony_ci    PageTransitionEnter({ duration: 0 });
24499da06d0Sopenharmony_ci    PageTransitionExit({ duration: 0 });
24599da06d0Sopenharmony_ci  }
24699da06d0Sopenharmony_ci
24799da06d0Sopenharmony_ci  getBtnCallState() {
24899da06d0Sopenharmony_ci    if (this.callList.length > 1) {
24999da06d0Sopenharmony_ci      let btnState = true;
25099da06d0Sopenharmony_ci      this.callList.forEach((v) => {
25199da06d0Sopenharmony_ci        if (v.callState === CallStateConst.callStateObj.CALL_STATUS_WAITING ||
25299da06d0Sopenharmony_ci          v.callState === CallStateConst.callStateObj.CALL_STATUS_INCOMING) {
25399da06d0Sopenharmony_ci          btnState = false;
25499da06d0Sopenharmony_ci        }
25599da06d0Sopenharmony_ci      });
25699da06d0Sopenharmony_ci      return btnState;
25799da06d0Sopenharmony_ci    } else {
25899da06d0Sopenharmony_ci      return this.callState() !== CallStateConst.callStateObj.CALL_STATUS_WAITING &&
25999da06d0Sopenharmony_ci        this.callState() !== CallStateConst.callStateObj.CALL_STATUS_INCOMING
26099da06d0Sopenharmony_ci    }
26199da06d0Sopenharmony_ci  }
26299da06d0Sopenharmony_ci
26399da06d0Sopenharmony_ci  build() {
26499da06d0Sopenharmony_ci    Flex({
26599da06d0Sopenharmony_ci      direction: FlexDirection.Column,
26699da06d0Sopenharmony_ci      alignItems: ItemAlign.Center,
26799da06d0Sopenharmony_ci      justifyContent: FlexAlign.SpaceBetween
26899da06d0Sopenharmony_ci    }) {
26999da06d0Sopenharmony_ci      if (this.callList.length > 1) {
27099da06d0Sopenharmony_ci        MultiContactCard({
27199da06d0Sopenharmony_ci          callData: $callData,
27299da06d0Sopenharmony_ci          isShowKeyboard: this.isShowKeyboard,
27399da06d0Sopenharmony_ci          callList: $callList,
27499da06d0Sopenharmony_ci          incomingData: $incomingData
27599da06d0Sopenharmony_ci        })
27699da06d0Sopenharmony_ci          .margin({ top: this.isShowKeyboard ? 0 : 56 })
27799da06d0Sopenharmony_ci          .layoutWeight(this.isShowKeyboard ? 1 : 0)
27899da06d0Sopenharmony_ci      } else {
27999da06d0Sopenharmony_ci        ContactCard({
28099da06d0Sopenharmony_ci          callData: $callData,
28199da06d0Sopenharmony_ci          isShowKeyboard: this.isShowKeyboard,
28299da06d0Sopenharmony_ci          callList: $callList
28399da06d0Sopenharmony_ci        })
28499da06d0Sopenharmony_ci          .margin({ top: this.isShowKeyboard ? 0 : 56 })
28599da06d0Sopenharmony_ci          .layoutWeight(this.isShowKeyboard ? 1 : 0)
28699da06d0Sopenharmony_ci      }
28799da06d0Sopenharmony_ci
28899da06d0Sopenharmony_ci      if (this.getBtnCallState()) {
28999da06d0Sopenharmony_ci        Column() {
29099da06d0Sopenharmony_ci          if (!this.isShowKeyboard) {
29199da06d0Sopenharmony_ci            FuncBtnGroup({ callData: $callData, callList: $callList })
29299da06d0Sopenharmony_ci              .margin({ bottom: 32 })
29399da06d0Sopenharmony_ci          } else {
29499da06d0Sopenharmony_ci            Keyboard({ callData: $callData })
29599da06d0Sopenharmony_ci              .margin({ bottom: 32 })
29699da06d0Sopenharmony_ci          }
29799da06d0Sopenharmony_ci          BottomBtn({
29899da06d0Sopenharmony_ci            callData: $callData,
29999da06d0Sopenharmony_ci            callList: $callList,
30099da06d0Sopenharmony_ci            hangup: $mHangup,
30199da06d0Sopenharmony_ci            onItemClick: () => {
30299da06d0Sopenharmony_ci              this.showKeyboard()
30399da06d0Sopenharmony_ci            }
30499da06d0Sopenharmony_ci          })
30599da06d0Sopenharmony_ci        }
30699da06d0Sopenharmony_ci      } else {
30799da06d0Sopenharmony_ci        Column() {
30899da06d0Sopenharmony_ci          IncomingCom({
30999da06d0Sopenharmony_ci            callList: $callList,
31099da06d0Sopenharmony_ci            callData: $callData,
31199da06d0Sopenharmony_ci            hangup: $mHangup})
31299da06d0Sopenharmony_ci
31399da06d0Sopenharmony_ci          if (this.isTripleCall()) {
31499da06d0Sopenharmony_ci            Column() {
31599da06d0Sopenharmony_ci              Text($r('app.string.end_holding_call'))
31699da06d0Sopenharmony_ci                .fontColor('#FFFFFF')
31799da06d0Sopenharmony_ci                .fontSize(14)
31899da06d0Sopenharmony_ci                .height(19)
31999da06d0Sopenharmony_ci                .lineHeight(19)
32099da06d0Sopenharmony_ci            }
32199da06d0Sopenharmony_ci            .margin({ top: 16 })
32299da06d0Sopenharmony_ci          }
32399da06d0Sopenharmony_ci        }
32499da06d0Sopenharmony_ci      }
32599da06d0Sopenharmony_ci    }
32699da06d0Sopenharmony_ci    .padding({ bottom: this.isTripleCall() ? 71 : 106 })
32799da06d0Sopenharmony_ci    .width('100%')
32899da06d0Sopenharmony_ci    .height('100%')
32999da06d0Sopenharmony_ci    .backgroundImage('assets/picture/wallpaper.png', ImageRepeat.NoRepeat)
33099da06d0Sopenharmony_ci    .backgroundImageSize(ImageSize.Cover)
33199da06d0Sopenharmony_ci  }
33299da06d0Sopenharmony_ci}
333