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