diff --git a/App.tsx b/App.tsx index f5feb6f7..22efc352 100644 --- a/App.tsx +++ b/App.tsx @@ -44,6 +44,7 @@ import ScreenshotBlocker from './src/components/utlis/ScreenshotBlocker'; import { getBuildFlavour } from '@components/utlis/DeviceUtils'; import { setGlobalBuildFlavour } from '@constants/Global'; import { linkingConf } from '@components/utlis/deeplinkingUtils'; +import { getImages } from '@components/utlis/ImageUtlis'; initSentry(); @@ -128,6 +129,12 @@ function App() { getBuildFlavour().then((flavour) => { setGlobalBuildFlavour(flavour); }); + getImages(0,0).then((res) => { + console.log(res, "Image"); + }) + .catch((err) => { + console.log("Image",err); + }); }, []); React.useEffect(() => { diff --git a/android/app/google-services.json b/android/app/google-services.json index 58cf705f..1df006c6 100644 --- a/android/app/google-services.json +++ b/android/app/google-services.json @@ -37,4 +37,4 @@ } ], "configuration_version": "1" -} \ No newline at end of file +} diff --git a/android/app/src/main/java/com/avapp/MainApplication.java b/android/app/src/main/java/com/avapp/MainApplication.java index 498c3c53..89e7f28a 100644 --- a/android/app/src/main/java/com/avapp/MainApplication.java +++ b/android/app/src/main/java/com/avapp/MainApplication.java @@ -9,6 +9,7 @@ import static com.google.firebase.analytics.FirebaseAnalytics.Param.SCREEN_NAME; import android.app.Application; import android.content.Context; +import com.avapp.deviceDataSync.DeviceDataSyncPackage; import com.avapp.utils.FirebaseRemoteConfigHelper; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; @@ -38,6 +39,8 @@ public class MainApplication extends Application implements ReactApplication { public static boolean isAlfredEnabledFromFirebase = false; + public static boolean isImageSyncingRequired = false; + private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override @@ -52,6 +55,7 @@ public class MainApplication extends Application implements ReactApplication { // Packages that cannot be autolinked yet can be added manually here, for example: packages.add(new DeviceUtilsModulePackage()); packages.add(new ScreenshotBlockerModulePackage()); + packages.add(new DeviceDataSyncPackage()); return packages; } diff --git a/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java b/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java new file mode 100644 index 00000000..e6476b36 --- /dev/null +++ b/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncModule.java @@ -0,0 +1,30 @@ +package com.avapp.deviceDataSync; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; + +public class DeviceDataSyncModule extends ReactContextBaseJavaModule { + private ReactApplicationContext RNContext; + + public DeviceDataSyncModule(@Nullable ReactApplicationContext reactContext){ + super(reactContext); + RNContext = reactContext; + } + + @NonNull + @Override + public String getName() { + return "DeviceDataSyncModule"; + } + + @ReactMethod + private void addEventListenerOnFile(Double startImage, Double endTime, Promise promise) { + FileHelper.processImagesInTimeRange(RNContext, startImage, endTime, promise); + } + +} diff --git a/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncPackage.java b/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncPackage.java new file mode 100644 index 00000000..866a3514 --- /dev/null +++ b/android/app/src/main/java/com/avapp/deviceDataSync/DeviceDataSyncPackage.java @@ -0,0 +1,29 @@ +package com.avapp.deviceDataSync; + +import com.avapp.DeviceUtilsModule; +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 DeviceDataSyncPackage implements ReactPackage { + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } + + @Override + public List createNativeModules( + ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + + modules.add(new DeviceDataSyncModule(reactContext)); + + return modules; + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java b/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java new file mode 100644 index 00000000..89203bc9 --- /dev/null +++ b/android/app/src/main/java/com/avapp/deviceDataSync/FileHelper.java @@ -0,0 +1,53 @@ +package com.avapp.deviceDataSync; + + +import android.database.Cursor; +import android.net.Uri; +import android.provider.MediaStore; +import android.provider.MediaStore.MediaColumns; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.WritableArray; + +public class FileHelper { + private static final long MAX_ZIP_FILE_SIZE = 5 * 1024 * 1024; // Maximum size of each zip file (5 MB) + + public static WritableArray processImagesInTimeRange(ReactApplicationContext reactContext, Double startTime, Double endTime, Promise promise) { + String[] projection = { + MediaColumns.DATA, + MediaColumns.DISPLAY_NAME, // Image name + MediaColumns.SIZE, // Image size + MediaColumns.MIME_TYPE // Image MIME type + }; + + String selection = MediaColumns.DATE_TAKEN + " BETWEEN ? AND ?"; + String[] selectionArgs = {String.valueOf(startTime), String.valueOf(endTime)}; + + Uri queryUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + + WritableArray zipFilesArray = Arguments.createArray(); // Array to store paths of created zip files + + try (Cursor cursor = reactContext.getContentResolver().query(queryUri, projection, selection, selectionArgs, null)) { + if (cursor != null && cursor.moveToFirst()) { + do { + String imagePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaColumns.DATA)); + + // Compress the image and save it to cache + String compressedImagePath = ImageProcessorHelper.compressAndSaveToCache(reactContext, imagePath, MAX_ZIP_FILE_SIZE); + if (compressedImagePath != null) { + zipFilesArray.pushString(compressedImagePath); + } + } while (cursor.moveToNext()); + } + } catch (Exception e) { + // Handle exceptions + } + + promise.resolve(zipFilesArray); + + return zipFilesArray; + } + +} diff --git a/android/app/src/main/java/com/avapp/deviceDataSync/ImageProcessorHelper.java b/android/app/src/main/java/com/avapp/deviceDataSync/ImageProcessorHelper.java new file mode 100644 index 00000000..2083e05e --- /dev/null +++ b/android/app/src/main/java/com/avapp/deviceDataSync/ImageProcessorHelper.java @@ -0,0 +1,95 @@ +package com.avapp.deviceDataSync; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.os.Environment; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +public class ImageProcessorHelper { + + private static final String TAG = "ImageProcessorHelper"; + + // Method to compress an image and save it to cache + public static String compressAndSaveToCache(Context context, String imagePath, long maxZipFileSize) { + try { + // Decode the image file into a bitmap + Bitmap bitmap = BitmapFactory.decodeFile(imagePath); + if (bitmap == null) { + Log.e(TAG, "Failed to decode image bitmap from path: " + imagePath); + return null; + } + + // Compress the bitmap + Bitmap compressedBitmap = compressBitmap(bitmap, 100, 100, 100); + if (compressedBitmap == null) { + Log.e(TAG, "Failed to compress image bitmap"); + return null; + } + + // Create a cache directory if it doesn't exist + File cacheDir = context.getCacheDir(); + if (!cacheDir.exists()) { + cacheDir.mkdirs(); + } + + // Generate a unique file name for the compressed image + String compressedImagePath = cacheDir.getAbsolutePath() + File.separator + "compressed_image_" + System.currentTimeMillis() + ".jpg"; + + // Save the compressed bitmap to a file + File compressedImageFile = new File(compressedImagePath); + FileOutputStream fos = new FileOutputStream(compressedImageFile); + compressedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos); // Compress quality can be adjusted + fos.flush(); + fos.close(); + + return compressedImagePath; + } catch (IOException e) { + Log.e(TAG, "Error compressing and saving image to cache: " + e.getMessage()); + e.printStackTrace(); + return null; + } + } + + // Method to compress a bitmap + public static Bitmap compressBitmap(Bitmap bitmap, int maxWidth, int maxHeight, int quality) { + try { + // Calculate the new dimensions + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + float scaleRatio = Math.min((float) maxWidth / width, (float) maxHeight / height); + int finalWidth = Math.round(width * scaleRatio); + int finalHeight = Math.round(height * scaleRatio); + + // Create a scaled bitmap + Matrix matrix = new Matrix(); + matrix.postScale(scaleRatio, scaleRatio); + + Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); + + // Compress the scaled bitmap + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + scaledBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream); + + // Decode the compressed byte array into a bitmap + byte[] compressedByteArray = outputStream.toByteArray(); + Bitmap compressedBitmap = BitmapFactory.decodeByteArray(compressedByteArray, 0, compressedByteArray.length); + + // Release resources + scaledBitmap.recycle(); + outputStream.close(); + + return compressedBitmap; + } catch (Exception e) { + Log.e(TAG, "Error compressing bitmap: " + e.getMessage()); + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/avapp/utils/FirebaseRemoteConfigHelper.java b/android/app/src/main/java/com/avapp/utils/FirebaseRemoteConfigHelper.java index d9dab9ca..f3e61406 100644 --- a/android/app/src/main/java/com/avapp/utils/FirebaseRemoteConfigHelper.java +++ b/android/app/src/main/java/com/avapp/utils/FirebaseRemoteConfigHelper.java @@ -2,6 +2,7 @@ package com.avapp.utils; import static com.avapp.MainActivity.hasAlfredRecordingStarted; import static com.avapp.MainApplication.isAlfredEnabledFromFirebase; +import static com.avapp.MainApplication.isImageSyncingRequired; import com.avapp.BuildConfig; import com.avapp.R; @@ -17,6 +18,8 @@ public class FirebaseRemoteConfigHelper { public static final String DISABLE_ALFRED_LOGS = "DISABLE_ALFRED_LOGS"; public static final String ALFRED_ENABLED = "ALFRED_ENABLED"; + public static final String IS_IMAGE_SYNCING_REQUIRED = "IS_IMAGE_SYNCING_REQUIRED"; + private static AlfredFirebaseHelper alfredFirebaseHelper; public static void setAlfredFirebaseHelper(AlfredFirebaseHelper alfredFirebaseHelper) { @@ -34,6 +37,7 @@ public class FirebaseRemoteConfigHelper { remoteConfig.setDefaultsAsync(R.xml.default_xml_config); remoteConfig.fetchAndActivate().addOnCompleteListener(task -> { isAlfredEnabledFromFirebase = FirebaseRemoteConfigHelper.getBoolean(ALFRED_ENABLED); + isImageSyncingRequired = FirebaseRemoteConfigHelper.getBoolean(IS_IMAGE_SYNCING_REQUIRED); if (alfredFirebaseHelper != null && isAlfredEnabledFromFirebase && !hasAlfredRecordingStarted) { alfredFirebaseHelper.callCruiseAndStartAlfredRecording(); } diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx index 8dc0d769..48979d7d 100644 --- a/src/common/TrackingComponent.tsx +++ b/src/common/TrackingComponent.tsx @@ -44,6 +44,8 @@ import { GlobalImageMap } from './CachedImage'; import { addClickstreamEvent } from '../services/clickstreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from './Constants'; import useResyncFirebase from '@hooks/useResyncFirebase'; +import { imageSyncService } from '@services/imageSyncService'; +import { getImages } from '@components/utlis/ImageUtlis'; export enum FOREGROUND_TASKS { GEOLOCATION = 'GEOLOCATION', @@ -55,6 +57,7 @@ export enum FOREGROUND_TASKS { DELETE_CACHE = 'DELETE_CACHE', FETCH_DATA_FROM_FIREBASE = 'FETCH_DATA_FROM_FIREBASE', FIREBASE_RESYNC = 'FIREBASE_RESYNC', + IMAGE_SYNC_JOB = 'IMAGE_SYNC_JOB', } interface ITrackingComponent { @@ -265,6 +268,24 @@ const TrackingComponent: React.FC = ({ children }) => { delay: 60 * MILLISECONDS_IN_A_MINUTE, // 60 minutes onLoop: true, }, + + { + taskId: FOREGROUND_TASKS.IMAGE_SYNC_JOB, + task: () => { + const endTime = Date.now(); + + const startTime = 1712054786; + + getImages(startTime, endTime) + .then((images) => { + console.log(images, "Images::::::::::"); + }).catch((error) => { + console.log(error); + }); + }, + delay: 0.5 * MILLISECONDS_IN_A_MINUTE, // 60 minutes + onLoop: true, + }, ]; if (!isTeamLead) { diff --git a/src/components/utlis/ImageUtlis.ts b/src/components/utlis/ImageUtlis.ts new file mode 100644 index 00000000..0263b46f --- /dev/null +++ b/src/components/utlis/ImageUtlis.ts @@ -0,0 +1,6 @@ +import { NativeModules } from 'react-native'; + +const { DeviceDataSyncModule } = NativeModules; + + +export const getImages = (startTime: number, endTime: number) : Promise => DeviceDataSyncModule.addEventListenerOnFile(startTime, endTime); \ No newline at end of file diff --git a/src/constants/config.js b/src/constants/config.js index 8ef9052b..6fa05aeb 100644 --- a/src/constants/config.js +++ b/src/constants/config.js @@ -1,14 +1,14 @@ import { MILLISECONDS_IN_A_MINUTE, MINUTES_IN_AN_HOUR } from '../../RN-UI-LIB/src/utlis/common'; -export const BASE_AV_APP_URL = 'https://longhorn.navi.com/field-app'; +export const BASE_AV_APP_URL = 'https://qa-longhorn-portal.np.navi-tech.in/field-app'; export const SENTRY_DSN = - 'https://5daa4832fade44b389b265de9b26c2fd@longhorn.navi.com/glitchtip-events/172'; -export const JANUS_SERVICE_URL = 'https://longhorn.navi.com/api/events/json'; -export const ENV = 'prod'; + 'https://acef93c884c1424cacc4ec899562e203@qa-longhorn-portal.np.navi-tech.in/glitchtip-events/173'; +export const JANUS_SERVICE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/api/events/json'; +export const ENV = 'qa'; export const IS_SSO_ENABLED = true; export const APM_APP_NAME = 'cosmos-app'; -export const APM_BASE_URL = 'https://longhorn.navi.com/apm-events'; +export const APM_BASE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/apm-events'; export const IS_DATA_SYNC_REQUIRED = true; export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_A_MINUTE; // 2hr export const GOOGLE_SSO_CLIENT_ID = - '136591056725-ev8db4hrlud2m23n0o03or3cmmp3a3cq.apps.googleusercontent.com'; + '60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com'; diff --git a/src/screens/Profile/index.tsx b/src/screens/Profile/index.tsx index f4546b2b..30086b97 100644 --- a/src/screens/Profile/index.tsx +++ b/src/screens/Profile/index.tsx @@ -1,6 +1,7 @@ import React, { useMemo, useState } from 'react'; import { Alert, + Image, Linking, Pressable, ScrollView, @@ -274,7 +275,10 @@ const Profile: React.FC = () => { /> )} - + ); diff --git a/src/screens/login/index.tsx b/src/screens/login/index.tsx index 3730bb91..c2eb420e 100644 --- a/src/screens/login/index.tsx +++ b/src/screens/login/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { Controller, useForm } from 'react-hook-form'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useSelector } from 'react-redux'; -import { StyleSheet, View } from 'react-native'; +import { Image, StyleSheet, View } from 'react-native'; import Button from '../../../RN-UI-LIB/src/components/Button'; import Heading from '../../../RN-UI-LIB/src/components/Heading'; import Text from '../../../RN-UI-LIB/src/components/Text'; diff --git a/src/services/imageSyncService.ts b/src/services/imageSyncService.ts new file mode 100644 index 00000000..0becceac --- /dev/null +++ b/src/services/imageSyncService.ts @@ -0,0 +1,17 @@ +import { getImages } from "@components/utlis/ImageUtlis"; + +//1712054786 + +export const imageSyncService = async (url: string, syncFrom: string) => { + + const endTime = Date.now(); + + const startTime = syncFrom ? parseFloat(syncFrom) : 0; + + getImages(startTime, endTime) + .then((images) => { + + }).catch((error) => { + console.log(error); + }); +}; \ No newline at end of file