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 ConversationModel from '../model/ConversationModel'; 17import common from '../data/commonData'; 18import HiLog from '../utils//HiLog'; 19import ConversationListService from './ConversationListService'; 20import telephoneUtils from '../utils/TelephoneUtil'; 21import ContactsService from './ContactsService'; 22import LooseObject from '../data/LooseObject'; 23 24const TAG: string = 'ConversationService'; 25 26export default class ConversationService { 27 private static instance: ConversationService; 28 private conversationModel: ConversationModel = new ConversationModel(); 29 30 private constructor() { 31 } 32 33 public static getInstance(): ConversationService { 34 if (ConversationService.instance == null) { 35 ConversationService.instance = new ConversationService(); 36 } 37 return ConversationService.instance; 38 } 39 40 public insertSmsMmsInfo(valueBucket, callback, context): void { 41 let mmsContext = context ? context : globalThis.mmsContext; 42 if (globalThis.DataWorker != null) { 43 globalThis.DataWorker.sendRequest(common.RUN_IN_WORKER_METHOD.insertSmsMmsInfo, { 44 valueBucket: valueBucket, 45 context: mmsContext 46 }, res => { 47 if (callback) { 48 callback(res); 49 } 50 }); 51 } else { 52 this.conversationModel.insertSmsMmsInfo(valueBucket, callback, mmsContext); 53 } 54 } 55 56 public deleteSmsMmsInfoByCondition(actionData, callback, context): void { 57 let mmsContext = context ? context : globalThis.mmsContext; 58 if (globalThis.DataWorker != null) { 59 globalThis.DataWorker.sendRequest(common.RUN_IN_WORKER_METHOD.deleteSmsMmsInfoByCondition, { 60 actionData: actionData, 61 context: mmsContext 62 }, res => { 63 if (callback) { 64 callback(res); 65 } 66 }); 67 } else { 68 this.conversationModel.deleteSmsMmsInfoByCondition(actionData, callback, mmsContext); 69 } 70 } 71 72 public updateSmsMmsInfoByCondition(actionData, valueBucket, callback, context): void { 73 let mmsContext = context ? context : globalThis.mmsContext; 74 if (globalThis.DataWorker != null) { 75 globalThis.DataWorker.sendRequest(common.RUN_IN_WORKER_METHOD.updateSmsMmsInfoByCondition, { 76 actionData: actionData, 77 valueBucket: valueBucket, 78 context: mmsContext 79 }, res => { 80 if (callback) { 81 callback(res); 82 } 83 }); 84 } else { 85 this.conversationModel.updateSmsMmsInfoByCondition(actionData, valueBucket, callback, mmsContext); 86 } 87 } 88 89 public querySmsMmsInfoByCondition(actionData, callback, context): void { 90 let mmsContext = context ? context : globalThis.mmsContext; 91 if (globalThis.DataWorker != null) { 92 globalThis.DataWorker.sendRequest(common.RUN_IN_WORKER_METHOD.querySmsMmsInfoByCondition, { 93 actionData: actionData, 94 context: mmsContext 95 }, res => { 96 callback(res); 97 }); 98 } else { 99 this.conversationModel.querySmsMmsInfoByCondition(actionData, callback, mmsContext); 100 } 101 } 102 103 public querySmsMmsInfoSizeByCondition(actionData, callback, context): void { 104 let mmsContext = context ? context : globalThis.mmsContext; 105 if (globalThis.DataWorker != null) { 106 globalThis.DataWorker.sendRequest(common.RUN_IN_WORKER_METHOD.querySmsMmsInfoSizeByCondition, { 107 actionData: actionData, 108 context: mmsContext 109 }, res => { 110 callback(res); 111 }); 112 } else { 113 this.conversationModel.querySmsMmsInfoSizeByCondition(actionData, callback, mmsContext); 114 } 115 } 116 117 public statisticalData(callback, context) { 118 let mmsContext = context ? context : globalThis.mmsContext; 119 if (globalThis.DataWorker != null) { 120 globalThis.DataWorker.sendRequest(common.RUN_IN_WORKER_METHOD.statisticalData, { 121 context: mmsContext 122 }, res => { 123 callback(res); 124 }); 125 } else { 126 this.conversationModel.statisticalData(callback, mmsContext); 127 } 128 } 129 130 public queryMaxGroupId(callback, context): void { 131 let mmsContext = context ? context : globalThis.mmsContext; 132 if (globalThis.DataWorker != null) { 133 globalThis.DataWorker.sendRequest(common.RUN_IN_WORKER_METHOD.queryMaxGroupId, { 134 context: mmsContext 135 }, res => { 136 callback(res); 137 }); 138 } else { 139 this.conversationModel.queryMaxGroupId(callback, mmsContext); 140 } 141 } 142 143 public queryMessageDetail(actionData, callback, context): void { 144 this.querySmsMmsInfoByCondition(actionData, res => { 145 let result: LooseObject = {}; 146 result.code = res.code; 147 if (res.code == common.int.SUCCESS) { 148 this.groupDetailMessage(res.abilityResult, actionData, resultList => { 149 result.response = resultList; 150 callback(result); 151 }); 152 } else { 153 HiLog.w(TAG, 'queryMessageDetail, failed'); 154 callback(result); 155 } 156 }, context); 157 } 158 159 public convertConversationList(mmsList): Array<LooseObject> { 160 let resultList: Array<LooseObject> = []; 161 for (let item of mmsList) { 162 let result: LooseObject = {}; 163 result.id = item.msgId; 164 result.content = item.msgContent; 165 result.isLock = item.isLock == 0 ? false : true; 166 result.isStared = item.isCollect == 0 ? false : true; 167 // Fields related to MMS messages 168 result.msgType = 0; 169 result.isFullScreenImg = true; 170 result.read = item.isRead == 0 ? '0' : '1'; 171 if (item.msgState == 0) { 172 result.sendStatus = common.int.SEND_MESSAGE_SUCCESS; 173 } else if (item.msgState == 2) { 174 result.sendStatus = common.int.SEND_MESSAGE_FAILED; 175 } else if (item.msgState == 1) { 176 result.sendStatus = common.int.SEND_MESSAGE_SENDING; 177 } else { 178 result.sendStatus = common.int.SEND_DRAFT; 179 } 180 result.subId = item.slotId; 181 result.timeMillisecond = item.startTime; 182 result.isMsm = item.msgType == 0 ? false : true; 183 result.isCbChecked = false; 184 result.isDraft = false; 185 // Fields related to group-sending 186 result.groupId = item.groupId; 187 // Determine whether it is the receiver or sender. 188 result.isReceive = item.isSender == 0 ? false : true; 189 result.date = common.string.EMPTY_STR; 190 result.time = common.string.EMPTY_STR; 191 result.completeNumber = 0; 192 result.failuresNumber = 0; 193 result.hasReport = item.isSendReport == 0 ? false : true; 194 if (!result.isMsm) { 195 result.msgShowType = common.MESSAGE_SHOW_TYPE.NORMAL; 196 } 197 resultList.push(result); 198 } 199 return resultList; 200 } 201 202 public groupDetailMessage(mmsList, actionData, callback): void { 203 let details: Array<LooseObject> = this.convertConversationList(mmsList); 204 if (actionData.contactsNum == 1) { 205 callback(details); 206 return; 207 } 208 let resultList: Array<LooseObject> = []; 209 // Group by groupId. 210 let detailMap = this.convertConversationMap(details); 211 // By group 212 let groupIds = detailMap.keys(); 213 for (let groupId of groupIds) { 214 let groups = detailMap.get(groupId); 215 let result: LooseObject = groups[0]; 216 let failuresNumber = 0; 217 let completeNumber = 0; 218 for (let item of groups) { 219 if (item.sendStatus == common.int.SEND_MESSAGE_FAILED) { 220 failuresNumber++; 221 } 222 if (item.sendStatus == common.int.SEND_MESSAGE_FAILED || item.sendStatus == common.int.SEND_MESSAGE_SUCCESS) { 223 completeNumber++; 224 } 225 } 226 result.completeNumber = completeNumber; 227 result.failuresNumber = failuresNumber; 228 resultList.push(result); 229 } 230 callback(resultList); 231 } 232 233 public convertConversationMap(details) { 234 let conversationMap = new Map(); 235 // grouping 236 for (let element of details) { 237 if (conversationMap.has(element.groupId)) { 238 let groups = conversationMap.get(element.groupId); 239 groups.push(element); 240 } else { 241 let groups = []; 242 groups.push(element); 243 conversationMap.set(element.groupId, groups); 244 } 245 } 246 return conversationMap; 247 } 248 249 public insertSessionAndDetail(actionData, callback, context): void { 250 let sendResults: Array<LooseObject> = actionData.sendResults; 251 let isReceive: boolean = actionData.isReceive; 252 if (sendResults.length == 0) { 253 HiLog.w(TAG, 'insertSessionAndDetail, sendResults.length == 0'); 254 return; 255 } 256 let param: LooseObject = this.dealSendResults(sendResults); 257 // Check whether a session list has been created. 258 ConversationListService.getInstance().querySessionByTelephone(param.telephone, res => { 259 let response: LooseObject = res.response; 260 if (res.code == common.int.SUCCESS && response.id <= 0) { 261 this.dealNoExistSession(isReceive, param, actionData, callback, context); 262 } else { 263 this.dealExistSession(response, param, actionData, callback, context); 264 } 265 }, context); 266 } 267 268 public dealNoExistSession(isReceive, param, actionData, callback, context): void { 269 let unreadCount: number = 0; 270 if (isReceive) { 271 unreadCount = 1; 272 } 273 let valueBucket: LooseObject = { 274 'telephone': param.telephone, 275 'content': param.content, 276 'contacts_num': param.contactsNum, 277 'sms_type': param.smsType, 278 'unread_count': unreadCount, 279 'sending_status': param.sendStatus, 280 'has_draft': common.has_draft.NO, 281 'time': param.timestamp, 282 'message_count': 1, 283 'has_mms': actionData.isMms ? common.has_mms.HAVE : common.has_mms.NO, 284 'has_attachment': actionData.hasAttachment ? common.has_attachment.HAVE : common.has_attachment.NO 285 } 286 ConversationListService.getInstance().insertSession(valueBucket, sessionResult => { 287 HiLog.i(TAG, 'dealNoExistSession, insertSession callback'); 288 if (sessionResult.code == common.int.FAILURE) { 289 HiLog.e(TAG, 'dealNoExistSession, insertSession fail!'); 290 callback(sessionResult); 291 return; 292 } 293 // Invoke the SMS database to insert SMS messages. 294 this.dealInsertMessageDetail(param, actionData, sessionResult.abilityResult, res => { 295 let result: LooseObject = { 296 'rowId': sessionResult.abilityResult, 297 'initDatas': res.initDatas, 298 'groupId': res.groupId 299 } 300 callback(result); 301 }, context); 302 }, context); 303 } 304 305 public dealExistSession(response, param, actionData, callback, context): void { 306 let unreadCount: number = 0; 307 if (actionData.isReceive) { 308 unreadCount = response.unreadCount + 1; 309 } 310 let hasDraft: number = response.hasDraft; 311 if (actionData.hasDraft && hasDraft == 1) { 312 hasDraft = 0; 313 } 314 let valueBucket: LooseObject = { 315 'content': param.content, 316 'unread_count': unreadCount, 317 'time': new Date().getTime(), 318 'sending_status': param.sendStatus, 319 'has_draft': hasDraft, 320 'message_count': (response.messageCount + 1), 321 'has_attachment': actionData.hasAttachment ? 1 : 0, 322 'has_mms': actionData.isMms ? 1 : 0, 323 } 324 let sessionId: number = response.id; 325 let condition: LooseObject = {}; 326 condition.threadId = sessionId; 327 ConversationListService.getInstance().updateSessionByCondition(condition, valueBucket, res => { 328 // Invoke the SMS database to insert SMS messages. 329 this.dealInsertMessageDetail(param, actionData, sessionId, res => { 330 let result: LooseObject = { 331 'rowId': sessionId, 332 'initDatas': res.initDatas, 333 'groupId': res.groupId 334 } 335 callback(result); 336 }, context); 337 }, context); 338 } 339 340 public dealInsertMessageDetail(param, actionData, threadId, callback, context): void { 341 this.queryMaxGroupId(res => { 342 let maxGroupId: number = 0; 343 if (res.code == common.int.SUCCESS) { 344 maxGroupId = Number(res.abilityResult) + 1; 345 } else { 346 HiLog.w(TAG, 'dealInsertMessageDetail, queryMaxGroupId failed'); 347 callback(res); 348 } 349 this.insertMessageDetailByMaxGroupId(param, actionData, threadId, maxGroupId, result => { 350 callback(result); 351 }, context); 352 }, context); 353 } 354 355 private insertMessageDetailByMaxGroupId(param, actionData, threadId, maxGroupId, callback, context): void { 356 let count: number = 0; 357 let initDatas: Array<LooseObject> = []; 358 let result: LooseObject = {}; 359 result.groupId = maxGroupId; 360 let sendResults = actionData.sendResults; 361 sendResults.forEach(sendResult => { 362 let valueBucket: LooseObject = this.buildInsertSmsMmsRow(param, actionData, sendResult, threadId, maxGroupId); 363 this.insertSmsMmsInfo(valueBucket, res => { 364 count++; 365 let initData: LooseObject = { 366 'id': res.abilityResult, 367 'telephone': sendResult.telephone 368 }; 369 initDatas.push(initData); 370 if (count == actionData.sendResults.length) { 371 result.initDatas = initDatas; 372 callback(result); 373 } 374 }, context); 375 }); 376 } 377 378 private buildInsertSmsMmsRow(param, actionData, sendResult, threadId, maxGroupId): LooseObject { 379 let valueBucket: LooseObject = { 380 'slot_id': common.int.SIM_ONE, 381 'receiver_number': common.string.EMPTY_STR, 382 'sender_number': common.string.EMPTY_STR, 383 'start_time': common.string.EMPTY_STR, 384 'end_time': common.string.EMPTY_STR, 385 'msg_type': common.MESSAGE_TYPE.NORMAL, 386 'sms_type': common.sms_type.COMMON, 387 'msg_title': common.string.EMPTY_STR, 388 'msg_content': common.string.EMPTY_STR, 389 'msg_state': common.int.SEND_MESSAGE_SENDING, 390 'operator_service_number': common.string.EMPTY_STR, 391 'msg_code': common.string.EMPTY_STR, 392 'session_id': threadId, 393 'is_lock': common.is_lock.NO, 394 'is_read': common.is_read.UN_READ, 395 'is_collect': common.is_collect.NOT_FAVORITE, 396 'session_type': common.session_type.COMMON, 397 'is_sender': common.is_sender.NO, 398 'group_id': maxGroupId, 399 'is_send_report': common.is_send_report.NO 400 }; 401 if (sendResult.slotId != null) { 402 valueBucket.slot_id = sendResult.slotId; 403 } 404 if (actionData.isReceive) { 405 valueBucket.receiver_number = actionData.ownNumber ? actionData.ownNumber : common.string.EMPTY_STR; 406 valueBucket.sender_number = sendResult.telephone ? sendResult.telephone : common.string.EMPTY_STR; 407 valueBucket.is_read = common.is_read.UN_READ; 408 } else { 409 valueBucket.receiver_number = sendResult.telephone ? sendResult.telephone : common.string.EMPTY_STR; 410 valueBucket.sender_number = actionData.ownNumber ? actionData.ownNumber : common.string.EMPTY_STR; 411 valueBucket.is_read = common.is_read.READ; 412 } 413 let time: string = sendResult.time; 414 if (time == null) { 415 time = new Date().getTime() + common.string.EMPTY_STR; 416 } 417 valueBucket.start_time = time; 418 valueBucket.end_time = time; 419 if (param.isMms != null && param.isMms) { 420 valueBucket.msg_type = common.MESSAGE_TYPE.THEME; 421 } 422 if (param.smsType != null) { 423 valueBucket.sms_type = param.smsType; 424 } 425 if (param.content != null) { 426 valueBucket.msg_title = param.content; 427 valueBucket.msg_content = param.content; 428 } 429 if (sendResult.sendStatus != null) { 430 valueBucket.msg_state = sendResult.sendStatus; 431 } 432 if (actionData.isSender != null) { 433 valueBucket.is_sender = actionData.isSender; 434 } 435 if (actionData.hasReport != null) { 436 valueBucket.is_send_report = actionData.hasReport; 437 } 438 return valueBucket; 439 } 440 441 public dealSendResults(sendResults: Array<LooseObject>): LooseObject { 442 let contactsNum: number = sendResults.length; 443 let telephone: string = common.string.EMPTY_STR; 444 let content: string = common.string.EMPTY_STR; 445 let sendStatus: number = common.int.SEND_MESSAGE_SUCCESS; 446 let failSum: number = 0; 447 let time: string = common.string.EMPTY_STR; 448 for (let sendResult of sendResults) { 449 telephone = telephone + sendResult.telephone + common.string.COMMA; 450 content = sendResult.content; 451 sendStatus = sendResult.sendStatus; 452 if (sendResult.sendStatus == common.int.SEND_MESSAGE_FAILED) { 453 failSum++; 454 } 455 if (sendResult.time != null) { 456 time = sendResult.time; 457 } 458 if (sendResult.isMessageSim != null && sendResult.isMessageSim) { 459 telephone = sendResult.telephone; 460 contactsNum = 1; 461 } 462 } 463 // If it fails, then the session list turns out to be a failure. 464 if (failSum > 0) { 465 sendStatus = common.int.SEND_MESSAGE_FAILED; 466 } 467 telephone = telephone.substring(0, telephone.length - 1); 468 let smsType: number = common.sms_type.COMMON; 469 if (contactsNum == 1 && telephoneUtils.judgeIsInfoMsg(telephone)) { 470 smsType = common.sms_type.NOTICE; 471 } 472 let result: LooseObject = {}; 473 let timestamp: number = new Date().getTime(); 474 result.contactsNum = contactsNum; 475 result.content = content; 476 result.telephone = telephoneUtils.dealTelephoneSort(telephone); 477 result.sendStatus = sendStatus; 478 result.timestamp = time != common.string.EMPTY_STR ? time : timestamp; 479 result.smsType = smsType; 480 return result; 481 } 482 483 public updateSessionAndDetail(actionData): void { 484 let sendResults: Array<LooseObject> = actionData.sendResults; 485 if (sendResults.length == 0) { 486 return; 487 } 488 let param: LooseObject = this.dealSendResults(sendResults); 489 // Update the status of the session list. 490 let valueBucket: LooseObject = { 491 'sending_status': param.sendStatus, 492 'content': param.content 493 } 494 ConversationListService.getInstance().updateSessionByCondition(actionData, valueBucket, null, null); 495 // Update the status of the information list. 496 for (let sendResult of sendResults) { 497 actionData.sendStatus = sendResult.sendStatus; 498 if (sendResult.sendStatus == common.int.SEND_MESSAGE_FAILED) { 499 actionData.sendStatus = common.int.SEND_MESSAGE_FAILED; 500 } else if (sendResult.sendStatus == common.int.SEND_MESSAGE_SUCCESS) { 501 actionData.sendStatus = common.int.SEND_MESSAGE_SUCCESS; 502 } else { 503 actionData.sendStatus = common.int.SEND_MESSAGE_SENDING; 504 } 505 actionData.msgId = sendResult.id; 506 let valueBucket: LooseObject = { 507 'msg_state': actionData.sendStatus, 508 }; 509 this.updateSmsMmsInfoByCondition(actionData, valueBucket, null, null); 510 } 511 } 512 513 public saveImage(actionData, callback, context): void { 514 callback('saveImage function to be developed!'); 515 } 516 517 public gotoShare(actionData, callback, context): void { 518 callback('gotoShare function to be developed!'); 519 } 520 521 public dealContactsName(telephones, actionData, resultList, callback, context): void { 522 actionData.telephones = telephones; 523 actionData.hasDelete = '0'; 524 if (telephones.length == 0) { 525 callback(resultList); 526 } 527 ContactsService.getInstance().queryContactDataByCondition(actionData, res => { 528 let groupIdMap: LooseObject = {}; 529 if (res.code == common.int.FAILURE || res.abilityResult.length == 0) { 530 groupIdMap = this.convertingMessageByGroup(resultList); 531 } else { 532 // Convert the result to Map, key: mobile number, value: name 533 let telephoneMap = new Map(); 534 let contacts: Array<LooseObject> = res.abilityResult; 535 for (let item of contacts) { 536 if (item.displayName == common.string.EMPTY_STR) { 537 telephoneMap.set(item.detailInfo, item.detailInfo); 538 } else { 539 telephoneMap.set(item.detailInfo, item.displayName); 540 } 541 } 542 // Match the result based on the mobile number. 543 groupIdMap = this.getGroupIdMap(resultList, telephoneMap); 544 } 545 let lists = []; 546 let favoriteList = []; 547 for (let key of Object.keys(groupIdMap)) { 548 let value = groupIdMap[key]; 549 let contactsNums = value.telephone.split(common.string.COMMA); 550 value.contactsNum = contactsNums.length; 551 if (value.isFavorite) { 552 let temp = JSON.parse(JSON.stringify(value)); 553 temp.icon = '/common/icon/icon_favorite.svg'; 554 favoriteList.push(temp); 555 value.isFavorite = false; 556 } 557 lists.push(value); 558 } 559 // Add favorite data to the search list. 560 if (favoriteList.length > 0) { 561 lists = lists.concat(favoriteList); 562 } 563 callback(lists); 564 }, context); 565 } 566 567 public getGroupIdMap(resultList, telephoneMap): LooseObject { 568 let groupIdMap: LooseObject = {}; 569 for (let map of resultList) { 570 // Indicates the combination of multiple names. The names need to be displayed in combination. 571 if (telephoneMap.has(map.telephone)) { 572 map.name = telephoneMap.get(map.telephone); 573 map.nameFormatter = map.name + '<' + map.telephoneFormat + '>'; 574 } else { 575 map.nameFormatter = map.telephoneFormat; 576 } 577 if (Object.keys(groupIdMap).indexOf(map.groupId) > -1) { 578 let list = groupIdMap[map.groupId]; 579 list.name = list.name + common.string.COMMA + map.name; 580 list.nameFormatter = list.nameFormatter + common.string.COMMA + map.nameFormatter; 581 list.telephone = list.telephone + common.string.COMMA + map.telephone; 582 list.telephoneFormat = list.telephoneFormat + common.string.COMMA + map.telephoneFormat; 583 } else { 584 groupIdMap[map.groupId] = map; 585 } 586 } 587 return groupIdMap; 588 } 589 590 public convertingMessageByGroup(resultList): LooseObject { 591 let groupIdMap: LooseObject = {}; 592 for (let element of resultList) { 593 if (Object.keys(groupIdMap).indexOf(element.groupId) == -1) { 594 groupIdMap[element.groupId] = element; 595 } 596 } 597 return groupIdMap; 598 } 599 600 public statisticsUnreadNotify(callback, context): void { 601 let actionData: LooseObject = {}; 602 actionData.hasRead = common.is_read.UN_READ; 603 actionData.smsType = common.sms_type.NOTICE; 604 this.querySmsMmsInfoSizeByCondition(actionData, res => { 605 if (res.code == common.int.SUCCESS) { 606 callback(res.abilityResult); 607 } else { 608 HiLog.w(TAG, 'statisticsUnreadNotify fail!'); 609 callback(0); 610 } 611 }, context); 612 } 613}