18779efd5Sopenharmony_ci/**
28779efd5Sopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd.
38779efd5Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48779efd5Sopenharmony_ci * you may not use this file except in compliance with the License.
58779efd5Sopenharmony_ci * You may obtain a copy of the License at
68779efd5Sopenharmony_ci *
78779efd5Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88779efd5Sopenharmony_ci *
98779efd5Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108779efd5Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118779efd5Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128779efd5Sopenharmony_ci * See the License for the specific language governing permissions and
138779efd5Sopenharmony_ci * limitations under the License.
148779efd5Sopenharmony_ci */
158779efd5Sopenharmony_ci
168779efd5Sopenharmony_ciimport ICallLogService from './ICallLogService';
178779efd5Sopenharmony_ciimport { CallLog } from './entity/CallLog';
188779efd5Sopenharmony_ciimport { CallType } from './entity/CallLog';
198779efd5Sopenharmony_ciimport { ArrayUtil } from '../../../../../common/src/main/ets/util/ArrayUtil';
208779efd5Sopenharmony_ciimport MergedCallLog from './entity/MergedCallLog';
218779efd5Sopenharmony_ciimport { StringUtil } from '../../../../../common/src/main/ets/util/StringUtil';
228779efd5Sopenharmony_ciimport { MergeRule } from './CallLogSetting';
238779efd5Sopenharmony_ciimport StringFormatUtil from '../../../../../entry/src/main/ets/util/StringFormatUtil';
248779efd5Sopenharmony_ci
258779efd5Sopenharmony_ciexport class CallLogService implements ICallLogService {
268779efd5Sopenharmony_ci  private static instance: CallLogService;
278779efd5Sopenharmony_ci  private mergeRule: MergeRule;
288779efd5Sopenharmony_ci  private context: Context;
298779efd5Sopenharmony_ci
308779efd5Sopenharmony_ci  private constructor() {
318779efd5Sopenharmony_ci  }
328779efd5Sopenharmony_ci
338779efd5Sopenharmony_ci  /*
348779efd5Sopenharmony_ci   * init if Call From serviceAbility globalThis.context is Null
358779efd5Sopenharmony_ci   *@param ctx Context used for dataShare
368779efd5Sopenharmony_ci   */
378779efd5Sopenharmony_ci  init(ctx: Context) {
388779efd5Sopenharmony_ci    this.context = ctx;
398779efd5Sopenharmony_ci  }
408779efd5Sopenharmony_ci
418779efd5Sopenharmony_ci  public static getInstance(): CallLogService {
428779efd5Sopenharmony_ci    if (!CallLogService.instance) {
438779efd5Sopenharmony_ci      CallLogService.instance = new CallLogService();
448779efd5Sopenharmony_ci    }
458779efd5Sopenharmony_ci    return CallLogService.instance;
468779efd5Sopenharmony_ci  }
478779efd5Sopenharmony_ci
488779efd5Sopenharmony_ci  setMergeRule(mergeRule: MergeRule) {
498779efd5Sopenharmony_ci    this.mergeRule = mergeRule;
508779efd5Sopenharmony_ci  }
518779efd5Sopenharmony_ci
528779efd5Sopenharmony_ci  mergeCallLogs(callLogs: CallLog[]) {
538779efd5Sopenharmony_ci    if (this.getMergeRule() === MergeRule.CONTACT) {
548779efd5Sopenharmony_ci      return this.mergeByContact(callLogs);
558779efd5Sopenharmony_ci    } else {
568779efd5Sopenharmony_ci      return this.mergeByTime(callLogs);
578779efd5Sopenharmony_ci    }
588779efd5Sopenharmony_ci  }
598779efd5Sopenharmony_ci
608779efd5Sopenharmony_ci  mergeMissedCalls(callLogs: CallLog[]) {
618779efd5Sopenharmony_ci    let mergeRule = this.getMergeRule();
628779efd5Sopenharmony_ci    let callLogList: CallLog[] = [];
638779efd5Sopenharmony_ci    let missedList: CallLog[] = [];
648779efd5Sopenharmony_ci    for (let callLog of callLogs) {
658779efd5Sopenharmony_ci      callLogList.push(callLog);
668779efd5Sopenharmony_ci      if (callLog.callType == CallType.MISSED ||
678779efd5Sopenharmony_ci      callLog.callType == CallType.REJECTED) {
688779efd5Sopenharmony_ci        //Filtering Missed Call Data
698779efd5Sopenharmony_ci        missedList.push(callLog);
708779efd5Sopenharmony_ci        let timeList = [];
718779efd5Sopenharmony_ci        // Filtering by Contact Missed Calls and Redialing
728779efd5Sopenharmony_ci        if (mergeRule === MergeRule.CONTACT) {
738779efd5Sopenharmony_ci          for (let k = 0; k < missedList.length; k++) {
748779efd5Sopenharmony_ci            let missedPhone = missedList[k].phoneNumber;
758779efd5Sopenharmony_ci            for (let i = 0; i < callLogList.length; i++) {
768779efd5Sopenharmony_ci              let allSpecialPhone = callLogList[i].phoneNumber;
778779efd5Sopenharmony_ci              if (missedPhone == allSpecialPhone) {
788779efd5Sopenharmony_ci                let timeNumber = callLogList[i].createTime;
798779efd5Sopenharmony_ci                let obj = {
808779efd5Sopenharmony_ci                  'id': i,
818779efd5Sopenharmony_ci                  'timeObj': timeNumber,
828779efd5Sopenharmony_ci                };
838779efd5Sopenharmony_ci                timeList.push(obj);
848779efd5Sopenharmony_ci                let max = timeList[0].timeObj;
858779efd5Sopenharmony_ci                for (let j = 0; j < timeList.length; j++) {
868779efd5Sopenharmony_ci                  if (timeList[j].timeObj > max) {
878779efd5Sopenharmony_ci                    max = timeList[j].timeObj;
888779efd5Sopenharmony_ci                    let n = timeList[j].id;
898779efd5Sopenharmony_ci                    if (callLogList[n].callType == CallType.OUT) {
908779efd5Sopenharmony_ci                      missedList.splice(k, 1);
918779efd5Sopenharmony_ci                    }
928779efd5Sopenharmony_ci                  } else {
938779efd5Sopenharmony_ci                    let m = timeList[0].id;
948779efd5Sopenharmony_ci                    if (callLogList[m].callType == CallType.OUT) {
958779efd5Sopenharmony_ci                      missedList.splice(k, 1);
968779efd5Sopenharmony_ci                    }
978779efd5Sopenharmony_ci                  }
988779efd5Sopenharmony_ci                }
998779efd5Sopenharmony_ci              }
1008779efd5Sopenharmony_ci            }
1018779efd5Sopenharmony_ci          }
1028779efd5Sopenharmony_ci        }
1038779efd5Sopenharmony_ci      }
1048779efd5Sopenharmony_ci    }
1058779efd5Sopenharmony_ci    if (mergeRule === MergeRule.CONTACT) {
1068779efd5Sopenharmony_ci      return this.mergeByContact(missedList);
1078779efd5Sopenharmony_ci    } else {
1088779efd5Sopenharmony_ci      return this.mergeByTime(missedList);
1098779efd5Sopenharmony_ci    }
1108779efd5Sopenharmony_ci  }
1118779efd5Sopenharmony_ci
1128779efd5Sopenharmony_ci  /**
1138779efd5Sopenharmony_ci   * In the case of merging by time, the post-processing of the call record
1148779efd5Sopenharmony_ci   * service data is optimized based on the original call record data.
1158779efd5Sopenharmony_ci   * @param callLogList
1168779efd5Sopenharmony_ci   * @return
1178779efd5Sopenharmony_ci   */
1188779efd5Sopenharmony_ci  private mergeByTime(callLogList: CallLog[]) {
1198779efd5Sopenharmony_ci    let resultList = [];
1208779efd5Sopenharmony_ci    if (ArrayUtil.isEmpty(callLogList)) {
1218779efd5Sopenharmony_ci      return resultList;
1228779efd5Sopenharmony_ci    }
1238779efd5Sopenharmony_ci    // Call records are cached from the first record.
1248779efd5Sopenharmony_ci    let tempElement = new MergedCallLog(callLogList[0]);
1258779efd5Sopenharmony_ci    // Indicates the creation time of the latest record.
1268779efd5Sopenharmony_ci    // After call records are merged, the time is displayed.
1278779efd5Sopenharmony_ci    let tempCallTime = callLogList[0].createTime;
1288779efd5Sopenharmony_ci    // Type of the call record that retains the latest record.
1298779efd5Sopenharmony_ci    // This type is displayed after the call record is combined.
1308779efd5Sopenharmony_ci    let tempCallType = callLogList[0].callType;
1318779efd5Sopenharmony_ci    let num = 1;
1328779efd5Sopenharmony_ci    let ids = [];
1338779efd5Sopenharmony_ci    ids.push(callLogList[0].id);
1348779efd5Sopenharmony_ci    for (let i = 1; i < callLogList.length; i++) {
1358779efd5Sopenharmony_ci      let element = callLogList[i];
1368779efd5Sopenharmony_ci      // Whether the cached field needs to be combined with the current field
1378779efd5Sopenharmony_ci      if (this.callLogMergeCheck(tempElement, element)) {
1388779efd5Sopenharmony_ci        num++;
1398779efd5Sopenharmony_ci        // Put the latest record ID into the merged array.
1408779efd5Sopenharmony_ci        ids.push(element.id);
1418779efd5Sopenharmony_ci      } else {
1428779efd5Sopenharmony_ci        //If the latest data is inconsistent with the cached data,
1438779efd5Sopenharmony_ci        // replace the num and ids data in the cached data and
1448779efd5Sopenharmony_ci        // save the cached data to the result set.
1458779efd5Sopenharmony_ci        tempElement.count = num;
1468779efd5Sopenharmony_ci        tempElement.ids = ids;
1478779efd5Sopenharmony_ci        // Displays the creation time of the latest saved record.
1488779efd5Sopenharmony_ci        tempElement.createTime = this.formatTime(tempCallTime);
1498779efd5Sopenharmony_ci        tempElement.callType = tempCallType;
1508779efd5Sopenharmony_ci        resultList.push(tempElement);
1518779efd5Sopenharmony_ci        /* Reset num and ids to the latest count and record,
1528779efd5Sopenharmony_ci        and reset tempCallTime to the latest creation time of the next record.*/
1538779efd5Sopenharmony_ci        num = 1;
1548779efd5Sopenharmony_ci        ids = [];
1558779efd5Sopenharmony_ci        tempCallTime = element.createTime;
1568779efd5Sopenharmony_ci        tempCallType = element.callType;
1578779efd5Sopenharmony_ci        ids.push(element.id);
1588779efd5Sopenharmony_ci      }
1598779efd5Sopenharmony_ci      tempElement = new MergedCallLog(element);
1608779efd5Sopenharmony_ci    }
1618779efd5Sopenharmony_ci    /* Put the last piece of cached data into the result set*/
1628779efd5Sopenharmony_ci    if (tempElement != null) {
1638779efd5Sopenharmony_ci      tempElement.count = num;
1648779efd5Sopenharmony_ci      tempElement.ids = ids;
1658779efd5Sopenharmony_ci      tempElement.createTime = this.formatTime(tempCallTime);
1668779efd5Sopenharmony_ci      tempElement.callType = tempCallType;
1678779efd5Sopenharmony_ci      resultList.push(tempElement);
1688779efd5Sopenharmony_ci    }
1698779efd5Sopenharmony_ci    return resultList;
1708779efd5Sopenharmony_ci  }
1718779efd5Sopenharmony_ci
1728779efd5Sopenharmony_ci  /**
1738779efd5Sopenharmony_ci   * In the case of merging by contact, the post-processing
1748779efd5Sopenharmony_ci   * of the call record service data is optimized based on the original call record data.
1758779efd5Sopenharmony_ci   *
1768779efd5Sopenharmony_ci   * @param {Array} callLogList
1778779efd5Sopenharmony_ci   * @return {Array} callLogList
1788779efd5Sopenharmony_ci   */
1798779efd5Sopenharmony_ci  private mergeByContact(callLogs: CallLog[]) {
1808779efd5Sopenharmony_ci    let resultList = [];
1818779efd5Sopenharmony_ci    if (ArrayUtil.isEmpty(callLogs)) {
1828779efd5Sopenharmony_ci      return resultList;
1838779efd5Sopenharmony_ci    }
1848779efd5Sopenharmony_ci    let contactTempMap = new Map();
1858779efd5Sopenharmony_ci    let phoneNumberMap = new Map();
1868779efd5Sopenharmony_ci    for (let i = 0; i < callLogs.length; i++) {
1878779efd5Sopenharmony_ci      let element = new MergedCallLog(callLogs[i]);
1888779efd5Sopenharmony_ci      element.createTime = this.formatTime(callLogs[i].createTime);
1898779efd5Sopenharmony_ci      // In the case of merging by contact, the combined record entry is fixed to 1.
1908779efd5Sopenharmony_ci      element.count = 1;
1918779efd5Sopenharmony_ci      // In the case of merging by contact, the IDs of
1928779efd5Sopenharmony_ci      // the merging record are fixed to the ID of the record.
1938779efd5Sopenharmony_ci      element.ids = [callLogs[i].id];
1948779efd5Sopenharmony_ci      // Call records without contacts are combined by phone number.
1958779efd5Sopenharmony_ci      if (StringUtil.isEmpty(element.quickSearchKey)) {
1968779efd5Sopenharmony_ci        if (!phoneNumberMap.has(element.phoneNumber)) {
1978779efd5Sopenharmony_ci          resultList.push(element);
1988779efd5Sopenharmony_ci          phoneNumberMap.set(element.phoneNumber, callLogs[i].phoneNumber);
1998779efd5Sopenharmony_ci        }
2008779efd5Sopenharmony_ci      } else { // Call records with contacts are merged by contact.
2018779efd5Sopenharmony_ci        let isContactKey = contactTempMap.has(element.quickSearchKey);
2028779efd5Sopenharmony_ci        if (!isContactKey) {
2038779efd5Sopenharmony_ci          resultList.push(element);
2048779efd5Sopenharmony_ci          contactTempMap.set(element.quickSearchKey, callLogs[i].quickSearchKey);
2058779efd5Sopenharmony_ci        }
2068779efd5Sopenharmony_ci      }
2078779efd5Sopenharmony_ci    }
2088779efd5Sopenharmony_ci    return resultList;
2098779efd5Sopenharmony_ci  }
2108779efd5Sopenharmony_ci
2118779efd5Sopenharmony_ci  /**
2128779efd5Sopenharmony_ci   * Obtain the call time.
2138779efd5Sopenharmony_ci   *
2148779efd5Sopenharmony_ci   * @param date Call record creation timestamp
2158779efd5Sopenharmony_ci   * @return {object} Talk time
2168779efd5Sopenharmony_ci   */
2178779efd5Sopenharmony_ci  private formatTime(date) {
2188779efd5Sopenharmony_ci    let result;
2198779efd5Sopenharmony_ci    // If the value is not a number, the value is not parsed.
2208779efd5Sopenharmony_ci    if (isNaN(date)) {
2218779efd5Sopenharmony_ci      return date;
2228779efd5Sopenharmony_ci    }
2238779efd5Sopenharmony_ci    let timestamp = parseInt(date) * 1000;
2248779efd5Sopenharmony_ci    let callTime = new Date(timestamp);
2258779efd5Sopenharmony_ci    let now = new Date();
2268779efd5Sopenharmony_ci    if (callTime.getTime() > now.getTime()) {
2278779efd5Sopenharmony_ci      result = callTime.getFullYear() + '/' + (callTime.getMonth() + 1) + '/' + callTime.getDate();
2288779efd5Sopenharmony_ci    } else if (callTime.getFullYear() == now.getFullYear()) {
2298779efd5Sopenharmony_ci      if (callTime.getMonth() == now.getMonth()) {
2308779efd5Sopenharmony_ci        // Same month of the same year
2318779efd5Sopenharmony_ci        let timeDiff = parseInt(((now.getTime() - callTime.getTime()) / 60000).toString());
2328779efd5Sopenharmony_ci        let dayDiff = now.getDate() - callTime.getDate();
2338779efd5Sopenharmony_ci        let hour = callTime.getHours().toString();
2348779efd5Sopenharmony_ci        let minutes = callTime.getMinutes() < 10 ? '0' + callTime.getMinutes() : callTime.getMinutes().toString();
2358779efd5Sopenharmony_ci        if (dayDiff == 0) {
2368779efd5Sopenharmony_ci          // 同天
2378779efd5Sopenharmony_ci          if (timeDiff == 0) {
2388779efd5Sopenharmony_ci            result = $r("app.string.justNow");
2398779efd5Sopenharmony_ci          } else if (timeDiff < 60) {
2408779efd5Sopenharmony_ci            result = $r("app.string.minutesAgo", timeDiff);
2418779efd5Sopenharmony_ci          } else {
2428779efd5Sopenharmony_ci            let timeDetail: any = {};
2438779efd5Sopenharmony_ci            if (parseInt(StringFormatUtil.judgeSysTime(this.context)) == 12) {
2448779efd5Sopenharmony_ci              timeDetail.time = StringFormatUtil.getDayMessage(hour, minutes);
2458779efd5Sopenharmony_ci            } else {
2468779efd5Sopenharmony_ci              timeDetail.time = $r("app.string.time_normal", hour, minutes);
2478779efd5Sopenharmony_ci            }
2488779efd5Sopenharmony_ci            result = timeDetail.time
2498779efd5Sopenharmony_ci          }
2508779efd5Sopenharmony_ci        } else if (dayDiff == 1) {
2518779efd5Sopenharmony_ci          result = $r("app.string.yesterday");
2528779efd5Sopenharmony_ci        } else {
2538779efd5Sopenharmony_ci          result = (callTime.getMonth() + 1) + '/' + callTime.getDate(); // 'MM/dd'
2548779efd5Sopenharmony_ci        }
2558779efd5Sopenharmony_ci      } else {
2568779efd5Sopenharmony_ci        result = (callTime.getMonth() + 1) + '/' + callTime.getDate();
2578779efd5Sopenharmony_ci      }
2588779efd5Sopenharmony_ci    } else {
2598779efd5Sopenharmony_ci      // 'yyyy/MM/dd'
2608779efd5Sopenharmony_ci      result = callTime.getFullYear() + '/' + (callTime.getMonth() + 1) + '/' + callTime.getDate();
2618779efd5Sopenharmony_ci    }
2628779efd5Sopenharmony_ci    return result;
2638779efd5Sopenharmony_ci  }
2648779efd5Sopenharmony_ci
2658779efd5Sopenharmony_ci  /**
2668779efd5Sopenharmony_ci   * Checks whether two call records need to be combined when the call records are combined by time.
2678779efd5Sopenharmony_ci   * If the call records need to be combined, true is returned. Otherwise, false is returned.
2688779efd5Sopenharmony_ci   *
2698779efd5Sopenharmony_ci   * @param oldElement Call records before merging
2708779efd5Sopenharmony_ci   * @param newElement Combined call records
2718779efd5Sopenharmony_ci   * @return
2728779efd5Sopenharmony_ci   */
2738779efd5Sopenharmony_ci  private callLogMergeCheck(oldElement: MergedCallLog, newElement: CallLog) {
2748779efd5Sopenharmony_ci    /* Merge Rules:
2758779efd5Sopenharmony_ci       1. The phone numbers are combined only when the phone numbers are the same.
2768779efd5Sopenharmony_ci        2. If the number is the same and the call type is 1, 2, 3, or 5,
2778779efd5Sopenharmony_ci        the call is combined. Types 1, 2, 3, and 5 are not combined.
2788779efd5Sopenharmony_ci    */
2798779efd5Sopenharmony_ci    if (oldElement.phoneNumber.trim() == newElement.phoneNumber.trim()) {
2808779efd5Sopenharmony_ci      if (oldElement.callType == CallType.IN || oldElement.callType == CallType.OUT) {
2818779efd5Sopenharmony_ci        if (newElement.callType == CallType.IN || newElement.callType == CallType.OUT) {
2828779efd5Sopenharmony_ci          return true;
2838779efd5Sopenharmony_ci        }
2848779efd5Sopenharmony_ci        return false;
2858779efd5Sopenharmony_ci      }
2868779efd5Sopenharmony_ci      if (newElement.callType == CallType.MISSED || newElement.callType == CallType.REJECTED) {
2878779efd5Sopenharmony_ci        return true;
2888779efd5Sopenharmony_ci      }
2898779efd5Sopenharmony_ci    }
2908779efd5Sopenharmony_ci    return false;
2918779efd5Sopenharmony_ci  }
2928779efd5Sopenharmony_ci
2938779efd5Sopenharmony_ci  private getMergeRule() {
2948779efd5Sopenharmony_ci    return this.mergeRule;
2958779efd5Sopenharmony_ci  }
2968779efd5Sopenharmony_ci}