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