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 dataShare from '@ohos.data.dataShare'; 17import dataSharePredicates from '@ohos.data.dataSharePredicates'; 18import ICallLogRepository from './ICallLogRepository'; 19import { CallLog } from '../entity/CallLog'; 20import CallLogBuilder from '../entity/CallLogBuilder'; 21import Calls from '../contract/Calls'; 22import { RawContacts } from '../../../../../contact/src/main/ets/contract/RawContacts'; 23import CallLogDelta from './CallLogDelta'; 24import { StringUtil } from '../../../../../../common/src/main/ets/util/StringUtil'; 25import { HiLog } from '../../../../../../common/src/main/ets/util/HiLog'; 26import { ArrayUtil } from '../../../../../../common/src/main/ets/util/ArrayUtil'; 27 28const TAG = 'CallLogRepository'; 29 30/** 31 * Account type service, which is used to display account characteristics such as account type holding contact details. 32 */ 33export class CallLogRepository implements ICallLogRepository { 34 private static instance: CallLogRepository; 35 private dataShareHelper; 36 private context: Context 37 38 private constructor() { 39 } 40 41 /* 42 * init if Call From serviceAbility globalThis.context is Null 43 *@param ctx Context used for dataShare 44 */ 45 init(ctx: Context) { 46 this.context = ctx; 47 } 48 49 public static getInstance(): CallLogRepository { 50 if (!CallLogRepository.instance) { 51 CallLogRepository.instance = new CallLogRepository() 52 } 53 return CallLogRepository.instance 54 } 55 56 private async getDataAbilityHelper() { 57 if (this.dataShareHelper == undefined) { 58 this.dataShareHelper = await dataShare.createDataShareHelper(this.context ? this.context : globalThis.context, 59 Calls.CONTENT_URI); 60 } 61 return this.dataShareHelper; 62 } 63 64 saveOne(callLog: CallLog, callback) { 65 if (callLog.id <= 0) { 66 this.getDataAbilityHelper().then((dataAbilityHelper) => { 67 let callLogDelta = new CallLogDelta(callLog); 68 dataAbilityHelper.insert(Calls.CALL_LOG_URI, callLogDelta.createValuesBucket()).then(data => { 69 callback(data); 70 }).catch(error => { 71 HiLog.w(TAG, 'saveOne error:%s', JSON.stringify(error.message)); 72 callback(); 73 }); 74 }).catch(error => { 75 HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message)); 76 callback(); 77 }); 78 } else { 79 callback(); 80 } 81 } 82 83 clear(callback) { 84 this.getDataAbilityHelper().then((dataAbilityHelper) => { 85 let condition = new dataSharePredicates.DataSharePredicates(); 86 dataAbilityHelper.delete(Calls.CALL_LOG_URI, condition).then(data => { 87 callback(data); 88 }).catch(error => { 89 HiLog.w(TAG, 'clear error:%s', JSON.stringify(error.message)); 90 callback(); 91 }); 92 }).catch(error => { 93 HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message)); 94 callback(); 95 }); 96 } 97 98 deleteById(id: number) { 99 return false; 100 } 101 102 deleteByIdIn(ids: number[], callback) { 103 if (ArrayUtil.isEmpty(ids)) { 104 callback(); 105 return; 106 } 107 this.getDataAbilityHelper().then((dataAbilityHelper) => { 108 let condition = new dataSharePredicates.DataSharePredicates(); 109 condition.in(Calls.ID, ids); 110 dataAbilityHelper.delete(Calls.CALL_LOG_URI, condition).then(data => { 111 callback(data); 112 }).catch(error => { 113 HiLog.w(TAG, 'deleteByIdIn error:%s', JSON.stringify(error.message)); 114 callback(); 115 }); 116 }).catch(error => { 117 HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message)); 118 callback(); 119 }); 120 } 121 122 deleteByNumber(number: string) { 123 return false; 124 } 125 126 deleteByLookupUri(number: string) { 127 return false; 128 } 129 130 readByNumber(number: string) { 131 return false; 132 } 133 134 readById(id: number) { 135 return false; 136 } 137 138 findAll(favorite: number, actionData, callback) { 139 this.getDataAbilityHelper().then((dataAbilityHelper) => { 140 let condition = new dataSharePredicates.DataSharePredicates(); 141 let offset = actionData.page < 3 ? (actionData.page - 1) * 50 : (actionData.page - 2) * 500 + 50; 142 if (0 === favorite) { 143 condition.equalTo(RawContacts.FAVORITE, favorite) 144 } 145 condition.limit(actionData.limit, offset); 146 condition.orderByDesc(Calls.CREATE_TIME); 147 dataAbilityHelper.query(Calls.CALL_LOG_URI, condition, null).then(resultSet => { 148 let rst: CallLog[] = []; 149 if (resultSet.rowCount === 0) { 150 resultSet.close(); 151 callback(rst); 152 } else { 153 resultSet.goToFirstRow(); 154 do { 155 let builder = CallLogBuilder.fromResultSet(resultSet); 156 if (builder.id > 0) { 157 rst.push(new CallLog(builder)); 158 } 159 } while (resultSet.goToNextRow()); 160 resultSet.close(); 161 callback(rst); 162 } 163 }).catch(error => { 164 HiLog.w(TAG, 'findAll error:' + JSON.stringify(error.message)); 165 callback([]); 166 }); 167 }).catch(error => { 168 HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message)); 169 callback([]); 170 }); 171 } 172 173 findByFeature(feature: number) { 174 return []; 175 } 176 177 findByNumberIn(numbers: number[], callback) { 178 if (ArrayUtil.isEmpty(numbers)) { 179 callback([]); 180 return; 181 } 182 this.getDataAbilityHelper().then((dataAbilityHelper) => { 183 let realPhoneNumbers = []; 184 for (let key in numbers) { 185 let phoneNumber = StringUtil.removeSpace(numbers[key].toString()); 186 realPhoneNumbers.push(phoneNumber); 187 } 188 let condition = new dataSharePredicates.DataSharePredicates(); 189 condition.in(Calls.PHONE_NUMBER, realPhoneNumbers); 190 condition.orderByDesc(Calls.CREATE_TIME); 191 dataAbilityHelper.query(Calls.CALL_LOG_URI, condition, null).then(resultSet => { 192 let rst: CallLog[] = []; 193 if (resultSet.rowCount === 0) { 194 resultSet.close(); 195 callback(rst); 196 } else { 197 resultSet.goToFirstRow(); 198 do { 199 let builder = CallLogBuilder.fromResultSet(resultSet); 200 if (builder.id > 0) { 201 rst.push(new CallLog(builder)); 202 } 203 } while (resultSet.goToNextRow()); 204 resultSet.close(); 205 callback(rst); 206 } 207 }).catch(error => { 208 HiLog.w(TAG, 'findByNumberIn error:%s', JSON.stringify(error.message)); 209 callback([]); 210 }); 211 }).catch(error => { 212 HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message)); 213 callback([]); 214 }); 215 } 216 217 markMissedCallLogAsRead(phoneNum?: string) { 218 this.getDataAbilityHelper().then((dataAbilityHelper) => { 219 let condition = new dataSharePredicates.DataSharePredicates(); 220 condition.equalTo(Calls.IS_READ, 0) 221 .and().equalTo(Calls.CALL_DIRECTION, 0); 222 if (phoneNum && !StringUtil.isEmpty(phoneNum)) { 223 condition.and().equalTo(Calls.PHONE_NUMBER, phoneNum); 224 } 225 dataAbilityHelper.update(Calls.CALL_LOG_URI, condition, { 226 'is_read': 1 227 }).then(value => { 228 HiLog.i(TAG, 'markMissedCallLogIsRead update succ :%d', value); 229 }).catch(error => { 230 HiLog.w(TAG, 'markMissedCallLogIsRead error:%s', JSON.stringify(error.message)); 231 }); 232 }).catch(error => { 233 HiLog.w(TAG, 'markMissedCallLogIsRead error:%s' + JSON.stringify(error.message)); 234 }); 235 } 236 237 findMissedCallLogUnread(callback, lastId?: number) { 238 this.getDataAbilityHelper().then((dataAbilityHelper) => { 239 let condition = new dataSharePredicates.DataSharePredicates(); 240 condition.equalTo(Calls.IS_READ, 0).and().equalTo(Calls.CALL_DIRECTION, 0).and().equalTo(Calls.ANSWER_STATE, 0); 241 242 if (lastId && lastId > -1) { 243 condition.and().greaterThan(Calls.ID, lastId); 244 } 245 condition.orderByDesc(Calls.ID); 246 dataAbilityHelper.query(Calls.CALL_LOG_URI, condition, null).then(resultSet => { 247 let rst: CallLog[] = []; 248 if (resultSet.rowCount === 0) { 249 resultSet.close(); 250 callback(rst); 251 } else { 252 resultSet.goToFirstRow(); 253 do { 254 let builder = CallLogBuilder.fromResultSet(resultSet); 255 if (builder.id > 0) { 256 rst.push(new CallLog(builder)); 257 } 258 } while (resultSet.goToNextRow()); 259 resultSet.close(); 260 callback(rst); 261 } 262 }).catch(error => { 263 HiLog.w(TAG, 'findMissedCallLogUnread resultSet parse error:%s', JSON.stringify(error.message)); 264 callback([]); 265 }); 266 }).catch(error => { 267 HiLog.w(TAG, 'findMissedCallLogUnread error:%s' + JSON.stringify(error.message)); 268 callback([]); 269 }); 270 } 271 272 notifyChange() { 273 this.getDataAbilityHelper().then((dataAbilityHelper) => { 274 if (dataAbilityHelper) { 275 dataAbilityHelper.notifyChange(Calls.CALL_LOG_URI).then((data) => { 276 HiLog.i(TAG, 'notifyChange success') 277 }).catch(error => { 278 HiLog.w(TAG, 'notifyChange error:%s' + JSON.stringify(error.message)); 279 }) 280 } 281 }).catch(error => { 282 HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message)); 283 }); 284 } 285 286 observers: Array<() => void> = []; 287 callback = () => { 288 HiLog.d(TAG, 'CallLog changed: Notifying observers...'); 289 for (const observer of this.observers) { 290 observer(); 291 } 292 } 293 294 registerCallLogDataChange() { 295 this.getDataAbilityHelper().then((dataAbilityHelper) => { 296 if (dataAbilityHelper) { 297 dataAbilityHelper.on('dataChange', Calls.CALL_LOG_URI, this.callback); 298 HiLog.i(TAG, 'registerCallLogDataChange success') 299 } 300 }).catch(error => { 301 HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message)); 302 }); 303 } 304 305 unregisterCallLogDataChange() { 306 this.getDataAbilityHelper().then((dataAbilityHelper) => { 307 if (dataAbilityHelper) { 308 HiLog.i(TAG, 'start unregisterCallLogDataChange'); 309 dataAbilityHelper.off('dataChange', Calls.CALL_LOG_URI, this.callback); 310 HiLog.i(TAG, 'unregisterCallLogDataChange success') 311 } 312 }).catch(error => { 313 HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message)); 314 }); 315 } 316 317 registerDataChangeObserver(observer: () => void) { 318 if (!observer) { 319 HiLog.i(TAG, `registerDataChangeObserver: observer is null.`); 320 return; 321 } 322 if (this.observers.length == 0) { 323 this.registerCallLogDataChange() 324 } 325 const isExist = this.observers.includes(observer); 326 if (isExist) { 327 return HiLog.i(TAG, 'registerDataChangeObserver: Observer has been attached already.'); 328 } 329 HiLog.i(TAG, 'registerDataChangeObserver: Attached an observer.'); 330 this.observers.push(observer); 331 } 332 333 unRegisterDataChangeObserver(observer: () => void) { 334 const observerIndex = this.observers.indexOf(observer); 335 if (observerIndex === -1) { 336 HiLog.i(TAG, 'unRegisterDataChangeObserver: Nonexistent observer.'); 337 return 338 } 339 this.observers.splice(observerIndex, 1); 340 HiLog.i(TAG, 'unRegisterDataChangeObserver: Detached an observer.'); 341 if (this.observers.length == 0) { 342 this.unregisterCallLogDataChange(); 343 } 344 } 345 346 findSearch(actionData, callback) { 347 this.getDataAbilityHelper().then((dataAbilityHelper) => { 348 let condition = new dataSharePredicates.DataSharePredicates(); 349 condition.in(Calls.DISPLAY_NAME, actionData.nameArray) 350 .or() 351 .like(Calls.PHONE_NUMBER, `%${actionData.teleNumber}%`); 352 dataAbilityHelper.query(Calls.CALL_LOG_URI, condition, null).then(resultSet => { 353 let rst: CallLog[] = []; 354 if (resultSet.rowCount === 0) { 355 resultSet.close(); 356 callback(rst); 357 } else { 358 resultSet.goToFirstRow(); 359 do { 360 let builder = CallLogBuilder.fromResultSet(resultSet); 361 if (builder.id > 0) { 362 rst.push(new CallLog(builder)); 363 } 364 } while (resultSet.goToNextRow()); 365 resultSet.close(); 366 callback(rst); 367 } 368 }).catch(error => { 369 HiLog.w(TAG, 'findAll error:' + JSON.stringify(error.message)); 370 callback([]); 371 }); 372 }).catch(error => { 373 HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message)); 374 callback([]); 375 }); 376 } 377}