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, `updateMissedCallNotifications, ${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 HiLog.i(TAG, `cancelAllMissedNotificationAction cancel all`); 94 MissedCallNotifier.getInstance().cancelAllNotification(); 95 CallLogRepository.getInstance().markMissedCallLogAsRead(); 96 } 97 98 private constructor() { 99 } 100 101 private getContext() { 102 if (this.context && this.context != undefined) { 103 return this.context; 104 } 105 return globalThis.context; 106 } 107 108 private registerDataChangeObserver() { 109 if (!this.onCallLogChanged || this.onCallLogChanged == undefined) { 110 this.onCallLogChanged = () => { 111 HiLog.i(TAG, "onCallLogChanged lastMissedId:" + this.lastMissedId); 112 this.sendMissedCallNotify(this.lastMissedId); 113 this.unRegisterDataChangeObserver(); 114 } 115 HiLog.i(TAG, "registerDataChangeObserver"); 116 CallLogRepository.getInstance().registerDataChangeObserver(this.onCallLogChanged); 117 } 118 } 119 120 private unRegisterDataChangeObserver() { 121 if (this.onCallLogChanged && this.onCallLogChanged != undefined) { 122 this.onCallLogChanged = undefined; 123 HiLog.i(TAG, "unRegisterDataChangeObserver"); 124 CallLogRepository.getInstance().unRegisterDataChangeObserver(this.onCallLogChanged); 125 } 126 } 127 128 private async getLastNotificationId() { 129 let preferences = await data_preferences.getPreferences(this.getContext(), PREFERENCE_NAME); 130 return <number> await preferences.get(LAST_MISSED_ID, -1); 131 } 132 133 private setLastNotificationId(id: number, lastId?: number): boolean { 134 if (!lastId || this.lastMissedId < id) { 135 HiLog.i(TAG, `setLastNotificationId: ${id}`); 136 this.lastMissedId = id; 137 this.unRegisterDataChangeObserver(); 138 data_preferences.getPreferences(this.getContext(), PREFERENCE_NAME, (err, preferences) => { 139 if (err) { 140 HiLog.e(TAG, JSON.stringify(err)); 141 } else { 142 preferences.put(LAST_MISSED_ID, id); 143 preferences.flush(); 144 } 145 }); 146 return true; 147 } 148 return false; 149 } 150 151 private sendMissedCallNotify(lastId?: number) { 152 CallLogRepository.getInstance().findMissedCallLogUnread((data: CallLog[]) => { 153 HiLog.i(TAG, "sendMissedCallNotify, callLog unread count:" + data.length); 154 if (data.length <= 0 || !this.setLastNotificationId(data[0].id, lastId)) { 155 HiLog.i(TAG, "sendMissedCallNotify, No new CallLog."); 156 return; 157 } 158 let phoneNums = new Set(); 159 for (let callLog of data) { 160 phoneNums.add(callLog.phoneNumber); 161 } 162 this.queryContactsName(Array.from(phoneNums), (numberMap) => { 163 let missedData: Map<string, MissedCallNotifyData> = new Map(); 164 for (let callLog of data) { 165 let displayName = callLog.phoneNumber; 166 if (numberMap.has(callLog.phoneNumber)) { 167 displayName = numberMap.get(callLog.phoneNumber); 168 } 169 if (!missedData.has(displayName)) { 170 missedData.set(displayName, { 171 phoneNumber: callLog.phoneNumber, 172 displayName: displayName, 173 id: callLog.id, 174 createTime: callLog.createTime, 175 count: 1, 176 ringDuration: callLog.ringDuration 177 }); 178 } else { 179 missedData.get(displayName).count++; 180 } 181 } 182 MissedCallNotifier.getInstance().updateMissedCallNotifications(missedData); 183 }) 184 }, lastId); 185 } 186 187 private queryContactsName(numberList, callback) { 188 if (numberList.length == 0) { 189 HiLog.w(TAG, "queryContactsName, has no number"); 190 callback(new Map()); 191 return; 192 } 193 ContactRepository.getInstance().queryContactDataByNumber(numberList, contacts => { 194 // Convert the result to Map, key: mobile number, value: name 195 let numberMap = this.getNumberMap(contacts); 196 callback(numberMap); 197 }); 198 } 199 200 private getNumberMap(contacts) { 201 let numberMap = new Map(); 202 for (let item of contacts) { 203 if (!StringUtil.isEmpty(item.displayName)) { 204 numberMap.set(item.detailInfo, item.displayName); 205 } 206 } 207 return numberMap; 208 } 209}