diff --git a/App.tsx b/App.tsx index 3740b542..191b87a5 100644 --- a/App.tsx +++ b/App.tsx @@ -21,7 +21,7 @@ import { hydrateGlobalImageMap } from '@common/CachedImage'; import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from '@common/Constants'; import { getAppVersion, sendDeviceDetailsToClickstream } from '@components/utlis/commonFunctions'; import { linkingConf } from '@components/utlis/deeplinkingUtils'; -import { getBuildFlavour, restartApp } from '@components/utlis/DeviceUtils'; +import { fetchSimDetails, getBuildFlavour, restartApp } from '@components/utlis/DeviceUtils'; import { initSentry } from '@components/utlis/sentry'; import { GLOBAL, setGlobalBuildFlavour } from '@constants/Global'; import { AppStates } from '@interfaces/appStates'; @@ -141,6 +141,7 @@ function App() { }); // Device Details sendDeviceDetailsToClickstream(); + fetchSimDetails(); }, []); React.useEffect(() => { diff --git a/android/app/build.gradle b/android/app/build.gradle index 8bd7588b..e7386bc0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -113,8 +113,8 @@ def jscFlavor = 'org.webkit:android-jsc:+' def enableHermes = project.ext.react.get("enableHermes", false); -def VERSION_CODE = 263 -def VERSION_NAME = "2.19.4" +def VERSION_CODE = 264 +def VERSION_NAME = "2.19.5" android { namespace "com.avapp" diff --git a/android/app/src/main/java/com/avapp/MainApplication.java b/android/app/src/main/java/com/avapp/MainApplication.java index 8163375e..4553fc54 100644 --- a/android/app/src/main/java/com/avapp/MainApplication.java +++ b/android/app/src/main/java/com/avapp/MainApplication.java @@ -11,12 +11,14 @@ import android.app.Application; import android.content.Context; import com.avapp.appInstallerModule.ApkInstallerPackage; +import com.avapp.callLogs.CallLogsModulePackage; import com.avapp.contentProvider.ContentProviderPackage; import com.avapp.callModule.CallModulePackage; import com.avapp.deviceDataSync.DeviceDataSyncPackage; import com.avapp.photoModule.PhotoModulePackage; import com.avapp.phoneStateBroadcastReceiver.PhoneStateModulePackage; import com.avapp.sharedPreference.SharedPreferencesPackage; +import com.avapp.simDetails.SimDetailsModulePackage; import com.avapp.utils.FirebaseRemoteConfigHelper; import com.avapp.wifiDetailsModule.WifiDetailsModulePackage; import com.avapp.restartApp.RestartPackage; @@ -76,6 +78,8 @@ public class MainApplication extends Application implements ReactApplication, Ap packages.add(new CallModulePackage()); packages.add(new SharedPreferencesPackage()); packages.add(new ContentProviderPackage()); + packages.add(new SimDetailsModulePackage()); + packages.add(new CallLogsModulePackage()); return packages; } diff --git a/android/app/src/main/java/com/avapp/callLogs/CallLogsModule.java b/android/app/src/main/java/com/avapp/callLogs/CallLogsModule.java new file mode 100644 index 00000000..d3e17eb3 --- /dev/null +++ b/android/app/src/main/java/com/avapp/callLogs/CallLogsModule.java @@ -0,0 +1,210 @@ +package com.avapp.callLogs; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.provider.CallLog; +import android.util.Log; + +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; + +import org.json.JSONArray; +import org.json.JSONException; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class CallLogsModule extends ReactContextBaseJavaModule { + + private final ReactApplicationContext reactContext; + + public CallLogsModule(ReactApplicationContext reactContext) { + super(reactContext); + this.reactContext = reactContext; + } + + @Override + public String getName() { + return "CallLogsDetails"; + } + + @ReactMethod + public void loadAll(Promise promise) { + load(-1, promise); + } + + @ReactMethod + public void load(int limit, Promise promise) { + loadWithFilter(limit, null, promise); + } + + @ReactMethod + public void loadWithFilter(int limit, @Nullable ReadableMap filter, Promise promise) { + if (ContextCompat.checkSelfPermission(reactContext, Manifest.permission.READ_CALL_LOG) + != PackageManager.PERMISSION_GRANTED) { + promise.reject("PERMISSION_DENIED", "READ_CALL_LOG permission not granted"); + return; + } + + WritableArray result = Arguments.createArray(); + Cursor cursor = null; + + String selection = ""; + List selectionArgsList = new ArrayList<>(); + boolean nullFilter = filter == null; + + if (!nullFilter && filter.hasKey("minTimestamp")) { + selection += CallLog.Calls.DATE + " >= ?"; + selectionArgsList.add(filter.getString("minTimestamp")); + } + + if (!nullFilter && filter.hasKey("maxTimestamp")) { + if (!selection.isEmpty()) selection += " AND "; + selection += CallLog.Calls.DATE + " <= ?"; + selectionArgsList.add(filter.getString("maxTimestamp")); + } + + try { + String[] selectionArgs = selectionArgsList.toArray(new String[0]); + cursor = reactContext.getContentResolver().query( + CallLog.Calls.CONTENT_URI, + null, + selection.isEmpty() ? null : selection, + selectionArgs, + CallLog.Calls.DATE + " DESC" + ); + if (cursor == null) { + promise.resolve(result); + return; + } + + int callLogCount = 0; + + final int NUMBER_COLUMN_INDEX = cursor.getColumnIndex(CallLog.Calls.NUMBER); + final int TYPE_COLUMN_INDEX = cursor.getColumnIndex(CallLog.Calls.TYPE); + final int DATE_COLUMN_INDEX = cursor.getColumnIndex(CallLog.Calls.DATE); + final int DURATION_COLUMN_INDEX = cursor.getColumnIndex(CallLog.Calls.DURATION); + final int NAME_COLUMN_INDEX = cursor.getColumnIndex(CallLog.Calls.CACHED_NAME); + final int FEATURES_COLUMN_INDEX = cursor.getColumnIndex(CallLog.Calls.FEATURES); + + while (cursor.moveToNext() && shouldContinue(limit, callLogCount)) { + String phoneNumber = cursor.getString(NUMBER_COLUMN_INDEX); + int duration = cursor.getInt(DURATION_COLUMN_INDEX); + String name = cursor.getString(NAME_COLUMN_INDEX); + String timestampStr = cursor.getString(DATE_COLUMN_INDEX); + long timestamp = safeParseLong(timestampStr, 0); + boolean featuresAvailable = FEATURES_COLUMN_INDEX != -1; + int features = featuresAvailable ? cursor.getInt(FEATURES_COLUMN_INDEX) : 0; + + String isVideoCall = "undetected"; + String isWiFiCall = "undetected"; + String isHDCall = "undetected"; + String isPulledExternally = "undetected"; + String isVolte = "undetected"; + + if(featuresAvailable) { + isVideoCall = String.valueOf((features & CallLog.Calls.FEATURES_VIDEO) != 0); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + isWiFiCall = String.valueOf((features & CallLog.Calls.FEATURES_WIFI) != 0); + isHDCall = String.valueOf((features & CallLog.Calls.FEATURES_HD_CALL) != 0); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) { + isPulledExternally = String.valueOf((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) != 0); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + isVolte = String.valueOf((features & CallLog.Calls.FEATURES_VOLTE) != 0); + } + } + + DateFormat df = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.MEDIUM, SimpleDateFormat.MEDIUM); + String dateTime = df.format(new Date(timestamp)); + + String type = resolveCallType(cursor.getInt(TYPE_COLUMN_INDEX)); + WritableMap callLog = Arguments.createMap(); + callLog.putString("phoneNumber", phoneNumber); + callLog.putInt("duration", duration); + callLog.putString("name", name); + callLog.putString("timestamp", timestampStr); + callLog.putString("dateTime", dateTime); + callLog.putString("type", type); + callLog.putString("isVideoCall", isVideoCall); + callLog.putString("isWiFiCall", isWiFiCall); + callLog.putString("isHDCall", isHDCall); + callLog.putString("isPulledExternally", isPulledExternally); + callLog.putString("isVolte", isVolte); + callLog.putInt("rawType", cursor.getInt(TYPE_COLUMN_INDEX)); + result.pushMap(callLog); + callLogCount++; + } + + promise.resolve(result); + } catch (Exception e) { + promise.reject("CALL_LOG_ERROR", "Unexpected error while loading call logs", e); + } finally { + if (cursor != null && !cursor.isClosed()) { + cursor.close(); + } + } + } + + private long safeParseLong(String value, long defaultValue) { + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public static String[] toStringArray(JSONArray array) { + if (array == null) + return null; + + String[] arr = new String[array.length()]; + for (int i = 0; i < arr.length; i++) { + arr[i] = array.optString(i); + } + return arr; + } + + private String resolveCallType(int callTypeCode) { + switch (callTypeCode) { + case CallLog.Calls.OUTGOING_TYPE: + return "OUTGOING"; + case CallLog.Calls.INCOMING_TYPE: + return "INCOMING"; + case CallLog.Calls.MISSED_TYPE: + return "MISSED"; + case CallLog.Calls.VOICEMAIL_TYPE: + return "VOICEMAIL"; + case CallLog.Calls.REJECTED_TYPE: + return "REJECTED"; + case CallLog.Calls.BLOCKED_TYPE: + return "BLOCKED"; + case CallLog.Calls.ANSWERED_EXTERNALLY_TYPE: + return "ANSWERED_EXTERNALLY"; + default: + return "UNKNOWN"; + } + } + + private boolean shouldContinue(int limit, int count) { + return limit < 0 || count < limit; + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/avapp/callLogs/CallLogsModulePackage.java b/android/app/src/main/java/com/avapp/callLogs/CallLogsModulePackage.java new file mode 100644 index 00000000..c6235d84 --- /dev/null +++ b/android/app/src/main/java/com/avapp/callLogs/CallLogsModulePackage.java @@ -0,0 +1,29 @@ +package com.avapp.callLogs; + +import androidx.annotation.NonNull; + +import com.avapp.simDetails.SimDetailsModule; +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class CallLogsModulePackage implements ReactPackage { + @NonNull + @Override + public List createNativeModules(@NonNull ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + modules.add(new CallLogsModule(reactContext)); + return modules; + } + + @NonNull + @Override + public List createViewManagers(@NonNull ReactApplicationContext reactContext) { + return Collections.emptyList(); + } +} diff --git a/android/app/src/main/java/com/avapp/simDetails/SimDetailsModule.java b/android/app/src/main/java/com/avapp/simDetails/SimDetailsModule.java new file mode 100644 index 00000000..45b44871 --- /dev/null +++ b/android/app/src/main/java/com/avapp/simDetails/SimDetailsModule.java @@ -0,0 +1,78 @@ +package com.avapp.simDetails; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; + +import androidx.core.app.ActivityCompat; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.Arguments; + +import java.util.List; + +public class SimDetailsModule extends ReactContextBaseJavaModule { + + ReactApplicationContext reactContext; + + public SimDetailsModule(ReactApplicationContext reactContext) { + super(reactContext); + this.reactContext = reactContext; + } + + @Override + public String getName() { + return "SimDetails"; + } + + @ReactMethod + public void getSimDetails(Promise promise) { + try { + WritableMap result = Arguments.createMap(); + + boolean hasPermission = ActivityCompat.checkSelfPermission(reactContext, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED; + result.putBoolean("hasPermission", hasPermission); + + WritableArray simsArray = Arguments.createArray(); + + if (hasPermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + SubscriptionManager manager = (SubscriptionManager) reactContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + TelephonyManager telManager = (TelephonyManager) reactContext.getSystemService(Context.TELEPHONY_SERVICE); + + List subscriptionInfos = manager.getActiveSubscriptionInfoList(); + + if (subscriptionInfos != null) { + for (SubscriptionInfo subInfo : subscriptionInfos) { + WritableMap simInfo = Arguments.createMap(); + simInfo.putString("carrierName", subInfo.getCarrierName().toString()); + simInfo.putString("displayName", subInfo.getDisplayName().toString()); + simInfo.putString("countryCode", subInfo.getCountryIso()); + simInfo.putInt("mcc", subInfo.getMcc()); + simInfo.putInt("mnc", subInfo.getMnc()); + simInfo.putBoolean("isNetworkRoaming", telManager.isNetworkRoaming()); + simInfo.putBoolean("isDataRoaming", subInfo.getDataRoaming() == 1); + simInfo.putInt("simSlotIndex", subInfo.getSimSlotIndex()); + simInfo.putString("phoneNumber", subInfo.getNumber()); + simInfo.putString("simSerialNumber", subInfo.getIccId()); + simInfo.putInt("subscriptionId", subInfo.getSubscriptionId()); + + simsArray.pushMap(simInfo); + } + } + } + result.putArray("sims", simsArray); + promise.resolve(result); + } catch (Exception e) { + promise.reject("SIM_ERROR", "Failed to get SIM details", e); + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/avapp/simDetails/SimDetailsModulePackage.java b/android/app/src/main/java/com/avapp/simDetails/SimDetailsModulePackage.java new file mode 100644 index 00000000..54fe33ed --- /dev/null +++ b/android/app/src/main/java/com/avapp/simDetails/SimDetailsModulePackage.java @@ -0,0 +1,29 @@ +package com.avapp.simDetails; + +import androidx.annotation.NonNull; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SimDetailsModulePackage implements ReactPackage { + + @NonNull + @Override + public List createNativeModules(@NonNull ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + modules.add(new SimDetailsModule(reactContext)); + return modules; + } + + @NonNull + @Override + public List createViewManagers(@NonNull ReactApplicationContext reactContext) { + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/buildFlavor/field/buildNumber.txt b/buildFlavor/field/buildNumber.txt index 175b6c5d..10b0c0db 100644 --- a/buildFlavor/field/buildNumber.txt +++ b/buildFlavor/field/buildNumber.txt @@ -1 +1 @@ -263 +264 diff --git a/buildFlavor/field/buildVersion.txt b/buildFlavor/field/buildVersion.txt index 87ca3a54..21be78bd 100644 --- a/buildFlavor/field/buildVersion.txt +++ b/buildFlavor/field/buildVersion.txt @@ -1 +1 @@ -2.19.4 +2.19.5 diff --git a/package.json b/package.json index 15676614..8bab9862 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "AV_APP", - "version": "2.19.4", - "buildNumber": "263", + "version": "2.19.5", + "buildNumber": "264", "private": true, "scripts": { "android:dev": "yarn move:dev && react-native run-android", diff --git a/src/common/Constants.ts b/src/common/Constants.ts index 0c809fec..0bd4d345 100644 --- a/src/common/Constants.ts +++ b/src/common/Constants.ts @@ -1644,6 +1644,11 @@ export const CLICKSTREAM_EVENT_NAMES = { name: 'FA_MAP_VIEW_EXPERIMENT_ERROR', description: 'Map view fetch experiment error', }, + + FA_FETCH_SIM_DETAILS: { + name: 'FA_FETCH_SIM_DETAILS', + description: 'Fetch SIM details', + } } as const; export enum MimeType { diff --git a/src/components/utlis/DeviceUtils.ts b/src/components/utlis/DeviceUtils.ts index 778de79f..51d277e5 100644 --- a/src/components/utlis/DeviceUtils.ts +++ b/src/components/utlis/DeviceUtils.ts @@ -1,12 +1,14 @@ import { NativeModules } from 'react-native'; -import {GenericObject} from "@common/GenericTypes"; +import { GenericObject } from '@common/GenericTypes'; +import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants'; +import { addClickstreamEvent } from '@services/clickstreamEventService'; interface CrashError { message: string; screenName: string; } -const { DeviceUtilsModule, RNRestart } = NativeModules; // this is the same name we returned in getName function. +const { DeviceUtilsModule, RNRestart, SimDetails } = NativeModules; // this is the same name we returned in getName function. export const restartJSBundle = () => RNRestart.restart(); @@ -27,7 +29,9 @@ export type buildConfig = { export const getBuildInfo = (): Promise => DeviceUtilsModule.getBuildInfo(); -export const getBlacklistedApps = (blacklistedAppsObject: Record): Promise => DeviceUtilsModule.getBlacklistedApps(blacklistedAppsObject); +export const getBlacklistedApps = ( + blacklistedAppsObject: Record +): Promise => DeviceUtilsModule.getBlacklistedApps(blacklistedAppsObject); export const alfredHandleSWWEvent = (error: Error) => { const { message = '', stack = '', name = '' } = error; @@ -72,8 +76,20 @@ export const sendContentToWhatsapp = ( export const isAppInstalled = (packageName: string): Promise => DeviceUtilsModule.isAppInstalled(packageName); -export const getAppInfo = (packageName: string): Promise => DeviceUtilsModule.getAppInfo(packageName); +export const getAppInfo = (packageName: string): Promise => + DeviceUtilsModule.getAppInfo(packageName); -export const getAppInfoFromFilePath = (filePath: string): Promise => DeviceUtilsModule.getAppInfoFromFilePath(filePath); +export const getAppInfoFromFilePath = (filePath: string): Promise => + DeviceUtilsModule.getAppInfoFromFilePath(filePath); -export const restartApp = ():Promise => DeviceUtilsModule.restartApp(); +export const restartApp = (): Promise => DeviceUtilsModule.restartApp(); + +export const fetchSimDetails = async () => { + try { + const details = await SimDetails.getSimDetails(); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_FETCH_SIM_DETAILS, { details }); + return details; + } catch (err) { + return null; + } +}; diff --git a/src/services/callLogSync.service.ts b/src/services/callLogSync.service.ts index 58c0000e..c335aa55 100644 --- a/src/services/callLogSync.service.ts +++ b/src/services/callLogSync.service.ts @@ -1,20 +1,14 @@ -// @ts-expect-error -import CallLogs from 'react-native-call-log'; // package does not have typescript implementation, used any type here import { DATA_SYNC_ENUM } from './dataSync.service'; import { getGzipData, getMaxByPropFromList } from '../components/utlis/commonFunctions'; import { API_STATUS_CODE } from '../components/utlis/apiHelper'; import { logError } from '../components/utlis/errorUtils'; -import { Permission, PermissionsAndroid } from 'react-native'; +import { NativeModules, Permission, PermissionsAndroid } from 'react-native'; import { addClickstreamEvent } from './clickstreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants'; import axios from 'axios'; const MAXIMUM_NUMBER_CALL_LOGS = 1000; -const callLogFilter = { - minTimestamp: 0, -}; - enum CallTypeEnum { OUTGOING = 'OUTGOING', INCOMING = 'INCOMING', @@ -34,6 +28,11 @@ interface ICallLogs { rawType: number; timestamp: string; type: CallTypeEnum; + isHDCall: boolean; + isPulledExternally: boolean; + isVideoCall: boolean; + isVolte: boolean; + isWiFiCall: boolean; } interface ICallLogsDetails { @@ -42,6 +41,11 @@ interface ICallLogsDetails { duration?: number; name?: string; type?: CallTypeEnum; + isHDCall?: boolean; + isPulledExternally?: boolean; + isVideoCall?: boolean; + isVolte?: boolean; + isWiFiCall?: boolean; } interface ICallLogsDataPayload { @@ -71,15 +75,22 @@ export const callLogSyncServiceTele = async (url: string, syncFrom: string) => { const isCallLogsReadPermissionGranted = await checkReadPermissions( PermissionsAndroid.PERMISSIONS.READ_CALL_LOG ); + const { CallLogsDetails } = NativeModules; + if (isCallLogsReadPermissionGranted) { return new Promise((resolve, reject) => { - CallLogs.loadAll().then(async (callLogJson: ICallLogs[]) => { + CallLogsDetails.loadAll().then(async (callLogJson: ICallLogs[]) => { const callLogsDetailsList: ICallLogsDetails[] = callLogJson.map((callLogItem) => ({ phoneNumber: callLogItem.phoneNumber, timestamp: Number.parseFloat(callLogItem.timestamp), duration: callLogItem.duration, name: callLogItem.name, type: callLogItem.type, + isHDCall: callLogItem.isHDCall, + isPulledExternally: callLogItem.isPulledExternally, + isVideoCall: callLogItem.isVideoCall, + isVolte: callLogItem.isVolte, + isWiFiCall: callLogItem.isWiFiCall, })); const maxCallLogsTimeStamp = getMaxByPropFromList( @@ -118,11 +129,13 @@ export const callLogSyncService = async (url: string, syncFrom: string) => { const isCallLogsReadPermissionGranted = await checkReadPermissions( PermissionsAndroid.PERMISSIONS.READ_CALL_LOG ); + const { CallLogsDetails } = NativeModules; + if (isCallLogsReadPermissionGranted) { return new Promise((resolve, reject) => { - if (syncFrom) callLogFilter.minTimestamp = parseFloat(syncFrom); - - CallLogs.load(MAXIMUM_NUMBER_CALL_LOGS, callLogFilter).then( + CallLogsDetails.loadWithFilter(MAXIMUM_NUMBER_CALL_LOGS, { + minTimestamp: syncFrom?.toString() || '0', + }).then( async (callLogJson: Array) => { const callLogsDetailsList: ICallLogsDetails[] = callLogJson.map((callLogItem) => ({ phoneNumber: callLogItem.phoneNumber, @@ -130,6 +143,11 @@ export const callLogSyncService = async (url: string, syncFrom: string) => { duration: callLogItem.duration, name: callLogItem.name, type: callLogItem.type, + isHDCall: callLogItem.isHDCall, + isPulledExternally: callLogItem.isPulledExternally, + isVideoCall: callLogItem.isVideoCall, + isVolte: callLogItem.isVolte, + isWiFiCall: callLogItem.isWiFiCall, })); const maxCallLogsTimeStamp = getMaxByPropFromList(