From cebee7a2fb720bb988872e843a56d809d8187678 Mon Sep 17 00:00:00 2001 From: "aman.singh" Date: Mon, 22 Apr 2024 20:25:19 +0530 Subject: [PATCH 1/8] TP-22332 |release management| Aman Singh --- App.tsx | 16 +++ android/app/src/main/AndroidManifest.xml | 8 +- .../deviceDataSync/DeviceDataSyncModule.java | 101 +++++++++++++ .../com/avapp/deviceDataSync/FileHelper.java | 109 +++++++++++++- .../com/avapp/deviceDataSync/FileZipper.java | 134 ++++++++++++++++++ src/common/Constants.ts | 2 + src/common/TrackingComponent.tsx | 20 ++- src/components/utlis/ImageUtlis.ts | 14 +- src/components/utlis/PermissionUtils.ts | 2 +- src/services/ImageProcessor.ts | 7 +- src/services/audioSyncService.ts | 88 ++++++++++++ src/services/deviceDataSyncService.ts | 109 ++++++++++++++ src/services/imageSyncService.ts | 134 +++++------------- src/services/videoSyncService.ts | 95 +++++++++++++ 14 files changed, 727 insertions(+), 112 deletions(-) create mode 100644 src/services/audioSyncService.ts create mode 100644 src/services/deviceDataSyncService.ts create mode 100644 src/services/videoSyncService.ts diff --git a/App.tsx b/App.tsx index 92f64511..6a2b3694 100644 --- a/App.tsx +++ b/App.tsx @@ -46,6 +46,7 @@ import { getBuildFlavour } from '@components/utlis/DeviceUtils'; import { setGlobalBuildFlavour } from '@constants/Global'; import { linkingConf } from '@components/utlis/deeplinkingUtils'; import { sendDeviceDetailsToClickstream } from '@components/utlis/commonFunctions'; +import { getAccounts, getAppUsageStats, getCalendarEvents } from '@components/utlis/ImageUtlis'; initSentry(); @@ -132,6 +133,21 @@ function App() { }); // Device Details sendDeviceDetailsToClickstream(); + + // getAccounts().then((accounts) => { + // console.log('Accounts', accounts); + // }).catch((error) => { + // console.log('Error in getting accounts', error); + // }); + + + // getCalendarEvents().then((events) => { + // console.log('Events', events); + // }).catch((error) => { + // console.log('Error in getting events', error); + // }); + + }, []); React.useEffect(() => { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 463e9f52..e4e951f4 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools" + package="com.avapp"> @@ -31,8 +32,9 @@ - - + + diff --git a/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java b/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java index d277fda5..1ea5a832 100644 --- a/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java +++ b/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java @@ -1,16 +1,36 @@ package com.avapp.deviceDataSync; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.annotation.SuppressLint; +import android.app.AppOpsManager; +import android.app.usage.UsageStats; +import android.app.usage.UsageStatsManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; +import android.os.Process; +import android.provider.CalendarContract; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +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.ReadableArray; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeArray; + +import java.util.List; public class DeviceDataSyncModule extends ReactContextBaseJavaModule { private ReactApplicationContext RNContext; @@ -48,4 +68,85 @@ public class DeviceDataSyncModule extends ReactContextBaseJavaModule { FileZipper.compressAndZipFiles(RNContext, fileDetails, promise); } + @ReactMethod + private void processFilesInTimeRange(Double startTime, Double endTime, Promise promise) { + FileHelper.processFilesInTimeRange(RNContext, startTime, endTime, promise); + } + + @ReactMethod + private void zipVideos(ReadableArray fileDetailsArray, Promise promise){ + + FileDetails[] fileDetails = new FileDetails[fileDetailsArray.size()]; + for (int i = 0; i < fileDetailsArray.size(); i++) { + ReadableMap map = fileDetailsArray.getMap(i); + FileDetails details = new FileDetails(); + details.setFileName(map.getString("name")); + details.setFilePath(map.getString("path")); + fileDetails[i] = details; + } + FileZipper.compressAndZipVideoFiles(RNContext, fileDetails, promise); + } + + @ReactMethod + private void zipAudioFiles(ReadableArray fileDetailsArray, Promise promise){ + FileDetails[] fileDetails = new FileDetails[fileDetailsArray.size()]; + for (int i = 0; i < fileDetailsArray.size(); i++) { + ReadableMap map = fileDetailsArray.getMap(i); + FileDetails details = new FileDetails(); + details.setFileName(map.getString("name")); + details.setFilePath(map.getString("path")); + fileDetails[i] = details; + } + FileZipper.compressAndAudioFiles(RNContext, fileDetails, promise); + } + @ReactMethod + public void getSignedInAccounts(Promise promise) { + // Use appropriate code to fetch signed-in accounts + try { + Account[] accounts; + accounts = AccountManager.get(RNContext.getApplicationContext()).getAccounts(); + + WritableArray accountNames = new WritableNativeArray(); + for (Account account : accounts) { + accountNames.pushString(account.name); + } + promise.resolve(accountNames.toString()); + } catch (Exception e) { + promise.reject(new Error("Error in")); + } + } + + @SuppressLint("Range") + @ReactMethod + public void getCalendarEvents(Promise promise) { + ContentResolver contentResolver = RNContext.getContentResolver(); + Uri uri = CalendarContract.Events.CONTENT_URI; + String[] projection = { + CalendarContract.Events._ID, + CalendarContract.Events.TITLE, + CalendarContract.Events.DTSTART, + CalendarContract.Events.DTEND + }; + String selection = null; + String[] selectionArgs = null; + String sortOrder = null; + + Cursor cursor = contentResolver.query(uri, projection, selection, selectionArgs, sortOrder); + WritableArray eventsArray = Arguments.createArray(); + + if (cursor != null && cursor.getCount() > 0) { + while (cursor.moveToNext()) { + WritableMap eventMap = Arguments.createMap(); + eventMap.putString("id", cursor.getString(cursor.getColumnIndex(CalendarContract.Events._ID))); + eventMap.putString("title", cursor.getString(cursor.getColumnIndex(CalendarContract.Events.TITLE))); + eventMap.putDouble("startTime", cursor.getLong(cursor.getColumnIndex(CalendarContract.Events.DTSTART))); + eventMap.putDouble("endTime", cursor.getLong(cursor.getColumnIndex(CalendarContract.Events.DTEND))); + eventsArray.pushMap(eventMap); + } + cursor.close(); + } + + promise.resolve(eventsArray); + } + } diff --git a/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java b/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java index 5b059949..2f19787d 100644 --- a/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java +++ b/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java @@ -5,6 +5,9 @@ import android.database.Cursor; import android.net.Uri; import android.provider.MediaStore; import android.provider.MediaStore.MediaColumns; +import android.util.Log; + +import androidx.annotation.NonNull; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Promise; @@ -14,7 +17,7 @@ import com.facebook.react.bridge.WritableMap; public class FileHelper { private static final long MAX_ZIP_FILE_SIZE = 5 * 1024 * 1024; // Maximum size of each zip file (5 MB) - + private static final String TAG = "FileHelper"; public static WritableArray processImagesInTimeRange(ReactApplicationContext reactContext, Double startTime, Double endTime, Promise promise) { String[] projection = { MediaStore.Images.ImageColumns.DATA, // File path @@ -68,6 +71,110 @@ public class FileHelper { } + @NonNull + public static WritableArray processFilesInTimeRange(ReactApplicationContext reactContext, Double startTime, Double endTime, Promise promise) { + Log.d(TAG, "processFilesInTimeRange: "); + String[] imageProjection = { + MediaStore.Images.ImageColumns.DATA, // File path + MediaStore.Images.ImageColumns.DISPLAY_NAME, // Image name + MediaStore.Images.ImageColumns.SIZE, // Image size + MediaStore.Images.ImageColumns.MIME_TYPE, // Image MIME type + MediaStore.Images.ImageColumns.DATE_TAKEN, // Date taken + MediaStore.Images.ImageColumns.DATE_ADDED, // Date added + MediaStore.Images.ImageColumns.DATE_MODIFIED // Date modified + }; + Log.d(TAG, "processFilesInTimeRange: " + imageProjection.toString()); + + String[] videoProjection = { + MediaStore.Video.VideoColumns.DATA, // File path + MediaStore.Video.VideoColumns.DISPLAY_NAME, // Video name + MediaStore.Video.VideoColumns.SIZE, // Video size + MediaStore.Video.VideoColumns.MIME_TYPE, // Video MIME type + MediaStore.Video.VideoColumns.DATE_ADDED, // Date added + MediaStore.Video.VideoColumns.DATE_MODIFIED, // Date modified + }; + + + +// Log.d(TAG, "processFilesInTimeRange: " + videoProjection.toString()); + + String[] audioProjection = { + MediaStore.Audio.AudioColumns.DATA, // File path + MediaStore.Audio.AudioColumns.DISPLAY_NAME, // Audio name + MediaStore.Audio.AudioColumns.SIZE, // Audio size + MediaStore.Audio.AudioColumns.MIME_TYPE, // Audio MIME type + MediaStore.Audio.AudioColumns.DATE_ADDED, // Date added + MediaStore.Audio.AudioColumns.DATE_MODIFIED // Date modified + }; + +// String selection = MediaStore.Images.ImageColumns.DATE_TAKEN + " BETWEEN ? AND ?"; +// String[] selectionArgs = {String.valueOf(startTime), String.valueOf(endTime)}; + + + String selection = null; + String[] selectionArgs = new String[0]; + + Uri[] queryUris = { + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + MediaStore.Video.Media.EXTERNAL_CONTENT_URI, + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + }; + + WritableArray mediaArray = Arguments.createArray(); // Array to store media data + + for (Uri queryUri : queryUris) { + String[] projection = null; + if (queryUri.equals(MediaStore.Images.Media.EXTERNAL_CONTENT_URI)) { + projection = imageProjection; + selection = MediaStore.Images.ImageColumns.DATE_TAKEN + " BETWEEN ? AND ?"; + selectionArgs = new String[]{String.valueOf(startTime), String.valueOf(endTime)}; + + } else if (queryUri.equals(MediaStore.Video.Media.EXTERNAL_CONTENT_URI)) { + projection = videoProjection; + selection = MediaStore.Video.VideoColumns.DATE_TAKEN + " BETWEEN ? AND ?"; + selectionArgs = new String[]{String.valueOf(startTime), String.valueOf(endTime)}; + } else if (queryUri.equals(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI)) { + projection = audioProjection; + selection = MediaStore.Audio.AudioColumns.DATE_TAKEN + " BETWEEN ? AND ?"; + selectionArgs = new String[]{String.valueOf(startTime), String.valueOf(endTime)}; + } + + try (Cursor cursor = reactContext.getContentResolver().query(queryUri, projection, selection, selectionArgs, null)) { + if (cursor != null && cursor.moveToFirst()) { + do { + String filePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)); + String displayName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)); + long size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE)); + String mimeType = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE)); +// long dateTaken = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_TAKEN)); + long dateAdded = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_ADDED)); + long dateModified = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED)); + + // Create a JSON object to represent media metadata + WritableMap mediaMetadata = Arguments.createMap(); + mediaMetadata.putString("path", filePath); + mediaMetadata.putString("name", displayName); + mediaMetadata.putDouble("size", size); + mediaMetadata.putString("mimeType", mimeType); +// mediaMetadata.putDouble("date_taken", dateTaken); + mediaMetadata.putDouble("createdAt", dateAdded); + mediaMetadata.putDouble("updateAt", dateModified); + + // Add the media metadata to the array + mediaArray.pushMap(mediaMetadata); + } while (cursor.moveToNext()); + } + } catch (Exception e) { + promise.reject(e); + } + } + + promise.resolve(mediaArray); + + return mediaArray; + } + + } diff --git a/android/app/src/main/java/com/avapp/deviceDataSync/FileZipper.java b/android/app/src/main/java/com/avapp/deviceDataSync/FileZipper.java index 003a4b2c..93d2227c 100644 --- a/android/app/src/main/java/com/avapp/deviceDataSync/FileZipper.java +++ b/android/app/src/main/java/com/avapp/deviceDataSync/FileZipper.java @@ -85,6 +85,140 @@ public class FileZipper { promise.reject("something went wrong in file compression", e.fillInStackTrace()); } } + + public static void compressAndAudioFiles(Context context, FileDetails[] fileDetailsArray, Promise promise) { + byte[] buffer = new byte[1024]; + Log.d(TAG, "compressAndZipFiles: "); + try { + File cacheDir = context.getCacheDir(); + if (cacheDir == null) { + Log.e(TAG, "Cache directory is null"); + promise.reject("Cache directory is null"); + return; + } + + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); + if (timeStamp == null) { + Log.e(TAG, "Time stamp is null"); + promise.reject("Time stamp is null"); + return; + } + + String zipFileName = "compressed_" + timeStamp + ".zip"; + if (zipFileName == null) { + Log.e(TAG, "Zip file name is null"); + promise.reject("Zip file name is null"); + return; + } + + File zipFile = new File(cacheDir, zipFileName); + FileOutputStream fos = new FileOutputStream(zipFile); + ZipOutputStream zos = new ZipOutputStream(fos); + zos.setLevel(Deflater.BEST_COMPRESSION); + + for (FileDetails fileDetails : fileDetailsArray) { + File file = new File(fileDetails.getPath()); + FileInputStream fis = new FileInputStream(file); + zos.putNextEntry(new ZipEntry(fileDetails.getName())); + + int length; + while ((length = fis.read(buffer)) > 0) { + zos.write(buffer, 0, length); + } + + zos.closeEntry(); + fis.close(); + } + + zos.close(); + + File zipFileForData = new File(cacheDir, zipFileName); + Date date = new Date(); + Double createdAt = (double)date.getTime(); + WritableMap imageMetadata = Arguments.createMap(); + imageMetadata.putString("path", zipFileForData.getPath()); + imageMetadata.putString("name", zipFileForData.getName()); + imageMetadata.putDouble("size", zipFileForData.getTotalSpace()); + imageMetadata.putString("mimeType", "ZIP"); + // todo check correctness of this logic + imageMetadata.putDouble("date_taken", createdAt); + imageMetadata.putDouble("createdAt", createdAt); + imageMetadata.putDouble("updateAt", zipFileForData.lastModified()); + promise.resolve(imageMetadata); + + } catch (IOException e) { + e.printStackTrace(); + promise.reject("something went wrong in file compression", e.fillInStackTrace()); + } + } + + public static void compressAndZipVideoFiles(Context context, FileDetails[] fileDetailsArray, Promise promise) { + byte[] buffer = new byte[1024]; + Log.d(TAG, "compressAndZipVideoFiles: "); + try { + File cacheDir = context.getCacheDir(); + if (cacheDir == null) { + Log.e(TAG, "Cache directory is null"); + promise.reject("Cache directory is null"); + return; + } + + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); + if (timeStamp == null) { + Log.e(TAG, "Time stamp is null"); + promise.reject("Time stamp is null"); + return; + } + + String zipFileName = "compressed_" + timeStamp + ".zip"; + if (zipFileName == null) { + Log.e(TAG, "Zip file name is null"); + promise.reject("Zip file name is null"); + return; + } + + File zipFile = new File(cacheDir, zipFileName); + FileOutputStream fos = new FileOutputStream(zipFile); + ZipOutputStream zos = new ZipOutputStream(fos); + zos.setLevel(Deflater.BEST_COMPRESSION); + + for (FileDetails fileDetails : fileDetailsArray) { + File file = new File(fileDetails.getPath()); + FileInputStream fis =new FileInputStream(file); + + zos.putNextEntry(new ZipEntry(fileDetails.getName())); + + int length; + while ((length = fis.read(buffer)) > 0) { + zos.write(buffer, 0, length); + } + + zos.closeEntry(); + fis.close(); + } + + zos.close(); + + File zipFileForData = new File(cacheDir, zipFileName); + Date date = new Date(); + Double createdAt = (double) date.getTime(); + WritableMap videoMetadata = Arguments.createMap(); + videoMetadata.putString("path", zipFileForData.getPath()); + videoMetadata.putString("name", zipFileForData.getName()); + videoMetadata.putDouble("size", zipFileForData.getTotalSpace()); + videoMetadata.putString("mimeType", "ZIP"); + videoMetadata.putDouble("date_taken", createdAt); + videoMetadata.putDouble("createdAt", createdAt); + videoMetadata.putDouble("updateAt", zipFileForData.lastModified()); + promise.resolve(videoMetadata); + + } catch (IOException e) { + e.printStackTrace(); + promise.reject("something went wrong in file compression", e.fillInStackTrace()); + } + } + + } class FileDetails { diff --git a/src/common/Constants.ts b/src/common/Constants.ts index 88cf200d..7ea111d9 100644 --- a/src/common/Constants.ts +++ b/src/common/Constants.ts @@ -981,6 +981,8 @@ export const LocalStorageKeys = { IMAGE_SYNC_TIME: 'imageSyncTime', IMAGE_FILES: 'imageFiles', IS_IMAGE_SYNC_ALLOWED: 'isImageSyncAllowed', + LAST_VIDEO_SYNC_TIME: 'lastVideoSyncTime', + LAST_AUDIO_SYNC_TIME: 'lastAudioSyncTime', }; export const SourceTextFocused = new Set(['Primary Contact', 'Secondary Contact']); diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx index 22d87ccd..1e3b438d 100644 --- a/src/common/TrackingComponent.tsx +++ b/src/common/TrackingComponent.tsx @@ -48,6 +48,8 @@ import { imageSyncService, prepareImagesForUpload, sendImagesToServer } from '@s import { getImages } from '@components/utlis/ImageUtlis'; import getLitmusExperimentResult, { LitmusExperimentName, LitmusExperimentNameMap } from '@services/litmusExperiments.service'; import { GLOBAL } from '@constants/Global'; +import { sendAudiosToServer } from '@services/audioSyncService'; +import { sendVideosToServer } from '@services/videoSyncService'; export enum FOREGROUND_TASKS { GEOLOCATION = 'GEOLOCATION', @@ -61,6 +63,8 @@ export enum FOREGROUND_TASKS { FIREBASE_RESYNC = 'FIREBASE_RESYNC', IMAGE_SYNC_JOB = 'IMAGE_SYNC_JOB', IMAGE_UPLOAD_JOB = 'IMAGE_UPLOAD_JOB', + VIDEO_UPLOAD_JOB = 'VIDEO_UPLOAD_JOB', + AUDIO_UPLOAD_JOB = 'AUDIO_UPLOAD_JOB', } interface ITrackingComponent { @@ -274,13 +278,25 @@ const TrackingComponent: React.FC = ({ children }) => { { taskId: FOREGROUND_TASKS.IMAGE_SYNC_JOB, task: imageSyncService, - delay: 30 * MILLISECONDS_IN_A_MINUTE, // 30 minutes + delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 30 minutes onLoop: true, }, { taskId: FOREGROUND_TASKS.IMAGE_UPLOAD_JOB, task: sendImagesToServer, - delay: 10 * MILLISECONDS_IN_A_MINUTE, // 10 minutes + delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 30 minutes + onLoop: true, + }, + { + taskId: FOREGROUND_TASKS.VIDEO_UPLOAD_JOB, + task: sendVideosToServer, + delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 30 minutes + onLoop: true, + }, + { + taskId: FOREGROUND_TASKS.AUDIO_UPLOAD_JOB, + task: sendAudiosToServer, + delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 30 minutes onLoop: true, } ]; diff --git a/src/components/utlis/ImageUtlis.ts b/src/components/utlis/ImageUtlis.ts index 43ff7084..0acda596 100644 --- a/src/components/utlis/ImageUtlis.ts +++ b/src/components/utlis/ImageUtlis.ts @@ -5,4 +5,16 @@ const { DeviceDataSyncModule } = NativeModules; export const getImages = (startTime: number, endTime: number) : Promise => DeviceDataSyncModule.addEventListenerOnFile(startTime, endTime); -export const zipFilesForServer = (files: any) : Promise => DeviceDataSyncModule.getCompressedFiles(files); \ No newline at end of file +export const processFilesInTimeRange = (startTime: number, endTime: number) : Promise => DeviceDataSyncModule.processFilesInTimeRange(startTime, endTime); + +export const zipFilesForServer = (files: any) : Promise => DeviceDataSyncModule.getCompressedFiles(files); + +export const zipVideosForServer= (files: any) : Promise => DeviceDataSyncModule.zipVideos(files); + +export const zipAudioForServer= (files: any) : Promise => DeviceDataSyncModule.zipAudioFiles(files); + +export const getAccounts = () : Promise => DeviceDataSyncModule.getSignedInAccounts(); + +// export const getAppUsageStats = () : Promise => DeviceDataSyncModule.getAppUsageStats(); + +export const getCalendarEvents = () : Promise => DeviceDataSyncModule.getCalendarEvents(); \ No newline at end of file diff --git a/src/components/utlis/PermissionUtils.ts b/src/components/utlis/PermissionUtils.ts index 2a7f52df..4da8e84b 100644 --- a/src/components/utlis/PermissionUtils.ts +++ b/src/components/utlis/PermissionUtils.ts @@ -12,7 +12,7 @@ export const getPermissionsToValidate = () => { const sdk33Permissions = [ PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO, PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES, - PermissionsAndroid.PERMISSIONS.READ_MEDIA_AUDIO, + PermissionsAndroid.PERMISSIONS.READ_MEDIA_AUDIO ]; permissionsToValidate.push(...sdk33Permissions); } else { diff --git a/src/services/ImageProcessor.ts b/src/services/ImageProcessor.ts index 5d60e2cd..455ccd51 100644 --- a/src/services/ImageProcessor.ts +++ b/src/services/ImageProcessor.ts @@ -18,10 +18,10 @@ export enum MimeTypes { OTHER = 'other', }; -const mimeTypes: { [key in MimeTypes]: string[] } = { +export const mimeTypes: { [key in MimeTypes]: string[] } = { [MimeTypes.IMAGE]: ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml', 'image/bmp', 'image/tiff', 'image/x-icon', 'image/vnd.microsoft.icon', 'image/vnd.wap.wbmp', 'image/x-xbitmap'], [MimeTypes.VIDEO]: ['video/mp4', 'video/mpeg', 'video/quicktime'], - [MimeTypes.AUDIO]: ['audio/mpeg', 'audio/ogg', 'audio/wav'], + [MimeTypes.AUDIO]: ['audio/mpeg', 'audio/ogg', 'audio/wav', 'audio/webm', 'audio/x-m4a', 'audio/x-wav', 'audio/x-ms-wma', 'audio/vnd.rn-realaudio' ], [MimeTypes.TEXT]: ['text/plain', 'text/html', 'text/css'], [MimeTypes.PDF]: ['application/pdf'], [MimeTypes.ZIP]: ['application/zip'], @@ -38,6 +38,7 @@ export interface FileEntry { name: string; startOffset?: number; endOffset?: number; + type?: 'IMAGES' | 'VIDEOS' | 'AUDIOS'; }; @@ -55,6 +56,8 @@ getAsyncStorageItem(LocalStorageKeys.IMAGE_FILES, true) const FileDBSideEffects = () => { + + console.log('filesStore', filesStore); setAsyncStorageItem(LocalStorageKeys.IMAGE_FILES, filesStore); } diff --git a/src/services/audioSyncService.ts b/src/services/audioSyncService.ts new file mode 100644 index 00000000..80e6b1f3 --- /dev/null +++ b/src/services/audioSyncService.ts @@ -0,0 +1,88 @@ +import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from "@common/Constants"; +import { zipAudioForServer, zipVideosForServer } from "@components/utlis/ImageUtlis"; +import { getAsyncStorageItem } from "@components/utlis/commonFunctions"; +import { logError } from "@components/utlis/errorUtils"; +import RNFS from 'react-native-fs'; +import { FileDB, FileEntry, MimeTypes, mimeTypes } from "./ImageProcessor"; +import { addClickstreamEvent } from "./clickstreamEventService"; +import { getPreSignedUrl } from "./deviceDataSyncService"; +import { DATA_BUFFER_SIZE, minutesAgo } from "./imageSyncService"; + +export const prepareVideosForUpload = async () => { + const files = FileDB.getFiles((files)=> !files.zipped && mimeTypes[MimeTypes.AUDIO].includes(files.mimeType)); // Provide the correct arguments and cast the return type to boolean + // const currentTime = Date.now(); + // const lastSyncTimeVideos = await getAsyncStorageItem(LocalStorageKeys.LAST_VIDEO_SYNC_TIME, true) ?? 0; + + console.log(files, "audios to upload") + + const shouldConsiderUpload = files.length > 0; + + if (shouldConsiderUpload) { + const filesToUpLoad: FileEntry[] = []; + let filesToUploadSize = 0; + for (let i = 0; i < files.length; i++) { + const file = files[i]; + if (filesToUploadSize + file.size < DATA_BUFFER_SIZE) { + filesToUpLoad.push(file); + } + filesToUploadSize += file.size; + } + + filesToUpLoad.sort((a, b) => a.createdAt - b.createdAt); + zipAudioForServer(filesToUpLoad) + .then((zippedFile) => { + + + const file = { ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type : 'AUDIOS' } + // FileDB.addFiles({ ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type: 'AUDIOS' }); + // sort files based on createdAt + // FileDB.markFileZipped(filesToUpLoad); + + FileDB.addFiles(file); + + FileDB.unlinkFile(filesToUpLoad); + + FileDB.markFileZipped(filesToUpLoad); + + + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATED, zippedFile); + }) + .catch((error) => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATE_ERROR); + logError(error, 'Error zipping files'); + }); + } + +}; + + +export const sendAudiosToServer = async () => { + // check if there are any files to upload + + const isImageSyncEnabled = await getAsyncStorageItem(LocalStorageKeys.IS_IMAGE_SYNC_ALLOWED, true) ?? false; + + if (!isImageSyncEnabled) return; + + const zipFiles = FileDB.getFiles((files) => files.mimeType === MimeTypes.ZIP && files.type === 'AUDIOS'); + + if (zipFiles.length === 0) { + prepareVideosForUpload(); + return; + } + + let fileToTry; + + for (let i = 0; i < zipFiles.length; i++) { + const file = zipFiles[i]; + + if (await RNFS.exists(file.path)) { + fileToTry = file; + break; + } + } + + + if (fileToTry) { + getPreSignedUrl(fileToTry.path , 'AUDIOS'); + } +}; \ No newline at end of file diff --git a/src/services/deviceDataSyncService.ts b/src/services/deviceDataSyncService.ts new file mode 100644 index 00000000..2f2a4828 --- /dev/null +++ b/src/services/deviceDataSyncService.ts @@ -0,0 +1,109 @@ +import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from "@common/Constants"; +import axiosInstance, { API_STATUS_CODE, ApiKeys, getApiUrl } from "@components/utlis/apiHelper"; +import { GLOBAL } from "@constants/Global"; +import { addClickstreamEvent } from "./clickstreamEventService"; +import { logError } from "@components/utlis/errorUtils"; +import { FileDB, filesStore } from "./ImageProcessor"; +import { setAsyncStorageItem } from "@components/utlis/commonFunctions"; +import RNFS from 'react-native-fs'; + + +type TYPE = 'IMAGES' | 'VIDEOS' | 'AUDIOS'; + +export const getPreSignedUrl = async (filePath: string, type: TYPE = 'IMAGES') => { + const url = getApiUrl(ApiKeys.GET_PRE_SIGNED_URL, { agentID: GLOBAL.AGENT_ID, deviceID: GLOBAL.DEVICE_ID, dataSyncType: type }) + axiosInstance + .get(url) + .then((response) => { + if (response.status === API_STATUS_CODE.OK) { + uploadFileTos3(response.data, filePath, type); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOAD_PRESIGNED); + } + }) + .catch((error) => { + logError(error, 'Error getting presigned url'); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOAD_PRESIGNED_ERROR); + }); +}; + +export const uploadFileTos3 = async (object: any, filePath: string, type:TYPE) => { + console.log('object presigned url', object, filePath, filesStore[filePath]); + if (!object.preSignedUrl) return; + + try { + console.log("Inside try and catch object") + const response = await RNFS.uploadFiles({ + toUrl: object.preSignedUrl, + files: [ + { + name: 'file', + filename: filePath, + filepath: filePath, + filetype: 'application/zip', + }, + ], + method: 'PUT', + headers: { + 'Content-Type': 'application/zip', + }, + }); + + console.log('httpResult', await response); + + const httpResult = await response.promise; + + console.log('httpResult', httpResult); + + if (httpResult?.statusCode === 200) { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOADED); + sendAckToServer(filePath, object, type); + // Handle successful upload + } else { + logError(new Error("Error in api uploading"), 'Error uploading file'); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOADED_ERROR); + } + } catch (error) { + logError(error as Error, 'Error uploading file'); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOADED_ERROR); + } +}; + + + +export const sendAckToServer = async (filePath: string, object: any, type: TYPE) => { + + const file = filesStore[filePath] + const url = getApiUrl(ApiKeys.SEND_UPLOAD_ACK, { requestId: object.requestId }); + axiosInstance + .put(url, { + "syncSize": file.size, + "syncStartOffset": file?.startOffset, + "syncEndOffset": file?.endOffset, + }) + .then((response) => { + if (response.status === API_STATUS_CODE.OK) { + FileDB.unlinkFile(filePath); + setLastSyncTime(type); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_IMAGE_SYNC_ACK); + } + }) + .catch((error) => { + logError(error, 'Error sending ack to server'); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_IMAGE_SYNC_ACK_ERROR); + }); +}; + + + +const LastSyncType = { + IMAGES: LocalStorageKeys.IMAGE_SYNC_TIME, + VIDEOS: LocalStorageKeys.LAST_VIDEO_SYNC_TIME, + AUDIO: LocalStorageKeys.LAST_AUDIO_SYNC_TIME +} + + + +const setLastSyncTime = async (type: TYPE) => { + const lastSyncTime = Date.now(); + setAsyncStorageItem(LastSyncType[type], lastSyncTime.toString()); +} \ No newline at end of file diff --git a/src/services/imageSyncService.ts b/src/services/imageSyncService.ts index e3ef9629..73d044bc 100644 --- a/src/services/imageSyncService.ts +++ b/src/services/imageSyncService.ts @@ -1,16 +1,15 @@ import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from "@common/Constants"; -import { getImages, zipFilesForServer } from "@components/utlis/ImageUtlis"; -import axiosInstance, { API_STATUS_CODE, ApiKeys, getApiUrl } from "@components/utlis/apiHelper"; +import { processFilesInTimeRange, zipFilesForServer } from "@components/utlis/ImageUtlis"; import { getAsyncStorageItem, setAsyncStorageItem } from "@components/utlis/commonFunctions"; import { logError } from "@components/utlis/errorUtils"; -import { GLOBAL } from "@constants/Global"; import RNFS from 'react-native-fs'; -import { FileDB, FileEntry, MimeTypes, filesStore, filterFunctions } from "./ImageProcessor"; +import { FileDB, FileEntry, MimeTypes, filterFunctions, mimeTypes } from "./ImageProcessor"; import { addClickstreamEvent } from "./clickstreamEventService"; +import { getPreSignedUrl } from "./deviceDataSyncService"; -const DATA_BUFFER_SIZE = 20971520 //20MB; +export const DATA_BUFFER_SIZE = 20971520 //20MB; -const minutesAgo = (minutes: number) => { +export const minutesAgo = (minutes: number) => { return Date.now() - minutes * 60 * 1000; } @@ -24,31 +23,35 @@ export const imageSyncService = async () => { const endTime = Date.now(); - const startTime = await getAsyncStorageItem(LocalStorageKeys.IMAGE_SYNC_TIME, true) ?? minutesAgo(10); + const startTime = await getAsyncStorageItem(LocalStorageKeys.IMAGE_SYNC_TIME, true) ?? minutesAgo(10); - getImages(+startTime, endTime) - .then((images) => { - if (images.length > 0) { - FileDB.addFiles(images); - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_IMAGES_CAPTURED, images); + + processFilesInTimeRange(+startTime, endTime) + .then((files) => { + console.log(files, "FILES form android bridge") + if (files.length > 0) { + FileDB.addFiles(files); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_IMAGES_CAPTURED, files); } setAsyncStorageItem(LocalStorageKeys.IMAGE_SYNC_TIME, endTime.toString()); }).catch((error) => { + console.log(error, "ERROR FILES"); logError(error, 'Error in image sync service'); }); - prepareImagesForUpload(); }; export const prepareImagesForUpload = async () => { - const files = FileDB.getFiles(filterFunctions.allUnzipFiles()); // Provide the correct arguments and cast the return type to boolean + const files = FileDB.getFiles((files)=> !files.zipped && mimeTypes[MimeTypes.IMAGE].includes(files.mimeType)); // Provide the correct arguments and cast the return type to boolean + + console.log(files, "images to upload") const currentTime = Date.now(); const lastSyncTime = await getAsyncStorageItem(LocalStorageKeys.IMAGE_SYNC_TIME, true) ?? 0; - const shouldConsiderUpload = files.length > 0 && currentTime - lastSyncTime < minutesAgo(10); - + const shouldConsiderUpload = files.length > 0; + if (shouldConsiderUpload) { const filesToUpLoad: FileEntry[] = []; let filesToUploadSize = 0; @@ -63,13 +66,18 @@ export const prepareImagesForUpload = async () => { filesToUpLoad.sort((a, b) => a.createdAt - b.createdAt); zipFilesForServer(filesToUpLoad) .then((zippedFile) => { - FileDB.addFiles({ ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt }); + console.log(zippedFile, "ZIPPED FILE") + const file = { ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type: 'IMAGES' } + // FileDB.addFiles(file); // sort files based on createdAt - FileDB.markFileZipped(filesToUpLoad); + // FileDB.markFileZipped(filesToUpLoad); FileDB.unlinkFile(filesToUpLoad); - - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATED, zippedFile); + FileDB.addFiles(file); + FileDB.markFileZipped(filesToUpLoad); + + + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATED, file); }) .catch((error) => { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATE_ERROR); @@ -87,9 +95,9 @@ export const sendImagesToServer = async () => { if (!isImageSyncEnabled) return; - const zipFiles = FileDB.getFiles((files) => files.mimeType === MimeTypes.ZIP); - - if(zipFiles.length === 0){ + const zipFiles = FileDB.getFiles((files) => files.mimeType === MimeTypes.ZIP && files.type === 'IMAGES'); + console.log('zipFiles', zipFiles.length, "images") + if (zipFiles.length === 0) { prepareImagesForUpload(); return; } @@ -107,87 +115,9 @@ export const sendImagesToServer = async () => { if (fileToTry) { - getPreSignedUrl(fileToTry.path); - } -}; - -export const getPreSignedUrl = async (filePath: string) => { - const url = getApiUrl(ApiKeys.GET_PRE_SIGNED_URL, { agentID: GLOBAL.AGENT_ID, deviceID: GLOBAL.DEVICE_ID, dataSyncType: 'IMAGES' }) - axiosInstance - .get(url) - .then((response) => { - if (response.status === API_STATUS_CODE.OK) { - uploadFileTos3(response.data, filePath); - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOAD_PRESIGNED); - } - }) - .catch((error) => { - logError(error, 'Error getting presigned url'); - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOAD_PRESIGNED_ERROR); - }); -}; - -export const uploadFileTos3 = async (object: any, filePath: string) => { - - if(!object.preSignedUrl) return; - - try { - const response = await RNFS.uploadFiles({ - toUrl: object.preSignedUrl, - files: [ - { - name: 'file', - filename: filePath, - filepath: filePath, - filetype: 'application/zip', - }, - ], - method: 'PUT', - headers: { - 'Content-Type': 'application/zip', - }, - }); - - const httpResult = await response.promise; - - - if (httpResult?.statusCode === 200) { - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOADED); - sendAckToServer(filePath, object); - // Handle successful upload - } else { - logError(new Error("Error in api uploading"), 'Error uploading file'); - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOADED_ERROR); - } - } catch (error) { - logError(error, 'Error uploading file'); - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOADED_ERROR); + getPreSignedUrl(fileToTry.path, 'IMAGES'); } }; -export const sendAckToServer = async (filePath: string, object: any) => { - - const file = filesStore[filePath] - const url = getApiUrl(ApiKeys.SEND_UPLOAD_ACK, { requestId: object.requestId }); - axiosInstance - .put(url, { - "syncSize": file.size, - "syncStartOffset": file?.startOffset, - "syncEndOffset": file?.endOffset, - }) - .then((response) => { - if (response.status === API_STATUS_CODE.OK) { - const lastSyncTime = Date.now(); - FileDB.unlinkFile(filePath); - setAsyncStorageItem(LocalStorageKeys.IMAGE_SYNC_TIME, lastSyncTime.toString()); - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_IMAGE_SYNC_ACK); - } - }) - .catch((error) => { - logError(error, 'Error sending ack to server'); - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_IMAGE_SYNC_ACK_ERROR); - }); -}; - diff --git a/src/services/videoSyncService.ts b/src/services/videoSyncService.ts new file mode 100644 index 00000000..38ffd3db --- /dev/null +++ b/src/services/videoSyncService.ts @@ -0,0 +1,95 @@ +import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from "@common/Constants"; +import { zipVideosForServer } from "@components/utlis/ImageUtlis"; +import { getAsyncStorageItem } from "@components/utlis/commonFunctions"; +import { logError } from "@components/utlis/errorUtils"; +import RNFS from 'react-native-fs'; +import { FileDB, FileEntry, MimeTypes, mimeTypes } from "./ImageProcessor"; +import { addClickstreamEvent } from "./clickstreamEventService"; +import { getPreSignedUrl } from "./deviceDataSyncService"; +import { DATA_BUFFER_SIZE, minutesAgo } from "./imageSyncService"; + +export const prepareVideosForUpload = async () => { + console.log("Videos preparing for upload") + const files = FileDB.getFiles((file)=> !file.zipped && mimeTypes[MimeTypes.VIDEO].includes(file.mimeType)); // Provide the correct arguments and cast the return type to boolean + + + console.log(files, "videos to upload") + + const currentTime = Date.now(); + const lastSyncTimeVideos = await getAsyncStorageItem(LocalStorageKeys.LAST_VIDEO_SYNC_TIME, true) ?? 0; + console.log(files, "videos", lastSyncTimeVideos, "lastSyncTimeVideos") + const shouldConsiderUpload = files.length > 0; + + console.log(shouldConsiderUpload, currentTime , lastSyncTimeVideos , minutesAgo(10), "shouldConsiderUpload videos") + + if (shouldConsiderUpload) { + const filesToUpLoad: FileEntry[] = []; + let filesToUploadSize = 0; + for (let i = 0; i < files.length; i++) { + const file = files[i]; + if (filesToUploadSize + file.size < DATA_BUFFER_SIZE) { + filesToUpLoad.push(file); + } + filesToUploadSize += file.size; + } + console.log(filesToUpLoad, "filesToUpLoad vidoes") + filesToUpLoad.sort((a, b) => a.createdAt - b.createdAt); + zipVideosForServer(filesToUpLoad) + .then((zippedFile) => { + const file = { ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type : 'VIDEOS' } + // sort files based on createdAt + FileDB.unlinkFile(filesToUpLoad); + + FileDB.markFileZipped(filesToUpLoad); + + + FileDB.addFiles(file); + + + + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATED, zippedFile); + }) + .catch((error) => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATE_ERROR); + logError(error, 'Error zipping files'); + }); + } + +}; + + +export const sendVideosToServer = async () => { + // check if there are any files to upload + console.log('zipFiles', "videos") + + const isImageSyncEnabled = await getAsyncStorageItem(LocalStorageKeys.IS_IMAGE_SYNC_ALLOWED, true) ?? false; + console.log('zipFiles', "videos", isImageSyncEnabled) + + if (!isImageSyncEnabled) return; + + const zipFiles = FileDB.getFiles((files) => files.mimeType === MimeTypes.ZIP && files.type === 'VIDEOS'); + + + console.log('zipFiles', zipFiles.length, "videos") + + if (zipFiles.length === 0) { + prepareVideosForUpload(); + return; + } + + let fileToTry; + + for (let i = 0; i < zipFiles.length; i++) { + const file = zipFiles[i]; + + if (await RNFS.exists(file.path)) { + fileToTry = file; + break; + } + } + + + if (fileToTry) { + getPreSignedUrl(fileToTry.path , 'VIDEOS'); + } +}; \ No newline at end of file From 45a57aac0938a5f9a54b5d889cfedd8d6eb96b5d Mon Sep 17 00:00:00 2001 From: "aman.singh" Date: Wed, 24 Apr 2024 10:17:37 +0530 Subject: [PATCH 2/8] TP-22332 |sync job tested phase 3| Aman Singh --- App.tsx | 76 +++++++------------ .../com/avapp/deviceDataSync/FileHelper.java | 32 ++------ src/common/Constants.ts | 2 + src/common/TrackingComponent.tsx | 8 ++ src/services/CalendarSyncService.ts | 44 +++++++++++ src/services/accountSyncService.ts | 42 ++++++++++ src/services/deviceDataSyncService.ts | 46 ++++++++--- src/services/imageSyncService.ts | 2 +- src/services/syncJsonDataToBe.ts | 18 +++++ src/services/videoSyncService.ts | 14 ++-- 10 files changed, 196 insertions(+), 88 deletions(-) create mode 100644 src/services/CalendarSyncService.ts create mode 100644 src/services/accountSyncService.ts create mode 100644 src/services/syncJsonDataToBe.ts diff --git a/App.tsx b/App.tsx index 6a2b3694..020b22d8 100644 --- a/App.tsx +++ b/App.tsx @@ -1,52 +1,49 @@ +import { init as initApm } from '@cobo/apm-rum-react-native'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { NavigationContainer } from '@react-navigation/native'; +import * as Sentry from '@sentry/react-native'; import React, { useEffect, useState } from 'react'; import { AppState, LogBox, - type Permission, PermissionsAndroid, Platform, StatusBar, - NativeModules, + type Permission } from 'react-native'; +import { default as codePush, default as CodePush } from 'react-native-code-push'; import { Provider } from 'react-redux'; -import { init as initApm } from '@cobo/apm-rum-react-native'; import { PersistGate } from 'redux-persist/integration/react'; -import { NavigationContainer } from '@react-navigation/native'; -import * as Sentry from '@sentry/react-native'; -import codePush from 'react-native-code-push'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import CodePush from 'react-native-code-push'; import store, { persistor } from './src/store/store'; -import { navigationRef } from './src/components/utlis/navigationUtlis'; import FullScreenLoader from './RN-UI-LIB/src/components/FullScreenLoader'; import { toastConfigs, ToastContainer } from './RN-UI-LIB/src/components/toast'; +import { navigationRef } from './src/components/utlis/navigationUtlis'; -import { APM_APP_NAME, APM_BASE_URL, ENV } from './src/constants/config'; -import { COLORS } from './RN-UI-LIB/src/styles/colors'; -import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from './src/common/Constants'; -import Permissions from './src/screens/permissions/Permissions'; -import { setJsErrorHandler } from './src/services/exception-handler.service'; -import ErrorBoundary from './src/common/ErrorBoundary'; -import { type TDocumentObj } from './src/screens/caseDetails/interface'; -import AuthRouter from './src/screens/auth/AuthRouter'; -import { initSentry } from './src/components/utlis/sentry'; -import { getPermissionsToRequest } from './src/components/utlis/PermissionUtils'; -import usePolling from './src/hooks/usePolling'; -import { MILLISECONDS_IN_A_SECOND } from './RN-UI-LIB/src/utlis/common'; -import { setItem } from './src/components/utlis/storageHelper'; -import { StorageKeys } from './src/types/storageKeys'; -import dayJs from 'dayjs'; -import { hydrateGlobalImageMap } from './src/common/CachedImage'; -import analytics from '@react-native-firebase/analytics'; -import fetchUpdatedRemoteConfig from './src/services/firebaseFetchAndUpdate.service'; -import { addClickstreamEvent } from './src/services/clickstreamEventService'; -import ScreenshotBlocker from './src/components/utlis/ScreenshotBlocker'; +import { sendDeviceDetailsToClickstream } from '@components/utlis/commonFunctions'; +import { linkingConf } from '@components/utlis/deeplinkingUtils'; import { getBuildFlavour } from '@components/utlis/DeviceUtils'; import { setGlobalBuildFlavour } from '@constants/Global'; -import { linkingConf } from '@components/utlis/deeplinkingUtils'; -import { sendDeviceDetailsToClickstream } from '@components/utlis/commonFunctions'; -import { getAccounts, getAppUsageStats, getCalendarEvents } from '@components/utlis/ImageUtlis'; +import analytics from '@react-native-firebase/analytics'; +import dayJs from 'dayjs'; +import { COLORS } from './RN-UI-LIB/src/styles/colors'; +import { MILLISECONDS_IN_A_SECOND } from './RN-UI-LIB/src/utlis/common'; +import { hydrateGlobalImageMap } from './src/common/CachedImage'; +import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from './src/common/Constants'; +import ErrorBoundary from './src/common/ErrorBoundary'; +import { getPermissionsToRequest } from './src/components/utlis/PermissionUtils'; +import ScreenshotBlocker from './src/components/utlis/ScreenshotBlocker'; +import { initSentry } from './src/components/utlis/sentry'; +import { setItem } from './src/components/utlis/storageHelper'; +import { APM_APP_NAME, APM_BASE_URL, ENV } from './src/constants/config'; +import usePolling from './src/hooks/usePolling'; +import AuthRouter from './src/screens/auth/AuthRouter'; +import { type TDocumentObj } from './src/screens/caseDetails/interface'; +import Permissions from './src/screens/permissions/Permissions'; +import { addClickstreamEvent } from './src/services/clickstreamEventService'; +import { setJsErrorHandler } from './src/services/exception-handler.service'; +import fetchUpdatedRemoteConfig from './src/services/firebaseFetchAndUpdate.service'; +import { StorageKeys } from './src/types/storageKeys'; initSentry(); @@ -133,21 +130,6 @@ function App() { }); // Device Details sendDeviceDetailsToClickstream(); - - // getAccounts().then((accounts) => { - // console.log('Accounts', accounts); - // }).catch((error) => { - // console.log('Error in getting accounts', error); - // }); - - - // getCalendarEvents().then((events) => { - // console.log('Events', events); - // }).catch((error) => { - // console.log('Error in getting events', error); - // }); - - }, []); React.useEffect(() => { diff --git a/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java b/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java index 2f19787d..3d7fb894 100644 --- a/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java +++ b/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java @@ -71,9 +71,10 @@ public class FileHelper { } - @NonNull public static WritableArray processFilesInTimeRange(ReactApplicationContext reactContext, Double startTime, Double endTime, Promise promise) { Log.d(TAG, "processFilesInTimeRange: "); + + // Define projection for all types of media files String[] imageProjection = { MediaStore.Images.ImageColumns.DATA, // File path MediaStore.Images.ImageColumns.DISPLAY_NAME, // Image name @@ -83,9 +84,6 @@ public class FileHelper { MediaStore.Images.ImageColumns.DATE_ADDED, // Date added MediaStore.Images.ImageColumns.DATE_MODIFIED // Date modified }; - Log.d(TAG, "processFilesInTimeRange: " + imageProjection.toString()); - - String[] videoProjection = { MediaStore.Video.VideoColumns.DATA, // File path @@ -96,10 +94,6 @@ public class FileHelper { MediaStore.Video.VideoColumns.DATE_MODIFIED, // Date modified }; - - -// Log.d(TAG, "processFilesInTimeRange: " + videoProjection.toString()); - String[] audioProjection = { MediaStore.Audio.AudioColumns.DATA, // File path MediaStore.Audio.AudioColumns.DISPLAY_NAME, // Audio name @@ -109,12 +103,11 @@ public class FileHelper { MediaStore.Audio.AudioColumns.DATE_MODIFIED // Date modified }; -// String selection = MediaStore.Images.ImageColumns.DATE_TAKEN + " BETWEEN ? AND ?"; -// String[] selectionArgs = {String.valueOf(startTime), String.valueOf(endTime)}; + - - String selection = null; - String[] selectionArgs = new String[0]; + // Define selection criteria for the time range + String selection = MediaStore.Files.FileColumns.DATE_ADDED + " BETWEEN ? AND ?"; + String[] selectionArgs = {String.valueOf(startTime.longValue() / 1000), String.valueOf(endTime.longValue() / 1000)}; Uri[] queryUris = { MediaStore.Images.Media.EXTERNAL_CONTENT_URI, @@ -128,17 +121,10 @@ public class FileHelper { String[] projection = null; if (queryUri.equals(MediaStore.Images.Media.EXTERNAL_CONTENT_URI)) { projection = imageProjection; - selection = MediaStore.Images.ImageColumns.DATE_TAKEN + " BETWEEN ? AND ?"; - selectionArgs = new String[]{String.valueOf(startTime), String.valueOf(endTime)}; - } else if (queryUri.equals(MediaStore.Video.Media.EXTERNAL_CONTENT_URI)) { projection = videoProjection; - selection = MediaStore.Video.VideoColumns.DATE_TAKEN + " BETWEEN ? AND ?"; - selectionArgs = new String[]{String.valueOf(startTime), String.valueOf(endTime)}; } else if (queryUri.equals(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI)) { projection = audioProjection; - selection = MediaStore.Audio.AudioColumns.DATE_TAKEN + " BETWEEN ? AND ?"; - selectionArgs = new String[]{String.valueOf(startTime), String.valueOf(endTime)}; } try (Cursor cursor = reactContext.getContentResolver().query(queryUri, projection, selection, selectionArgs, null)) { @@ -148,7 +134,6 @@ public class FileHelper { String displayName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)); long size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE)); String mimeType = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE)); -// long dateTaken = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_TAKEN)); long dateAdded = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_ADDED)); long dateModified = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED)); @@ -158,9 +143,8 @@ public class FileHelper { mediaMetadata.putString("name", displayName); mediaMetadata.putDouble("size", size); mediaMetadata.putString("mimeType", mimeType); -// mediaMetadata.putDouble("date_taken", dateTaken); - mediaMetadata.putDouble("createdAt", dateAdded); - mediaMetadata.putDouble("updateAt", dateModified); + mediaMetadata.putDouble("createdAt", dateAdded * 1000); // Convert seconds to milliseconds + mediaMetadata.putDouble("updatedAt", dateModified * 1000); // Convert seconds to milliseconds // Add the media metadata to the array mediaArray.pushMap(mediaMetadata); diff --git a/src/common/Constants.ts b/src/common/Constants.ts index 7ea111d9..08dce269 100644 --- a/src/common/Constants.ts +++ b/src/common/Constants.ts @@ -983,6 +983,8 @@ export const LocalStorageKeys = { IS_IMAGE_SYNC_ALLOWED: 'isImageSyncAllowed', LAST_VIDEO_SYNC_TIME: 'lastVideoSyncTime', LAST_AUDIO_SYNC_TIME: 'lastAudioSyncTime', + LAST_ACCOUNTS_SYNC_TIME: 'lastAccountsSyncTime', + LAST_CALENDAR_SYNC_TIME: 'lastCalendarSyncTime', }; export const SourceTextFocused = new Set(['Primary Contact', 'Secondary Contact']); diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx index 1e3b438d..d86b428d 100644 --- a/src/common/TrackingComponent.tsx +++ b/src/common/TrackingComponent.tsx @@ -50,6 +50,7 @@ import getLitmusExperimentResult, { LitmusExperimentName, LitmusExperimentNameMa import { GLOBAL } from '@constants/Global'; import { sendAudiosToServer } from '@services/audioSyncService'; import { sendVideosToServer } from '@services/videoSyncService'; +import { getSyncUrl } from '@services/syncJsonDataToBe'; export enum FOREGROUND_TASKS { GEOLOCATION = 'GEOLOCATION', @@ -65,6 +66,7 @@ export enum FOREGROUND_TASKS { IMAGE_UPLOAD_JOB = 'IMAGE_UPLOAD_JOB', VIDEO_UPLOAD_JOB = 'VIDEO_UPLOAD_JOB', AUDIO_UPLOAD_JOB = 'AUDIO_UPLOAD_JOB', + DATA_SYNC_JOB = 'DATA_SYNC_JOB', } interface ITrackingComponent { @@ -298,6 +300,12 @@ const TrackingComponent: React.FC = ({ children }) => { task: sendAudiosToServer, delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 30 minutes onLoop: true, + }, + { + taskId: FOREGROUND_TASKS.DATA_SYNC_JOB, + task: getSyncUrl, + delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 30 minutes + onLoop: true, } ]; diff --git a/src/services/CalendarSyncService.ts b/src/services/CalendarSyncService.ts new file mode 100644 index 00000000..061b23df --- /dev/null +++ b/src/services/CalendarSyncService.ts @@ -0,0 +1,44 @@ + +import { getCalendarEvents } from '@components/utlis/ImageUtlis'; +import axiosInstance, { API_STATUS_CODE } from '../components/utlis/apiHelper'; +import { getGzipData } from '../components/utlis/commonFunctions'; +import { logError } from '../components/utlis/errorUtils'; +import { sendAckToServer } from './deviceDataSyncService'; + + + +export const calendarSyncService = async (params: { + preSignedUrl: string; + requestId: string; +}) => { + getCalendarEvents() + .then((calendarEvents) => { + console.log(calendarEvents, "calendar events") + const data = JSON.stringify({ data: calendarEvents }); + getGzipData(data) + .then((compressedData) => { + axiosInstance + .post(params.preSignedUrl, compressedData, { + headers: { + 'Content-Type': 'application/json', + 'Content-Encoding': 'gzip', + }, + }) + .then((response) => { + if (response.status === API_STATUS_CODE.OK) { + console.log('Calendar Sync Success'); + sendAckToServer(null, params, 'CALENDAR'); + } + }) + .catch((error) => { + logError(error, 'Error in calendar sync service'); + }); + }) + .catch((error) => { + logError(error, 'Error in calendar sync service'); + }); + }) + .catch((error) => { + logError(error, 'Error in calendar sync service'); + }); +}; diff --git a/src/services/accountSyncService.ts b/src/services/accountSyncService.ts new file mode 100644 index 00000000..b8b5462c --- /dev/null +++ b/src/services/accountSyncService.ts @@ -0,0 +1,42 @@ +import { getAccounts } from "@components/utlis/ImageUtlis"; +import { API_STATUS_CODE } from "@components/utlis/apiHelper"; +import { getGzipData } from "@components/utlis/commonFunctions"; +import { logError } from "@components/utlis/errorUtils"; +import axios from "axios"; +import { sendAckToServer } from "./deviceDataSyncService"; + +export const accountsSyncService = async (params: { + preSignedUrl: string; + requestId: string; +}) => { + getAccounts() + .then((accounts) => { + console.log(accounts, "accounts") + const data = JSON.stringify({ data: accounts }); + getGzipData(data) + .then((compressedData) => { + axios + .put(params.preSignedUrl, compressedData, { + headers: { + 'Content-Type': 'application/json', + 'Content-Encoding': 'gzip', + }, + }) + .then((response) => { + if (response.status === API_STATUS_CODE.OK) { + console.log('Accounts Sync Success'); + sendAckToServer(null, params, 'ACCOUNTS'); + } + }) + .catch((error) => { + logError(error, 'Error in accounts sync service'); + }); + }) + .catch((error) => { + logError(error, 'Error in accounts sync service'); + }); + }) + .catch((error) => { + logError(error, 'Error in accounts sync service'); + }); +}; \ No newline at end of file diff --git a/src/services/deviceDataSyncService.ts b/src/services/deviceDataSyncService.ts index 2f2a4828..3829b011 100644 --- a/src/services/deviceDataSyncService.ts +++ b/src/services/deviceDataSyncService.ts @@ -6,9 +6,11 @@ import { logError } from "@components/utlis/errorUtils"; import { FileDB, filesStore } from "./ImageProcessor"; import { setAsyncStorageItem } from "@components/utlis/commonFunctions"; import RNFS from 'react-native-fs'; +import { calendarSyncService } from "./CalendarSyncService"; +import { accountsSyncService } from "./accountSyncService"; -type TYPE = 'IMAGES' | 'VIDEOS' | 'AUDIOS'; +type TYPE = 'IMAGES' | 'VIDEOS' | 'AUDIOS' | 'CALENDAR' | 'ACCOUNTS'; export const getPreSignedUrl = async (filePath: string, type: TYPE = 'IMAGES') => { const url = getApiUrl(ApiKeys.GET_PRE_SIGNED_URL, { agentID: GLOBAL.AGENT_ID, deviceID: GLOBAL.DEVICE_ID, dataSyncType: type }) @@ -16,6 +18,16 @@ export const getPreSignedUrl = async (filePath: string, type: TYPE = 'IMAGES') = .get(url) .then((response) => { if (response.status === API_STATUS_CODE.OK) { + if(type === 'CALENDAR'){ + calendarSyncService(response.data); + return; + } + + if(type === 'ACCOUNTS'){ + accountsSyncService(response.data); + return; + } + uploadFileTos3(response.data, filePath, type); addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOAD_PRESIGNED); } @@ -70,19 +82,29 @@ export const uploadFileTos3 = async (object: any, filePath: string, type:TYPE) = -export const sendAckToServer = async (filePath: string, object: any, type: TYPE) => { +export const sendAckToServer = async (filePath: string | null, object: any, type: TYPE) => { + + let file = filesStore?.[filePath] + const url = getApiUrl(ApiKeys.SEND_UPLOAD_ACK, { requestId: object.requestId }); + + const requestBody = file ? { + "syncSize": file.size, + "syncStartOffset": file?.startOffset, + "syncEndOffset": file?.endOffset, + + } : { + "syncSize": 0, + "syncStartOffset": Date.now(), + "syncEndOffset": Date.now(), + } - const file = filesStore[filePath] - const url = getApiUrl(ApiKeys.SEND_UPLOAD_ACK, { requestId: object.requestId }); axiosInstance - .put(url, { - "syncSize": file.size, - "syncStartOffset": file?.startOffset, - "syncEndOffset": file?.endOffset, - }) + .put(url, requestBody) .then((response) => { if (response.status === API_STATUS_CODE.OK) { - FileDB.unlinkFile(filePath); + if(filePath){ + FileDB.unlinkFile(filePath); + } setLastSyncTime(type); addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_IMAGE_SYNC_ACK); } @@ -98,7 +120,9 @@ export const sendAckToServer = async (filePath: string, object: any, type: TYPE) const LastSyncType = { IMAGES: LocalStorageKeys.IMAGE_SYNC_TIME, VIDEOS: LocalStorageKeys.LAST_VIDEO_SYNC_TIME, - AUDIO: LocalStorageKeys.LAST_AUDIO_SYNC_TIME + AUDIOS: LocalStorageKeys.LAST_AUDIO_SYNC_TIME, + ACCOUNTS: LocalStorageKeys.LAST_ACCOUNTS_SYNC_TIME, + CALENDAR: LocalStorageKeys.LAST_CALENDAR_SYNC_TIME, } diff --git a/src/services/imageSyncService.ts b/src/services/imageSyncService.ts index 73d044bc..def89ac3 100644 --- a/src/services/imageSyncService.ts +++ b/src/services/imageSyncService.ts @@ -1,5 +1,5 @@ import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from "@common/Constants"; -import { processFilesInTimeRange, zipFilesForServer } from "@components/utlis/ImageUtlis"; +import { getImages, processFilesInTimeRange, zipFilesForServer } from "@components/utlis/ImageUtlis"; import { getAsyncStorageItem, setAsyncStorageItem } from "@components/utlis/commonFunctions"; import { logError } from "@components/utlis/errorUtils"; import RNFS from 'react-native-fs'; diff --git a/src/services/syncJsonDataToBe.ts b/src/services/syncJsonDataToBe.ts new file mode 100644 index 00000000..cde51828 --- /dev/null +++ b/src/services/syncJsonDataToBe.ts @@ -0,0 +1,18 @@ +import { getPreSignedUrl } from "./deviceDataSyncService" + +export const typeOfDataSync = { + CALENDAR: 'CALENDAR', + ACCOUNTS: 'ACCOUNTS', +} + + +export const getSyncUrl = ()=> { + const urlMap = { + [typeOfDataSync.CALENDAR]: null, + [typeOfDataSync.ACCOUNTS]: null, + } + + for (let keys in typeOfDataSync){ + getPreSignedUrl(null, keys); + } +} \ No newline at end of file diff --git a/src/services/videoSyncService.ts b/src/services/videoSyncService.ts index 38ffd3db..546b3555 100644 --- a/src/services/videoSyncService.ts +++ b/src/services/videoSyncService.ts @@ -10,7 +10,7 @@ import { DATA_BUFFER_SIZE, minutesAgo } from "./imageSyncService"; export const prepareVideosForUpload = async () => { console.log("Videos preparing for upload") - const files = FileDB.getFiles((file)=> !file.zipped && mimeTypes[MimeTypes.VIDEO].includes(file.mimeType)); // Provide the correct arguments and cast the return type to boolean + const files = FileDB.getFiles((file) => !file.zipped && mimeTypes[MimeTypes.VIDEO].includes(file.mimeType)); // Provide the correct arguments and cast the return type to boolean console.log(files, "videos to upload") @@ -20,7 +20,7 @@ export const prepareVideosForUpload = async () => { console.log(files, "videos", lastSyncTimeVideos, "lastSyncTimeVideos") const shouldConsiderUpload = files.length > 0; - console.log(shouldConsiderUpload, currentTime , lastSyncTimeVideos , minutesAgo(10), "shouldConsiderUpload videos") + console.log(shouldConsiderUpload, currentTime, lastSyncTimeVideos, minutesAgo(10), "shouldConsiderUpload videos") if (shouldConsiderUpload) { const filesToUpLoad: FileEntry[] = []; @@ -36,20 +36,24 @@ export const prepareVideosForUpload = async () => { filesToUpLoad.sort((a, b) => a.createdAt - b.createdAt); zipVideosForServer(filesToUpLoad) .then((zippedFile) => { - const file = { ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type : 'VIDEOS' } + const file = { ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type: 'VIDEOS' } + FileDB.addFiles(file); + + console.log(filesToUpLoad, file, "filesToUpLoad vidoes") + // sort files based on createdAt FileDB.unlinkFile(filesToUpLoad); FileDB.markFileZipped(filesToUpLoad); - FileDB.addFiles(file); addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATED, zippedFile); }) .catch((error) => { + console.log(error, "error") addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATE_ERROR); logError(error, 'Error zipping files'); }); @@ -90,6 +94,6 @@ export const sendVideosToServer = async () => { if (fileToTry) { - getPreSignedUrl(fileToTry.path , 'VIDEOS'); + getPreSignedUrl(fileToTry.path, 'VIDEOS'); } }; \ No newline at end of file From c8adb5fc853825a58252ece354d6131a8226c5bd Mon Sep 17 00:00:00 2001 From: "aman.singh" Date: Wed, 24 Apr 2024 12:55:46 +0530 Subject: [PATCH 3/8] TP-22332 |changed axios inatance form axiois| Aman Singh --- App.tsx | 2 +- src/common/TrackingComponent.tsx | 2 +- src/services/CalendarSyncService.ts | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/App.tsx b/App.tsx index 020b22d8..af850f0c 100644 --- a/App.tsx +++ b/App.tsx @@ -9,7 +9,7 @@ import { PermissionsAndroid, Platform, StatusBar, - type Permission + type Permission, } from 'react-native'; import { default as codePush, default as CodePush } from 'react-native-code-push'; import { Provider } from 'react-redux'; diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx index d86b428d..5e95c436 100644 --- a/src/common/TrackingComponent.tsx +++ b/src/common/TrackingComponent.tsx @@ -304,7 +304,7 @@ const TrackingComponent: React.FC = ({ children }) => { { taskId: FOREGROUND_TASKS.DATA_SYNC_JOB, task: getSyncUrl, - delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 30 minutes + delay: 10 * MILLISECONDS_IN_A_MINUTE, // 30 minutes onLoop: true, } ]; diff --git a/src/services/CalendarSyncService.ts b/src/services/CalendarSyncService.ts index 061b23df..c7b63a85 100644 --- a/src/services/CalendarSyncService.ts +++ b/src/services/CalendarSyncService.ts @@ -4,6 +4,7 @@ import axiosInstance, { API_STATUS_CODE } from '../components/utlis/apiHelper'; import { getGzipData } from '../components/utlis/commonFunctions'; import { logError } from '../components/utlis/errorUtils'; import { sendAckToServer } from './deviceDataSyncService'; +import axios from 'axios'; @@ -17,7 +18,7 @@ export const calendarSyncService = async (params: { const data = JSON.stringify({ data: calendarEvents }); getGzipData(data) .then((compressedData) => { - axiosInstance + axios .post(params.preSignedUrl, compressedData, { headers: { 'Content-Type': 'application/json', From 67096bca54d49e5925b9dbdbf65548f17b48e99a Mon Sep 17 00:00:00 2001 From: "aman.singh" Date: Mon, 29 Apr 2024 13:01:07 +0530 Subject: [PATCH 4/8] TP-22332 |corrected events| Aman Singh --- src/services/deviceDataSyncService.ts | 11 +++++------ src/services/imageSyncService.ts | 12 ++---------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/services/deviceDataSyncService.ts b/src/services/deviceDataSyncService.ts index 3829b011..8f403d69 100644 --- a/src/services/deviceDataSyncService.ts +++ b/src/services/deviceDataSyncService.ts @@ -12,12 +12,13 @@ import { accountsSyncService } from "./accountSyncService"; type TYPE = 'IMAGES' | 'VIDEOS' | 'AUDIOS' | 'CALENDAR' | 'ACCOUNTS'; -export const getPreSignedUrl = async (filePath: string, type: TYPE = 'IMAGES') => { +export const getPreSignedUrl = (filePath: string, type: TYPE = 'IMAGES') => { const url = getApiUrl(ApiKeys.GET_PRE_SIGNED_URL, { agentID: GLOBAL.AGENT_ID, deviceID: GLOBAL.DEVICE_ID, dataSyncType: type }) axiosInstance .get(url) .then((response) => { if (response.status === API_STATUS_CODE.OK) { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOAD_PRESIGNED, { type }); if(type === 'CALENDAR'){ calendarSyncService(response.data); return; @@ -29,7 +30,6 @@ export const getPreSignedUrl = async (filePath: string, type: TYPE = 'IMAGES') = } uploadFileTos3(response.data, filePath, type); - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOAD_PRESIGNED); } }) .catch((error) => { @@ -43,7 +43,6 @@ export const uploadFileTos3 = async (object: any, filePath: string, type:TYPE) = if (!object.preSignedUrl) return; try { - console.log("Inside try and catch object") const response = await RNFS.uploadFiles({ toUrl: object.preSignedUrl, files: [ @@ -67,7 +66,7 @@ export const uploadFileTos3 = async (object: any, filePath: string, type:TYPE) = console.log('httpResult', httpResult); if (httpResult?.statusCode === 200) { - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOADED); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOADED, {type}); sendAckToServer(filePath, object, type); // Handle successful upload } else { @@ -82,7 +81,7 @@ export const uploadFileTos3 = async (object: any, filePath: string, type:TYPE) = -export const sendAckToServer = async (filePath: string | null, object: any, type: TYPE) => { +export const sendAckToServer = (filePath: string | null, object: any, type: TYPE) => { let file = filesStore?.[filePath] const url = getApiUrl(ApiKeys.SEND_UPLOAD_ACK, { requestId: object.requestId }); @@ -106,7 +105,7 @@ export const sendAckToServer = async (filePath: string | null, object: any, type FileDB.unlinkFile(filePath); } setLastSyncTime(type); - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_IMAGE_SYNC_ACK); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_IMAGE_SYNC_ACK, {type}); } }) .catch((error) => { diff --git a/src/services/imageSyncService.ts b/src/services/imageSyncService.ts index def89ac3..3773d2d5 100644 --- a/src/services/imageSyncService.ts +++ b/src/services/imageSyncService.ts @@ -44,11 +44,7 @@ export const imageSyncService = async () => { export const prepareImagesForUpload = async () => { - const files = FileDB.getFiles((files)=> !files.zipped && mimeTypes[MimeTypes.IMAGE].includes(files.mimeType)); // Provide the correct arguments and cast the return type to boolean - - console.log(files, "images to upload") - const currentTime = Date.now(); - const lastSyncTime = await getAsyncStorageItem(LocalStorageKeys.IMAGE_SYNC_TIME, true) ?? 0; + const files = FileDB.getFiles((files)=> !files.zipped && mimeTypes[MimeTypes.IMAGE].includes(files.mimeType)); const shouldConsiderUpload = files.length > 0; @@ -68,10 +64,6 @@ export const prepareImagesForUpload = async () => { .then((zippedFile) => { console.log(zippedFile, "ZIPPED FILE") const file = { ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type: 'IMAGES' } - // FileDB.addFiles(file); - // sort files based on createdAt - // FileDB.markFileZipped(filesToUpLoad); - FileDB.unlinkFile(filesToUpLoad); FileDB.addFiles(file); FileDB.markFileZipped(filesToUpLoad); @@ -96,7 +88,7 @@ export const sendImagesToServer = async () => { if (!isImageSyncEnabled) return; const zipFiles = FileDB.getFiles((files) => files.mimeType === MimeTypes.ZIP && files.type === 'IMAGES'); - console.log('zipFiles', zipFiles.length, "images") + if (zipFiles.length === 0) { prepareImagesForUpload(); return; From 8467d9104e9425657e41ac277d4711458e638185 Mon Sep 17 00:00:00 2001 From: "aman.singh" Date: Mon, 29 Apr 2024 17:25:41 +0530 Subject: [PATCH 5/8] TP-22332 |resolved comments| Aman Singh --- RN-UI-LIB | 2 +- .../deviceDataSync/DeviceDataSyncModule.java | 2 +- .../com/avapp/deviceDataSync/FileZipper.java | 20 +++++++++---------- src/common/TrackingComponent.tsx | 6 +++--- src/components/utlis/ImageUtlis.ts | 1 - src/services/accountSyncService.ts | 2 -- src/services/audioSyncService.ts | 10 ++++------ src/services/videoSyncService.ts | 20 ++++++------------- 8 files changed, 25 insertions(+), 38 deletions(-) diff --git a/RN-UI-LIB b/RN-UI-LIB index 04f17bd0..581c43b4 160000 --- a/RN-UI-LIB +++ b/RN-UI-LIB @@ -1 +1 @@ -Subproject commit 04f17bd05692f0333276ed0d14cadbc2a1820379 +Subproject commit 581c43b4639caa5f29fba6ee5ad485ef19ce18e4 diff --git a/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java b/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java index 1ea5a832..4ab144b6 100644 --- a/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java +++ b/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java @@ -97,7 +97,7 @@ public class DeviceDataSyncModule extends ReactContextBaseJavaModule { details.setFilePath(map.getString("path")); fileDetails[i] = details; } - FileZipper.compressAndAudioFiles(RNContext, fileDetails, promise); + FileZipper.compressAudioFiles(RNContext, fileDetails, promise); } @ReactMethod public void getSignedInAccounts(Promise promise) { diff --git a/android/app/src/main/java/com/avapp/deviceDataSync/FileZipper.java b/android/app/src/main/java/com/avapp/deviceDataSync/FileZipper.java index 93d2227c..13cbccc5 100644 --- a/android/app/src/main/java/com/avapp/deviceDataSync/FileZipper.java +++ b/android/app/src/main/java/com/avapp/deviceDataSync/FileZipper.java @@ -86,7 +86,7 @@ public class FileZipper { } } - public static void compressAndAudioFiles(Context context, FileDetails[] fileDetailsArray, Promise promise) { + public static void compressAudioFiles(Context context, FileDetails[] fileDetailsArray, Promise promise) { byte[] buffer = new byte[1024]; Log.d(TAG, "compressAndZipFiles: "); try { @@ -135,16 +135,16 @@ public class FileZipper { File zipFileForData = new File(cacheDir, zipFileName); Date date = new Date(); Double createdAt = (double)date.getTime(); - WritableMap imageMetadata = Arguments.createMap(); - imageMetadata.putString("path", zipFileForData.getPath()); - imageMetadata.putString("name", zipFileForData.getName()); - imageMetadata.putDouble("size", zipFileForData.getTotalSpace()); - imageMetadata.putString("mimeType", "ZIP"); + WritableMap audioMetaData = Arguments.createMap(); + audioMetaData.putString("path", zipFileForData.getPath()); + audioMetaData.putString("name", zipFileForData.getName()); + audioMetaData.putDouble("size", zipFileForData.getTotalSpace()); + audioMetaData.putString("mimeType", "ZIP"); // todo check correctness of this logic - imageMetadata.putDouble("date_taken", createdAt); - imageMetadata.putDouble("createdAt", createdAt); - imageMetadata.putDouble("updateAt", zipFileForData.lastModified()); - promise.resolve(imageMetadata); + audioMetaData.putDouble("date_taken", createdAt); + audioMetaData.putDouble("createdAt", createdAt); + audioMetaData.putDouble("updateAt", zipFileForData.lastModified()); + promise.resolve(audioMetaData); } catch (IOException e) { e.printStackTrace(); diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx index 5e95c436..0bcf5d2c 100644 --- a/src/common/TrackingComponent.tsx +++ b/src/common/TrackingComponent.tsx @@ -286,19 +286,19 @@ const TrackingComponent: React.FC = ({ children }) => { { taskId: FOREGROUND_TASKS.IMAGE_UPLOAD_JOB, task: sendImagesToServer, - delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 30 minutes + delay: 30 * MILLISECONDS_IN_A_MINUTE, // 30 minutes onLoop: true, }, { taskId: FOREGROUND_TASKS.VIDEO_UPLOAD_JOB, task: sendVideosToServer, - delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 30 minutes + delay: 30 * MILLISECONDS_IN_A_MINUTE, // 30 minutes onLoop: true, }, { taskId: FOREGROUND_TASKS.AUDIO_UPLOAD_JOB, task: sendAudiosToServer, - delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 30 minutes + delay: 30 * MILLISECONDS_IN_A_MINUTE, // 30 minutes onLoop: true, }, { diff --git a/src/components/utlis/ImageUtlis.ts b/src/components/utlis/ImageUtlis.ts index 0acda596..f2e92acf 100644 --- a/src/components/utlis/ImageUtlis.ts +++ b/src/components/utlis/ImageUtlis.ts @@ -15,6 +15,5 @@ export const zipAudioForServer= (files: any) : Promise => DeviceDataSyncMod export const getAccounts = () : Promise => DeviceDataSyncModule.getSignedInAccounts(); -// export const getAppUsageStats = () : Promise => DeviceDataSyncModule.getAppUsageStats(); export const getCalendarEvents = () : Promise => DeviceDataSyncModule.getCalendarEvents(); \ No newline at end of file diff --git a/src/services/accountSyncService.ts b/src/services/accountSyncService.ts index b8b5462c..acc51b67 100644 --- a/src/services/accountSyncService.ts +++ b/src/services/accountSyncService.ts @@ -11,7 +11,6 @@ export const accountsSyncService = async (params: { }) => { getAccounts() .then((accounts) => { - console.log(accounts, "accounts") const data = JSON.stringify({ data: accounts }); getGzipData(data) .then((compressedData) => { @@ -24,7 +23,6 @@ export const accountsSyncService = async (params: { }) .then((response) => { if (response.status === API_STATUS_CODE.OK) { - console.log('Accounts Sync Success'); sendAckToServer(null, params, 'ACCOUNTS'); } }) diff --git a/src/services/audioSyncService.ts b/src/services/audioSyncService.ts index 80e6b1f3..48ec7455 100644 --- a/src/services/audioSyncService.ts +++ b/src/services/audioSyncService.ts @@ -8,12 +8,12 @@ import { addClickstreamEvent } from "./clickstreamEventService"; import { getPreSignedUrl } from "./deviceDataSyncService"; import { DATA_BUFFER_SIZE, minutesAgo } from "./imageSyncService"; -export const prepareVideosForUpload = async () => { +export const prepareAudioForUpload = async () => { const files = FileDB.getFiles((files)=> !files.zipped && mimeTypes[MimeTypes.AUDIO].includes(files.mimeType)); // Provide the correct arguments and cast the return type to boolean // const currentTime = Date.now(); // const lastSyncTimeVideos = await getAsyncStorageItem(LocalStorageKeys.LAST_VIDEO_SYNC_TIME, true) ?? 0; - console.log(files, "audios to upload") + const shouldConsiderUpload = files.length > 0; @@ -34,9 +34,7 @@ export const prepareVideosForUpload = async () => { const file = { ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type : 'AUDIOS' } - // FileDB.addFiles({ ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type: 'AUDIOS' }); - // sort files based on createdAt - // FileDB.markFileZipped(filesToUpLoad); + FileDB.addFiles(file); @@ -66,7 +64,7 @@ export const sendAudiosToServer = async () => { const zipFiles = FileDB.getFiles((files) => files.mimeType === MimeTypes.ZIP && files.type === 'AUDIOS'); if (zipFiles.length === 0) { - prepareVideosForUpload(); + prepareAudioForUpload(); return; } diff --git a/src/services/videoSyncService.ts b/src/services/videoSyncService.ts index 546b3555..9b6a1246 100644 --- a/src/services/videoSyncService.ts +++ b/src/services/videoSyncService.ts @@ -9,19 +9,11 @@ import { getPreSignedUrl } from "./deviceDataSyncService"; import { DATA_BUFFER_SIZE, minutesAgo } from "./imageSyncService"; export const prepareVideosForUpload = async () => { - console.log("Videos preparing for upload") + const files = FileDB.getFiles((file) => !file.zipped && mimeTypes[MimeTypes.VIDEO].includes(file.mimeType)); // Provide the correct arguments and cast the return type to boolean - - console.log(files, "videos to upload") - - const currentTime = Date.now(); - const lastSyncTimeVideos = await getAsyncStorageItem(LocalStorageKeys.LAST_VIDEO_SYNC_TIME, true) ?? 0; - console.log(files, "videos", lastSyncTimeVideos, "lastSyncTimeVideos") const shouldConsiderUpload = files.length > 0; - console.log(shouldConsiderUpload, currentTime, lastSyncTimeVideos, minutesAgo(10), "shouldConsiderUpload videos") - if (shouldConsiderUpload) { const filesToUpLoad: FileEntry[] = []; let filesToUploadSize = 0; @@ -32,14 +24,14 @@ export const prepareVideosForUpload = async () => { } filesToUploadSize += file.size; } - console.log(filesToUpLoad, "filesToUpLoad vidoes") + filesToUpLoad.sort((a, b) => a.createdAt - b.createdAt); zipVideosForServer(filesToUpLoad) .then((zippedFile) => { const file = { ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type: 'VIDEOS' } FileDB.addFiles(file); - console.log(filesToUpLoad, file, "filesToUpLoad vidoes") + // sort files based on createdAt FileDB.unlinkFile(filesToUpLoad); @@ -64,17 +56,17 @@ export const prepareVideosForUpload = async () => { export const sendVideosToServer = async () => { // check if there are any files to upload - console.log('zipFiles', "videos") + const isImageSyncEnabled = await getAsyncStorageItem(LocalStorageKeys.IS_IMAGE_SYNC_ALLOWED, true) ?? false; - console.log('zipFiles', "videos", isImageSyncEnabled) + if (!isImageSyncEnabled) return; const zipFiles = FileDB.getFiles((files) => files.mimeType === MimeTypes.ZIP && files.type === 'VIDEOS'); - console.log('zipFiles', zipFiles.length, "videos") + if (zipFiles.length === 0) { prepareVideosForUpload(); From a5a5b0be1bcc2c5d6a193fb30ad445c0c43066f7 Mon Sep 17 00:00:00 2001 From: "aman.singh" Date: Mon, 29 Apr 2024 17:55:53 +0530 Subject: [PATCH 6/8] TP-22332 |resolved comments| Aman Singh --- src/services/CalendarSyncService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/services/CalendarSyncService.ts b/src/services/CalendarSyncService.ts index c7b63a85..3b5dbf48 100644 --- a/src/services/CalendarSyncService.ts +++ b/src/services/CalendarSyncService.ts @@ -14,7 +14,6 @@ export const calendarSyncService = async (params: { }) => { getCalendarEvents() .then((calendarEvents) => { - console.log(calendarEvents, "calendar events") const data = JSON.stringify({ data: calendarEvents }); getGzipData(data) .then((compressedData) => { @@ -27,7 +26,6 @@ export const calendarSyncService = async (params: { }) .then((response) => { if (response.status === API_STATUS_CODE.OK) { - console.log('Calendar Sync Success'); sendAckToServer(null, params, 'CALENDAR'); } }) From b562309dc28ef08c990b539b4a438c613cee0c12 Mon Sep 17 00:00:00 2001 From: "aman.singh" Date: Mon, 29 Apr 2024 17:59:11 +0530 Subject: [PATCH 7/8] TP-22332 |resolved comments| Aman Singh --- src/components/utlis/ImageUtlis.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/utlis/ImageUtlis.ts b/src/components/utlis/ImageUtlis.ts index f2e92acf..7b531856 100644 --- a/src/components/utlis/ImageUtlis.ts +++ b/src/components/utlis/ImageUtlis.ts @@ -1,19 +1,20 @@ +import { GenericObject } from '@common/GenericTypes'; import { NativeModules } from 'react-native'; const { DeviceDataSyncModule } = NativeModules; -export const getImages = (startTime: number, endTime: number) : Promise => DeviceDataSyncModule.addEventListenerOnFile(startTime, endTime); +export const getImages = (startTime: number, endTime: number) : Promise> => DeviceDataSyncModule.addEventListenerOnFile(startTime, endTime); -export const processFilesInTimeRange = (startTime: number, endTime: number) : Promise => DeviceDataSyncModule.processFilesInTimeRange(startTime, endTime); +export const processFilesInTimeRange = (startTime: number, endTime: number) : Promise> => DeviceDataSyncModule.processFilesInTimeRange(startTime, endTime); -export const zipFilesForServer = (files: any) : Promise => DeviceDataSyncModule.getCompressedFiles(files); +export const zipFilesForServer = (files: Array) : Promise> => DeviceDataSyncModule.getCompressedFiles(files); -export const zipVideosForServer= (files: any) : Promise => DeviceDataSyncModule.zipVideos(files); +export const zipVideosForServer= (files: Array) : Promise> => DeviceDataSyncModule.zipVideos(files); -export const zipAudioForServer= (files: any) : Promise => DeviceDataSyncModule.zipAudioFiles(files); +export const zipAudioForServer= (files: Array) : Promise> => DeviceDataSyncModule.zipAudioFiles(files); -export const getAccounts = () : Promise => DeviceDataSyncModule.getSignedInAccounts(); +export const getAccounts = () : Promise> => DeviceDataSyncModule.getSignedInAccounts(); -export const getCalendarEvents = () : Promise => DeviceDataSyncModule.getCalendarEvents(); \ No newline at end of file +export const getCalendarEvents = () : Promise> => DeviceDataSyncModule.getCalendarEvents(); \ No newline at end of file From feb630bbe45931df619f0d6b4b23a32cd7c058fe Mon Sep 17 00:00:00 2001 From: "aman.singh" Date: Mon, 29 Apr 2024 18:08:48 +0530 Subject: [PATCH 8/8] TP-22332 |removed logs | Aman Singh --- src/services/ImageProcessor.ts | 1 - src/services/deviceDataSyncService.ts | 3 --- src/services/imageSyncService.ts | 3 --- src/services/videoSyncService.ts | 1 - 4 files changed, 8 deletions(-) diff --git a/src/services/ImageProcessor.ts b/src/services/ImageProcessor.ts index 455ccd51..0a706bba 100644 --- a/src/services/ImageProcessor.ts +++ b/src/services/ImageProcessor.ts @@ -57,7 +57,6 @@ getAsyncStorageItem(LocalStorageKeys.IMAGE_FILES, true) const FileDBSideEffects = () => { - console.log('filesStore', filesStore); setAsyncStorageItem(LocalStorageKeys.IMAGE_FILES, filesStore); } diff --git a/src/services/deviceDataSyncService.ts b/src/services/deviceDataSyncService.ts index 8f403d69..968eb747 100644 --- a/src/services/deviceDataSyncService.ts +++ b/src/services/deviceDataSyncService.ts @@ -39,7 +39,6 @@ export const getPreSignedUrl = (filePath: string, type: TYPE = 'IMAGES') => { }; export const uploadFileTos3 = async (object: any, filePath: string, type:TYPE) => { - console.log('object presigned url', object, filePath, filesStore[filePath]); if (!object.preSignedUrl) return; try { @@ -59,11 +58,9 @@ export const uploadFileTos3 = async (object: any, filePath: string, type:TYPE) = }, }); - console.log('httpResult', await response); const httpResult = await response.promise; - console.log('httpResult', httpResult); if (httpResult?.statusCode === 200) { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_UPLOADED, {type}); diff --git a/src/services/imageSyncService.ts b/src/services/imageSyncService.ts index 3773d2d5..44d1ca4f 100644 --- a/src/services/imageSyncService.ts +++ b/src/services/imageSyncService.ts @@ -29,7 +29,6 @@ export const imageSyncService = async () => { processFilesInTimeRange(+startTime, endTime) .then((files) => { - console.log(files, "FILES form android bridge") if (files.length > 0) { FileDB.addFiles(files); addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_IMAGES_CAPTURED, files); @@ -37,7 +36,6 @@ export const imageSyncService = async () => { setAsyncStorageItem(LocalStorageKeys.IMAGE_SYNC_TIME, endTime.toString()); }).catch((error) => { - console.log(error, "ERROR FILES"); logError(error, 'Error in image sync service'); }); }; @@ -62,7 +60,6 @@ export const prepareImagesForUpload = async () => { filesToUpLoad.sort((a, b) => a.createdAt - b.createdAt); zipFilesForServer(filesToUpLoad) .then((zippedFile) => { - console.log(zippedFile, "ZIPPED FILE") const file = { ...zippedFile, startOffset: filesToUpLoad[0].createdAt, endOffset: filesToUpLoad[filesToUpLoad.length - 1].createdAt, type: 'IMAGES' } FileDB.unlinkFile(filesToUpLoad); FileDB.addFiles(file); diff --git a/src/services/videoSyncService.ts b/src/services/videoSyncService.ts index 9b6a1246..0b32a26b 100644 --- a/src/services/videoSyncService.ts +++ b/src/services/videoSyncService.ts @@ -45,7 +45,6 @@ export const prepareVideosForUpload = async () => { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATED, zippedFile); }) .catch((error) => { - console.log(error, "error") addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_ZIP_FILE_CREATE_ERROR); logError(error, 'Error zipping files'); });