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 */ 15 16import { HiLog, sharedPreferencesUtils } from 'common'; 17import { MissedCallNotifier, MissedCallNotifyData } from './MissedCallNotifier' 18import { CallLogRepository } from '../repo/CallLogRepository'; 19import { ContactRepository } from '../../../../../../feature/contact/src/main/ets/repo/ContactRepository'; 20import { StringUtil } from '../../../../../../common/src/main/ets/util/StringUtil'; 21import { CallLog } from '../entity/CallLog' 22 23const TAG = 'MissedCallService'; 24const LAST_MISSED_ID: string = 'Last_Missed_id' 25const PREFERENCE_NAME: string = 'CONTACT_PREFERENCE'; 26 27export class MissedCallService { 28 private static sInstance: MissedCallService = undefined; 29 private context: Context = undefined; 30 private lastMissedId = -1; 31 private onCallLogChanged = () => { 32 if (this.lastMissedId != -1) { 33 HiLog.i(TAG, 'onCallLogChanged lastMissedId:' + this.lastMissedId); 34 this.sendMissedCallNotify(this.lastMissedId); 35 } 36 this.unRegisterDataChangeObserver(); 37 } 38 39 /** 40 * getInstance for MissedCallService 41 */ 42 public static getInstance() { 43 if (!MissedCallService.sInstance || MissedCallService.sInstance == undefined) { 44 MissedCallService.sInstance = new MissedCallService(); 45 } 46 return MissedCallService.sInstance; 47 } 48 49 /** 50 * init 51 * 52 * @param ctx context needed init 53 */ 54 public init(ctx: Context) { 55 this.context = ctx; 56 sharedPreferencesUtils.init(ctx); 57 MissedCallNotifier.getInstance().init(ctx); 58 CallLogRepository.getInstance().init(ctx); 59 ContactRepository.getInstance().init(ctx); 60 } 61 62 /** 63 * updateAllMissedCallNotifications 64 */ 65 public async updateAllMissedCallNotifications() { 66 HiLog.i(TAG, 'updateMissedCallNotifications'); 67 MissedCallNotifier.getInstance().cancelAllNotification(); 68 this.sendMissedCallNotify(); 69 } 70 71 /** 72 * updateMissedCallNotifications 73 */ 74 public async updateMissedCallNotifications() { 75 if (this.lastMissedId == -1) { 76 this.lastMissedId = await this.getLastNotificationId(); 77 } 78 this.registerDataChangeObserver(); 79 HiLog.i(TAG, 'updateMissedCallNotifications, lastMissedId:' + this.lastMissedId); 80 this.sendMissedCallNotify(this.lastMissedId); 81 } 82 83 /** 84 * 85 * Unread missed call notification 86 */ 87 public async unreadCallNotification(map:Map<string,string>) { 88 MissedCallNotifier.getInstance().sendUnreadCallNotification(map); 89 } 90 91 /** 92 * cancelMissedNotificationAction 93 * 94 * @param data MissedCallNotifyData need cancel notify 95 */ 96 public async cancelMissedNotificationAction(data: MissedCallNotifyData) { 97 HiLog.i(TAG, `cancelMissedNotificationAction, ${JSON.stringify(data)}`); 98 MissedCallNotifier.getInstance().cancelNotificationById(data.id, data.count); 99 CallLogRepository.getInstance().markMissedCallLogAsRead(data.phoneNumber); 100 } 101 102 /** 103 * cancelAllMissedNotificationAction 104 */ 105 public async cancelAllMissedNotificationAction() { 106 let unreadMissed = await MissedCallNotifier.getInstance().getMissedBadgeNumber() 107 if (unreadMissed > 0) { 108 HiLog.i(TAG, `cancelAllMissedNotificationAction cancel all`); 109 MissedCallNotifier.getInstance().cancelAllNotification(); 110 CallLogRepository.getInstance().markMissedCallLogAsRead(); 111 } 112 } 113 114 private constructor() { 115 } 116 117 private getContext() { 118 if (this.context && this.context != undefined) { 119 return this.context; 120 } 121 return globalThis.context; 122 } 123 124 private registerDataChangeObserver() { 125 HiLog.i(TAG, 'registerDataChangeObserver'); 126 CallLogRepository.getInstance().registerDataChangeObserver(this.onCallLogChanged); 127 } 128 129 private unRegisterDataChangeObserver() { 130 HiLog.i(TAG, 'unRegisterDataChangeObserver'); 131 CallLogRepository.getInstance().unRegisterDataChangeObserver(this.onCallLogChanged); 132 } 133 134 private async getLastNotificationId() { 135 return <number> await sharedPreferencesUtils.getFromPreferences(LAST_MISSED_ID, -1) 136 } 137 138 private setLastNotificationId(id: number, lastId?: number): boolean { 139 if (!lastId || this.lastMissedId < id) { 140 HiLog.i(TAG, `setLastNotificationId: ${id}`); 141 this.lastMissedId = id; 142 this.unRegisterDataChangeObserver(); 143 sharedPreferencesUtils.saveToPreferences(LAST_MISSED_ID, id) 144 return true; 145 } 146 return false; 147 } 148 149 private sendMissedCallNotify(lastId?: number) { 150 CallLogRepository.getInstance().findMissedCallLogUnread((data: CallLog[]) => { 151 HiLog.i(TAG, 'sendMissedCallNotify, callLog unread count:' + data.length); 152 if (data.length <= 0 || !this.setLastNotificationId(data[0].id, lastId)) { 153 HiLog.i(TAG, 'sendMissedCallNotify, No new CallLog.'); 154 return; 155 } 156 let phoneNums = new Set(); 157 for (let callLog of data) { 158 phoneNums.add(callLog.phoneNumber); 159 } 160 this.queryContactsName(Array.from(phoneNums), (numberMap) => { 161 let missedData: Map<string, MissedCallNotifyData> = new Map(); 162 for (let callLog of data) { 163 let displayName = callLog.phoneNumber; 164 if (numberMap.has(callLog.phoneNumber)) { 165 displayName = numberMap.get(callLog.phoneNumber); 166 } 167 if (!missedData.has(displayName)) { 168 missedData.set(displayName, { 169 phoneNumber: callLog.phoneNumber, 170 displayName: displayName, 171 id: callLog.id, 172 createTime: callLog.createTime, 173 count: 1, 174 ringDuration: callLog.ringDuration 175 }); 176 } else { 177 missedData.get(displayName).count++; 178 } 179 } 180 MissedCallNotifier.getInstance().updateMissedCallNotifications(missedData); 181 }) 182 }, lastId); 183 } 184 185 private queryContactsName(numberList, callback) { 186 if (numberList.length == 0) { 187 HiLog.w(TAG, 'queryContactsName, has no number'); 188 callback(new Map()); 189 return; 190 } 191 ContactRepository.getInstance().queryContactDataByNumber(numberList, contacts => { 192 // Convert the result to Map, key: mobile number, value: name 193 let numberMap = this.getNumberMap(contacts); 194 callback(numberMap); 195 }); 196 } 197 198 private getNumberMap(contacts) { 199 let numberMap = new Map(); 200 for (let item of contacts) { 201 if (!StringUtil.isEmpty(item.displayName)) { 202 numberMap.set(item.detailInfo, item.displayName); 203 } 204 } 205 return numberMap; 206 } 207}