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 16/** 17 * @file: call manager service 18 */ 19import CallServiceProxy from '../model/CallServiceProxy'; 20import TelephonyCall from './TelephonyApi'; 21import commonEvent from '@ohos.commonEvent'; 22import LogUtils from '../common/utils/LogUtils'; 23import CallManager from '../model/CallManager'; 24import VibrationAndProximityUtils from '../common/utils/VibrationAndProximityUtils'; 25 26let subscriber; 27const TAG = 'CallManagerService'; 28const CALL_BUNDLE_NAME = 'com.ohos.callui'; 29const ABILITY_NAME = 'com.ohos.callui.MainAbility'; 30const CALL_STATUS_INCOMING = 4; 31const CALL_STATUS_WAITING = 5; 32const CALL_STATUS_DIALING = 2; 33const CALL_STATUS_DISCONNECTED = 6; 34const CALL_STATUS_DISCONNECTING = 7; 35const CALL_EVENT = 0; 36const CALL_CLICK = 1; 37const CALL_SCREEN_OFF = 2; 38const events = ['callui.event.callEvent', 'callui.event.click', 'usual.event.SCREEN_OFF']; 39 40/** 41 * class CallManagerService 42 */ 43export default class CallManagerService { 44 private mTelephonyCall: TelephonyCall; 45 private callData = null; 46 private callList = []; 47 private context; 48 private static sCallManagerService: CallManagerService; 49 50 public static getInstance(): CallManagerService { 51 if (!CallManagerService.sCallManagerService) { 52 CallManagerService.sCallManagerService = new CallManagerService(); 53 } 54 return CallManagerService.sCallManagerService; 55 } 56 57 public constructor() { 58 } 59 60 61 public init(context): void { 62 this.mTelephonyCall = new TelephonyCall; 63 this.addRegisterListener(); 64 this.addSubscriber(); 65 this.context = context; 66 // voice 67 VibrationAndProximityUtils.addVoiceObserver(); 68 } 69 70 /** 71 * add callui app subscriber 72 */ 73 async addSubscriber(): Promise<void> { 74 LogUtils.i(TAG, 'addSubscriber'); 75 subscriber = await new Promise((resolve) => { 76 commonEvent.createSubscriber({ 77 events, 78 publisherPermission: 'ohos.permission.GET_TELEPHONY_STATE' 79 }, (err, data) => { 80 resolve(data); 81 }); 82 }); 83 84 commonEvent.subscribe(subscriber, (err, res) => { 85 if (err.code === 0) { 86 if (res.event === events[CALL_EVENT]) { 87 const obj = JSON.parse(res.data); 88 if (obj && obj.key === 'getInitCallData') { 89 this.publishData(this.callData); 90 } 91 } 92 93 if (res.event === events[CALL_CLICK]) { 94 const {callId, btnType} = res.parameters; 95 this.btnclickAgent(callId, btnType); 96 } 97 98 if (res.event === events[CALL_SCREEN_OFF]) { 99 VibrationAndProximityUtils.stopVibration(); 100 CallServiceProxy.getInstance().muteRinger(); 101 } 102 } else { 103 LogUtils.i(TAG, 'addSubscriber commonEvent.subscribe failed err :' + JSON.stringify(err)); 104 } 105 subscriber.finishCommonEvent() 106 .then(() => { 107 LogUtils.i(TAG, 'addSubscriber finishCommonEvent'); 108 }); 109 }); 110 } 111 112 /** 113 * click button agent 114 * 115 * @param { number } callId - call id 116 * @param { string } btnType - button type 117 */ 118 btnclickAgent(callId, btnType): void { 119 LogUtils.i(TAG, 'btnclickAgent btnType :' + btnType); 120 this.getMapObj(btnType, callId); 121 } 122 123 getMapObj(typeKey, callId): void { 124 if (typeKey === 'answer') { 125 this.mTelephonyCall.acceptCall(callId); 126 this.startAbility(this.callData); 127 } 128 if (typeKey === 'reject') { 129 this.mTelephonyCall.rejectCall(callId); 130 } 131 if (typeKey === 'hangUp') { 132 this.mTelephonyCall.hangUpCall(callId); 133 } 134 } 135 136 /** 137 * add register listener 138 */ 139 addRegisterListener(): void { 140 this.mTelephonyCall.registerCallStateCallback(this.getCallData.bind(this)); 141 } 142 143 /** 144 * get callData 145 * 146 * @param { Object } callData - Object 147 */ 148 getCallData(callData): void { 149 this.callData = callData; 150 this.updateCallList(); 151 const {callState} = this.callData; 152 153 /** 154 * single call or dialing pull up the application 155 */ 156 if (callState === CALL_STATUS_INCOMING || callState === CALL_STATUS_WAITING || callState === CALL_STATUS_DIALING) { 157 this.startAbility(callData); 158 if (this.callList.length > 1) { 159 this.publishData(callData); 160 } 161 if (callState === CALL_STATUS_DIALING) { 162 // add Proximity Listener 163 VibrationAndProximityUtils.suspendScreen(); 164 } 165 } else if (callState !== CALL_STATUS_DISCONNECTING) { 166 this.publishData(callData); 167 } 168 if (callState === CALL_STATUS_INCOMING || callState === CALL_STATUS_WAITING) { 169 // incoming, vibration 170 VibrationAndProximityUtils.startVibration(); 171 } else { 172 VibrationAndProximityUtils.stopVibration(); 173 } 174 } 175 176 /** 177 * service disconnected 178 * 179 * @return void 180 */ 181 public onDisconnected(): void { 182 if (this.callData != null && this.callData.callState === CALL_STATUS_DISCONNECTED) { 183 return; 184 } else { 185 this.callData.callState = CALL_STATUS_DISCONNECTED; 186 this.publishData(this.callData); 187 } 188 } 189 190 /** 191 * start ability 192 * 193 * @param { Object } callData - Object 194 */ 195 startAbility(callData): void { 196 this.context.startAbility({ 197 bundleName: CALL_BUNDLE_NAME, 198 abilityName: ABILITY_NAME, 199 parameters: callData 200 }).then((data) => { 201 LogUtils.i(TAG, 'callUI service startAbility data :' + JSON.stringify(data)); 202 }).catch((err) => { 203 LogUtils.i(TAG, 'callUI service startAbility err :' + JSON.stringify(err)); 204 }); 205 } 206 207 /** 208 * update callList 209 */ 210 updateCallList(): void { 211 const {callState, callId} = this.callData; 212 const targetObj = this.callList.find((v) => v.callId === callId); 213 if (targetObj) { 214 Object.assign(targetObj, { 215 ...this.callData 216 }); 217 } else { 218 this.callList.push({ 219 ...this.callData 220 }); 221 } 222 if (callState === CALL_STATUS_DISCONNECTED) { 223 const index = this.callList.findIndex((v) => v.callId === callId); 224 this.callList.splice(index, 1); 225 } 226 } 227 228 /** 229 * commonEvent publish data 230 * 231 * @param { Object } callData - Object 232 */ 233 publishData(callData): void { 234 CallManager.getInstance().update(callData); 235 } 236 237 /** 238 * unsubscribe 239 */ 240 unsubscribe(): void { 241 commonEvent.unsubscribe(subscriber, (err) => { 242 if (err.code !== 0) { 243 LogUtils.i(TAG, 'unsubscribe commonEvent.unsubscribe err:' + JSON.stringify(err)); 244 } 245 }); 246 } 247 248 /** 249 * remove register listener 250 */ 251 removeRegisterListener(): void { 252 this.mTelephonyCall.unRegisterCallStateCallback(); 253 } 254}