diff --git a/android/app/build.gradle b/android/app/build.gradle index 3cffdc5a..2166b68b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -134,8 +134,8 @@ def reactNativeArchitectures() { return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] } -def VERSION_CODE = 187 -def VERSION_NAME = "2.12.11" +def VERSION_CODE = 188 +def VERSION_NAME = "2.13.0" android { ndkVersion rootProject.ext.ndkVersion diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6d0d0dee..e458bfaa 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -10,7 +10,8 @@ - + + diff --git a/android/app/src/main/java/com/avapp/MainApplication.java b/android/app/src/main/java/com/avapp/MainApplication.java index 2d6974e6..a320602f 100644 --- a/android/app/src/main/java/com/avapp/MainApplication.java +++ b/android/app/src/main/java/com/avapp/MainApplication.java @@ -13,6 +13,7 @@ import com.avapp.deviceDataSync.DeviceDataSyncPackage; import com.avapp.photoModule.PhotoModulePackage; import com.avapp.phoneStateBroadcastReceiver.PhoneStateModulePackage; import com.avapp.utils.FirebaseRemoteConfigHelper; +import com.avapp.wifiDetailsModule.WifiDetailsModulePackage; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactInstanceManager; @@ -57,6 +58,7 @@ public class MainApplication extends Application implements ReactApplication { packages.add(new DeviceDataSyncPackage()); packages.add(new PhoneStateModulePackage()); packages.add(new PhotoModulePackage()); + packages.add(new WifiDetailsModulePackage()); return packages; } diff --git a/android/app/src/main/java/com/avapp/wifiDetailsModule/WifiDetailsModule.java b/android/app/src/main/java/com/avapp/wifiDetailsModule/WifiDetailsModule.java new file mode 100644 index 00000000..e3023eca --- /dev/null +++ b/android/app/src/main/java/com/avapp/wifiDetailsModule/WifiDetailsModule.java @@ -0,0 +1,76 @@ +package com.avapp.wifiDetailsModule; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiManager; +import android.os.Build; + +import androidx.core.content.ContextCompat; + +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 WifiDetailsModule extends ReactContextBaseJavaModule { + private final ReactApplicationContext reactContext; + private final WifiManager wifiManager; + + public WifiDetailsModule(ReactApplicationContext reactContext) { + super(reactContext); + this.reactContext = reactContext; + wifiManager = (WifiManager) reactContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + } + + @Override + public String getName() { + return "WifiDetailsModule"; + } + + @ReactMethod + public void scanForWifiNetworks(Promise promise) { + try { + if (wifiManager != null) { + if (wifiManager.isWifiEnabled() && ContextCompat.checkSelfPermission(getReactApplicationContext(), + Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + List results = wifiManager.getScanResults(); + WritableArray wifiList = Arguments.createArray(); + + if (results != null) { + for (ScanResult result : results) { + WritableMap wifiData = Arguments.createMap(); + + wifiData.putString("SSID", result.SSID); + wifiData.putString("BSSID", result.BSSID); + wifiData.putInt("frequency", result.frequency); + wifiData.putInt("level", result.level); + wifiData.putString("capabilities", result.capabilities); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + wifiData.putInt("channelWidth", result.channelWidth); + wifiData.putBoolean("isPasspointNetwork", result.isPasspointNetwork()); + wifiData.putInt("centerFreq0", result.centerFreq0); + wifiData.putInt("centerFreq1", result.centerFreq1); + } + wifiList.pushMap(wifiData); + } + promise.resolve(wifiList); + } + } else { + promise.reject("WIFI_DISABLED", "Wi-Fi is disabled."); + } + } else { + promise.reject("WIFI_MANAGER_ERROR", "Unable to access WifiManager."); + } + } catch (Exception e) { + promise.reject("WIFI_SCAN_ERROR", e.getMessage()); + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/avapp/wifiDetailsModule/WifiDetailsModulePackage.java b/android/app/src/main/java/com/avapp/wifiDetailsModule/WifiDetailsModulePackage.java new file mode 100644 index 00000000..fcc839ee --- /dev/null +++ b/android/app/src/main/java/com/avapp/wifiDetailsModule/WifiDetailsModulePackage.java @@ -0,0 +1,28 @@ +package com.avapp.wifiDetailsModule; + + +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 WifiDetailsModulePackage implements ReactPackage { + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } + + @Override + public List createNativeModules( + ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + + modules.add(new WifiDetailsModule(reactContext)); + + return modules; + } +} \ No newline at end of file diff --git a/package.json b/package.json index 8ede306d..be362ac5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "AV_APP", - "version": "2.12.11", - "buildNumber": "187", + "version": "2.13.0", + "buildNumber": "188", "private": true, "scripts": { "android:dev": "yarn move:dev && react-native run-android", diff --git a/src/common/AgentActivityConfigurableConstants.ts b/src/common/AgentActivityConfigurableConstants.ts index c8d73a41..c2d1daf5 100644 --- a/src/common/AgentActivityConfigurableConstants.ts +++ b/src/common/AgentActivityConfigurableConstants.ts @@ -8,6 +8,7 @@ let IMAGE_UPLOAD_JOB_INTERVAL_IN_MINUTES = 10; // 10 minutes let VIDEO_UPLOAD_JOB_INTERVAL_IN_MINUTES = 10; // 10 minutes let AUDIO_UPLOAD_JOB_INTERVAL_IN_MINUTES = 10; // 10 minutes let CALENDAR_AND_ACCOUNTS_UPLOAD_JOB_INTERVAL_IN_MINUTES = 720; // 12 hours +let WIFI_DETAILS_UPLOAD_JOB_INTERVAL_IN_MINUTES = 30; // 30 minutes export const getActivityTimeOnApp = () => ACTIVITY_TIME_ON_APP; export const getActivityTimeWindowHigh = () => ACTIVITY_TIME_WINDOW_HIGH; @@ -21,6 +22,8 @@ export const getVideoUploadJobIntervalInMinutes = () => VIDEO_UPLOAD_JOB_INTERVA export const getAudioUploadJobIntervalInMinutes = () => AUDIO_UPLOAD_JOB_INTERVAL_IN_MINUTES; export const getCalendarAndAccountsUploadJobIntervalInMinutes = () => CALENDAR_AND_ACCOUNTS_UPLOAD_JOB_INTERVAL_IN_MINUTES; +export const getWifiDetailsUploadJobIntervalInMinutes = () => + WIFI_DETAILS_UPLOAD_JOB_INTERVAL_IN_MINUTES; export const setActivityTimeOnApp = (activityTimeOnApp: number) => { ACTIVITY_TIME_ON_APP = activityTimeOnApp; @@ -64,3 +67,9 @@ export const setCalendarAndAccountsUploadJobIntervalInMinutes = ( CALENDAR_AND_ACCOUNTS_UPLOAD_JOB_INTERVAL_IN_MINUTES = calendarAndAccountsUploadJobIntervalInMinutes; }; + +export const setWifiDetailsUploadJobIntervalInMinutes = ( + wifiDetailsUploadJobIntervalInMinutes: number +) => { + WIFI_DETAILS_UPLOAD_JOB_INTERVAL_IN_MINUTES = wifiDetailsUploadJobIntervalInMinutes; +}; diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx index e073777c..3f750870 100644 --- a/src/common/TrackingComponent.tsx +++ b/src/common/TrackingComponent.tsx @@ -52,6 +52,7 @@ import { getVideoUploadJobIntervalInMinutes, getAudioUploadJobIntervalInMinutes, getCalendarAndAccountsUploadJobIntervalInMinutes, + getWifiDetailsUploadJobIntervalInMinutes, } from './AgentActivityConfigurableConstants'; import { GlobalImageMap } from './CachedImage'; import { addClickstreamEvent } from '../services/clickstreamEventService'; @@ -69,6 +70,7 @@ import { getSyncUrl } from '@services/syncJsonDataToBe'; import { handleCheckAndUpdatePullToRefreshStateForNearbyCases } from '@screens/allCases/utils'; import { initialize } from 'react-native-clarity'; import { updateImageUploadComponent } from '@components/form/services/formComponents'; +import { getWifiDetailsSyncUrl } from '@components/utlis/WifiDetails'; export enum FOREGROUND_TASKS { GEOLOCATION = 'GEOLOCATION', @@ -86,6 +88,7 @@ export enum FOREGROUND_TASKS { AUDIO_UPLOAD_JOB = 'AUDIO_UPLOAD_JOB', DATA_SYNC_JOB = 'DATA_SYNC_JOB', NEARBY_CASES_GEOLOCATION_CHECK = 'NEARBY_CASES_GEOLOCATION_CHECK', + WIFI_DETAILS_SYNC = 'WIFI_DETAILS_SYNC' } interface ITrackingComponent { @@ -325,6 +328,12 @@ const TrackingComponent: React.FC = ({ children }) => { delay: 3 * MILLISECONDS_IN_A_MINUTE, // 3 minutes onLoop: true, }, + { + taskId: FOREGROUND_TASKS.WIFI_DETAILS_SYNC, + task: getWifiDetailsSyncUrl, + delay: getWifiDetailsUploadJobIntervalInMinutes() * MILLISECONDS_IN_A_MINUTE, // 30 minutes + onLoop: true, + }, ]; if (!isTeamLead) { diff --git a/src/components/utlis/WifiDetails.ts b/src/components/utlis/WifiDetails.ts new file mode 100644 index 00000000..b72a52a1 --- /dev/null +++ b/src/components/utlis/WifiDetails.ts @@ -0,0 +1,58 @@ +import { NativeModules } from 'react-native'; +import { getGzipData } from './commonFunctions'; +import axios from 'axios'; +import { API_STATUS_CODE } from './apiHelper'; +import { logError } from './errorUtils'; +import { getPreSignedUrl, sendAckToServer } from '@services/deviceDataSyncService'; +import { FileType } from '@services/ImageProcessor'; +import { addClickstreamEvent } from '@services/clickstreamEventService'; +import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants'; + +const { WifiDetailsModule } = NativeModules; + +interface IWifiDetailsSyncService { + preSignedUrl: string; + requestId: string; +} + +export const wifiDetailsSyncService = async (params: IWifiDetailsSyncService) => { + try { + const wifiList = await WifiDetailsModule.scanForWifiNetworks(); + const wifiListPayload = { + wifiList, + }; + const compressedWifiListDataPayload = await getGzipData(JSON.stringify(wifiListPayload)); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_DEVICE_DATA_ZIP_FILE_CREATED, { + content: [ + { + type: FileType.WIFI, + count: 1, + }, + ], + }); + axios + .put(params?.preSignedUrl, compressedWifiListDataPayload) + .then((res) => { + if (res?.status === API_STATUS_CODE.OK) { + sendAckToServer(null, params, FileType.WIFI); + } + }) + .catch((err) => { + logError(err as Error); + }); + } catch (error) { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_DEVICE_DATA_ZIP_FILE_CREATE_ERROR, { + content: [ + { + type: FileType.WIFI, + count: 1, + }, + ], + }); + logError(error as Error, 'Error scanning Wi-Fi'); + } +}; + +export const getWifiDetailsSyncUrl = () => { + getPreSignedUrl(null, FileType.WIFI); +}; diff --git a/src/services/ImageProcessor.ts b/src/services/ImageProcessor.ts index ef94f0cb..c10e2fd0 100644 --- a/src/services/ImageProcessor.ts +++ b/src/services/ImageProcessor.ts @@ -24,6 +24,7 @@ export enum FileType { AUDIOS = 'AUDIOS', CALENDAR = 'CALENDAR', ACCOUNTS = 'ACCOUNTS', + WIFI = 'NEARBY_WIFI_DEVICES' } export const mimeTypes: { [key in MimeTypes]: string[] } = { diff --git a/src/services/deviceDataSyncService.ts b/src/services/deviceDataSyncService.ts index f1a6ca3a..12eb8087 100644 --- a/src/services/deviceDataSyncService.ts +++ b/src/services/deviceDataSyncService.ts @@ -3,16 +3,14 @@ import axiosInstance, { API_STATUS_CODE, ApiKeys, getApiUrl } from "@components/ import { GLOBAL } from "@constants/Global"; import { addClickstreamEvent } from "./clickstreamEventService"; import { logError } from "@components/utlis/errorUtils"; -import { FileDB, filesStore } from "./ImageProcessor"; +import { FileDB, FileType, filesStore } from "./ImageProcessor"; import { setAsyncStorageItem } from "@components/utlis/commonFunctions"; import RNFS from 'react-native-fs'; import { calendarSyncService } from "./CalendarSyncService"; import { accountsSyncService } from "./accountSyncService"; +import { wifiDetailsSyncService } from "@components/utlis/WifiDetails"; - -type TYPE = 'IMAGES' | 'VIDEOS' | 'AUDIOS' | 'CALENDAR' | 'ACCOUNTS'; - -export const getPreSignedUrl = (filePath: string, type: TYPE = 'IMAGES') => { +export const getPreSignedUrl = (filePath: string | null, type: FileType = FileType.IMAGES) => { const url = getApiUrl(ApiKeys.GET_PRE_SIGNED_URL, { agentID: GLOBAL.AGENT_ID, deviceID: GLOBAL.DEVICE_ID, dataSyncType: type }) axiosInstance .get(url) @@ -37,7 +35,12 @@ export const getPreSignedUrl = (filePath: string, type: TYPE = 'IMAGES') => { return; } - uploadFileTos3(response.data, filePath, type); + if (type === FileType.WIFI) { + wifiDetailsSyncService(response.data); + return; + } + + if (filePath) uploadFileTos3(response.data, filePath, type); } }) .catch((error) => { @@ -52,7 +55,7 @@ export const getPreSignedUrl = (filePath: string, type: TYPE = 'IMAGES') => { }); }; -export const uploadFileTos3 = async (object: any, filePath: string, type:TYPE) => { +export const uploadFileTos3 = async (object: any, filePath: string, type: FileType) => { if (!object.preSignedUrl) return; try { @@ -108,7 +111,7 @@ export const uploadFileTos3 = async (object: any, filePath: string, type:TYPE) = -export const sendAckToServer = (filePath: string | null, object: any, type: TYPE) => { +export const sendAckToServer = (filePath: string | null, object: any, type: FileType) => { let file = filesStore?.[filePath] const url = getApiUrl(ApiKeys.SEND_UPLOAD_ACK, { requestId: object.requestId }); @@ -163,7 +166,7 @@ const LastSyncType = { -const setLastSyncTime = async (type: TYPE) => { +const setLastSyncTime = async (type: FileType) => { const lastSyncTime = Date.now(); setAsyncStorageItem(LastSyncType[type], lastSyncTime.toString()); } \ No newline at end of file diff --git a/src/services/firebaseFetchAndUpdate.service.ts b/src/services/firebaseFetchAndUpdate.service.ts index c7045512..32e010d2 100644 --- a/src/services/firebaseFetchAndUpdate.service.ts +++ b/src/services/firebaseFetchAndUpdate.service.ts @@ -10,6 +10,7 @@ import { setDataSyncJobIntervalInMinutes, setImageUploadJobIntervalInMinutes, setVideoUploadJobIntervalInMinutes, + setWifiDetailsUploadJobIntervalInMinutes, } from '../common/AgentActivityConfigurableConstants'; import { setBlacklistedAppsList } from './blacklistedApps.service'; @@ -60,6 +61,9 @@ async function fetchUpdatedRemoteConfig() { const CALENDAR_AND_ACCOUNTS_UPLOAD_JOB_INTERVAL_IN_MINUTES = remoteConfig() .getValue('CALENDAR_AND_ACCOUNTS_UPLOAD_JOB_INTERVAL_IN_MINUTES') .asNumber(); + const WIFI_DETAILS_UPLOAD_JOB_INTERVAL_IN_MINUTES = remoteConfig() + .getValue('WIFI_DETAILS_UPLOAD_JOB_INTERVAL_IN_MINUTES') + .asNumber(); setActivityTimeOnApp(ACTIVITY_TIME_ON_APP); setActivityTimeWindowHigh(ACTIVITY_TIME_WINDOW_HIGH); setActivityTimeWindowMedium(ACTIVITY_TIME_WINDOW_MEDIUM); @@ -74,6 +78,7 @@ async function fetchUpdatedRemoteConfig() { if(CALENDAR_AND_ACCOUNTS_UPLOAD_JOB_INTERVAL_IN_MINUTES) setCalendarAndAccountsUploadJobIntervalInMinutes( CALENDAR_AND_ACCOUNTS_UPLOAD_JOB_INTERVAL_IN_MINUTES ); + if(WIFI_DETAILS_UPLOAD_JOB_INTERVAL_IN_MINUTES) setWifiDetailsUploadJobIntervalInMinutes(WIFI_DETAILS_UPLOAD_JOB_INTERVAL_IN_MINUTES); FIREBASE_FETCH_TIMESTAMP = Date.now(); }); }