1/** 2 * Copyright (c) 2023 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 { addAirPlaneModeListener, removeAirPlaneModeListener, turnOffAirPlaneMode } from '../common/utils/AirplaneMode'; 17import ConfirmDialogEx from './dialog/ConfirmDialogEx'; 18import EnvironmentProp from './dialog/EnvironmentProp'; 19import mediaQuery from '@ohos.mediaquery'; 20import wantAgent from '@ohos.wantAgent'; 21import ContactCard from '../common/components/ContactCard'; 22import FuncBtnGroup from '../common/components/FuncBtnGroup'; 23import BottomBtn from '../common/components/BottomBtn'; 24import Keyboard from '../common/components/Keyboard'; 25import IncomingCom from '../common/components/IncomingCom'; 26import CallStateConst from '../common/constant/CallStateConst'; 27import LogUtils from '../common/utils/LogUtils'; 28import byTrace from "@ohos.bytrace" 29import NotificationManager from '../model/NotificationManager'; 30import CallManager from '../model/CallManager'; 31import backgroundTaskManager from '@ohos.backgroundTaskManager'; 32import * as Constants from '../common/utils/Constants'; 33import CallDataManager from '../model/CallDataManager'; 34import MultiContactCard from '../common/components/MultiContactCard'; 35import DefaultCallData from '../common/struct/TypeUtils' 36import CallListStruct from '../common/struct/CallListStruct' 37import CallTimeListStruct from '../common/struct/CallTimeListStruct' 38 39const AIRPLANE_MODE = 1; 40const TAG = "Index"; 41 42/** 43 * @file: Main interface 44 */ 45@Entry 46@Component 47struct Index { 48 @State callData: DefaultCallData = new DefaultCallData(); 49 @State isShowKeyboard: boolean = false; 50 @State callList: Array<CallListStruct> = []; 51 @State callTimeList: Array<CallTimeListStruct> = []; 52 private notificationManager: NotificationManager; 53 private mCallDataManager: CallDataManager; 54 @State curBp: string = 'md'; 55 @State incomingData: DefaultCallData = new DefaultCallData(); 56 @State mHangup: boolean = false; 57 private smListener: mediaQuery.MediaQueryListener; 58 private mdListener: mediaQuery.MediaQueryListener; 59 private lgListener: mediaQuery.MediaQueryListener; 60 private isNeedCallAgain: boolean = false; 61 62 airplaneModeDialogController: CustomDialogController = new CustomDialogController({ 63 //Components shared by the TIP for creating or updating a contact by mistake and the TIP for deleting a contact 64 builder: ConfirmDialogEx({ 65 cancel: () => { 66 this.airplaneModeDialogController.close(); 67 LogUtils.i(TAG, 'dial airplaneModeDialogController cancel'); 68 this.mCallDataManager.clearCall(this.callData); 69 }, 70 confirm: () => { 71 LogUtils.i(TAG, 'dial airplaneModeDialogController confirm'); 72 this.airplaneModeDialogController.close(); 73 this.isNeedCallAgain = true; 74 turnOffAirPlaneMode(); 75 }, 76 title: $r('app.string.turn_off_airplane_and_calls'), 77 cancelText: $r('app.string.cancel'), 78 confirmText: $r('app.string.shutDown') 79 }), 80 autoCancel: false, 81 alignment: (EnvironmentProp.isTablet() ? DialogAlignment.Center : DialogAlignment.Bottom), 82 offset: { dx: 0, dy: -16 } 83 }); 84 85 isBreakpointSM = (mediaQueryResult) => { 86 if (mediaQueryResult.matches) { 87 this.curBp = 'sm' 88 AppStorage.SetOrCreate('curBp', this.curBp) 89 } 90 } 91 isBreakpointMD = (mediaQueryResult) => { 92 if (mediaQueryResult.matches) { 93 this.curBp = 'md' 94 AppStorage.SetOrCreate('curBp', this.curBp) 95 } 96 } 97 isBreakpointLG = (mediaQueryResult) => { 98 if (mediaQueryResult.matches) { 99 this.curBp = 'lg' 100 AppStorage.SetOrCreate('curBp', this.curBp) 101 } 102 } 103 104 aboutToAppear(): void { 105 LogUtils.i(TAG, "aboutToAppear") 106 byTrace.startTrace('aboutToAppear', 0); 107 this.notificationManager = new NotificationManager(); 108 CallManager.getInstance()?.init(this); 109 AppStorage.SetOrCreate<NotificationManager>('notificationManager', this.notificationManager); 110 this.mCallDataManager = CallDataManager.getInstance(); 111 byTrace.finishTrace('aboutToAppear', 0); 112 this.smListener = mediaQuery.matchMediaSync('(320vp<width<=520vp)'); 113 this.smListener?.on("change", this.isBreakpointSM); 114 this.mdListener = mediaQuery.matchMediaSync('(520vp<width<=840vp)'); 115 this.mdListener?.on("change", this.isBreakpointMD); 116 this.lgListener = mediaQuery.matchMediaSync('(840vp<width)'); 117 this.lgListener?.on("change", this.isBreakpointLG); 118 this.initAirPlaneMode(); 119 } 120 121 onPageShow() { 122 LogUtils.i(TAG, "onPageShow"); 123 byTrace.startTrace('onPageShow', 1); 124 globalThis.appInactiveState = false; 125 this.stopBackgroundRunning(); 126 this.notificationManager?.cancelNotification(); 127 this.notificationManager?.sendCapsuleNotification(this.incomingData ? this.callData : this.incomingData, false); 128 byTrace.finishTrace('onPageShow', 1); 129 LogUtils.i(TAG, "onPageShow end"); 130 } 131 132 initAirPlaneMode() { 133 LogUtils.i(TAG, 'initAirPlaneMode'); 134 try { 135 addAirPlaneModeListener((data) => { 136 LogUtils.i(TAG, 'initAirPlaneMode callback'); 137 if (data == AIRPLANE_MODE) { 138 this.airplaneModeDialogController.open(); 139 AppStorage.SetOrCreate('AirplaneMode', true); 140 } else if (this.isNeedCallAgain) { 141 this.isNeedCallAgain = false; 142 AppStorage.SetOrCreate('AirplaneMode', false); 143 this.mCallDataManager.clearCall(this.callData); 144 } 145 }); 146 } catch(err) { 147 LogUtils.e(TAG, `initAirPlaneMode err = ${JSON.stringify(err)}`); 148 } 149 } 150 151 onPageHide() { 152 LogUtils.i(TAG, "onPageHide"); 153 if (!this.mHangup) { 154 globalThis.appInactiveState = true; 155 this.updateNotification(); 156 } 157 LogUtils.i(TAG, "onPageHide end"); 158 } 159 160 updateNotification() { 161 const {callState, callId} = this.callData; 162 if (callState !== CallStateConst.callStateObj.CALL_STATUS_DISCONNECTED && callId) { 163 if (this.mCallDataManager.hasAliveCall()) { 164 this.startBackgroundRunning(); 165 } 166 this.notificationManager?.sendNotification(this.incomingData ? this.callData : this.incomingData); 167 this.notificationManager?.sendCapsuleNotification(this.incomingData ? this.callData : this.incomingData, true); 168 } 169 } 170 171 aboutToDisappear() { 172 LogUtils.i(TAG, "aboutToDisappear"); 173 this.stopBackgroundRunning(); 174 this.smListener?.off('change', this.isBreakpointSM); 175 this.mdListener?.off('change', this.isBreakpointMD); 176 this.lgListener?.off('change', this.isBreakpointLG); 177 removeAirPlaneModeListener(); 178 } 179 180 onBackPress() { 181 LogUtils.i(TAG, "onBackPress"); 182 } 183 184 private stopBackgroundRunning() { 185 backgroundTaskManager.stopBackgroundRunning(globalThis.calluiAbilityContext).then(() => { 186 LogUtils.i(TAG, "stopBackgroundRunning succeeded"); 187 }).catch((err) => { 188 LogUtils.i(TAG, "stopBackgroundRunning failed"); 189 }); 190 } 191 192 private startBackgroundRunning() { 193 let wantAgentInfo = { 194 wants: [{ 195 bundleName: Constants.CALL_BUNDLE_NAME, 196 abilityName: Constants.CALL_ABILITY_NAME, 197 }], 198 requestCode: 0, 199 operationType: wantAgent.OperationType.START_ABILITY, 200 wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] 201 }; 202 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => { 203 backgroundTaskManager.startBackgroundRunning(globalThis.calluiAbilityContext, 204 backgroundTaskManager.BackgroundMode.VOIP, wantAgentObj).then(() => { 205 LogUtils.i(TAG, "startBackgroundRunning succeeded") 206 }).catch((err) => { 207 LogUtils.i(TAG, "startBackgroundRunning failed cause:" + JSON.stringify(err)) 208 }); 209 }); 210 } 211 212 /** 213 * method to control the display of DTMF keyboard 214 * 215 * parent component pass by value child component 216 */ 217 public showKeyboard() { 218 this.isShowKeyboard = !this.isShowKeyboard; 219 } 220 221 /** 222 * Call status 223 * 224 * @return {number} - callState 225 */ 226 private callState() { 227 LogUtils.i(TAG, "callState : " + this.callData.callState) 228 return this.callData.callState; 229 } 230 231 /** 232 * is Triple Call 233 * 234 * @return {boolean} - isTripleCall 235 */ 236 private isTripleCall() { 237 return (this.callData.callState === CallStateConst.callStateObj.CALL_STATUS_WAITING 238 || this.callData.callState === CallStateConst.callStateObj.CALL_STATUS_INCOMING) 239 && this.callList.length === 3; 240 } 241 242 pageTransition() { 243 PageTransitionEnter({ duration: 0 }); 244 PageTransitionExit({ duration: 0 }); 245 } 246 247 getBtnCallState() { 248 if (this.callList.length > 1) { 249 let btnState = true; 250 this.callList.forEach((v) => { 251 if (v.callState === CallStateConst.callStateObj.CALL_STATUS_WAITING || v.callState === CallStateConst.callStateObj.CALL_STATUS_INCOMING) { 252 btnState = false; 253 } 254 }); 255 return btnState; 256 } else { 257 return this.callState() !== CallStateConst.callStateObj.CALL_STATUS_WAITING 258 && this.callState() !== CallStateConst.callStateObj.CALL_STATUS_INCOMING 259 } 260 } 261 262 build() { 263 Flex({ 264 direction: FlexDirection.Column, 265 alignItems: ItemAlign.Center, 266 justifyContent: FlexAlign.SpaceBetween 267 }) { 268 if (this.callList.length > 1) { 269 MultiContactCard({ 270 callData: $callData, 271 isShowKeyboard: this.isShowKeyboard, 272 callList: $callList, 273 incomingData: $incomingData 274 }) 275 .margin({ top: this.isShowKeyboard ? 0 : 56 }) 276 .layoutWeight(this.isShowKeyboard ? 1 : 0) 277 } else { 278 ContactCard({ 279 callData: $callData, 280 isShowKeyboard: this.isShowKeyboard, 281 callList: $callList 282 }) 283 .margin({ top: this.isShowKeyboard ? 0 : 56 }) 284 .layoutWeight(this.isShowKeyboard ? 1 : 0) 285 } 286 287 if (this.getBtnCallState()) { 288 Column() { 289 if (!this.isShowKeyboard) { 290 FuncBtnGroup({ callData: $callData, callList: $callList }) 291 .margin({ bottom: 32 }) 292 } else { 293 Keyboard({ callData: $callData }) 294 .margin({ bottom: 32 }) 295 } 296 BottomBtn({ 297 callData: $callData, 298 callList: $callList, 299 hangup: $mHangup, 300 onItemClick: () => { 301 this.showKeyboard() 302 } 303 }) 304 } 305 } else { 306 Column() { 307 IncomingCom({ 308 callList: $callList, 309 callData: $callData, 310 hangup: $mHangup}) 311 312 if (this.isTripleCall()) { 313 Column() { 314 Text($r("app.string.end_holding_call")) 315 .fontColor('#FFFFFF') 316 .fontSize(14) 317 .height(19) 318 .lineHeight(19) 319 } 320 .margin({ top: 16 }) 321 } 322 } 323 } 324 } 325 .padding({ bottom: this.isTripleCall() ? 71 : 106 }) 326 .width("100%") 327 .height("100%") 328 .backgroundImage('assets/picture/wallpaper.png', ImageRepeat.NoRepeat) 329 .backgroundImageSize(ImageSize.Cover) 330 } 331} 332