1/** 2 * Copyright (c) 2022 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 */ 15import Notification from '@ohos.notificationManager' 16import NotificationManager from '@ohos.notificationManager' 17import WantAgent from '@ohos.app.ability.wantAgent'; 18import { HiLog, sharedPreferencesUtils } from '../../../../../../common'; 19import notificationSubscribe from '@ohos.notificationSubscribe'; 20import call from '@ohos.telephony.call'; 21import { NotificationSubscriber } from 'notification/notificationSubscriber'; 22 23const TAG = 'MissedCallNotifier'; 24 25const BUNDLE_NAME: string = 'com.ohos.contacts'; 26const ABILITY_NAME: string = 'com.ohos.contacts.MainAbility'; 27const GROUP_NAME: string = 'MissedCall' 28const KEY_MISSED_BADGE_NUM = 'missed_badge_number' 29const KEY_ID = 'unread_call_notification_id' 30const KEY_DISPLAY_NAME = 'unread_call_notification_displayName' 31const KEY_COUNT = 'unread_call_notification_count' 32const KEY_CREATE_TIME = 'unread_call_notification_create_time' 33const KEY_RING_DURATION = 'unread_call_notification_ring_duration' 34const actionBtnMaps = 35 { 36 'notification.event.dialBack': $r('app.string.dial_back'), 37 'notification.event.message': $r('app.string.message'), 38 }; 39 40export class MissedCallNotifyData { 41 id?: number; 42 displayName?: string; 43 readonly phoneNumber: string; 44 count?: number; 45 createTime?: number; 46 ringDuration?: number; 47} 48 49export class MissedCallNotifier { 50 private label: string; 51 private context: Context; 52 private static sInstance: MissedCallNotifier = undefined; 53 private missedBadgeNumber: number = -1; 54 private UnReadMissedCallData: MissedCallNotifyData; 55 56 /** 57 * getInstance for MissedCallNotifier 58 */ 59 public static getInstance() { 60 if (!MissedCallNotifier.sInstance || MissedCallNotifier.sInstance == undefined) { 61 MissedCallNotifier.sInstance = new MissedCallNotifier(); 62 } 63 return MissedCallNotifier.sInstance; 64 } 65 66 /** 67 * init 68 * 69 * @param ctx context needed init 70 */ 71 public init(ctx: Context) { 72 this.context = ctx; 73 if (!this.label) { 74 this.label = this.getContext()?.resourceManager.getStringSync($r('app.string.missed_call')); 75 } 76 sharedPreferencesUtils.init(this.getContext()); 77 } 78 79 /** 80 * update Missed Call Notification 81 * 82 * @param missedData missedCallData - missed call data for notification 83 */ 84 public async updateMissedCallNotifications(missedData: Map<string, MissedCallNotifyData>) { 85 let notifyData = await this.getMissedCallNotifyDatas(); 86 HiLog.i(TAG, `updateMissedCallNotifications notifyData:${notifyData.length} missedData: ${missedData.size}`) 87 let badgeNumber: number = 0; 88 if (notifyData.length > 0) { 89 for (let notify of notifyData) { 90 let key: string = notify.displayName; 91 if (missedData.has(key)) { 92 let missed = missedData.get(key) 93 missedData.delete(key) 94 if (missed.id != notify.id) { 95 notify.createTime = missed.createTime; 96 notify.ringDuration = missed.ringDuration; 97 notify.count += missed.count; 98 this.sendNotification(notify); 99 } 100 } 101 badgeNumber += notify.count; 102 } 103 } 104 for (let notify of missedData.values()) { 105 await this.sendNotification(notify); 106 badgeNumber += notify.count; 107 } 108 if (badgeNumber > 0) { 109 this.setMissedBadgeNumber(badgeNumber); 110 } 111 } 112 113 /** 114 * cancel Missed Call Notification 115 */ 116 public cancelAllNotification() { 117 HiLog.i(TAG, 'cancelNotification,cancel all') 118 this.setMissedBadgeNumber(0); 119 NotificationManager.cancelGroup(GROUP_NAME).catch(error => { 120 HiLog.e(TAG, `cancelNotification,err ${JSON.stringify(error)}}`) 121 }); 122 } 123 124 /** 125 * get Missed Call BadgeNumber 126 */ 127 public async getMissedBadgeNumber() { 128 if (this.missedBadgeNumber == -1) { 129 this.missedBadgeNumber = <number> await sharedPreferencesUtils.getFromPreferences(KEY_MISSED_BADGE_NUM, -1); 130 } 131 return this.missedBadgeNumber; 132 } 133 134 /** 135 * cancel Missed Call Notification By NotificationId 136 * 137 * @param id Notification Id 138 */ 139 public async cancelNotificationById(id: number, count: number) { 140 HiLog.i(TAG, 'cancelNotificationById:' + id) 141 NotificationManager.cancel(id, this.label).catch(error => { 142 HiLog.e(TAG, `cancelNotificationById,err ${JSON.stringify(error)}}`) 143 }); 144 let badgeNumber = await this.getMissedBadgeNumber(); 145 badgeNumber -= count; 146 if (badgeNumber >= 0) { 147 this.setMissedBadgeNumber(badgeNumber); 148 } 149 } 150 151 private constructor() { 152 } 153 154 private getContext() { 155 if (this.context && this.context != undefined) { 156 return this.context; 157 } 158 return globalThis.context; 159 } 160 161 private async getMissedCallNotifyDatas() { 162 HiLog.i(TAG, `getMissedCallNotifyDatas in`) 163 let result: Array<MissedCallNotifyData> = []; 164 const notifications: Array<NotificationManager.NotificationRequest> = 165 await NotificationManager.getAllActiveNotifications() 166 for (let notify of notifications) { 167 if (notify.groupName == GROUP_NAME && notify.extraInfo) { 168 result.push(<MissedCallNotifyData> notify.extraInfo); 169 } 170 } 171 HiLog.i(TAG, `getMissedCallNotifyDatas result: ${result.length}`) 172 return result; 173 } 174 175 private async sendNotification(missedCallData: MissedCallNotifyData) { 176 const {id, displayName, count, createTime, ringDuration} = missedCallData; 177 sharedPreferencesUtils.saveToPreferences(KEY_ID, id); 178 sharedPreferencesUtils.saveToPreferences(KEY_DISPLAY_NAME, displayName); 179 sharedPreferencesUtils.saveToPreferences(KEY_COUNT, count); 180 sharedPreferencesUtils.saveToPreferences(KEY_CREATE_TIME, createTime); 181 sharedPreferencesUtils.saveToPreferences(KEY_RING_DURATION, ringDuration) 182 HiLog.i(TAG, `sendNotification in id:${id}, count:${count}, createTime:${createTime}`) 183 let str_text = this.getContext()?.resourceManager.getStringSync($r('app.string.contacts_ring_times')) + 184 ringDuration + this.getContext()?.resourceManager.getStringSync($r('app.string.contacts_time_sec')); 185 if (ringDuration === 0) { 186 str_text = $r('app.string.missed_call') 187 } 188 const notificationRequest: NotificationManager.NotificationRequest = { 189 content: { 190 contentType: Notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, 191 normal: { 192 title: count > 1 ? `${displayName} (${count})` : displayName, 193 text: str_text, 194 additionalText: missedCallData.phoneNumber 195 }, 196 }, 197 id: id, 198 label: this.label, 199 groupName: GROUP_NAME, 200 slotType: Notification.SlotType.SOCIAL_COMMUNICATION, 201 deliveryTime: new Date().getTime(), 202 extraInfo: missedCallData 203 } 204 205 let wantAgentObj = await this.getWantAgent(missedCallData, 'notification.event.click'); 206 notificationRequest.wantAgent = wantAgentObj; 207 notificationRequest.actionButtons = []; 208 for (const key of Object.keys(actionBtnMaps)) { 209 const wantAgent = await this.getWantAgent(missedCallData, key); 210 const title = this.getContext()?.resourceManager.getStringSync(actionBtnMaps[key]); 211 notificationRequest.actionButtons.push({ 212 title: title, 213 wantAgent: wantAgent 214 }); 215 } 216 notificationRequest.removalWantAgent = await this.createWantAgentForCommonEvent(missedCallData, 'notification.event.cancel'); 217 NotificationManager.publish(notificationRequest).then(() => { 218 HiLog.i(TAG, '===>publish promise success req.id : ' + notificationRequest.id); 219 }).catch((err) => { 220 HiLog.e(TAG, '===>publish promise failed because ' + JSON.stringify(err)); 221 }); 222 HiLog.i(TAG, 'sendNotification end') 223 } 224 225 /** 226 * send Unread Call Notification 227 */ 228 public async sendUnreadCallNotification(map: Map<string, string>) { 229 let id: number = <number> await sharedPreferencesUtils.getFromPreferences(KEY_ID, -1); 230 let displayName: string = <string> await sharedPreferencesUtils.getFromPreferences(KEY_DISPLAY_NAME, ''); 231 let count: number = <number> await sharedPreferencesUtils.getFromPreferences(KEY_COUNT, -1); 232 let createTime: number = <number> await sharedPreferencesUtils.getFromPreferences(KEY_CREATE_TIME, -1); 233 let ringDuration: number = <number> await sharedPreferencesUtils.getFromPreferences(KEY_RING_DURATION, -1); 234 let missCallData = map.get('missedPhoneJson') 235 const parameters = JSON.parse(JSON.stringify(missCallData)); 236 for (let i = 0; i < parameters.phoneNumberList.length; i++) { 237 const missedPhoneNumber = parameters.phoneNumberList[i] 238 const missedNum = parameters.countList[i] 239 if (i === (parameters.phoneNumberList.length -1)) { 240 this.UnReadMissedCallData = { 241 phoneNumber: missedPhoneNumber, 242 displayName: missedPhoneNumber, 243 id: i, 244 createTime: createTime, 245 count: count, 246 ringDuration: ringDuration 247 } 248 } else { 249 this.UnReadMissedCallData = { 250 phoneNumber: missedPhoneNumber, 251 displayName: missedPhoneNumber, 252 id: i, 253 createTime: createTime, 254 count: count, 255 ringDuration: 0 256 } 257 } 258 this.sendNotification(this.UnReadMissedCallData); 259 } 260 } 261 262 private setMissedBadgeNumber(newBadgeNum: number) { 263 HiLog.i(TAG, 'setMissedBadgeNumber :' + newBadgeNum); 264 this.missedBadgeNumber = newBadgeNum; 265 NotificationManager.setBadgeNumber(newBadgeNum); 266 sharedPreferencesUtils.saveToPreferences(KEY_MISSED_BADGE_NUM, newBadgeNum); 267 } 268 269 /** 270 * create wantAgent for common event 271 * 272 * @param mAction 273 * @return return the created WantAgent object. 274 */ 275 private async createWantAgentForCommonEvent(missedCallData, action?: string) { 276 return await WantAgent.getWantAgent({ 277 wants: [{ action: 'contact.event.CANCEL_MISSED', parameters: { 278 action: action, 279 missedCallData: missedCallData 280 }, }], 281 actionType: WantAgent.OperationType.SEND_COMMON_EVENT, 282 requestCode: 0 283 }) 284 } 285 286 private getWantAgent(missedCallData: MissedCallNotifyData, action: string) { 287 let data: any = {} 288 data.action = action, 289 data.missedCallData = missedCallData 290 if (action == 'notification.event.dialBack') { 291 HiLog.i(TAG, 'getWantAgent add page_flag_edit_before_calling') 292 return this.createWantAgentForCommonEvent(missedCallData, action); 293 } 294 return WantAgent.getWantAgent({ 295 wants: [{ 296 deviceId: '', 297 bundleName: BUNDLE_NAME, 298 abilityName: ABILITY_NAME, 299 uri: '', 300 type: 'phone', 301 parameters: data, 302 entities: [] 303 }], 304 requestCode: 0, 305 actionType: WantAgent.OperationType.START_ABILITY, 306 wantAgentFlags: [WantAgent.WantAgentFlags.ONE_TIME_FLAG] 307 }); 308 } 309}