diff --git a/README.md b/README.md index 0948e4a37..f6477204f 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,113 @@ # android-registration-client -Reference Android Registration Client Software - WIP - -# Use of Java Packages: - 1. Added all the java packages in android directory of the project: - 2. Add implementation in settings.gradle file of the project: - Eg: include(:) - 3. Set the server base url in android/build.gradle file. - 4. Add implementation for packages in android/app/build.gradle files inside dependency block as: - implementation project(:) - -# Creation of Services in the project's java package. - 1. Added all the required functions by creating individual services for each functionality. - 2. Using AppComponent interface which inherits Dagger for dependecny injection. - 3. Dependency injection performed in MainActivity.java file by calling inject() method. - -# Creation of MethodChannels in dart classes. - 1. Create MethodChannel with a constant string name in a dart class and try to invoke a method - provided by this channel. - 2. This invoke method will take the function name and arguments which will be sent to native side - for getting a response. - 3. The function name in invoke method and on native side should be the same as: - methodChannel.invokeMethod("fetchDetails"); - 4. On native side, in MainActivity file, inside MethodChennel() use a switch case to check for method name - call.method argument which checks "fetchDetails" case. On matching, it implements "fetchDetails" method. - case "fetchDetails: - Service().fetchDetails(result); - break; - -# Fetch Machine Details - 1. Method channel on Credential Page invokes "getMachineDetails" method and gets a response from - native side as a string. - 2. Then this string can be decoded into a Map and the details can be displayed - on a page. - 3. It also contains functionality to copy the details and save a file containing details in internal storage. - -# Perform Online Login - 1. First create a machine with given machine details in admin portal. - 2. Try to login by providing correct userId and password. - 3. After pressing login button, "login" method will be invoked from method channel. - 3. If user is able to login with given credentials, perform master data sync by pressing the sync data button. - 4. After that based on the roles of the user which is logged in, he will be prompted to a either home page or onboarding page. - -# Performing Offline Login - 1. After performing online login and master data sync, a hashed value of the password will be saved in the db. - 2. So if the user provides offline login, then the hashed value for password will be checked and matched with the entered password value. - 3. If the value matches, then the user is able to perform offline login, else screen will display required error messages. + +The Android Registration Client is a tablet application that serves as a portable version of the existing desktop Registration Client. It has been developed to support accessibility on all Android devices. The creation of the Android Registration Client was driven by the need to meet the mobility requirements of countries adopting MOSIP. + +# Developer Guide + +The documentation here will guide you through the pre-requisites and the other necessary details required for Android Registration Client developer setup. + +The android-registration-client repository contains the Android Registration Client software for MOSIP. The feature-flutter branch focuses on integrating Flutter into the client. + +## Setup + +To set up the Android Registration Client with Flutter and Android Studio, follow the steps below: + +#### Prerequisites + +* Flutter SDK (3.10.4): Install Flutter by following the official [Flutter installation guide](https://flutter.dev/docs/get-started/install). +* Android Studio (or Any IDE of your choice): Download and install Android Studio from the official [Android Studio website](https://developer.android.com/studio). + +#### Step 1: Clone the Repository + +The `develop` branch of android-reg-client is currently being actively developed. If you wish to access this branch, you can clone the repository by executing the following command in your terminal. Alternatively, you can download one of the releases available in the repository's release section. + +``` +git clone -b feature-flutter https://github.com/mosip/android-registration-client.git +``` + +**Active Branches**: + +* [release-0.11.x](https://github.com/mosip/android-registration-client/tree/release-0.11.x)(developer release branch) +* [develop](https://github.com/mosip/android-registration-client/tree/develop)(active development branch) + +#### Step 2: Set up Flutter in Android Studio + +1. To begin, launch Android Studio. +2. Next, select **Open an existing Android Studio project** and navigate to the cloned repository. +3. Open the `android-registration-client` directory as a project in Android Studio. +4. In order to integrate Flutter with Android Studio, install the Flutter plugin by accessing `File > Settings > Plugins` and searching for **Flutter**. Proceed to click on **Install** to install the plugin. +5. To ensure proper functionality, configure the Flutter SDK path by navigating to `File > Settings > Languages & Frameworks > Flutter` and specifying the Flutter SDK path as the location where you have installed Flutter. +6. Finally, save the changes by clicking on the "Apply" button. + +**Customizing the Registration Client** + +* Styling of the application can be configured by modifying these files `lib/utils/app_style.dart, lib/utils/app_config.dart` +* Application language bundles can be added to this path `lib/l10n` After adding the bundle run the below command to generate Localization data (Required for the first time). + +``` +flutter gen-l10n +``` + +* The label and application logo can be changed here android/app/src/main/AndroidManifest.xml + +#### Step 3: Build and Run the Application + +* The `pigeon.sh` file consists of the necessary commands for downloading dependencies and generating Flutter - Android native communication code. Please execute the `pigeon.sh` file or execute the commands within the file separately. +* Ensure you have connected an Android device or initiated an Android emulator. +* Open the terminal within Android Studio or use an external terminal. +* Navigate to the `android-registration-client` directory. +* Run the following command to build and execute the application: + +``` +flutter run +``` + +#### Step 4: Build, debug, and release APK + +Execute the commands below to debug and release the APK + +``` +// Debug APK +flutter build apk --debug + +// Release APK +flutter build apk --release +``` + +### Set up Mock MDS for Biometric Scan + +The Mock MDS tool can be utilized to simulate the functionalities of biometric devices. The Mock MDS application is compliant with CTK standards and can serve as a substitute for Android SBI modules during testing and validation. + +1. Install the Mock MDS application. +2. Access the **Settings** menu. +3. Under Device Configuration, choose **Registration** from the dropdown menu. +4. In P12 Configuration: + * Enter the necessary credentials for the Device Key and upload the Device P12 file. + * Enter the required credentials for the FTM Key and upload the FTM P12 file. + * Complete all fields in MOSIP IDA Configuration. +5. In Modality Configuration, specify the quality score for Face, Finger, and Iris scans(these values can also be adjusted during testing). +6. Click on the **Save** button. +7. Go back to the Home Page and select `LOAD AND VALIDATE CERTIFICATES`. + +A toast message will be displayed indicating the success of the validation process. + +**Note**: To view the released version of the Mock SBI APK, click [here](https://github.com/mosip/android-camera-mds/releases/tag/vDP1). + +To download the Mock SBI APK, click on `camera-mds.zip`. + +### Contributions + +If you would like to contribute to the Android Registration Client, please follow the guidelines outlined [here](https://docs.mosip.io/1.2.0/community/code-contributions). + +### License + +The Android Registration Client is licensed under the [MIT License](https://github.com/mosip/android-registration-client/blob/develop/LICENSE). + +### Support + +If you encounter any issues or have any questions, please open an issue on the [GitHub repository](https://github.com/mosip/android-registration-client/issues). + +### Sources + +* [Flutter- Get started: Install](https://flutter.dev/docs/get-started/install) +* [Android Studio- Download](https://developer.android.com/studio) diff --git a/android/app/build.gradle b/android/app/build.gradle index 2dc12ee81..8bc7b4ef4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -135,8 +135,9 @@ dependencies { annotationProcessor 'com.google.dagger:dagger-compiler:2.41' annotationProcessor 'com.google.dagger:dagger-android-processor:2.41' implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.squareup.okhttp3:mockwebserver:3.14.9' -// implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.squareup.okhttp3:okhttp:4.9.2' + implementation 'com.squareup.okio:okio:3.4.0' + implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.lifecycle:lifecycle-runtime:2.4.1' @@ -144,7 +145,7 @@ dependencies { implementation "androidx.fragment:fragment:$fragment_version" implementation "androidx.room:room-runtime:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version" - implementation 'com.cronutils:cron-utils:9.1.5' + implementation 'com.cronutils:cron-utils:9.1.7' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' @@ -181,9 +182,9 @@ dependencies { implementation 'org.apache.commons:commons-lang3:3.12.0' // https://mvnrepository.com/artifact/commons-codec/commons-codec implementation 'commons-codec:commons-codec:1.15' - implementation group: 'commons-io', name: 'commons-io', version: '2.11.0' + implementation group: 'commons-io', name: 'commons-io', version: '2.14.0' - implementation 'org.bouncycastle:bcprov-jdk15on:1.59' + implementation 'org.bouncycastle:bcprov-jdk15on:1.66' implementation 'com.auth0.android:jwtdecode:2.0.1' implementation project(':clientmanager') @@ -240,8 +241,8 @@ dependencies { implementation 'androidx.navigation:navigation-ui:2.4.2' implementation 'javax.inject:javax.inject:1' - implementation 'com.fasterxml.jackson.core:jackson-core:2.13.2' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2' + implementation 'com.fasterxml.jackson.core:jackson-core:2.15.0-rc1' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4.1' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.2' @@ -292,7 +293,7 @@ dependencies { implementation 'org.apache.velocity:velocity:1.7' implementation 'org.mvel:mvel2:2.4.14.Final' - implementation('com.tom-roush:pdfbox-android:2.0.20.0') { + implementation('com.tom-roush:pdfbox-android:2.0.27.0') { exclude group: 'org.bouncycastle' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c7466adc1..85ced5362 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,6 +8,9 @@ + + + { - createBackgroundTask("registrationPacketUploadJob"); + createBackgroundTask(finalJobApiName); }, 1, TimeUnit.MINUTES); } - } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - createBackgroundTask("registrationPacketUploadJob"); - IntentFilter intentFilterUpload = new IntentFilter("REGISTRATION_PACKET_UPLOAD"); +// createBackgroundTask("registrationPacketUploadJob"); + IntentFilter intentFilterUpload = new IntentFilter("SYNC_JOB_TRIGGER"); registerReceiver(broadcastReceiver, intentFilterUpload); } + private void initializeAutoSync() { + try { + CenterMachineDto dto = masterDataService.getRegistrationCenterMachineDetails(); + if (dto != null && dto.getMachineRefId() != null) { + Log.d(getClass().getSimpleName(), "Machine configured - initializing auto sync"); + scheduleAllActiveJobs(); + } else { + Log.w(getClass().getSimpleName(), "Machine not configured yet - skipping auto sync initialization"); + } + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Error initializing auto sync", e); + } + } + + // Schedule all active jobs from database + void scheduleAllActiveJobs() { + new Thread(() -> { + try { + List activeJobs = syncJobDefRepository.getAllSyncJobDefList(); + int scheduledCount = 0; + + for (SyncJobDef job : activeJobs) { + if (job.getIsActive() != null && job.getIsActive() && job.getApiName() != null) { + Log.d(getClass().getSimpleName(), "Scheduling job: " + job.getApiName() + + " (ID: " + job.getId() + ", Cron: " + job.getSyncFreq() + ")"); + + runOnUiThread(() -> createBackgroundTask(job.getApiName())); + scheduledCount++; + } + } + + Log.d(getClass().getSimpleName(), "Scheduled " + scheduledCount + " active jobs"); + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Error scheduling jobs", e); + } + }).start(); + } + @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(broadcastReceiver); } - void createBackgroundTask(String api){ - Intent intent = new Intent(this, UploadBackgroundService.class); - PendingIntent pendingIntent; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - pendingIntent = PendingIntent.getForegroundService( - this, - 0, // Request code - intent, - PendingIntent.FLAG_IMMUTABLE - ); - } else { - pendingIntent = PendingIntent.getService( - this, - 0, // Request code - intent, - PendingIntent.FLAG_UPDATE_CURRENT - ); - } - AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); - if (alarmManager != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) { - Intent permissionIntent = new Intent(android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM); - permissionIntent.setData(Uri.fromParts("package", getPackageName(), null)); - startActivity(permissionIntent); - } - long alarmTime = batchJob.getIntervalMillis(api); - long currentTime = System.currentTimeMillis(); - long delay = alarmTime > currentTime ? alarmTime - currentTime : alarmTime - currentTime; - Log.d(getClass().getSimpleName(), String.valueOf(delay)+ " Next Execution"); - -// alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(), 30000, pendingIntent); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { - alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pendingIntent); - } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { - alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pendingIntent); + public void createBackgroundTask(String api){ + try { + Intent intent = new Intent(this, UploadBackgroundService.class); + intent.putExtra(UploadBackgroundService.EXTRA_JOB_API_NAME, api); // Pass job API name + + // Use unique request code per job to prevent conflicts + int requestCode = Math.abs(api.hashCode() % 10000); + + PendingIntent pendingIntent; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + pendingIntent = PendingIntent.getForegroundService(this, requestCode, intent, PendingIntent.FLAG_IMMUTABLE); } else { - alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pendingIntent); + pendingIntent = PendingIntent.getService(this, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } + + AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + if (alarmManager != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) { + Intent permissionIntent = new Intent(android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM); + permissionIntent.setData(Uri.fromParts("package", getPackageName(), null)); + startActivity(permissionIntent); + return; + } + + // Get next execution time from cron expression + long alarmTime = batchJob.getIntervalMillis(api); + long currentTime = System.currentTimeMillis(); + long delay = alarmTime - currentTime; + + // Ensure delay is positive (prevent immediate execution) + if (delay < 0 || delay < 60000) { + Log.w(getClass().getSimpleName(), api + " - Calculated delay is too small (" + delay + "ms), using 1 minute minimum"); + delay = 60000; // Minimum 1 minute + } + + Log.d(getClass().getSimpleName(), api + " - Request code: " + requestCode + + ", Next execution in: " + (delay / 1000) + " seconds"); + + // Cancel old alarm before scheduling new one + alarmManager.cancel(pendingIntent); + + // Schedule alarm + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pendingIntent); + } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pendingIntent); + } else { + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pendingIntent); + } + + Log.d(getClass().getSimpleName(), api + " - Alarm scheduled successfully"); } + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Error scheduling job: " + api, e); } } @@ -269,6 +332,7 @@ public void initializeAppComponent() { .hostApiModule(new HostApiModule(getApplication())) .build(); appComponent.inject(this); + initializeAutoSync(); } @Override @@ -297,6 +361,7 @@ public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { MasterDataSyncPigeon.SyncApi.setup(flutterEngine.getDartExecutor().getBinaryMessenger(), masterDataSyncApi); masterDataSyncApi.setCallbackActivity(this, batchJob); AuditResponsePigeon.AuditResponseApi.setup(flutterEngine.getDartExecutor().getBinaryMessenger(), auditDetailsApi); + GlobalConfigSettingsPigeon.GlobalConfigSettingsApi.setup(flutterEngine.getDartExecutor().getBinaryMessenger(), globalConfigSettingsApi); } @Override @@ -313,6 +378,12 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { case 3: biometricsDetailsApi.parseRCaptureResponse(data.getExtras()); break; + case 4: + biometricsDetailsApi.parseDiscoverResponseForList(data.getExtras()); + break; + case 5: + biometricsDetailsApi.handleDeviceInfoResponseForList(data.getExtras()); + break; } } } diff --git a/android/app/src/main/java/io/mosip/registration_client/UploadBackgroundService.java b/android/app/src/main/java/io/mosip/registration_client/UploadBackgroundService.java index ee3159bf2..43ff484ac 100644 --- a/android/app/src/main/java/io/mosip/registration_client/UploadBackgroundService.java +++ b/android/app/src/main/java/io/mosip/registration_client/UploadBackgroundService.java @@ -22,6 +22,8 @@ public class UploadBackgroundService extends Service { private static final int NOTIFICATION_ID = 1; private static final String CHANNEL_ID = "ForegroundServiceChannel"; + public static final String EXTRA_JOB_API_NAME = "job_api_name"; + @Override public void onCreate() { super.onCreate(); @@ -29,11 +31,17 @@ public void onCreate() { @Override public int onStartCommand(Intent intent, int flags, int startId) { + // Get job API name from intent, default to packet upload + String jobApiName = intent != null ? intent.getStringExtra(EXTRA_JOB_API_NAME) : "registrationPacketUploadJob"; + createNotificationChannel(); Notification notification = createNotification(); startForeground(NOTIFICATION_ID, notification); - Log.d(getClass().getSimpleName(), "Sync & Upload Packets in background activity"); - Intent broadcastIntent = new Intent("REGISTRATION_PACKET_UPLOAD"); + Log.d(getClass().getSimpleName(), "Sync job triggered: " + jobApiName); + + // Send broadcast with job API name + Intent broadcastIntent = new Intent("SYNC_JOB_TRIGGER"); + broadcastIntent.putExtra(EXTRA_JOB_API_NAME, jobApiName); sendBroadcast(broadcastIntent); return START_STICKY; } diff --git a/android/app/src/main/java/io/mosip/registration_client/api_services/AuthenticationApi.java b/android/app/src/main/java/io/mosip/registration_client/api_services/AuthenticationApi.java index e0c2e4437..6a2768c11 100644 --- a/android/app/src/main/java/io/mosip/registration_client/api_services/AuthenticationApi.java +++ b/android/app/src/main/java/io/mosip/registration_client/api_services/AuthenticationApi.java @@ -19,6 +19,7 @@ import androidx.annotation.NonNull; +import java.util.Arrays; import java.util.List; import javax.inject.Inject; @@ -242,4 +243,26 @@ public void forgotPasswordUrl(@NonNull AuthResponsePigeon.Result result) String response = this.globalParamRepository.getCachedStringForgotPassword(); result.success(response); } + + @Override + public void getIdleTime(@NonNull AuthResponsePigeon.Result result) { + String response = this.globalParamRepository.getCachedStringIdleTime(); + result.success(response); + } + + @Override + public void getAutoLogoutPopupTimeout(@NonNull AuthResponsePigeon.Result result) { + String response = this.globalParamRepository.getCachedStringRefreshedLoginTime(); + result.success(response); + } + + @Override + public void getRolesByUserId(@NonNull String userId, @NonNull AuthResponsePigeon.Result> result) { + try { + List roles = loginService.getRolesByUserId(userId); + result.success(roles); + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Getting user role failed!" + Arrays.toString(e.getStackTrace())); + } + } } diff --git a/android/app/src/main/java/io/mosip/registration_client/api_services/BiometricsDetailsApi.java b/android/app/src/main/java/io/mosip/registration_client/api_services/BiometricsDetailsApi.java index 108d5c50d..e7b580b90 100644 --- a/android/app/src/main/java/io/mosip/registration_client/api_services/BiometricsDetailsApi.java +++ b/android/app/src/main/java/io/mosip/registration_client/api_services/BiometricsDetailsApi.java @@ -29,11 +29,8 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -56,7 +53,6 @@ import io.mosip.registration.clientmanager.exception.ClientCheckedException; import io.mosip.registration.clientmanager.repository.GlobalParamRepository; import io.mosip.registration.clientmanager.service.Biometrics095Service; -import io.mosip.registration.clientmanager.service.RegistrationServiceImpl; import io.mosip.registration.clientmanager.service.UserOnboardService; import io.mosip.registration.clientmanager.spi.AuditManagerService; import io.mosip.registration.clientmanager.spi.RegistrationService; @@ -87,7 +83,7 @@ public class BiometricsDetailsApi implements BiometricsPigeon.BiometricsApi { private int qualityThreshold; List biometricsDtoList; - private List listBitmaps1 = new ArrayList<>(); + private final List listBitmaps1 = new ArrayList<>(); private byte[] byteArrayTester; private List listByteArrayTester1 = new ArrayList<>(); BiometricsPigeon.Result result1; @@ -108,6 +104,11 @@ public class BiometricsDetailsApi implements BiometricsPigeon.BiometricsApi { private static final String RIGHT = "Right"; private static final String LEFT = "Left"; + private final List deviceDetailsList = new ArrayList<>(); + private BiometricsPigeon.Result> deviceListResult; + + private Modality currentDeviceListModality; + @@ -546,6 +547,8 @@ public void saveOperatorBiometrics(@NonNull BiometricsPigeon.Result resu userOnboardService.onboardOperator(userOnboardService.getOperatorBiometrics(), () -> { if(userOnboardService.getIsOnboardSuccess()) { result.success("OK"); + } else { + result.success("FAILED"); } }); }catch (Exception e){ @@ -663,6 +666,81 @@ public void conditionalBioAttributeValidation(@NonNull String fieldId, @NonNull } } + @Override + public void getListOfDevices(@NonNull String modality, @NonNull BiometricsPigeon.Result> result) { + deviceDetailsList.clear(); + deviceListResult = result; + currentDeviceListModality = getModality(modality); + try { + Intent intent = new Intent(); + intent.setAction(RegistrationConstants.DISCOVERY_INTENT_ACTION); + queryPackage(intent); + DiscoverRequest discoverRequest = new DiscoverRequest(); + discoverRequest.setType(currentDeviceListModality.getSingleType().value()); + intent.putExtra(RegistrationConstants.SBI_INTENT_REQUEST_KEY, + objectMapper.writeValueAsBytes(discoverRequest)); + activity.startActivityForResult(intent, 4); + } catch (Exception e) { + Log.e(TAG, "Error starting device discovery", e); + result.success(new ArrayList<>()); + } + } + + public void handleDeviceInfoResponseForList(Bundle bundle) { + try { + byte[] infoBytes = bundle.getByteArray(RegistrationConstants.SBI_INTENT_RESPONSE_KEY); + String[] deviceInfo = biometricsService.handleDeviceInfoResponse(currentDeviceListModality, infoBytes); + BiometricsPigeon.DeviceInfo deviceInfoObj = new BiometricsPigeon.DeviceInfo.Builder() + .setDeviceName(deviceInfo[0]) + .setDeviceId(deviceInfo[1]) + .setConnectionStatus(deviceInfo[2]) + .build(); + + deviceDetailsList.add(deviceInfoObj); + + if (deviceListResult != null) deviceListResult.success(deviceDetailsList); + } catch (Exception e) { + Log.e(TAG, "Error handling device info response", e); + if (deviceListResult != null) deviceListResult.success(new ArrayList<>()); + } + } + + // Device listing flow: parse discovery response, then request device info + public void parseDiscoverResponseForList(Bundle bundle) { + try { + byte[] bytes = bundle.getByteArray(RegistrationConstants.SBI_INTENT_RESPONSE_KEY); + String discoveredCallbackId = biometricsService.handleDiscoveryResponse(currentDeviceListModality, bytes); + if (discoveredCallbackId != null) { + infoForList(discoveredCallbackId); + } else { + if (deviceListResult != null) deviceListResult.success(new ArrayList<>()); + } + } catch (BiometricsServiceException e) { + auditManagerService.audit(AuditEvent.DISCOVER_SBI_PARSE_FAILED, Components.REGISTRATION, e.getMessage()); + Log.e(TAG, "Failed to parse discover response for list", e); + if (deviceListResult != null) deviceListResult.success(new ArrayList<>()); + } + } + + private void infoForList(String callbackId) { + if (callbackId == null) { + Log.e(TAG, "No SBI found"); + if (deviceListResult != null) deviceListResult.success(new ArrayList<>()); + return; + } + + try { + Intent intent = new Intent(); + intent.setAction(callbackId + RegistrationConstants.D_INFO_INTENT_ACTION); + queryPackage(intent); + activity.startActivityForResult(intent, 5); + } catch (ClientCheckedException ex) { + auditManagerService.audit(AuditEvent.DEVICE_INFO_FAILED, Components.REGISTRATION, ex.getMessage()); + Log.e(TAG, ex.getMessage(), ex); + if (deviceListResult != null) deviceListResult.success(new ArrayList<>()); + } + } + public static Boolean customMatcher(String str1, String str2) { Boolean result = false; if (str1.matches(LEFT_INDEX_FINGER) && str2.matches("leftIndex")) { @@ -707,28 +785,28 @@ public static Boolean customMatcher(String str1, String str2) { return result; } - public static Map objectToMap(Object object) { - Map map = new HashMap<>(); + // public static Map objectToMap(Object object) { + // Map map = new HashMap<>(); - // Get all fields of the object using reflection - Field[] fields = object.getClass().getDeclaredFields(); + // // Get all fields of the object using reflection + // Field[] fields = object.getClass().getDeclaredFields(); - for (Field field : fields) { - field.setAccessible(true); // Make the private fields accessible + // for (Field field : fields) { + // // field.setAccessible(true); // Make the private fields accessible - try { - Object fieldValue = field.get(object); - if (fieldValue != null) { - // Convert field value to String and add it to the map - map.put(field.getName(), fieldValue.toString()); - } - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } + // try { + // Object fieldValue = field.get(object); + // if (fieldValue != null) { + // // Convert field value to String and add it to the map + // map.put(field.getName(), fieldValue.toString()); + // } + // } catch (IllegalAccessException e) { + // e.printStackTrace(); + // } + // } - return map; - } + // return map; + // } private void queryPackage(Intent intent) throws ClientCheckedException { List activities = activity.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_ALL); diff --git a/android/app/src/main/java/io/mosip/registration_client/api_services/GlobalConfigSettingsApi.java b/android/app/src/main/java/io/mosip/registration_client/api_services/GlobalConfigSettingsApi.java new file mode 100644 index 000000000..37cca3aac --- /dev/null +++ b/android/app/src/main/java/io/mosip/registration_client/api_services/GlobalConfigSettingsApi.java @@ -0,0 +1,88 @@ +package io.mosip.registration_client.api_services; + +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.mosip.registration.clientmanager.repository.GlobalParamRepository; +import io.mosip.registration.clientmanager.spi.LocalConfigService; +import io.mosip.registration.clientmanager.spi.MasterDataService; +import io.mosip.registration_client.model.GlobalConfigSettingsPigeon; + +@Singleton +public class GlobalConfigSettingsApi implements GlobalConfigSettingsPigeon.GlobalConfigSettingsApi { + + MasterDataService masterDataService; + + LocalConfigService localConfigService; + + GlobalParamRepository globalParamRepository; + + @Inject + public GlobalConfigSettingsApi(MasterDataService masterDataService, LocalConfigService localConfigService, GlobalParamRepository globalParamRepository) { + this.masterDataService = masterDataService; + this.localConfigService = localConfigService; + this.globalParamRepository = globalParamRepository; + } + + @Override + public void getRegistrationParams(@NonNull GlobalConfigSettingsPigeon.Result> result) { + try { + Map response = masterDataService.getRegistrationParams(); + result.success(response); + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Error fetching registration params.", e); + result.error(e); + } + } + + @Override + public void getLocalConfigurations(@NonNull GlobalConfigSettingsPigeon.Result> result) { + try { + Map response = localConfigService.getLocalConfigurations(); + result.success(response); + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Error fetching local configurations.", e); + result.error(e); + } + } + + @Override + public void getPermittedConfigurationNames(@NonNull GlobalConfigSettingsPigeon.Result> result) { + try { + List response = localConfigService.getPermittedConfiguration(); + result.success(response); + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Error fetching permitted configuration names.", e); + result.error(e); + } + } + + @Override + public void modifyConfigurations(@NonNull Map localPreferences, @NonNull GlobalConfigSettingsPigeon.Result result) { + try { + localConfigService.modifyConfigurations(localPreferences); + result.success(null); + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Error modifying configurations.", e); + result.error(e); + } + } + + @Override + public void getGpsEnableFlag(@NonNull GlobalConfigSettingsPigeon.Result result) { + String gpsFlag = ""; + try { + gpsFlag = globalParamRepository.getCachedStringGpsDeviceEnableFlag(); + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Error fetching GPS enable flag", e); + } + result.success(gpsFlag); + } +} \ No newline at end of file diff --git a/android/app/src/main/java/io/mosip/registration_client/api_services/MasterDataSyncApi.java b/android/app/src/main/java/io/mosip/registration_client/api_services/MasterDataSyncApi.java index f154ff620..1f6f1a4c5 100644 --- a/android/app/src/main/java/io/mosip/registration_client/api_services/MasterDataSyncApi.java +++ b/android/app/src/main/java/io/mosip/registration_client/api_services/MasterDataSyncApi.java @@ -20,6 +20,7 @@ import android.os.Build; import android.os.SystemClock; import android.util.Log; +import android.widget.Toast; import androidx.annotation.NonNull; @@ -54,6 +55,8 @@ import io.mosip.registration.clientmanager.repository.SyncJobDefRepository; import io.mosip.registration.clientmanager.repository.TemplateRepository; import io.mosip.registration.clientmanager.repository.UserDetailRepository; +import io.mosip.registration.clientmanager.constant.RegistrationConstants; +import io.mosip.registration.clientmanager.service.JobManagerServiceImpl; import io.mosip.registration.clientmanager.spi.AuditManagerService; import io.mosip.registration.clientmanager.spi.JobManagerService; import io.mosip.registration.clientmanager.spi.MasterDataService; @@ -165,9 +168,8 @@ public void getLastSyncTime(@NonNull MasterDataSyncPigeon.Result result) { + public void getPolicyKeySync(@NonNull Boolean isManualSync, @NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { CenterMachineDto centerMachineDto = masterDataService.getRegistrationCenterMachineDetails(); - if (centerMachineDto == null) { result.success(syncResult("PolicyKeySync", 5, "policy_key_sync_failed")); return; @@ -175,33 +177,33 @@ public void getPolicyKeySync(@NonNull Boolean isManualSync, @NonNull MasterDataS try { masterDataService.syncCertificate(() -> { - Log.i(TAG, "Policy Key Sync Completed"); + result.success(syncResult("PolicyKeySync", 5, masterDataService.onResponseComplete())); - }, REG_APP_ID, centerMachineDto.getMachineRefId(), REG_APP_ID, centerMachineDto.getMachineRefId(), isManualSync); + }, REG_APP_ID, centerMachineDto.getMachineRefId(), REG_APP_ID, centerMachineDto.getMachineRefId(), isManualSync, jobId); } catch (Exception e) { e.printStackTrace(); } } @Override - public void getGlobalParamsSync(@NonNull Boolean isManualSync, @NonNull MasterDataSyncPigeon.Result result) { + public void getGlobalParamsSync(@NonNull Boolean isManualSync, @NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { try { masterDataService.syncGlobalParamsData(() -> { Log.i(TAG, "Sync Global Params Completed."); result.success(syncResult("GlobalParamsSync", 1, masterDataService.onResponseComplete())); - }, isManualSync); + }, isManualSync, jobId); } catch (Exception e) { e.printStackTrace(); } } @Override - public void getUserDetailsSync(@NonNull Boolean isManualSync, @NonNull MasterDataSyncPigeon.Result result) { + public void getUserDetailsSync(@NonNull Boolean isManualSync, @NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { try { masterDataService.syncUserDetails(() -> { Log.i(TAG, "User details sync Completed."); result.success(syncResult("UserDetailsSync", 3, masterDataService.onResponseComplete())); - }, isManualSync); + }, isManualSync, jobId); } catch (Exception e) { e.printStackTrace(); } @@ -221,12 +223,12 @@ public void getIDSchemaSync(@NonNull Boolean isManualSync, @NonNull MasterDataSy } @Override - public void getMasterDataSync(@NonNull Boolean isManualSync, @NonNull MasterDataSyncPigeon.Result result) { + public void getMasterDataSync(@NonNull Boolean isManualSync, @NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { try { masterDataService.syncMasterData(() -> { Log.i(TAG, "Master Data Sync Completed."); result.success(syncResult("MasterDataSync", 2, masterDataService.onResponseComplete())); - }, 0, isManualSync); + }, 0, isManualSync, jobId); } catch (Exception e) { Log.e(TAG, "Master Data Sync Failed.", e); e.printStackTrace(); @@ -243,12 +245,12 @@ private MasterDataSyncPigeon.Sync syncResult(String syncType, int progress, Stri } @Override - public void getCaCertsSync(@NonNull Boolean isManualSync, @NonNull MasterDataSyncPigeon.Result result) { + public void getCaCertsSync(@NonNull Boolean isManualSync, @NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { masterDataService.syncCACertificates(() -> { Log.i(TAG, "CA Certificate Sync Completed"); resetAlarm("registrationPacketUploadJob"); result.success(syncResult("CACertificatesSync", 6, masterDataService.onResponseComplete())); - }, isManualSync); + }, isManualSync, jobId); } @Override @@ -268,13 +270,13 @@ public void getReasonList(@NonNull String langCode, @NonNull MasterDataSyncPigeo } @Override - public void getPreRegIds(@NonNull MasterDataSyncPigeon.Result result) { + public void getPreRegIds(@NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { if (NetworkUtils.isNetworkConnected(this.context)) { try { preRegistrationDataSyncService.fetchPreRegistrationIds(() -> { Log.i(TAG, "Application Id's Sync Completed"); result.success("Application Id's Sync Completed."); - }); + }, jobId); } catch (Exception e) { e.printStackTrace(); } @@ -283,12 +285,12 @@ public void getPreRegIds(@NonNull MasterDataSyncPigeon.Result result) { @Override - public void getKernelCertsSync(@NonNull Boolean isManualSync, @NonNull MasterDataSyncPigeon.Result result) { + public void getKernelCertsSync(@NonNull Boolean isManualSync, @NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { try { masterDataService.syncCertificate(() -> { Log.i(TAG, "Policy Key Sync Completed"); result.success(syncResult("KernelCertsSync", 7, masterDataService.onResponseComplete())); - }, KERNEL_APP_ID, "SIGN", "SERVER-RESPONSE", "SIGN-VERIFY", isManualSync); + }, KERNEL_APP_ID, "SIGN", "SERVER-RESPONSE", "SIGN-VERIFY", isManualSync, jobId); } catch (Exception e) { e.printStackTrace(); } @@ -339,4 +341,159 @@ void resetAlarm(String api) { } } } + + @Override + public void deleteAuditLogs(@NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { + try { + boolean deletedRes = auditManagerService.deleteAuditLogs(); + // Also persist timestamps so UI can show Last/Next immediately when triggered manually + try { + if(deletedRes){ + masterDataService.logLastSyncCompletionDateTime(jobId); + Toast.makeText(context, "Deleted Audit logs", Toast.LENGTH_LONG).show(); + } + } catch (Exception e) { + Log.e(TAG, "Failed to store CA certificates sync last sync time", e); + Toast.makeText(context, "Failed to Deleted Audit logs", Toast.LENGTH_LONG).show(); + } + result.success(deletedRes); + } catch (Exception e) { + result.error(e); + Toast.makeText(context, "Failed to deleted Audit logs", Toast.LENGTH_LONG).show(); + } + } + + @Override + public void deletePreRegRecords(@NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { + try { + // Call fetchAndDeleteRecords from PreRegistrationDataSyncService + preRegistrationDataSyncService.fetchAndDeleteRecords(); + masterDataService.logLastSyncCompletionDateTime(jobId); + Toast.makeText(context, "Deleted Pre-reg records", Toast.LENGTH_LONG).show(); + result.success(true); + } catch (Exception e) { + result.error(e); + Toast.makeText(context, "Failed to deleted Pre-reg records", Toast.LENGTH_LONG).show(); + } + } + + @Override + public void getLastSyncTimeByJobId(@NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { + int syncJobId = jobManagerService.generateJobServiceId(jobId); + String lastSyncTime = jobManagerService.getLastSyncTime(syncJobId); + result.success(lastSyncTime); + } + + @Override + public void getNextSyncTimeByJobId(@NonNull String jobId, @NonNull MasterDataSyncPigeon.Result result) { + int syncJobId = jobManagerService.generateJobServiceId(jobId); + String nextSyncTime = jobManagerService.getNextSyncTime(syncJobId); + result.success(nextSyncTime); + } + + @Override + public void getActiveSyncJobs(@NonNull MasterDataSyncPigeon.Result> result) { + List list = syncJobDefRepository.getActiveSyncJobs(); + List value = new ArrayList<>(); + try { + for (SyncJobDef job : list) { + value.add(objectMapper.writeValueAsString(job)); + } + } catch (Exception e) { + Log.e(TAG, "Failed to serialize active sync jobs", e); + } + result.success(value); + } + + // Execute job based on API name + public void executeJobByApiName(String jobApiName, Context context) { + new Thread(() -> { + try { + + // Get job ID from database for tracking last/next sync + String jobId = getJobIdByApiName(jobApiName); + + // Execute appropriate sync job + switch (jobApiName) { + case "registrationPacketUploadJob": + batchJob.syncRegistrationPackets(context); + break; + case "packetSyncStatusJob": + packetService.syncAllPacketStatus(); + break; + case "masterSyncJob": + masterDataService.syncMasterData(() -> { + Log.d(getClass().getSimpleName(), "Master data sync callback"); + }, 0, true, jobId); + break; + case "synchConfigDataJob": + masterDataService.syncGlobalParamsData(() -> { + Log.d(getClass().getSimpleName(), "Config data sync callback"); + }, true, jobId); + break; + case "userDetailServiceJob": + masterDataService.syncUserDetails(() -> { + Log.d(getClass().getSimpleName(), "User details sync callback"); + }, true, jobId); + break; + case "keyPolicySyncJob": + CenterMachineDto centerMachineDto = masterDataService.getRegistrationCenterMachineDetails(); + if (centerMachineDto != null && centerMachineDto.getMachineRefId() != null) { + masterDataService.syncCertificate(() -> { + Log.d(getClass().getSimpleName(), "Policy key sync callback"); + }, REG_APP_ID, centerMachineDto.getMachineRefId(), REG_APP_ID, centerMachineDto.getMachineRefId(), true, jobId); + } else { + Log.w(getClass().getSimpleName(), "Skipping keyPolicySyncJob - machine details not available"); + } + break; + case "publicKeySyncJob": + // Public key sync for KERNEL app (SIGN certificates) + masterDataService.syncCertificate(() -> { + Log.d(getClass().getSimpleName(), "Public key sync callback"); + }, KERNEL_APP_ID, "SIGN", "SERVER-RESPONSE", "SIGN-VERIFY", true, jobId); + break; + case "syncCertificateJob": + // CA certificate sync + masterDataService.syncCACertificates(() -> { + Log.d(getClass().getSimpleName(), "CA cert sync callback"); + }, true, jobId); + break; + case "preRegistrationDataSyncJob": + preRegistrationDataSyncService.fetchPreRegistrationIds(() -> { + Log.i(TAG, "Application Id's Sync Completed"); + }, jobId); + break; + + case "deleteAuditLogsJob": + auditManagerService.deleteAuditLogs(); + masterDataService.logLastSyncCompletionDateTime(jobId); + break; + + case "preRegistrationPacketDeletionJob": + preRegistrationDataSyncService.fetchAndDeleteRecords(); + masterDataService.logLastSyncCompletionDateTime(jobId); + break; + default: + Log.w(getClass().getSimpleName(), "Unknown job: " + jobApiName); + } + Log.d(getClass().getSimpleName(), "Completed: " + jobApiName); + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Job failed: " + jobApiName, e); + } + }).start(); + } + + private String getJobIdByApiName(String apiName) { + try { + List jobs = syncJobDefRepository.getAllSyncJobDefList(); + for (SyncJobDef job : jobs) { + if (apiName.equals(job.getApiName())) { + return job.getId(); + } + } + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Error getting job ID for: " + apiName, e); + } + return ""; // Return empty string if not found + } } diff --git a/android/app/src/main/java/io/mosip/registration_client/api_services/ProcessSpecDetailsApi.java b/android/app/src/main/java/io/mosip/registration_client/api_services/ProcessSpecDetailsApi.java index 3092d9369..c33033326 100644 --- a/android/app/src/main/java/io/mosip/registration_client/api_services/ProcessSpecDetailsApi.java +++ b/android/app/src/main/java/io/mosip/registration_client/api_services/ProcessSpecDetailsApi.java @@ -23,6 +23,7 @@ import javax.inject.Singleton; import io.mosip.registration.clientmanager.dto.uispec.ProcessSpecDto; +import io.mosip.registration.clientmanager.dto.uispec.SettingsSpecDto; import io.mosip.registration.clientmanager.repository.GlobalParamRepository; import io.mosip.registration.clientmanager.repository.IdentitySchemaRepository; import io.mosip.registration.clientmanager.spi.AuditManagerService; @@ -37,6 +38,8 @@ public class ProcessSpecDetailsApi implements ProcessSpecPigeon.ProcessSpecApi { GlobalParamRepository globalParamRepository; AuditManagerService auditManagerService; RegistrationService registrationService; + ObjectMapper objectMapper; + ObjectWriter objectWriter; @Inject public ProcessSpecDetailsApi(Context context, @@ -50,6 +53,8 @@ public ProcessSpecDetailsApi(Context context, this.globalParamRepository = globalParamRepository; this.registrationService = registrationService; this.auditManagerService = auditManagerService; + this.objectMapper = new ObjectMapper(); + this.objectWriter = objectMapper.writer().withDefaultPrettyPrinter(); } @Override @@ -148,4 +153,20 @@ public void getMaxLanguageCount(@NonNull ProcessSpecPigeon.Result result) } result.success((long) maxLangCount); } + + @Override + public void getSettingSpec(@NonNull ProcessSpecPigeon.Result> result) { + List settingSpecList = new ArrayList<>(); + try { + List settingsSpecDto = identitySchemaRepository.getSettingsSchema(context, identitySchemaRepository.getLatestSchemaVersion()); + settingsSpecDto = settingsSpecDto == null ? new ArrayList<>() : settingsSpecDto; + for (SettingsSpecDto dto : settingsSpecDto) { + String json = objectWriter.writeValueAsString(dto); + settingSpecList.add(json); + } + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Error in getSettingSpec", e); + } + result.success(settingSpecList); + } } diff --git a/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java b/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java index 534e23319..5fe49881f 100644 --- a/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java +++ b/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java @@ -106,10 +106,16 @@ public void submitRegistrationDto(@NonNull String makerName, @NonNull Registrati String response = ""; String errorCode = ""; try { - response = this.registrationService.getRegistrationDto().getRId(); + RegistrationDto registrationDto = this.registrationService.getRegistrationDto(); + if (registrationDto.getAdditionalInfoRequestId() != null) { + response = registrationDto.getAdditionalInfoRequestId().split("-")[0]; + } else { + response = registrationDto.getRId(); + } registrationService.submitRegistrationDto(makerName); } catch (Exception e) { errorCode = e.getMessage(); + Log.i("RegistrationApi", "Registration submission failed: " + errorCode); auditManagerService.audit(AuditEvent.CREATE_PACKET_FAILED, Components.REGISTRATION, errorCode); Log.e(getClass().getSimpleName(), "Failed on registration submission", e); } @@ -130,5 +136,23 @@ public void setApplicationId(@NonNull String applicationId, @NonNull Registratio Log.e(getClass().getSimpleName(), "Set application ID failed: " + Arrays.toString(e.getStackTrace())); } } + + @Override + public void setAdditionalReqId(@NonNull String additionalReqId, @NonNull RegistrationDataPigeon.Result result) { + try { + this.registrationDto.setAdditionalInfoRequestId(additionalReqId); + } catch (Exception e) { + Log.e(getClass().getSimpleName(), "Set additional request ID failed: " + Arrays.toString(e.getStackTrace())); + } + } + + @Override + public void setMachineLocation(@NonNull Double latitude, @NonNull Double longitude, @NonNull RegistrationDataPigeon.Result result) { + try { + this.registrationDto.setGeoLocation(longitude, latitude); + } catch (Exception e) { + Log.e(getClass().getSimpleName(),"Set machine location failed:" + Arrays.toString(e.getStackTrace())); + } + } } diff --git a/android/clientmanager/build.gradle b/android/clientmanager/build.gradle index 35246dc8d..1b1cc29a7 100644 --- a/android/clientmanager/build.gradle +++ b/android/clientmanager/build.gradle @@ -67,6 +67,8 @@ dependencies { implementation 'javax.validation:validation-api:2.0.1.Final' + implementation 'com.cronutils:cron-utils:9.2.0' + //AspectJ implementation 'org.aspectj:aspectjrt:1.9.7' @@ -105,22 +107,24 @@ dependencies { // https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.okio:okio:3.4.0' + implementation 'com.squareup.okhttp3:okhttp:4.9.2' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // JWT decoding library implementation 'com.auth0.android:jwtdecode:2.0.1' // https://mvnrepository.com/artifact/commons-io/commons-io - implementation group: 'commons-io', name: 'commons-io', version: '2.11.0' + implementation group: 'commons-io', name: 'commons-io', version: '2.14.0' - implementation 'com.fasterxml.jackson.core:jackson-core:2.13.2' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2' + implementation 'com.fasterxml.jackson.core:jackson-core:2.15.0-rc1' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4.1' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.2' implementation 'org.apache.velocity:velocity:1.7' implementation 'org.mvel:mvel2:2.4.14.Final' - implementation('com.tom-roush:pdfbox-android:2.0.20.0') { + implementation('com.tom-roush:pdfbox-android:2.0.27.0') { exclude group: 'org.bouncycastle' } @@ -144,4 +148,5 @@ dependencies { implementation("io.mosip.kernel:kernel-biometrics-api:1.2.0.3"){ transitive = false } + implementation 'com.google.code.gson:gson:2.10.1' } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java index d82266c66..9aeaf7c57 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java @@ -7,12 +7,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; +import javax.inject.Provider; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; import io.mosip.registration.clientmanager.dao.ApplicantValidDocumentDao; import io.mosip.registration.clientmanager.dao.FileSignatureDao; +import io.mosip.registration.clientmanager.dao.LocalConfigDAO; import io.mosip.registration.clientmanager.dao.PreRegistrationDataSyncDao; import io.mosip.registration.clientmanager.dao.PreRegistrationDataSyncRepositoryDao; import io.mosip.registration.clientmanager.entity.PreRegistrationList; @@ -34,6 +36,8 @@ import io.mosip.registration.clientmanager.repository.TemplateRepository; import io.mosip.registration.clientmanager.repository.UserBiometricRepository; import io.mosip.registration.clientmanager.repository.UserDetailRepository; +import io.mosip.registration.clientmanager.repository.UserRoleRepository; +import io.mosip.registration.clientmanager.repository.PermittedLocalConfigRepository; import io.mosip.registration.clientmanager.service.AuditManagerServiceImpl; import io.mosip.registration.clientmanager.service.Biometrics095Service; import io.mosip.registration.clientmanager.service.JobManagerServiceImpl; @@ -51,6 +55,7 @@ import io.mosip.registration.clientmanager.spi.AuditManagerService; import io.mosip.registration.clientmanager.spi.JobManagerService; import io.mosip.registration.clientmanager.spi.JobTransactionService; +import io.mosip.registration.clientmanager.spi.LocationValidationService; import io.mosip.registration.clientmanager.spi.MasterDataService; import io.mosip.registration.clientmanager.spi.PacketService; import io.mosip.registration.clientmanager.spi.PreRegistrationDataSyncService; @@ -163,12 +168,15 @@ public MasterDataService provideMasterDataService(ObjectMapper objectMapper, Syn CertificateManagerService certificateManagerService, LanguageRepository languageRepository, JobManagerService jobManagerService, - FileSignatureDao fileSignatureDao) { + FileSignatureDao fileSignatureDao, JobTransactionService jobTransactionService, PermittedLocalConfigRepository permittedLocalConfigRepository, + LocalConfigDAO localConfigDAO) { + return new MasterDataServiceImpl(appContext, objectMapper, syncRestService, clientCryptoManagerService, machineRepository, reasonListRepository, registrationCenterRepository, documentTypeRepository, applicantValidDocRepository, templateRepository, dynamicFieldRepository, locationRepository, globalParamRepository, identitySchemaRepository, blocklistedWordRepository, syncJobDefRepository, userDetailRepository, - certificateManagerService, languageRepository, jobManagerService, fileSignatureDao); + certificateManagerService, languageRepository, jobManagerService, fileSignatureDao, jobTransactionService, permittedLocalConfigRepository, localConfigDAO); + } @@ -180,8 +188,8 @@ SyncRestUtil provideSyncRestFactory(ClientCryptoManagerService clientCryptoManag @Provides @Singleton - LoginService provideLoginService(ClientCryptoManagerService clientCryptoManagerService, UserDetailRepository userDetailRepository) { - return new LoginService(appContext, clientCryptoManagerService, userDetailRepository); + LoginService provideLoginService(ClientCryptoManagerService clientCryptoManagerService, UserDetailRepository userDetailRepository, UserRoleRepository userRoleRepository) { + return new LoginService(appContext, clientCryptoManagerService, userDetailRepository, userRoleRepository); } @Provides @@ -193,10 +201,14 @@ RegistrationService provideRegistrationService(PacketWriterService packetWriterS ClientCryptoManagerService clientCryptoManagerService, KeyStoreRepository keyStoreRepository, GlobalParamRepository globalParamRepository, - AuditManagerService auditManagerService) { + AuditManagerService auditManagerService, + RegistrationCenterRepository registrationCenterRepository, + LocationValidationService locationValidationService, + Provider preRegistrationDataSyncServiceProvider, + Biometrics095Service biometricService) { return new RegistrationServiceImpl(appContext, packetWriterService, registrationRepository, masterDataService, identitySchemaRepository, clientCryptoManagerService, - keyStoreRepository, globalParamRepository, auditManagerService); + keyStoreRepository, globalParamRepository, auditManagerService, registrationCenterRepository,locationValidationService, preRegistrationDataSyncServiceProvider, biometricService); } @Provides diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/ClientDatabase.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/ClientDatabase.java index 353da3b60..3d6965edb 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/ClientDatabase.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/ClientDatabase.java @@ -28,8 +28,11 @@ import io.mosip.registration.clientmanager.dao.TemplateDao; import io.mosip.registration.clientmanager.dao.UserBiometricDao; import io.mosip.registration.clientmanager.dao.UserDetailDao; +import io.mosip.registration.clientmanager.dao.PermittedLocalConfigDao; +import io.mosip.registration.clientmanager.dao.LocalPreferencesDao; import io.mosip.registration.clientmanager.dao.UserPasswordDao; import io.mosip.registration.clientmanager.dao.UserTokenDao; +import io.mosip.registration.clientmanager.dao.UserRoleDao; import io.mosip.registration.clientmanager.entity.ApplicantValidDocument; import io.mosip.registration.clientmanager.entity.Audit; import io.mosip.registration.clientmanager.entity.BlocklistedWord; @@ -43,6 +46,7 @@ import io.mosip.registration.clientmanager.entity.Location; import io.mosip.registration.clientmanager.entity.LocationHierarchy; import io.mosip.registration.clientmanager.entity.MachineMaster; +import io.mosip.registration.clientmanager.entity.PermittedLocalConfig; import io.mosip.registration.clientmanager.entity.ReasonList; import io.mosip.registration.clientmanager.entity.ProcessSpec; import io.mosip.registration.clientmanager.entity.PreRegistrationList; @@ -54,38 +58,27 @@ import io.mosip.registration.clientmanager.entity.UserDetail; import io.mosip.registration.clientmanager.entity.UserPassword; import io.mosip.registration.clientmanager.entity.UserToken; +import io.mosip.registration.clientmanager.entity.UserRole; +import io.mosip.registration.clientmanager.entity.LocalPreferences; import io.mosip.registration.keymanager.dao.CACertificateStoreDao; import io.mosip.registration.keymanager.dao.KeyStoreDao; import io.mosip.registration.keymanager.entity.CACertificateStore; import io.mosip.registration.keymanager.entity.KeyStore; -@Database(entities = {UserToken.class, Registration.class, ReasonList.class, RegistrationCenter.class, +@Database(entities = {UserToken.class, UserRole.class, Registration.class, ReasonList.class, RegistrationCenter.class, MachineMaster.class, DocumentType.class, DynamicField.class, ApplicantValidDocument.class, Template.class, KeyStore.class, Location.class, GlobalParam.class, IdentitySchema.class, LocationHierarchy.class, BlocklistedWord.class, SyncJobDef.class, UserDetail.class, UserBiometric.class, UserPassword.class, JobTransaction.class, - CACertificateStore.class, Language.class, Audit.class, FileSignature.class, ProcessSpec.class,PreRegistrationList.class}, - version = 1, exportSchema = false) + CACertificateStore.class, Language.class, Audit.class, FileSignature.class, ProcessSpec.class,PreRegistrationList.class, + PermittedLocalConfig.class, LocalPreferences.class}, + version = 2, exportSchema = false) public abstract class ClientDatabase extends RoomDatabase { - private static final String DATABASE_NAME = "regclient"; - private static ClientDatabase INSTANCE; - - public synchronized static ClientDatabase getDatabase(Context context) { - if (INSTANCE == null) { - INSTANCE = buildDatabase(context); - } - return INSTANCE; - } - - public static ClientDatabase buildDatabase(Context context) { - return Room.databaseBuilder(context, ClientDatabase.class, DATABASE_NAME) - .allowMainThreadQueries() - .build(); - } - public abstract UserTokenDao userTokenDao(); + public abstract UserRoleDao userRoleDao(); + public abstract RegistrationDao registrationDao(); public abstract ReasonListDao reasonListDao(); @@ -136,9 +129,9 @@ public static ClientDatabase buildDatabase(Context context) { public abstract PreRegistrationDataSyncRepositoryDao preRegistrationDataSyncRepositoryDao(); - public static void destroyDB() { - INSTANCE = null; - } + public abstract PermittedLocalConfigDao permittedLocalConfigDao(); + + public abstract LocalPreferencesDao localPreferencesDao(); } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/ClientDatabaseMigrations.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/ClientDatabaseMigrations.java new file mode 100644 index 000000000..73d7cc09d --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/ClientDatabaseMigrations.java @@ -0,0 +1,76 @@ +package io.mosip.registration.clientmanager.config; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.room.migration.Migration; +import androidx.sqlite.db.SupportSQLiteDatabase; +import android.database.Cursor; + +public final class ClientDatabaseMigrations { + + private static final String TAG = ClientDatabaseMigrations.class.getSimpleName(); + + private ClientDatabaseMigrations() {} + + public static final Migration MIGRATION_1_2 = new Migration(1, 2) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("CREATE TABLE IF NOT EXISTS `permitted_local_config` (" + + "`code` TEXT NOT NULL, " + + "`name` TEXT, " + + "`config_type` TEXT, " + + "`is_active` INTEGER, " + + "`is_deleted` INTEGER, " + + "`del_dtimes` INTEGER, " + + "PRIMARY KEY(`code`))"); + + database.execSQL("CREATE TABLE IF NOT EXISTS `local_preferences` (" + + "`id` TEXT NOT NULL, " + + "`name` TEXT, " + + "`val` TEXT, " + + "`config_type` TEXT, " + + "`cr_by` TEXT, " + + "`cr_dtime` INTEGER, " + + "`upd_by` TEXT, " + + "`upd_dtimes` INTEGER, " + + "`is_deleted` INTEGER, " + + "`del_dtimes` INTEGER, " + + "PRIMARY KEY(`id`))"); + + database.execSQL("CREATE TABLE IF NOT EXISTS `user_role` (" + + "`usr_id` TEXT NOT NULL, " + + "`role_code` TEXT NOT NULL, " + + "`lang_code` TEXT, " + + "PRIMARY KEY(`usr_id`, `role_code`))"); + + if (!hasColumn(database, "registration", "id")) { + database.execSQL("ALTER TABLE `registration` ADD COLUMN `id` TEXT"); + } + + // Note: This UPDATE has no effect on fresh migrations since local_preferences was just created empty. + // Remove if not needed for schema correction scenarios. + database.execSQL("UPDATE local_preferences SET config_type = 'CONFIGURATION' WHERE config_type IS NULL"); + Log.i(TAG, "Migration 1_2 completed successfully."); + } + }; + + private static boolean hasColumn(@NonNull SupportSQLiteDatabase database, @NonNull String table, @NonNull String column) { + Cursor cursor = null; + try { + cursor = database.query("PRAGMA table_info(`" + table + "`)"); + int nameIndex = cursor.getColumnIndex("name"); + while (cursor.moveToNext()) { + if (column.equalsIgnoreCase(cursor.getString(nameIndex))) { + return true; + } + } + return false; + } finally { + if (cursor != null) { + cursor.close(); + } + } + } +} + diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/RoomModule.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/RoomModule.java index 2f8797848..5a7741353 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/RoomModule.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/RoomModule.java @@ -36,10 +36,14 @@ import io.mosip.registration.clientmanager.dao.IdentitySchemaDao; import io.mosip.registration.clientmanager.dao.JobTransactionDao; import io.mosip.registration.clientmanager.dao.LanguageDao; +import io.mosip.registration.clientmanager.dao.LocalConfigDAO; +import io.mosip.registration.clientmanager.dao.LocalConfigDAOImpl; +import io.mosip.registration.clientmanager.dao.LocalPreferencesDao; import io.mosip.registration.clientmanager.dao.LocationDao; import io.mosip.registration.clientmanager.dao.LocationHierarchyDao; import io.mosip.registration.clientmanager.dao.MachineMasterDao; import io.mosip.registration.clientmanager.dao.ProcessSpecDao; +import io.mosip.registration.clientmanager.dao.PermittedLocalConfigDao; import io.mosip.registration.clientmanager.dao.PreRegistrationDataSyncRepositoryDao; import io.mosip.registration.clientmanager.dao.ReasonListDao; import io.mosip.registration.clientmanager.dao.RegistrationCenterDao; @@ -49,6 +53,7 @@ import io.mosip.registration.clientmanager.dao.UserBiometricDao; import io.mosip.registration.clientmanager.dao.UserDetailDao; import io.mosip.registration.clientmanager.dao.UserPasswordDao; +import io.mosip.registration.clientmanager.dao.UserRoleDao; import io.mosip.registration.clientmanager.dao.UserTokenDao; import io.mosip.registration.clientmanager.entity.UserBiometric; import io.mosip.registration.clientmanager.repository.ApplicantValidDocRepository; @@ -59,6 +64,8 @@ import io.mosip.registration.clientmanager.repository.GlobalParamRepository; import io.mosip.registration.clientmanager.repository.IdentitySchemaRepository; import io.mosip.registration.clientmanager.repository.JobTransactionRepository; +import io.mosip.registration.clientmanager.repository.LocalPreferencesRepository; +import io.mosip.registration.clientmanager.repository.PermittedLocalConfigRepository; import io.mosip.registration.clientmanager.repository.LanguageRepository; import io.mosip.registration.clientmanager.repository.LocationRepository; import io.mosip.registration.clientmanager.repository.MachineRepository; @@ -68,6 +75,7 @@ import io.mosip.registration.clientmanager.repository.TemplateRepository; import io.mosip.registration.clientmanager.repository.UserBiometricRepository; import io.mosip.registration.clientmanager.repository.UserDetailRepository; +import io.mosip.registration.clientmanager.repository.UserRoleRepository; import io.mosip.registration.keymanager.dao.CACertificateStoreDao; import io.mosip.registration.keymanager.dao.KeyStoreDao; import io.mosip.registration.keymanager.repository.CACertificateStoreRepository; @@ -104,6 +112,7 @@ public RoomModule(Application application, ApplicationInfo applicationInfo) { } clientDatabase = Room.databaseBuilder(application, ClientDatabase.class, DATABASE_NAME) .openHelperFactory(new SupportFactory(dbPwd.getBytes())) + .addMigrations(ClientDatabaseMigrations.MIGRATION_1_2) .allowMainThreadQueries() .build(); } catch (Exception e) { @@ -275,6 +284,12 @@ UserPasswordDao providesUserPasswordDao(ClientDatabase clientDatabase) { return clientDatabase.userPasswordDao(); } + @Singleton + @Provides + UserRoleDao providesUserRoleDao(ClientDatabase clientDatabase) { + return clientDatabase.userRoleDao(); + } + @Singleton @Provides JobTransactionDao providesJobTransactionDao(ClientDatabase clientDatabase) { @@ -368,8 +383,8 @@ LocationRepository provideLocationRepository(LocationDao locationDao, LocationHi @Provides @Singleton - GlobalParamRepository provideGlobalParamRepository(GlobalParamDao globalParamDao) { - return new GlobalParamRepository(globalParamDao); + GlobalParamRepository provideGlobalParamRepository(GlobalParamDao globalParamDao, LocalConfigDAO localConfigDAO) { + return new GlobalParamRepository(globalParamDao, localConfigDAO); } @Provides @@ -397,6 +412,12 @@ UserDetailRepository provideUserDetailRepository(UserDetailDao userDetailDao, Us return new UserDetailRepository(userDetailDao, userTokenDao, userPasswordDao); } + @Provides + @Singleton + UserRoleRepository provideUserRoleRepository(UserRoleDao userRoleDao) { + return new UserRoleRepository(userRoleDao); + } + @Provides @Singleton UserBiometricRepository provideUserBiometricRepository(UserBiometricDao userBiometricDao, UserDetailDao userDetailDao) { @@ -433,4 +454,25 @@ AuditRepository provideAuditRepository(AuditDao auditDao) { PreRegistrationDataSyncRepositoryDao providesPreRegistrationDataSyncRepository(ClientDatabase clientDatabase) { return clientDatabase.preRegistrationDataSyncRepositoryDao(); } + + @Provides + @Singleton + PermittedLocalConfigDao providesPermittedLocalConfigDao(ClientDatabase clientDatabase) { + return clientDatabase.permittedLocalConfigDao(); + } + + @Provides + @Singleton + LocalPreferencesDao providesLocalPreferencesDao(ClientDatabase clientDatabase) { + return clientDatabase.localPreferencesDao(); + } + + @Provides + @Singleton + LocalConfigDAO provideLocalConfigDAO(PermittedLocalConfigDao permittedLocalConfigDao, LocalPreferencesDao localPreferencesDao) { + return new LocalConfigDAOImpl( + new PermittedLocalConfigRepository(permittedLocalConfigDao), + new LocalPreferencesRepository(localPreferencesDao) + ); + } } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/SessionManager.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/SessionManager.java index b9122920b..7f9c5a0d3 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/SessionManager.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/SessionManager.java @@ -61,7 +61,7 @@ public List saveAuthToken(@NonNull String token) throws Exception { if(roles.isEmpty()) throw new Exception("Unauthorized access, No roles"); - if(!roles.contains("REGISTRATION_SUPERVISOR") && !roles.contains("REGISTRATION_OFFICER")) + if(!roles.contains("REGISTRATION_SUPERVISOR") && !roles.contains("REGISTRATION_OFFICER") && !roles.contains("REGISTRATION_OPERATOR")) throw new Exception("Unauthorized access, Required roles not found"); SharedPreferences.Editor editor = this.context.getSharedPreferences(this.context.getString(R.string.app_name), diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/constant/RegistrationConstants.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/constant/RegistrationConstants.java index c9a8f4207..bf441c0e1 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/constant/RegistrationConstants.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/constant/RegistrationConstants.java @@ -29,6 +29,11 @@ public class RegistrationConstants { public static final String ENABLE = "Y"; public static final String DISABLE = "N"; + // Permitted Local Config Types + public static final String PERMITTED_JOB_TYPE = "JOB"; + public static final String PERMITTED_CONFIG_TYPE = "CONFIGURATION"; + public static final String PERMITTED_SHORTCUT = "SHORTCUT"; + //SBI intents public static final String DISCOVERY_INTENT_ACTION = "io.sbi.device"; public static final String D_INFO_INTENT_ACTION = ".Info"; @@ -61,6 +66,8 @@ public class RegistrationConstants { public static final String AGEGROUP_CONFIG = "mosip.registration.agegroup-config"; public static final String ALLOWED_BIO_ATTRIBUTES = "mosip.registration.allowed-bioattributes"; public static final String DEFAULT_APP_TYPE_CODE = "mosip.registration.default-app-type-code"; + public static final String IDLE_TIME = "mosip.registration.idle_time"; + public static final String REFRESHED_LOGIN_TIME = "mosip.registration.refreshed_login_time"; public static final String RESPONSE = "response"; public static final String ERRORS = "errors"; @@ -104,5 +111,11 @@ public class RegistrationConstants { public static final String RIGHT_THUMB = "Right Thumb"; public static final String RIGHT = "Right"; public static final String LEFT = "Left"; + public static final String PRE_REG_DELETION_CONFIGURED_DAYS = "mosip.registration.pre_reg_deletion_configured_days"; + public static final String PRE_REG_DELETE_SUCCESS = "PRE_REG_DELETE_SUCCESS"; + public static final String PRE_REG_DELETE_FAILURE = "PRE_REG_DELETE_FAILURE"; + public static final String JOB_TRIGGER_POINT_USER = "User"; + public static final String GPS_DEVICE_ENABLE_FLAG = "mosip.registration.gps_device_enable_flag"; + public static final String DIST_FRM_MACHINE_TO_CENTER = "mosip.registration.distance.from.machine.to.center"; } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/GlobalParamDao.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/GlobalParamDao.java index e61c29732..d7cca1370 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/GlobalParamDao.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/GlobalParamDao.java @@ -24,4 +24,7 @@ public interface GlobalParamDao { @Insert(onConflict = OnConflictStrategy.REPLACE) void insertAll(List globalParams); + @Query("SELECT * FROM global_param WHERE name LIKE :pattern AND status = 1 AND value IS NOT NULL") + List findByNameLikeAndIsActiveTrueAndValIsNotNull(String pattern); + } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/LocalConfigDAO.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/LocalConfigDAO.java new file mode 100644 index 000000000..357960bb3 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/LocalConfigDAO.java @@ -0,0 +1,30 @@ +package io.mosip.registration.clientmanager.dao; + +import java.util.List; +import java.util.Map; + +import io.mosip.registration.clientmanager.entity.PermittedLocalConfig; + +/** + * DAO interface for local configuration operations + * Handles both permitted configurations and local preferences + */ +public interface LocalConfigDAO { + + /** + * Get permitted configurations by type + */ + List getPermittedConfigurations(String configType); + + /** + * Get local configurations as a map (name -> value) + */ + Map getLocalConfigurations(); + + /** + * Modify configurations by saving local preferences + */ + void modifyConfigurations(Map localPreferences); + + void cleanUpLocalPreferences(); +} diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/LocalConfigDAOImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/LocalConfigDAOImpl.java new file mode 100644 index 000000000..04b328f41 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/LocalConfigDAOImpl.java @@ -0,0 +1,128 @@ +package io.mosip.registration.clientmanager.dao; + +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.mosip.registration.clientmanager.constant.RegistrationConstants; +import io.mosip.registration.clientmanager.entity.LocalPreferences; +import io.mosip.registration.clientmanager.entity.PermittedLocalConfig; +import io.mosip.registration.clientmanager.repository.LocalPreferencesRepository; +import io.mosip.registration.clientmanager.repository.PermittedLocalConfigRepository; + +@Singleton +public class LocalConfigDAOImpl implements LocalConfigDAO { + + private static final String TAG = LocalConfigDAOImpl.class.getSimpleName(); + private PermittedLocalConfigRepository permittedLocalConfigRepository; + private LocalPreferencesRepository localPreferencesRepository; + + @Inject + public LocalConfigDAOImpl(PermittedLocalConfigRepository permittedLocalConfigRepository, + LocalPreferencesRepository localPreferencesRepository) { + this.permittedLocalConfigRepository = permittedLocalConfigRepository; + this.localPreferencesRepository = localPreferencesRepository; + } + + @Override + public List getPermittedConfigurations(String configType) { + List permittedConfigs = + permittedLocalConfigRepository.getPermittedConfigsByType(configType); + + List permittedConfigurations = new ArrayList<>(); + if (permittedConfigs != null && !permittedConfigs.isEmpty()) { + for (PermittedLocalConfig config : permittedConfigs) { + permittedConfigurations.add(config.getName()); + } + } + return permittedConfigurations; + } + + @Override + public Map getLocalConfigurations() { + return localPreferencesRepository.getLocalConfigurations(); + } + + @Override + public void modifyConfigurations(Map localPreferences) { + + for (Map.Entry entry : localPreferences.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + + try { + LocalPreferences existingPreference = localPreferencesRepository.findByIsDeletedFalseAndName(name); + + if (existingPreference != null) { + // Update existing record + existingPreference.setVal(value); + existingPreference.setUpdBy(RegistrationConstants.JOB_TRIGGER_POINT_USER); + existingPreference.setUpdDtimes(System.currentTimeMillis()); + localPreferencesRepository.save(existingPreference); + } else { + // Create new record if it doesn't exist + saveLocalPreference(name, value, RegistrationConstants.PERMITTED_CONFIG_TYPE); + } + + } catch (Exception e) { + Log.e(TAG, "Error modifying configuration: " + name, e); + } + } + } + + + /** + * Save local preference to database + */ + private void saveLocalPreference(String name, String value, String configType) { + LocalPreferences localPreference = new LocalPreferences(UUID.randomUUID().toString()); + localPreference.setName(name); + localPreference.setVal(value); + localPreference.setConfigType(configType); + localPreference.setCrBy(RegistrationConstants.JOB_TRIGGER_POINT_USER); + localPreference.setCrDtime(System.currentTimeMillis()); + localPreference.setIsDeleted(false); + + localPreferencesRepository.save(localPreference); + } + + /** + * Clean up local preferences based on permitted configs. + * Delete local preference if key is removed from permitted configs. + * Mark as deleted if key is deactivated in permitted configs. + */ + public void cleanUpLocalPreferences() { + List permittedConfigs = + permittedLocalConfigRepository.getPermittedConfigsByType(RegistrationConstants.PERMITTED_CONFIG_TYPE); + + Map localConfigs = getLocalConfigurations(); + + Map permittedStatusMap = new java.util.HashMap<>(); + for (PermittedLocalConfig config : permittedConfigs) { + permittedStatusMap.put(config.getName(), config.getIsActive()); + } + + for (String key : localConfigs.keySet()) { + LocalPreferences pref = localPreferencesRepository.findByIsDeletedFalseAndName(key); + if (pref == null) continue; + + if (!permittedStatusMap.containsKey(key)) { + localPreferencesRepository.delete(pref); + Log.i(TAG, "Local preference deleted (row removed): " + key); + } else if (!permittedStatusMap.get(key)) { + // Key deactivated, mark as deleted + pref.setIsDeleted(true); + pref.setUpdBy(RegistrationConstants.JOB_TRIGGER_POINT_USER); + pref.setUpdDtimes(System.currentTimeMillis()); + localPreferencesRepository.save(pref); + Log.i(TAG, "Local preference marked deleted (row deactivated): " + key); + } + } + } +} diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/LocalPreferencesDao.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/LocalPreferencesDao.java new file mode 100644 index 000000000..f283f269d --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/LocalPreferencesDao.java @@ -0,0 +1,30 @@ +package io.mosip.registration.clientmanager.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.OnConflictStrategy; +import androidx.room.Query; +import androidx.room.Update; + +import java.util.List; + +import io.mosip.registration.clientmanager.entity.LocalPreferences; + +@Dao +public interface LocalPreferencesDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insert(LocalPreferences localPreference); + + @Update + void update(LocalPreferences localPreference); + + @Query("SELECT * FROM local_preferences WHERE is_deleted = 0 AND config_type = :configType") + List findByIsDeletedFalseAndConfigType(String configType); + + @Query("SELECT * FROM local_preferences WHERE is_deleted = 0 AND name = :name") + LocalPreferences findByIsDeletedFalseAndName(String name); + + @Query("DELETE FROM local_preferences WHERE name = :name") + void deleteByName(String name); +} diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PermittedLocalConfigDao.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PermittedLocalConfigDao.java new file mode 100644 index 000000000..6e4e187de --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PermittedLocalConfigDao.java @@ -0,0 +1,21 @@ +package io.mosip.registration.clientmanager.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.OnConflictStrategy; +import androidx.room.Query; + +import java.util.List; + +import io.mosip.registration.clientmanager.entity.PermittedLocalConfig; + +@Dao +public interface PermittedLocalConfigDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insertAll(List permittedConfigs); + + @Query("SELECT * FROM permitted_local_config WHERE is_active = 1 AND is_deleted = 0 AND config_type = :configType") + List findByIsActiveTrueAndType(String configType); + +} diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PreRegistrationDataSyncDao.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PreRegistrationDataSyncDao.java index 0ad3583db..ff0bdd738 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PreRegistrationDataSyncDao.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PreRegistrationDataSyncDao.java @@ -1,8 +1,11 @@ package io.mosip.registration.clientmanager.dao; -import io.mosip.registration.clientmanager.entity.PreRegistrationList; +import java.sql.Timestamp; +import java.util.Date; import java.util.List; +import io.mosip.registration.clientmanager.entity.PreRegistrationList; + /** * This class is used to fetch the specific pre registration by passing pre registration id as parameter, @@ -17,26 +20,28 @@ */ public interface PreRegistrationDataSyncDao { - + public PreRegistrationList get(String preRegId); - + public void save(PreRegistrationList preRegistration); - - public List fetchRecordsToBeDeleted(String startDate); - + public List fetchRecordsToBeDeleted(Date startDate); + + public long update(String id,String updatedBy,String updatedTime); - - //public void deleteAll(List preRegistrationList); - - // List getAllPreRegPackets(); + public void deleteAll(List preRegistrationList); + + + // List getAllPreRegPackets(); + - public String getLastPreRegPacketDownloadedTime(); public PreRegistrationList getById(String id); -} + + public Timestamp getLastPreRegPacketDownloadedTimeAsTimestamp(); +} \ No newline at end of file diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PreRegistrationDataSyncRepositoryDao.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PreRegistrationDataSyncRepositoryDao.java index b25dcbb8d..fd9b8774e 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PreRegistrationDataSyncRepositoryDao.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/PreRegistrationDataSyncRepositoryDao.java @@ -2,6 +2,7 @@ package io.mosip.registration.clientmanager.dao; import androidx.room.Dao; +import androidx.room.Delete; import androidx.room.Insert; import androidx.room.OnConflictStrategy; import androidx.room.Query; @@ -46,4 +47,7 @@ public interface PreRegistrationDataSyncRepositoryDao { @Query("SELECT * FROM pre_registration_list WHERE id = :id LIMIT 1") PreRegistrationList getById(String id); + @Delete + void deleteAll(List preRegistrationList); + } \ No newline at end of file diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/UserRoleDao.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/UserRoleDao.java new file mode 100644 index 000000000..6aa7effc9 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dao/UserRoleDao.java @@ -0,0 +1,28 @@ +package io.mosip.registration.clientmanager.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.OnConflictStrategy; +import androidx.room.Query; + +import java.util.List; + +import io.mosip.registration.clientmanager.entity.UserRole; + +@Dao +public interface UserRoleDao { + + @Query("SELECT * FROM user_role WHERE usr_id = :usrId") + List findByUsrId(String usrId); + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insert(UserRole userRole); + + @Query("DELETE FROM user_role WHERE usr_id = :usrId") + void deleteByUsrId(String usrId); + + @Query("DELETE FROM user_role") + void deleteAll(); +} + + diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/http/IdSchemaResponse.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/http/IdSchemaResponse.java index fee13d73e..f5249fb13 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/http/IdSchemaResponse.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/http/IdSchemaResponse.java @@ -6,6 +6,7 @@ import io.mosip.registration.clientmanager.dto.uispec.FieldSpecDto; import io.mosip.registration.clientmanager.dto.uispec.ProcessSpecDto; +import io.mosip.registration.clientmanager.dto.uispec.SettingsSpecDto; import lombok.Data; @@ -19,4 +20,5 @@ public class IdSchemaResponse { private List schema; private String schemaJson; private String effectiveFrom; + private List settings; } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/registration/GeoLocationDto.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/registration/GeoLocationDto.java new file mode 100644 index 000000000..36746f205 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/registration/GeoLocationDto.java @@ -0,0 +1,11 @@ +package io.mosip.registration.clientmanager.dto.registration; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class GeoLocationDto { + private double longitude; + private double latitude; +} \ No newline at end of file diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/registration/RegistrationDto.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/registration/RegistrationDto.java index e9edbe8dc..7c498d06a 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/registration/RegistrationDto.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/registration/RegistrationDto.java @@ -56,6 +56,7 @@ public class RegistrationDto extends Observable { public static final String AGE_GROUP_CONFIG = "{'INFANT':'0-5','MINOR':'6-17','ADULT':'18-200'}"; private String rId; + private String packetId; private String flowType; private String process; private String preRegistrationId; @@ -84,7 +85,10 @@ public class RegistrationDto extends Observable { private OperatorDto reviewer; public Map SELECTED_CODES = new HashMap<>(); + private GeoLocationDto geoLocationDto; + private String applicationId; + private String additionalInfoRequestId; public RegistrationDto(@NonNull String rid, @NonNull String flowType, @NonNull String process, @NonNull Double schemaVersion, @NonNull List languages, @@ -479,4 +483,8 @@ public void addWithoutDocument(String fieldId, String docType, String format,Str this.documents.put(fieldId, documentDto); } } + + public void setGeoLocation(double longitude, double latitude) { + this.geoLocationDto = new GeoLocationDto(longitude, latitude); + } } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/uispec/ScreenSpecDto.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/uispec/ScreenSpecDto.java index e7b3d9b57..d84f82015 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/uispec/ScreenSpecDto.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/uispec/ScreenSpecDto.java @@ -20,5 +20,5 @@ public class ScreenSpecDto { private List fields; private Integer order; private Boolean preRegFetchRequired; - + private Boolean additionalInfoRequestIdRequired; } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/uispec/SettingsSpecDto.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/uispec/SettingsSpecDto.java new file mode 100644 index 000000000..edc9bbca5 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/dto/uispec/SettingsSpecDto.java @@ -0,0 +1,30 @@ +package io.mosip.registration.clientmanager.dto.uispec; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.HashMap; +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class SettingsSpecDto { + + private String name; + private HashMap description; + private HashMap label; + private String fxml; + private String icon; + @JsonProperty("shortcut-icon") + private String shortcutIcon; + private String order; + @JsonProperty("access-control") + private List accessControl; + +} diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/LocalPreferences.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/LocalPreferences.java new file mode 100644 index 000000000..2ae3e6152 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/LocalPreferences.java @@ -0,0 +1,48 @@ +package io.mosip.registration.clientmanager.entity; + +import androidx.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; +import lombok.Data; + +/** + * The Entity Class for Local Preferences + * Stores user-specific configuration overrides that can override global parameters + */ +@Entity(tableName = "local_preferences") +@Data +public class LocalPreferences { + + @PrimaryKey + @NonNull + @ColumnInfo(name = "id") + private String id; + + @ColumnInfo(name = "name") + private String name; + + @ColumnInfo(name = "val") + private String val; + + @ColumnInfo(name = "config_type") + private String configType; + + @ColumnInfo(name = "cr_by") + private String crBy; + + @ColumnInfo(name = "cr_dtime") + private Long crDtime; + + @ColumnInfo(name = "upd_by") + private String updBy; + + @ColumnInfo(name = "upd_dtimes") + private Long updDtimes; + + @ColumnInfo(name = "is_deleted") + private Boolean isDeleted; + + @ColumnInfo(name = "del_dtimes") + private Long delDtimes; +} \ No newline at end of file diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/PermittedLocalConfig.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/PermittedLocalConfig.java new file mode 100644 index 000000000..9381c3163 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/PermittedLocalConfig.java @@ -0,0 +1,36 @@ +package io.mosip.registration.clientmanager.entity; + +import androidx.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; +import lombok.Data; + +/** + * The Entity Class for Permitted Local Config + * Stores server-defined permitted configuration values that can be overridden locally + */ +@Entity(tableName = "permitted_local_config") +@Data +public class PermittedLocalConfig { + + @PrimaryKey + @NonNull + @ColumnInfo(name = "code") + private String code; + + @ColumnInfo(name = "name") + private String name; + + @ColumnInfo(name = "config_type") + private String type; + + @ColumnInfo(name = "is_active") + private Boolean isActive; + + @ColumnInfo(name = "is_deleted") + private Boolean isDeleted; + + @ColumnInfo(name = "del_dtimes") + private Long delDtimes; +} diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/Registration.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/Registration.java index 6507d904f..a8287a781 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/Registration.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/Registration.java @@ -99,6 +99,9 @@ public class Registration { @ColumnInfo(name = "upd_dtimes") private Long updDtimes; + @ColumnInfo(name = "id") + private String id; + @Override public String toString() { return packetId + "\n" + (serverStatus == null ? clientStatus : serverStatus); diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/UserRole.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/UserRole.java new file mode 100644 index 000000000..8d9276899 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/entity/UserRole.java @@ -0,0 +1,27 @@ +package io.mosip.registration.clientmanager.entity; + +import androidx.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Entity; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Entity(tableName = "user_role", primaryKeys = {"usr_id", "role_code"}) +@Data +@AllArgsConstructor +public class UserRole { + + @NonNull + @ColumnInfo(name = "usr_id") + private String usrId; + + @NonNull + @ColumnInfo(name = "role_code") + private String roleCode; + + @ColumnInfo(name = "lang_code") + private String langCode; +} + + diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/jobs/ConfigDataSyncJob.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/jobs/ConfigDataSyncJob.java index 9674a908a..ff7e02b1e 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/jobs/ConfigDataSyncJob.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/jobs/ConfigDataSyncJob.java @@ -39,7 +39,7 @@ public void onCreate() { public boolean triggerJob(int jobId) { Log.d(TAG, TAG + " Started"); try { - masterDataService.syncGlobalParamsData(() -> {}, true); + masterDataService.syncGlobalParamsData(() -> {}, true, ""); long timeStampInSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()); logJobTransaction(jobId, timeStampInSeconds); return true; diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/jobs/DeleteAuditLogsJob.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/jobs/DeleteAuditLogsJob.java new file mode 100644 index 000000000..ff61fc5f8 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/jobs/DeleteAuditLogsJob.java @@ -0,0 +1,50 @@ +package io.mosip.registration.clientmanager.jobs; + +import android.annotation.SuppressLint; +import android.util.Log; + +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +import dagger.android.AndroidInjection; +import io.mosip.registration.clientmanager.spi.AuditManagerService; + +@SuppressLint("SpecifyJobSchedulerIdRange") +public class DeleteAuditLogsJob extends SyncJobServiceBase { + + private static final String TAG = DeleteAuditLogsJob.class.getSimpleName(); + + @Inject + AuditManagerService auditManagerService; + + + + public DeleteAuditLogsJob() { + configureBuilder(); + } + + @Override + public void onCreate() { + super.onCreate(); + AndroidInjection.inject(this); + } + + @Override + public boolean triggerJob(int jobId) { + Log.d(TAG, TAG + " Started"); + try { + // Do not modify AUDIT_EXPORTED_TILL here; rely on existing configured value + boolean ok = auditManagerService.deleteAuditLogs(); + long nowMs = System.currentTimeMillis(); + long timeStampInSeconds = TimeUnit.MILLISECONDS.toSeconds(nowMs); + logJobTransaction(jobId, timeStampInSeconds); + return ok; + } catch (Exception e) { + Log.e(TAG, TAG + " failed", e); + } + return false; + } +} + + diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/GlobalParamRepository.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/GlobalParamRepository.java index 9138bbcb2..12c4d5885 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/GlobalParamRepository.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/GlobalParamRepository.java @@ -4,9 +4,11 @@ import io.mosip.registration.clientmanager.constant.RegistrationConstants; import io.mosip.registration.clientmanager.dao.GlobalParamDao; +import io.mosip.registration.clientmanager.dao.LocalConfigDAO; import io.mosip.registration.clientmanager.entity.GlobalParam; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -21,11 +23,14 @@ public class GlobalParamRepository { private static final String TAG = GlobalParamRepository.class.getSimpleName(); private static Map globalParamMap = new HashMap<>(); private GlobalParamDao globalParamDao; + private LocalConfigDAO localConfigDAO; @Inject - public GlobalParamRepository(GlobalParamDao globalParamDao) { + public GlobalParamRepository(GlobalParamDao globalParamDao, LocalConfigDAO localConfigDAO) { this.globalParamDao = globalParamDao; - refreshGlobalParams(); + this.localConfigDAO = localConfigDAO; + + refreshConfigurationCache(); } public String getGlobalParamValue(String id) { @@ -65,12 +70,14 @@ public int getMinLanguageCount() { public void saveGlobalParam(String id, String value) { GlobalParam globalParam = new GlobalParam(id, id, value, true); globalParamDao.insertGlobalParam(globalParam); + // Update the merged cache directly for immediate effect globalParamMap.put(id, value); } public void saveGlobalParams(List globalParam) { globalParamDao.insertAll(globalParam); - refreshGlobalParams(); + // Refresh with merged configuration to include any local preferences + refreshConfigurationCache(); } public List getGlobalParams() { @@ -106,10 +113,22 @@ public String getCachedStringMAVELScript(){ return globalParamMap.getOrDefault(RegistrationConstants.APPLICANT_TYPE_MVEL_SCRIPT,"applicanttype.mvel"); } - public String getCachedStringPreRegPacketLocation(){ + public String getCachedStringPreRegPacketLocation() { return globalParamMap.get(RegistrationConstants.PRE_REG_PACKET_LOCATION); } + public Map getGlobalParamsByPattern(String pattern) { + + List globalParams = globalParamDao.findByNameLikeAndIsActiveTrueAndValIsNotNull(pattern); + Map globalParamMap = new LinkedHashMap<>(); + + for (GlobalParam param : globalParams) { + globalParamMap.put(param.getName(), param.getValue() != null ? param.getValue().trim() : param.getValue()); + } + + return globalParamMap; + } + public List getSelectedHandles() { String value = globalParamMap.getOrDefault(RegistrationConstants.SELECTED_HANDLES, ""); return Arrays.asList(value.split(RegistrationConstants.COMMA)).stream() @@ -122,4 +141,46 @@ public List getSelectedHandles() { public String getCachedStringForgotPassword() { return globalParamMap.get(RegistrationConstants.FORGOT_PASSWORD_URL); } + + public String getCachedStringIdleTime() { + return globalParamMap.get(RegistrationConstants.IDLE_TIME); + } + + public String getCachedStringRefreshedLoginTime() { + return globalParamMap.get(RegistrationConstants.REFRESHED_LOGIN_TIME); + } + + public String getCachedStringGpsDeviceEnableFlag() { + return globalParamMap.get(RegistrationConstants.GPS_DEVICE_ENABLE_FLAG); + } + + public String getCachedStringMachineToCenterDistance() { + return globalParamMap.get(RegistrationConstants.DIST_FRM_MACHINE_TO_CENTER); + } + + /** + * Refresh configuration cache by merging global params with local preferences + */ + public void refreshConfigurationCache() { + + try { + // Get fresh global parameters from database + List globalParams = globalParamDao.getGlobalParams(); + Map freshGlobalParams = new HashMap<>(); + for (GlobalParam globalParam : globalParams) { + freshGlobalParams.put(globalParam.getId(), globalParam.getValue()); + } + + // Get local preferences (overrides) + Map localConfigs = localConfigDAO.getLocalConfigurations(); + + // Merge: local preferences override global parameters + globalParamMap.clear(); + globalParamMap.putAll(freshGlobalParams); + globalParamMap.putAll(localConfigs); // Local preferences take precedence + } catch (Exception e) { + Log.e(TAG, "Error refreshing configuration cache", e); + } + + } } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/IdentitySchemaRepository.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/IdentitySchemaRepository.java index f59912ac8..141772129 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/IdentitySchemaRepository.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/IdentitySchemaRepository.java @@ -17,6 +17,7 @@ import io.mosip.registration.clientmanager.dto.uispec.ProcessSpecDto; import io.mosip.registration.clientmanager.dto.uispec.RequiredDto; import io.mosip.registration.clientmanager.dto.uispec.ScreenSpecDto; +import io.mosip.registration.clientmanager.dto.uispec.SettingsSpecDto; import io.mosip.registration.clientmanager.entity.IdentitySchema; import io.mosip.registration.clientmanager.entity.ProcessSpec; import io.mosip.registration.packetmanager.util.HMACUtils2; @@ -120,6 +121,15 @@ public ProcessSpecDto getNewProcessSpec(Context context, Double version) throws return getIdSchemaResponse(context, identitySchema).getNewProcess(); } + public List getSettingsSchema(Context context, Double version) throws Exception { + IdentitySchema identitySchema = identitySchemaDao.findIdentitySchema(version, SCHEMA_PREFIX+version); + + if(identitySchema == null) + throw new Exception("Identity schema not found for version : " + version); + + return getIdSchemaResponse(context, identitySchema).getSettings(); + } + public List getAllFieldSpec(Context context, Double version) throws Exception { List schemaFields = new ArrayList<>(); ProcessSpecDto processSpec = getNewProcessSpec(context, version); @@ -329,7 +339,7 @@ private IdSchemaResponse migrate115UiSpecToLTSProcessSpec(IdSchemaResponse idSch FieldSpecDto field = objectMapper.treeToValue(element, FieldSpecDto.class); consentFields.add(field); } - ScreenSpecDto consentScreen = new ScreenSpecDto("Consent", consentScreenLabel, consentFields, 1, false); + ScreenSpecDto consentScreen = new ScreenSpecDto("Consent", consentScreenLabel, consentFields, 1, false, false); screens.add(consentScreen); } catch (JsonProcessingException e) { Log.e(TAG, "Failed to build consent screen", e); @@ -341,7 +351,7 @@ private IdSchemaResponse migrate115UiSpecToLTSProcessSpec(IdSchemaResponse idSch demoScreenLabel.put(primaryLanguage, this.globalParamRepository.getCachedStringGlobalParam("demographicsScreenName_"+primaryLanguage)); if (secondaryLanguage != null) demoScreenLabel.put(secondaryLanguage, this.globalParamRepository.getCachedStringGlobalParam("demographicsScreenName_"+secondaryLanguage)); - ScreenSpecDto demographicScreen = new ScreenSpecDto("DemographicDetails", demoScreenLabel, demographics, 2, true); + ScreenSpecDto demographicScreen = new ScreenSpecDto("DemographicDetails", demoScreenLabel, demographics, 2, true, false); screens.add(demographicScreen); Log.i(TAG, "Building demographics screen completed"); @@ -350,7 +360,7 @@ private IdSchemaResponse migrate115UiSpecToLTSProcessSpec(IdSchemaResponse idSch docScreenLabel.put(primaryLanguage, this.globalParamRepository.getCachedStringGlobalParam("documentsScreenName_"+primaryLanguage)); if (secondaryLanguage != null) docScreenLabel.put(secondaryLanguage, this.globalParamRepository.getCachedStringGlobalParam("documentsScreenName_"+secondaryLanguage)); - ScreenSpecDto documentsScreen = new ScreenSpecDto("Documents", docScreenLabel, documents, 3, false); + ScreenSpecDto documentsScreen = new ScreenSpecDto("Documents", docScreenLabel, documents, 3, false, false); screens.add(documentsScreen); Log.i(TAG, "Building documents screen completed"); @@ -359,7 +369,7 @@ private IdSchemaResponse migrate115UiSpecToLTSProcessSpec(IdSchemaResponse idSch bioScreenLabel.put(primaryLanguage, this.globalParamRepository.getCachedStringGlobalParam("biometricsScreenName_"+primaryLanguage)); if (secondaryLanguage != null) bioScreenLabel.put(secondaryLanguage, this.globalParamRepository.getCachedStringGlobalParam("biometricsScreenName_"+secondaryLanguage)); - ScreenSpecDto biometricsScreen = new ScreenSpecDto("BiometricDetails", bioScreenLabel, biometrics, 4, false); + ScreenSpecDto biometricsScreen = new ScreenSpecDto("BiometricDetails", bioScreenLabel, biometrics, 4, false, false); screens.add(biometricsScreen); Log.i(TAG, "Building biometrics screen completed"); diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/LocalPreferencesRepository.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/LocalPreferencesRepository.java new file mode 100644 index 000000000..55f9431b4 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/LocalPreferencesRepository.java @@ -0,0 +1,90 @@ +package io.mosip.registration.clientmanager.repository; + +import android.util.Log; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import io.mosip.registration.clientmanager.constant.RegistrationConstants; +import io.mosip.registration.clientmanager.dao.LocalPreferencesDao; +import io.mosip.registration.clientmanager.entity.LocalPreferences; + +public class LocalPreferencesRepository { + + private static final String TAG = LocalPreferencesRepository.class.getSimpleName(); + private LocalPreferencesDao localPreferencesDao; + + @Inject + public LocalPreferencesRepository(LocalPreferencesDao localPreferencesDao) { + this.localPreferencesDao = localPreferencesDao; + } + + /** + * Get local configurations as a map (name -> value) + */ + public Map getLocalConfigurations() { + try { + List localPreferences = localPreferencesDao + .findByIsDeletedFalseAndConfigType(RegistrationConstants.PERMITTED_CONFIG_TYPE); + + Map localConfigMap = new HashMap<>(); + if (localPreferences != null) { + for (LocalPreferences localPreference : localPreferences) { + localConfigMap.put(localPreference.getName(), localPreference.getVal()); + } + } + return localConfigMap; + } catch (Exception e) { + Log.e(TAG, "Error getting local configurations", e); + return new HashMap<>(); + } + } + + /** + * Find local preference by name + */ + public LocalPreferences findByIsDeletedFalseAndName(String name) { + try { + return localPreferencesDao.findByIsDeletedFalseAndName(name); + } catch (Exception e) { + Log.e(TAG, "Error finding local preference by name: " + name, e); + return null; + } + } + + /** + * Save local preference + */ + public void save(LocalPreferences localPreference) { + try { + localPreferencesDao.insert(localPreference); + } catch (Exception e) { + Log.e(TAG, "Error saving local preference: " + localPreference.getName(), e); + } + } + + /** + * Update local preference + */ + public void update(LocalPreferences localPreference) { + try { + localPreferencesDao.update(localPreference); + } catch (Exception e) { + Log.e(TAG, "Error updating local preference: " + localPreference.getName(), e); + } + } + + /** + * Delete local preference by name + */ + public void delete(LocalPreferences localPreference) { + try { + localPreferencesDao.deleteByName(localPreference.getName()); + } catch (Exception e) { + Log.e(TAG, "Error deleting local preference: " + localPreference.getName(), e); + } + } +} diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/PermittedLocalConfigRepository.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/PermittedLocalConfigRepository.java new file mode 100644 index 000000000..e8e2b2a0b --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/PermittedLocalConfigRepository.java @@ -0,0 +1,45 @@ +package io.mosip.registration.clientmanager.repository; + +import android.util.Log; + +import java.util.List; + +import javax.inject.Inject; + +import io.mosip.registration.clientmanager.dao.PermittedLocalConfigDao; +import io.mosip.registration.clientmanager.entity.PermittedLocalConfig; + +public class PermittedLocalConfigRepository { + + private static final String TAG = PermittedLocalConfigRepository.class.getSimpleName(); + private PermittedLocalConfigDao permittedLocalConfigDao; + + @Inject + public PermittedLocalConfigRepository(PermittedLocalConfigDao permittedLocalConfigDao) { + this.permittedLocalConfigDao = permittedLocalConfigDao; + } + + /** + * Save permitted local configurations + */ + public void savePermittedConfigs(List permittedConfigs) { + try { + permittedLocalConfigDao.insertAll(permittedConfigs); + } catch (Exception e) { + Log.e(TAG, "Error saving permitted configurations", e); + } + } + + /** + * Get permitted configurations by type + */ + public List getPermittedConfigsByType(String configType) { + try { + return permittedLocalConfigDao.findByIsActiveTrueAndType(configType); + } catch (Exception e) { + Log.e(TAG, "Error getting permitted configurations by type: " + configType, e); + return null; + } + } + +} diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/RegistrationRepository.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/RegistrationRepository.java index 29b1eb236..56e445ee2 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/RegistrationRepository.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/RegistrationRepository.java @@ -67,7 +67,7 @@ public void updateStatus(String packetId, String serverStatus, String clientStat } public Registration insertRegistration(String packetId, String containerPath, String centerId, - String registrationType, JSONObject additionalInfo) throws Exception { + String registrationType, JSONObject additionalInfo, String additionalInfoReqId, String rid, String applicationId) throws Exception { Registration registration = new Registration(packetId); registration.setFilePath(containerPath); registration.setRegType(registrationType); @@ -76,8 +76,11 @@ public Registration insertRegistration(String packetId, String containerPath, St registration.setServerStatus(null); registration.setCrDtime(System.currentTimeMillis()); registration.setCrBy("110006"); + registration.setId(rid); + registration.setAppId(applicationId); //TODO use objectMapper registration.setAdditionalInfo(additionalInfo.toString().getBytes(StandardCharsets.UTF_8)); + registration.setAdditionalInfoReqId(additionalInfoReqId); this.registrationDao.insert(registration); return registration; } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/SyncJobDefRepository.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/SyncJobDefRepository.java index 24fc52499..ce561fa2b 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/SyncJobDefRepository.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/SyncJobDefRepository.java @@ -1,5 +1,7 @@ package io.mosip.registration.clientmanager.repository; +import android.util.Log; + import java.util.List; import javax.inject.Inject; @@ -29,4 +31,9 @@ public void saveSyncJobDef(SyncJobDef syncJobDef) { public List getAllSyncJobDefList() { return this.syncJobDefDao.findAll(); } + + public List getActiveSyncJobs() { + List activeJobs = this.syncJobDefDao.findAllByActiveStatus(true); + return activeJobs; + } } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/UserRoleRepository.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/UserRoleRepository.java new file mode 100644 index 000000000..baccd8c2e --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/repository/UserRoleRepository.java @@ -0,0 +1,35 @@ +package io.mosip.registration.clientmanager.repository; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import io.mosip.registration.clientmanager.dao.UserRoleDao; +import io.mosip.registration.clientmanager.entity.UserRole; + +public class UserRoleRepository { + + private final UserRoleDao userRoleDao; + + @Inject + public UserRoleRepository(UserRoleDao userRoleDao) { + this.userRoleDao = userRoleDao; + } + + public void saveRoles(String userId, List roleCodes) { + userRoleDao.deleteByUsrId(userId); + for (String code : roleCodes) { + userRoleDao.insert(new UserRole(userId, code, null)); + } + } + + public List getRolesByUserId(String userId) { + List rows = userRoleDao.findByUsrId(userId); + List codes = new ArrayList<>(); + for (UserRole r : rows) { + codes.add(r.getRoleCode()); + } + return codes; + } +} \ No newline at end of file diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/AuditManagerServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/AuditManagerServiceImpl.java index dd7e77912..2366975d1 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/AuditManagerServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/AuditManagerServiceImpl.java @@ -93,23 +93,16 @@ public void audit(AuditEvent auditEventEnum, String appModuleId, String appModul @Override public boolean deleteAuditLogs() { - Log.i(TAG, "Deletion of Audit Logs Started"); String tillDate = globalParamRepository.getGlobalParamValue(RegistrationConstants.AUDIT_EXPORTED_TILL); - if (tillDate != null && !tillDate.isEmpty()) { - try { - /* Delete Audits before given Time */ - long tillDateLong = Long.parseLong(tillDate); - auditRepository.deleteAllAuditsTillDate(tillDateLong); - Log.i(TAG, "deleteAuditLogs: Deletion of Audit Logs Completed for datetime before : {}" + tillDateLong); - return true; - } catch (RuntimeException runtimeException) { - Log.e(TAG, "deleteAuditLogs: Deletion of Audit Logs failed", runtimeException); - return false; - } - } else { - Log.e(TAG, "deleteAuditLogs: Deletion of Audit Logs failed, tillDate missing"); + try { + long tillDateLong = (tillDate != null) ? Long.parseLong(tillDate) : System.currentTimeMillis(); + auditRepository.deleteAllAuditsTillDate(tillDateLong); + globalParamRepository.saveGlobalParam(RegistrationConstants.AUDIT_EXPORTED_TILL, null); + return true; + } catch (RuntimeException runtimeException) { + Log.e(TAG, "Error in deleting audit logs", runtimeException); return false; } } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/Biometrics095Service.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/Biometrics095Service.java index a19e8e476..b22bc55a9 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/Biometrics095Service.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/Biometrics095Service.java @@ -13,7 +13,10 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Base64; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import javax.inject.Inject; @@ -66,6 +69,7 @@ public class Biometrics095Service extends BiometricsService { private final UserBiometricRepository userBiometricRepository; private IBioApiV2 iBioApiV2; SharedPreferences sharedPreferences; + public Map BIO_DEVICES; @Inject @@ -78,6 +82,7 @@ public Biometrics095Service(Context context, ObjectMapper objectMapper, this.clientCryptoManagerService = clientCryptoManagerService; this.userBiometricRepository = userBiometricRepository; this.iBioApiV2 = new MatchSDK(); + this.BIO_DEVICES = new HashMap<>(); sharedPreferences = this.context.getSharedPreferences( this.context.getString(R.string.app_name), Context.MODE_PRIVATE); @@ -167,6 +172,7 @@ public List handleRCaptureResponse(Modality modality, InputStream public String[] handleDeviceInfoResponse(Modality modality, byte[] response) throws BiometricsServiceException { String callbackId = null; String serialNo = null; + String deviceStatus = null; try { List list = objectMapper.readValue(response, new TypeReference>() {}); @@ -187,10 +193,12 @@ public String[] handleDeviceInfoResponse(Modality modality, byte[] response) thr if(deviceDto.getCallbackId().contains(".Info")) { callbackId = deviceDto.getCallbackId().replace(".Info", ""); } + deviceStatus = deviceDto.getDeviceStatus(); String digitalIdPayload = getJWTPayLoad(deviceDto.getDigitalId()); byte[] decodedDigitalIdPayload = Base64.getUrlDecoder().decode(digitalIdPayload); DigitalId digitalId = objectMapper.readValue(decodedDigitalIdPayload, DigitalId.class); serialNo = digitalId.getSerialNo(); + addBioDevice(modality, deviceDto.getDeviceCode(), digitalId); } catch (BiometricsServiceException e) { auditManagerService.audit(AuditEvent.DEVICE_INFO_PARSE_FAILED, Components.REGISTRATION, e.getMessage()); Toast.makeText(context, "No SBI found!", Toast.LENGTH_LONG).show(); @@ -201,7 +209,7 @@ public String[] handleDeviceInfoResponse(Modality modality, byte[] response) thr throw new BiometricsServiceException(SBIError.SBI_DINFO_INVALID_REPSONSE.getErrorCode(), SBIError.SBI_DINFO_INVALID_REPSONSE.getErrorMessage()); } - return new String[] { callbackId, serialNo }; + return new String[] { callbackId, serialNo, deviceStatus}; } public String handleDiscoveryResponse(Modality modality, byte[] response) throws BiometricsServiceException { @@ -289,4 +297,20 @@ public void validateJWTResponse(final String signedData, final String domain) } } + public void addBioDevice(Modality modality, String deviceCode, DigitalId digitalId) { + Map registeredDevice = new LinkedHashMap<>(); + Map digitalIdMap = new HashMap<>(); + digitalIdMap.put("serialNo", digitalId.getSerialNo()); + digitalIdMap.put("make", digitalId.getMake()); + digitalIdMap.put("model", digitalId.getModel()); + digitalIdMap.put("type", digitalId.getType()); + digitalIdMap.put("deviceProviderId", digitalId.getDeviceProviderId()); + digitalIdMap.put("deviceProvider", digitalId.getDeviceProvider()); + digitalIdMap.put("dateTime", digitalId.getDateTime()); + digitalIdMap.put("deviceSubType", digitalId.getDeviceSubType()); + registeredDevice.put("deviceServiceVersion", "0.9.5"); + registeredDevice.put("digitalId", digitalIdMap); + registeredDevice.put("deviceCode", deviceCode); + BIO_DEVICES.put(modality, registeredDevice); + } } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/JobManagerServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/JobManagerServiceImpl.java index 630ef282b..3c90c3182 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/JobManagerServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/JobManagerServiceImpl.java @@ -11,6 +11,7 @@ import org.apache.commons.lang3.NotImplementedException; import java.text.DateFormat; +import java.time.Instant; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; @@ -18,10 +19,12 @@ import io.mosip.registration.clientmanager.R; import io.mosip.registration.clientmanager.entity.SyncJobDef; import io.mosip.registration.clientmanager.jobs.ConfigDataSyncJob; +import io.mosip.registration.clientmanager.jobs.DeleteAuditLogsJob; import io.mosip.registration.clientmanager.jobs.PacketStatusSyncJob; import io.mosip.registration.clientmanager.repository.SyncJobDefRepository; import io.mosip.registration.clientmanager.spi.JobManagerService; import io.mosip.registration.clientmanager.spi.JobTransactionService; +import io.mosip.registration.clientmanager.util.CronExpressionParser; import io.mosip.registration.clientmanager.util.DateUtil; /** @@ -32,7 +35,7 @@ public class JobManagerServiceImpl implements JobManagerService { private static final String TAG = JobManagerServiceImpl.class.getSimpleName(); - private static final int JOB_PERIODIC_SECONDS = 15 * 60; + private static final int JOB_PERIODIC_SECONDS = (15 * 60) * 1000; private static final int NUM_LENGTH_LIMIT = 5; Context context; @@ -96,22 +99,13 @@ public int scheduleJob(int jobId, String apiName, String syncFreq) { ComponentName componentName = new ComponentName(context, clientJobService); JobInfo info; - if (syncFreq == null || syncFreq.trim().isEmpty()) { - //To schedule only once - info = new JobInfo.Builder(jobId, componentName) - .setRequiresCharging(false) - .setPersisted(false) - .build(); - - } else { - //To schedule periodically - //TODO set cron wise - info = new JobInfo.Builder(jobId, componentName) - .setRequiresCharging(false) - .setPersisted(true) - .setPeriodic(JOB_PERIODIC_SECONDS * 1000) - .build(); - } + //To schedule periodically + long periodMillis = JOB_PERIODIC_SECONDS * 1000L; + info = new JobInfo.Builder(jobId, componentName) + .setRequiresCharging(false) + .setPersisted(true) + .setPeriodic(periodMillis) + .build(); return jobScheduler.schedule(info); } @@ -167,15 +161,27 @@ public String getLastSyncTime(int jobId) { @Override public String getNextSyncTime(int jobId) { - //TODO implementation using CRON job - long lastSyncTimeSeconds = jobTransactionService.getLastSyncTime(jobId); - String nextSync = context.getString(R.string.NA); + SyncJobDef jobDef = getJobDefByJobId(jobId); + if (jobDef == null) { + return "NA"; + } + + String cronExpression = jobDef.getSyncFreq(); + // Try cron-based calculation first + if (CronExpressionParser.isValidCronExpression(cronExpression)) { + Instant nextExecution = CronExpressionParser.getNextExecutionTime(cronExpression); + if (nextExecution != null) { + return dateUtil.getDateTime(nextExecution.toEpochMilli()); + } + } + // Fallback: use last sync time + interval + long lastSyncTimeSeconds = jobTransactionService.getLastSyncTime(jobId); if (lastSyncTimeSeconds > 0) { - long nextSyncTimeSeconds = lastSyncTimeSeconds + JOB_PERIODIC_SECONDS; - nextSync = dateUtil.getDateTime(nextSyncTimeSeconds); + return dateUtil.getDateTime(lastSyncTimeSeconds + JOB_PERIODIC_SECONDS); } - return nextSync; + + return "NA"; } @Override @@ -196,8 +202,20 @@ private Class getJobServiceImplClass(String jobAPIName) { return PacketStatusSyncJob.class; case "synchConfigDataJob": return ConfigDataSyncJob.class; + case "deleteAuditLogsJob": + return DeleteAuditLogsJob.class; default: return null; } } + + private SyncJobDef getJobDefByJobId(int jobId) { + List allJobs = syncJobDefRepository.getAllSyncJobDefList(); + for (SyncJobDef jobDef : allJobs) { + if (jobId == generateJobServiceId(jobDef.getId())) { + return jobDef; + } + } + return null; + } } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/LocalConfigServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/LocalConfigServiceImpl.java new file mode 100644 index 000000000..34df86013 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/LocalConfigServiceImpl.java @@ -0,0 +1,37 @@ +package io.mosip.registration.clientmanager.service; + +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.mosip.registration.clientmanager.constant.RegistrationConstants; +import io.mosip.registration.clientmanager.dao.LocalConfigDAO; +import io.mosip.registration.clientmanager.spi.LocalConfigService; + +@Singleton +public class LocalConfigServiceImpl implements LocalConfigService { + + private LocalConfigDAO localConfigDAO; + + @Inject + public LocalConfigServiceImpl(LocalConfigDAO localConfigDAO) { + this.localConfigDAO = localConfigDAO; + } + + @Override + public Map getLocalConfigurations() { + return localConfigDAO.getLocalConfigurations(); + } + + @Override + public void modifyConfigurations(Map localPreferences) { + localConfigDAO.modifyConfigurations(localPreferences); + } + + @Override + public List getPermittedConfiguration() { + return localConfigDAO.getPermittedConfigurations(RegistrationConstants.PERMITTED_CONFIG_TYPE); + } +} diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/LocationValidationServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/LocationValidationServiceImpl.java new file mode 100644 index 000000000..a399a4f19 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/LocationValidationServiceImpl.java @@ -0,0 +1,42 @@ +package io.mosip.registration.clientmanager.service; + +import android.util.Log; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.mosip.registration.clientmanager.spi.LocationValidationService; + +/** + * Implementation of LocationValidationService for distance calculation + * + * @author sachin sp + */ +@Singleton +public class LocationValidationServiceImpl implements LocationValidationService { + + private static final String TAG = LocationValidationServiceImpl.class.getSimpleName(); + + @Inject + public LocationValidationServiceImpl() { + } + + @Override + public double getDistance(double machineLongitude, double machineLatitude, + double centerLongitude, double centerLatitude) { + double earthRadiusInKM = 6371; + double longitudeDiff = Math.toRadians(centerLongitude - machineLongitude); + double latitudeDiff = Math.toRadians(centerLatitude - machineLatitude); + + double var = Math.sin(latitudeDiff / 2) * Math.sin(latitudeDiff / 2) + + Math.sin(longitudeDiff / 2) * Math.sin(longitudeDiff / 2) * + Math.cos(Math.toRadians(machineLatitude)) * Math.cos(Math.toRadians(centerLatitude)); + + double distance = earthRadiusInKM * (2 * Math.atan2(Math.sqrt(var), Math.sqrt(1 - var))); + + Log.i(TAG, String.format("Distance calculated: %.2f km", distance)); + + return distance; + } +} + diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/LoginService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/LoginService.java index 0c1ab1ab2..7c14efa11 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/LoginService.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/LoginService.java @@ -10,6 +10,7 @@ import org.json.JSONObject; import io.mosip.registration.clientmanager.repository.UserDetailRepository; +import io.mosip.registration.clientmanager.repository.UserRoleRepository; import io.mosip.registration.keymanager.dto.CryptoRequestDto; import io.mosip.registration.keymanager.dto.CryptoResponseDto; import io.mosip.registration.keymanager.spi.ClientCryptoManagerService; @@ -34,12 +35,16 @@ public class LoginService { @Inject UserDetailRepository userDetailRepository; + @Inject + UserRoleRepository userRoleRepository; + @Inject ClientCryptoManagerService clientCryptoManagerService; - public LoginService(Context context, ClientCryptoManagerService clientCryptoManagerService, UserDetailRepository userDetailRepository) { + public LoginService(Context context, ClientCryptoManagerService clientCryptoManagerService, UserDetailRepository userDetailRepository, UserRoleRepository userRoleRepository) { this.clientCryptoManagerService = clientCryptoManagerService; this.userDetailRepository = userDetailRepository; + this.userRoleRepository = userRoleRepository; this.sessionManager = SessionManager.getSessionManager(context); } @@ -80,6 +85,7 @@ public List saveAuthToken(String authResponse, String userId) throws Exc long rExpiry = Long.parseLong(jsonObject.getString("refreshExpiryTime")); userDetailRepository.saveUserAuthToken(userId, token, refreshToken, tExpiry, rExpiry); List roles=this.sessionManager.saveAuthToken(token); + userRoleRepository.saveRoles(userId, roles); return roles; } catch (Exception ex) { Log.e(TAG, ex.getMessage(), ex); @@ -111,4 +117,8 @@ public void clearAuthToken(Context context){ throw ex; } } + + public List getRolesByUserId(String userId) { + return userRoleRepository.getRolesByUserId(userId); + } } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/MasterDataServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/MasterDataServiceImpl.java index 23f7109aa..73de638fe 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/MasterDataServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/MasterDataServiceImpl.java @@ -12,6 +12,7 @@ import io.mosip.registration.clientmanager.R; import io.mosip.registration.clientmanager.constant.RegistrationConstants; import io.mosip.registration.clientmanager.dao.FileSignatureDao; +import io.mosip.registration.clientmanager.dao.LocalConfigDAO; import io.mosip.registration.clientmanager.dto.CenterMachineDto; import io.mosip.registration.clientmanager.dto.ReasonListDto; import io.mosip.registration.clientmanager.dto.http.*; @@ -23,11 +24,13 @@ import io.mosip.registration.clientmanager.entity.Language; import io.mosip.registration.clientmanager.entity.Location; import io.mosip.registration.clientmanager.entity.MachineMaster; +import io.mosip.registration.clientmanager.entity.PermittedLocalConfig; import io.mosip.registration.clientmanager.entity.ReasonList; import io.mosip.registration.clientmanager.entity.RegistrationCenter; import io.mosip.registration.clientmanager.entity.SyncJobDef; import io.mosip.registration.clientmanager.repository.*; import io.mosip.registration.clientmanager.spi.JobManagerService; +import io.mosip.registration.clientmanager.spi.JobTransactionService; import io.mosip.registration.clientmanager.spi.MasterDataService; import io.mosip.registration.clientmanager.spi.SyncRestService; import io.mosip.registration.clientmanager.util.SyncRestUtil; @@ -62,6 +65,7 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -105,9 +109,12 @@ public class MasterDataServiceImpl implements MasterDataService { private LanguageRepository languageRepository; private JobManagerService jobManagerService; private FileSignatureDao fileSignatureDao; + private PermittedLocalConfigRepository permittedLocalConfigRepository; + private LocalConfigDAO localConfigDAO; private String regCenterId; private String result = ""; SharedPreferences sharedPreferences; + private JobTransactionService jobTransactionService; @Inject public MasterDataServiceImpl(Context context, ObjectMapper objectMapper, SyncRestService syncRestService, @@ -128,7 +135,10 @@ public MasterDataServiceImpl(Context context, ObjectMapper objectMapper, SyncRes CertificateManagerService certificateManagerService, LanguageRepository languageRepository, JobManagerService jobManagerService, - FileSignatureDao fileSignatureDao) { + FileSignatureDao fileSignatureDao, + JobTransactionService jobTransactionService, + PermittedLocalConfigRepository permittedLocalConfigRepository, + LocalConfigDAO localConfigDAO) { this.context = context; this.objectMapper = objectMapper; this.syncRestService = syncRestService; @@ -150,6 +160,9 @@ public MasterDataServiceImpl(Context context, ObjectMapper objectMapper, SyncRes this.languageRepository = languageRepository; this.jobManagerService = jobManagerService; this.fileSignatureDao = fileSignatureDao; + this.jobTransactionService = jobTransactionService; + this.permittedLocalConfigRepository = permittedLocalConfigRepository; + this.localConfigDAO = localConfigDAO; sharedPreferences = this.context.getSharedPreferences( this.context.getString(R.string.app_name), Context.MODE_PRIVATE); @@ -178,7 +191,7 @@ public CenterMachineDto getRegistrationCenterMachineDetails() { } @Override - public void syncCertificate(Runnable onFinish, String applicationId, String referenceId, String setApplicationId, String setReferenceId, boolean isManualSync) { + public void syncCertificate(Runnable onFinish, String applicationId, String referenceId, String setApplicationId, String setReferenceId, boolean isManualSync, String jobId) { CenterMachineDto centerMachineDto = getRegistrationCenterMachineDetails(); if (centerMachineDto == null) { result = POLICY_KEY_SYNC_FAILED; @@ -200,6 +213,11 @@ public void onResponse(Call> call, Response certificateRequestDto.setReferenceId(setReferenceId); certificateRequestDto.setCertificateData(response.body().getResponse().getCertificate()); certificateManagerService.uploadOtherDomainCertificate(certificateRequestDto); + try { + logLastSyncCompletionDateTime(jobId); + } catch (Exception e) { + Log.e(TAG, "Failed to store policy sync last sync time", e); + } if (isManualSync) { Toast.makeText(context, "Policy key Sync Completed", Toast.LENGTH_LONG).show(); } @@ -241,9 +259,8 @@ public void onFailure(Call> call, Throwable } @Override - public void syncMasterData(Runnable onFinish, int retryNo, boolean isManualSync) { + public void syncMasterData(Runnable onFinish, int retryNo, boolean isManualSync, String jobId) { CenterMachineDto centerMachineDto = getRegistrationCenterMachineDetails(); - Map queryParams = new HashMap<>(); try { @@ -284,7 +301,7 @@ public void onResponse(Call> call, Response> call, Response> call, Throwable t) } @Override - public void syncGlobalParamsData(Runnable onFinish, boolean isManualSync) throws Exception { + public void syncGlobalParamsData(Runnable onFinish, boolean isManualSync, String jobId) throws Exception { Log.i(TAG, "config data sync is started"); String serverVersion = getServerVersionFromConfigs(); @@ -345,6 +367,11 @@ public void onResponse(Call>> call, Response if (isManualSync) { Toast.makeText(context, context.getString(R.string.global_config_sync_completed), Toast.LENGTH_LONG).show(); } + try { + logLastSyncCompletionDateTime(jobId); + } catch (Exception e) { + Log.e(TAG, "Failed to store master data sync last sync time", e); + } onFinish.run(); } else { result = GLOBAL_PARAMS_SYNC_FAILED; @@ -376,11 +403,17 @@ public void onFailure(Call>> call, Throwable @SuppressWarnings("unchecked") private void saveGlobalParams(Map responseMap) { try { + Map globalParamMap = new HashMap<>(); if (responseMap.get("configDetail") != null) { Map configDetailJsonMap = (Map) responseMap.get("configDetail"); + if (configDetailJsonMap != null && configDetailJsonMap.get("globalConfiguration") != null) { + String encryptedGlobalConfigs = configDetailJsonMap.get("globalConfiguration").toString(); + parseToMap(getParams(encryptedGlobalConfigs), globalParamMap); + } + if (configDetailJsonMap != null && configDetailJsonMap.get("registrationConfiguration") != null) { String encryptedConfigs = configDetailJsonMap.get("registrationConfiguration").toString(); parseToMap(getParams(encryptedConfigs), globalParamMap); @@ -502,7 +535,7 @@ private void saveProcessSpec(IdSchemaResponse idSchemaResponse, String jsonStrin @Override - public void syncUserDetails(Runnable onFinish, boolean isManualSync) throws Exception { + public void syncUserDetails(Runnable onFinish, boolean isManualSync, String jobId) throws Exception { String serverVersion = getServerVersionFromConfigs(); if (serverVersion.startsWith(SERVER_VERSION_1_1_5)) { result = ""; @@ -510,6 +543,11 @@ public void syncUserDetails(Runnable onFinish, boolean isManualSync) throws Exce Toast.makeText(context, "User Sync Completed", Toast.LENGTH_LONG).show(); } Log.i(TAG, "Found 115 version, skipping userdetails sync"); + try { + logLastSyncCompletionDateTime(jobId); + } catch (Exception e) { + Log.e(TAG, "Failed to store user details sync last sync time", e); + } onFinish.run(); return; } @@ -527,6 +565,12 @@ public void onResponse(Call> call, Response< if (isManualSync) { Toast.makeText(context, "User Sync Completed", Toast.LENGTH_LONG).show(); } + try { + logLastSyncCompletionDateTime(jobId); + } catch (Exception e) { + Log.e(TAG, "Failed to store user details sync last sync time", e); + } + onFinish.run(); } else { result = USER_DETAILS_SYNC_FAILED; @@ -564,7 +608,7 @@ private void saveUserDetails(String encData) { } @Override - public void syncCACertificates(Runnable onFinish, boolean isManualSync) { + public void syncCACertificates(Runnable onFinish, boolean isManualSync, String jobId) { Call> call = syncRestService.getCACertificates(null, BuildConfig.CLIENT_VERSION); call.enqueue(new Callback>() { @@ -580,6 +624,11 @@ public void onResponse(Call> call, Res if (isManualSync) { Toast.makeText(context, "CA Certificate Sync Completed", Toast.LENGTH_LONG).show(); } + try { + logLastSyncCompletionDateTime(jobId); + } catch (Exception e) { + Log.e(TAG, "Failed to store CA certificates sync last sync time", e); + } onFinish.run(); return; } catch (Throwable t) { @@ -698,6 +747,12 @@ private String getCurrentTime() { return Instant.now().toString(); } + @Override + public void logLastSyncCompletionDateTime(String jobIdString) { + int jobId = jobManagerService.generateJobServiceId(jobIdString); + jobTransactionService.LogJobTransaction(jobId, Instant.now().toEpochMilli()); + } + private void downloadUrlData(Path path, JSONObject jsonObject, boolean isManualSync) { Log.i(TAG, "Started downloading mvel script: " + path.toString()); try { @@ -915,6 +970,26 @@ private void saveStructuredData(String entityName, String data, boolean applican } } break; + case "PermittedLocalConfig": + JSONArray permittedConfigsJsonArray = getDecryptedDataList(data); + List permittedConfigs = new ArrayList<>(); + for (int i = 0; i < permittedConfigsJsonArray.length(); i++) { + JSONObject jsonObjects = new JSONObject(permittedConfigsJsonArray.getString(i)); + + PermittedLocalConfig premittedConfig = new PermittedLocalConfig(jsonObjects.getString("code")); + premittedConfig.setName(jsonObjects.getString("name")); + premittedConfig.setType(jsonObjects.getString("type")); + premittedConfig.setIsActive(jsonObjects.getBoolean("isActive")); + premittedConfig.setIsDeleted(jsonObjects.optBoolean("isDeleted", false)); + premittedConfig.setDelDtimes(jsonObjects.optLong("delDtimes", 0L)); + permittedConfigs.add(premittedConfig); + } + permittedLocalConfigRepository.savePermittedConfigs(permittedConfigs); + + if (localConfigDAO != null) { + localConfigDAO.cleanUpLocalPreferences(); + } + break; } } @@ -1001,4 +1076,10 @@ public String getGlobalParamValue(String id) { String value = globalParamRepository.getGlobalParamValue(id); return value == null ? "" : value; } + + @Override + public Map getRegistrationParams() { + return globalParamRepository.getGlobalParamsByPattern("mosip.registration%"); + } + } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PacketServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PacketServiceImpl.java index a4c228425..8d31c65b7 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PacketServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PacketServiceImpl.java @@ -132,7 +132,7 @@ public void syncRegistration(@NonNull String packetId, AsyncPacketTaskCallBack c wrapper.setVersion(PACKET_SYNC_VERSION); wrapper.setRequest(new ArrayList()); SyncRIDRequest syncRIDRequest = new SyncRIDRequest(); - syncRIDRequest.setRegistrationId(registration.getPacketId()); + syncRIDRequest.setRegistrationId(registration.getId()); syncRIDRequest.setRegistrationType(registration.getRegType().toUpperCase()); if (!serverVersion.startsWith(SERVER_VERSION_1_1_5)) { syncRIDRequest.setPacketId(registration.getPacketId()); @@ -301,7 +301,7 @@ public void syncAllPacketStatus() { for (Registration reg : registrations) { PacketIdDto packet = new PacketIdDto(); if (serverVersion!=null && serverVersion.startsWith(SERVER_VERSION_1_1_5)) { - packet.setRegistrationId(reg.getPacketId()); + packet.setRegistrationId(reg.getId()); } else { packet.setPacketId(reg.getPacketId()); } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncDaoImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncDaoImpl.java index 469bb4780..b6067f503 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncDaoImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncDaoImpl.java @@ -2,6 +2,8 @@ import android.util.Log; +import java.sql.Timestamp; +import java.util.Date; import java.util.List; import javax.inject.Inject; @@ -46,12 +48,12 @@ public void save(PreRegistrationList preRegistration) { this.preRegistrationRepositoryDao.insert(preRegistration); } - public List fetchRecordsToBeDeleted(String startDate) { + public List fetchRecordsToBeDeleted(Date startDate) { Log.i(TAG, "REGISTRATION - PRE_REGISTRATION_DATA_SYNC_RECORD_FETCH - PRE_REGISTRATION_DATA_SYNC_DAO_IMPL" + "Fetch Records that needs to be deleted"); - return this.preRegistrationRepositoryDao.findByAppointmentDateBeforeAndIsDeleted(startDate, false); + return this.preRegistrationRepositoryDao.findByAppointmentDateBeforeAndIsDeleted(startDate.toString(), false); } public long update(String id, String updatedBy, String updatedTime) { @@ -70,4 +72,28 @@ public String getLastPreRegPacketDownloadedTime() { .findTopByOrderByLastUpdatedPreRegTimeStampDesc(); return preRegistrationList != null ? preRegistrationList.getLastUpdatedPreRegTimeStamp() : null; } -} + + @Override + public void deleteAll(List preRegistrationList) { + try { + this.preRegistrationRepositoryDao.deleteAll(preRegistrationList); + } catch (Exception e) { + Log.e(TAG, "Error deleting pre-registration records: " + e.getMessage()); + throw new RuntimeException("Failed to delete pre-registration records", e); + } + } + + @Override + public Timestamp getLastPreRegPacketDownloadedTimeAsTimestamp() { + PreRegistrationList preRegistrationList = this.preRegistrationRepositoryDao + .findTopByOrderByLastUpdatedPreRegTimeStampDesc(); + if (preRegistrationList != null && preRegistrationList.getLastUpdatedPreRegTimeStamp() != null) { + try { + return Timestamp.valueOf(preRegistrationList.getLastUpdatedPreRegTimeStamp()); + } catch (Exception e) { + Log.e(TAG, "Error parsing timestamp: " + e.getMessage()); + } + } + return null; + } +} \ No newline at end of file diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncServiceImpl.java index cec0a520b..254aff425 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncServiceImpl.java @@ -1,5 +1,6 @@ package io.mosip.registration.clientmanager.service; +import static android.content.ContentValues.TAG; import static io.mosip.registration.clientmanager.config.SessionManager.USER_NAME; import android.content.Context; @@ -12,11 +13,14 @@ import io.mosip.registration.clientmanager.constant.RegistrationConstants; import io.mosip.registration.clientmanager.dao.PreRegistrationDataSyncDao; import io.mosip.registration.clientmanager.dto.CenterMachineDto; +import io.mosip.registration.clientmanager.dto.ErrorResponseDto; import io.mosip.registration.clientmanager.dto.PreRegArchiveDto; import io.mosip.registration.clientmanager.dto.PreRegistrationDataSyncDto; import io.mosip.registration.clientmanager.dto.PreRegistrationDataSyncRequestDto; import io.mosip.registration.clientmanager.dto.PreRegistrationDto; import io.mosip.registration.clientmanager.dto.PreRegistrationIdsDto; +import io.mosip.registration.clientmanager.dto.ResponseDto; +import io.mosip.registration.clientmanager.dto.SuccessResponseDto; import io.mosip.registration.clientmanager.dto.http.ResponseWrapper; import io.mosip.registration.clientmanager.dto.http.ServiceError; import io.mosip.registration.clientmanager.entity.PreRegistrationList; @@ -37,6 +41,8 @@ import androidx.annotation.NonNull; import org.apache.commons.io.FileUtils; + +import java.io.File; import java.time.Instant; import java.util.Date; import java.sql.Timestamp; @@ -45,7 +51,10 @@ import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.Calendar; +import java.util.LinkedList; +import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.TimeZone; import java.util.UUID; import java.util.WeakHashMap; @@ -88,13 +97,18 @@ public PreRegistrationDataSyncServiceImpl(Context context,PreRegistrationDataSyn } @Override - public void fetchPreRegistrationIds(Runnable onFinish) { + public void fetchPreRegistrationIds(Runnable onFinish, String jobId) { Log.i(TAG,"Fetching Pre-Registration Id's started {}"); CenterMachineDto centerMachineDto = this.masterDataService.getRegistrationCenterMachineDetails(); if (centerMachineDto == null) { result = APPLICATION_ID_SYNC_FAILED; onFinish.run(); + try { + masterDataService.logLastSyncCompletionDateTime(jobId); + } catch (Exception e) { + Log.e(TAG, "Failed to log pre reg data sync completion", e); + } return; } @@ -135,9 +149,9 @@ public void onResponse(Call> call, Respon getPreRegistrationPackets(preRegIds); Log.i(TAG,"Fetching Application data ended successfully"); } - Toast.makeText(context, "Application Id Sync Completed", Toast.LENGTH_LONG).show(); - result = ""; - onFinish.run(); + Toast.makeText(context, "Application Id Sync Completed", Toast.LENGTH_LONG).show(); + result = ""; + onFinish.run(); } catch (Exception e) { result = APPLICATION_ID_SYNC_FAILED; Log.e(TAG, APPLICATION_ID_SYNC_FAILED, e); @@ -214,7 +228,7 @@ public Map getPreRegistration(@NonNull String preRegistrationId, private PreRegistrationList fetchPreRegistration(String preRegistrationId, String lastUpdatedTimeStamp) throws Exception { Log.i(TAG,"Fetching Pre-Registration started for {}"+ preRegistrationId); - // PreRegistrationList preRegistration; + // PreRegistrationList preRegistration; /* Check in Database whether required record already exists or not */ preRegistration = this.preRegistrationDao.get(preRegistrationId); @@ -263,7 +277,7 @@ private Map setPacketToResponse(byte[] decryptedPacket, String p } private PreRegistrationList downloadAndSavePacket(@NonNull String preRegistrationId, - String lastUpdatedTimeStamp) throws ClientCheckedException, ExecutionException, InterruptedException { + String lastUpdatedTimeStamp) throws ClientCheckedException, ExecutionException, InterruptedException { CenterMachineDto centerMachineDto = this.masterDataService.getRegistrationCenterMachineDetails(); if (centerMachineDto == null) { @@ -307,7 +321,7 @@ private PreRegistrationList downloadAndSavePacket(@NonNull String preRegistratio } private PreRegistrationList preparePreRegistration(CenterMachineDto centerMachineDto, - PreRegistrationDto preRegistrationDto, String appointmentDate,String lastUpdatedTimeStamp) { + PreRegistrationDto preRegistrationDto, String appointmentDate,String lastUpdatedTimeStamp) { LocalDateTime currentUTCTime = LocalDateTime.now(ZoneOffset.UTC); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @@ -318,7 +332,7 @@ private PreRegistrationList preparePreRegistration(CenterMachineDto centerMachin preRegistrationList.setId(id); preRegistrationList.setPreRegId(preRegistrationDto.getPreRegId()); if(appointmentDate!=null){ - preRegistrationList.setAppointmentDate(appointmentDate); + preRegistrationList.setAppointmentDate(appointmentDate); } preRegistrationList.setLastUpdatedPreRegTimeStamp(lastUpdatedTimeStamp == null ? String.valueOf(Timestamp.valueOf(formattedCurrentUTCTime)) : lastUpdatedTimeStamp); @@ -364,4 +378,129 @@ private String getFromDate(Timestamp reqTime) { return formatDate(cal); } + + /** + Fetch and delete records based on configured days + */ + public synchronized ResponseDto fetchAndDeleteRecords() { + + ResponseDto responseDTO = new ResponseDto(); + if (getGlobalConfigValueOf(RegistrationConstants.PRE_REG_DELETION_CONFIGURED_DAYS) != null) { + + Calendar startCal = Calendar.getInstance(); + startCal.add(Calendar.DATE, -(Integer + .parseInt(Objects.requireNonNull(getGlobalConfigValueOf(RegistrationConstants.PRE_REG_DELETION_CONFIGURED_DAYS))))); + + Date startDate = Date.from(startCal.toInstant()); + + // fetch the records that needs to be deleted + List preRegList = preRegistrationDao.fetchRecordsToBeDeleted(startDate); + + deletePreRegRecords(responseDTO, preRegList); + } + + return responseDTO; + } + + /** + * Delete pre-registration records + */ + public void deletePreRegRecords(ResponseDto responseDTO, final List preRegList) { + + if (preRegList != null && !preRegList.isEmpty()) { + + List preRegistrationsToBeDeletedList = new LinkedList<>(); + + for (PreRegistrationList preRegRecord : preRegList) { + + if (null != preRegRecord) { + try { + File preRegPacket = FileUtils.getFile(preRegRecord.getPacketPath()); + if (preRegPacket.exists() && preRegPacket.delete()) { + preRegistrationsToBeDeletedList.add(preRegRecord); + } + } catch (Exception e) { + Log.e(TAG, "Error deleting packet file: " + e.getMessage()); + } + } + } + + if (!preRegistrationsToBeDeletedList.isEmpty()) { + deleteRecords(responseDTO, preRegistrationsToBeDeletedList); + } else { + setErrorResponse(responseDTO, RegistrationConstants.PRE_REG_DELETE_FAILURE, null); + } + } else { + setSuccessResponse(responseDTO, RegistrationConstants.PRE_REG_DELETE_SUCCESS, null); + } + } + + /** + * Delete records. + */ + private ResponseDto deleteRecords(ResponseDto responseDTO, List preRegList) { + + try { + preRegistrationDao.deleteAll(preRegList); + + setSuccessResponse(responseDTO, RegistrationConstants.PRE_REG_DELETE_SUCCESS, null); + } catch (RuntimeException runtimeException) { + Log.i(TAG, "REGISTRATION - PRE_REGISTRATION_DELETE - PRE_REGISTRATION_DATA_SYNC_SERVICE_IMPL - " + runtimeException.getMessage()); + setErrorResponse(responseDTO, RegistrationConstants.PRE_REG_DELETE_FAILURE, null); + } + + return responseDTO; + } + + /** + * Get pre-registration record for deletion + */ + public PreRegistrationList getPreRegistrationRecordForDeletion(String preRegistrationId) { + if (preRegistrationId == null || preRegistrationId.isEmpty()) { + Log.e(TAG, "REGISTRATION - PRE_REGISTRATION_DATA_SYNC - PRE_REGISTRATION_DATA_SYNC_SERVICE_IMPL - The PreRegistrationId is empty"); + } + return preRegistrationDao.get(preRegistrationId); + } + + /** + * Get last pre-reg packet downloaded time + */ + @Override + public Timestamp getLastPreRegPacketDownloadedTime() { + return preRegistrationDao.getLastPreRegPacketDownloadedTimeAsTimestamp(); + } + + /** + * Get global config value + */ + private String getGlobalConfigValueOf(String key) { + try { + return globalParamRepository.getCachedStringGlobalParam(key); + } catch (Exception e) { + Log.e(TAG, "Error getting global config value for key: " + key, e); + return null; + } + } + + /** + * Set success response + */ + private void setSuccessResponse(ResponseDto responseDTO, String code, String message) { + SuccessResponseDto successResponseDto = new SuccessResponseDto(); + successResponseDto.setCode(code); + successResponseDto.setMessage(message != null ? message : "Operation completed successfully"); + responseDTO.setSuccessResponseDTO(successResponseDto); + } + + /** + * Set error response + */ + private void setErrorResponse(ResponseDto responseDTO, String code, String message) { + ErrorResponseDto errorResponseDto = new ErrorResponseDto(); + errorResponseDto.setCode(code); + errorResponseDto.setMessage(message != null ? message : "Operation failed"); + List errorList = new LinkedList<>(); + errorList.add(errorResponseDto); + responseDTO.setErrorResponseDTOs(errorList); + } } \ No newline at end of file diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java index 5031bcf62..0a3da5c43 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java @@ -1,6 +1,7 @@ package io.mosip.registration.clientmanager.service; import static io.mosip.registration.keymanager.util.KeyManagerConstant.EMPTY; +import static io.mosip.registration.packetmanager.util.PacketManagerConstant.OTHER_KEY_CONFIGURED; import static io.mosip.registration.packetmanager.util.PacketManagerConstant.OTHER_KEY_EXCEPTION; import static io.mosip.registration.packetmanager.util.PacketManagerConstant.OTHER_KEY_FORCE_CAPTURED; import static io.mosip.registration.packetmanager.util.PacketManagerConstant.OTHER_KEY_PAYLOAD; @@ -29,12 +30,12 @@ import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -48,19 +49,27 @@ import io.mosip.registration.clientmanager.constant.Modality; import io.mosip.registration.clientmanager.constant.RegistrationConstants; import io.mosip.registration.clientmanager.dto.CenterMachineDto; +import io.mosip.registration.clientmanager.dto.ResponseDto; import io.mosip.registration.clientmanager.dto.registration.BiometricsDto; +import io.mosip.registration.clientmanager.dto.registration.GeoLocationDto; import io.mosip.registration.clientmanager.dto.registration.RegistrationDto; import io.mosip.registration.clientmanager.dto.uispec.FieldSpecDto; import io.mosip.registration.clientmanager.entity.Audit; import io.mosip.registration.clientmanager.entity.Registration; +import io.mosip.registration.clientmanager.entity.RegistrationCenter; import io.mosip.registration.clientmanager.exception.ClientCheckedException; import io.mosip.registration.clientmanager.exception.RegBaseCheckedException; import io.mosip.registration.clientmanager.repository.GlobalParamRepository; import io.mosip.registration.clientmanager.repository.IdentitySchemaRepository; +import io.mosip.registration.clientmanager.repository.RegistrationCenterRepository; import io.mosip.registration.clientmanager.repository.RegistrationRepository; import io.mosip.registration.clientmanager.spi.AuditManagerService; +import io.mosip.registration.clientmanager.spi.LocationValidationService; import io.mosip.registration.clientmanager.spi.MasterDataService; import io.mosip.registration.clientmanager.spi.RegistrationService; +import io.mosip.registration.clientmanager.entity.PreRegistrationList; +import io.mosip.registration.clientmanager.spi.PreRegistrationDataSyncService; +import javax.inject.Provider; import io.mosip.registration.keymanager.repository.KeyStoreRepository; import io.mosip.registration.keymanager.spi.ClientCryptoManagerService; import io.mosip.registration.keymanager.util.CryptoUtil; @@ -100,8 +109,13 @@ public class RegistrationServiceImpl implements RegistrationService { private KeyStoreRepository keyStoreRepository; private GlobalParamRepository globalParamRepository; private AuditManagerService auditManagerService; + private RegistrationCenterRepository registrationCenterRepository; + private LocationValidationService locationValidationService; + private Provider preRegistrationDataSyncServiceProvider; public static final String BOOLEAN_FALSE = "false"; + private Biometrics095Service biometricService; + @Inject public RegistrationServiceImpl(Context context, PacketWriterService packetWriterService, RegistrationRepository registrationRepository, @@ -110,7 +124,11 @@ public RegistrationServiceImpl(Context context, PacketWriterService packetWriter ClientCryptoManagerService clientCryptoManagerService, KeyStoreRepository keyStoreRepository, GlobalParamRepository globalParamRepository, - AuditManagerService auditManagerService) { + AuditManagerService auditManagerService, + RegistrationCenterRepository registrationCenterRepository, + LocationValidationService locationValidationService, + Provider preRegistrationDataSyncServiceProvider, + Biometrics095Service biometricService) { this.context = context; this.registrationDto = null; this.packetWriterService = packetWriterService; @@ -121,6 +139,10 @@ public RegistrationServiceImpl(Context context, PacketWriterService packetWriter this.keyStoreRepository = keyStoreRepository; this.globalParamRepository = globalParamRepository; this.auditManagerService = auditManagerService; + this.registrationCenterRepository = registrationCenterRepository; + this.locationValidationService = locationValidationService; + this.preRegistrationDataSyncServiceProvider = preRegistrationDataSyncServiceProvider; + this.biometricService = biometricService; } @Override @@ -189,33 +211,41 @@ public void submitRegistrationDto(String makerName) throws Exception { throw new ClientCheckedException(context, R.string.err_004); } + // Validate location before submission + validateLocation(); + + if (this.registrationDto.getAdditionalInfoRequestId() != null) { + String newAppId = this.registrationDto.getAdditionalInfoRequestId().split("-")[0]; + this.registrationDto.setApplicationId(newAppId); + this.registrationDto.setRId(newAppId); + } List selectedHandles = this.globalParamRepository.getSelectedHandles(); if(selectedHandles != null) { if (this.registrationDto.getFlowType().equals("NEW") || - this.registrationDto.getFlowType().equals("Update")) { + this.registrationDto.getFlowType().equals("Update")) { this.registrationDto.getDemographics().put("selectedHandles", selectedHandles); } } // try { - String individualBiometricsFieldId = this.globalParamRepository.getCachedStringGlobalParam(RegistrationConstants.INDIVIDUAL_BIOMETRICS_ID); - String serverVersion = this.globalParamRepository.getCachedStringGlobalParam(RegistrationConstants.SERVER_VERSION); - - for (String fieldName : this.registrationDto.getDemographics().keySet()) { - switch (this.registrationDto.getFlowType()) { - case "Update": - if (this.registrationDto.getDemographics().get(fieldName) != null && (this.registrationDto.getUpdatableFields().contains(fieldName) || - fieldName.equals("UIN"))) - packetWriterService.setField(this.registrationDto.getRId(), fieldName, this.registrationDto.getDemographics().get(fieldName)); - break; - case "Correction": - case "Lost": - case "NEW": - if (this.registrationDto.getDemographics().get(fieldName) != null) - packetWriterService.setField(this.registrationDto.getRId(), fieldName, this.registrationDto.getDemographics().get(fieldName)); - break; - } + String individualBiometricsFieldId = this.globalParamRepository.getCachedStringGlobalParam(RegistrationConstants.INDIVIDUAL_BIOMETRICS_ID); + String serverVersion = this.globalParamRepository.getCachedStringGlobalParam(RegistrationConstants.SERVER_VERSION); + + for (String fieldName : this.registrationDto.getDemographics().keySet()) { + switch (this.registrationDto.getFlowType()) { + case "Update": + if (this.registrationDto.getDemographics().get(fieldName) != null && (this.registrationDto.getUpdatableFields().contains(fieldName) || + fieldName.equals("UIN"))) + packetWriterService.setField(this.registrationDto.getRId(), fieldName, this.registrationDto.getDemographics().get(fieldName)); + break; + case "Correction": + case "Lost": + case "NEW": + if (this.registrationDto.getDemographics().get(fieldName) != null) + packetWriterService.setField(this.registrationDto.getRId(), fieldName, this.registrationDto.getDemographics().get(fieldName)); + break; } + } this.registrationDto.getAllDocumentFields().forEach(entry -> { Document document = new Document(); @@ -223,71 +253,83 @@ public void submitRegistrationDto(String makerName) throws Exception { document.setFormat(entry.getValue().getFormat()); document.setRefNumber(entry.getValue().getRefNumber()); document.setDocument(("pdf".equalsIgnoreCase(entry.getValue().getFormat()))?combineByteArray(entry.getValue().getContent()):convertImageToPDF(entry.getValue().getContent())); - Log.i(TAG, entry.getKey() + " >> PDF document size :" + document.getDocument().length); + packetWriterService.setDocument(this.registrationDto.getRId(), entry.getKey(), document); + packetWriterService.addMetaInfo(this.registrationDto.getRId(),"documents", document); }); - if (serverVersion!=null && serverVersion.startsWith("1.1.5")) { - this.registrationDto.getBestBiometrics(individualBiometricsFieldId, Modality.EXCEPTION_PHOTO).forEach( b -> { - Document document = new Document(); - document.setType("EOP"); - document.setFormat("jpg"); - document.setValue("POE_EOP"); - document.setDocument(convertImageToBytes(b.getBioValue())); - Log.i(TAG,"Adding Proof of Exception document with size :" + document.getDocument().length); - packetWriterService.setDocument(this.registrationDto.getRId(), "proofOfException", document); - }); - } - - this.registrationDto.CAPTURED_BIO_FIELDS.forEach( field -> { - BiometricRecord biometricRecord = getBiometricRecord(field, serverVersion); - biometricRecord.getSegments().removeIf(Objects::isNull); - packetWriterService.setBiometric(this.registrationDto.getRId(), field, biometricRecord); + if (serverVersion!=null && serverVersion.startsWith("1.1.5")) { + this.registrationDto.getBestBiometrics(individualBiometricsFieldId, Modality.EXCEPTION_PHOTO).forEach( b -> { + Document document = new Document(); + document.setType("EOP"); + document.setFormat("jpg"); + document.setValue("POE_EOP"); + document.setDocument(convertImageToBytes(b.getBioValue())); + packetWriterService.setDocument(this.registrationDto.getRId(), "proofOfException", document); }); + } - CenterMachineDto centerMachineDto = this.masterDataService.getRegistrationCenterMachineDetails(); + // Process biometrics and add metadata to packet + setBiometrics(this.registrationDto); - packetWriterService.addAudits(this.registrationDto.getRId(), getAudits()); - addMetaInfoMap(centerMachineDto.getCenterId(), centerMachineDto.getMachineId(), makerName); + CenterMachineDto centerMachineDto = this.masterDataService.getRegistrationCenterMachineDetails(); - String containerPath = packetWriterService.persistPacket(this.registrationDto.getRId(), - this.registrationDto.getSchemaVersion().toString(), - identitySchemaRepository.getSchemaJson(context, this.registrationDto.getSchemaVersion()), - SOURCE, - this.registrationDto.getProcess(), - true, centerMachineDto.getMachineRefId()); + packetWriterService.addAudits(this.registrationDto.getRId(), getAudits()); + addMetaInfoMap(centerMachineDto.getCenterId(), centerMachineDto.getMachineId(), makerName); - Log.i(TAG, "Packet created : " + containerPath); + String containerPath = packetWriterService.persistPacket(this.registrationDto.getRId(), + this.registrationDto.getSchemaVersion().toString(), + identitySchemaRepository.getSchemaJson(context, this.registrationDto.getSchemaVersion()), + SOURCE, + this.registrationDto.getProcess(), + true, centerMachineDto.getMachineRefId()); - if (containerPath == null || containerPath.trim().isEmpty()) { - throw new ClientCheckedException(context, R.string.err_005); - } - JSONObject additionalInfo = new JSONObject(); - additionalInfo.put("langCode", this.registrationDto.getSelectedLanguages().get(0)); - //TODO add name, phone and email in additional info - List fullName = new ArrayList<>(); - String fullNameKey = getKey(this.registrationDto, RegistrationConstants.UI_SCHEMA_SUBTYPE_FULL_NAME); - if(fullNameKey != null) { - List fullNameKeys = Arrays.asList(fullNameKey.split(RegistrationConstants.COMMA)); - for (String key : fullNameKeys) { - Object fullNameObj = this.registrationDto.getDemographics().get(key); - fullName.add(getAdditionalInfo(fullNameObj)); - } + + if (containerPath != null || !containerPath.trim().isEmpty()) { + String packetId = containerPath.substring(containerPath.lastIndexOf("/") + 1); + packetId = packetId.replace(".zip", ""); + this.registrationDto.setPacketId(packetId); + } + + JSONObject additionalInfo = new JSONObject(); + additionalInfo.put("langCode", this.registrationDto.getSelectedLanguages().get(0)); + //TODO add name, phone and email in additional info + List fullName = new ArrayList<>(); + String fullNameKey = getKey(this.registrationDto, RegistrationConstants.UI_SCHEMA_SUBTYPE_FULL_NAME); + if(fullNameKey != null) { + List fullNameKeys = Arrays.asList(fullNameKey.split(RegistrationConstants.COMMA)); + for (String key : fullNameKeys) { + Object fullNameObj = this.registrationDto.getDemographics().get(key); + fullName.add(getAdditionalInfo(fullNameObj)); } + } - Object emailObj = this.registrationDto.getDemographics().get(getKey(this.registrationDto, RegistrationConstants.UI_SCHEMA_SUBTYPE_EMAIL)); - Object phoneObj = this.registrationDto.getDemographics().get(getKey(this.registrationDto, RegistrationConstants.UI_SCHEMA_SUBTYPE_PHONE)); + Object emailObj = this.registrationDto.getDemographics().get(getKey(this.registrationDto, RegistrationConstants.UI_SCHEMA_SUBTYPE_EMAIL)); + Object phoneObj = this.registrationDto.getDemographics().get(getKey(this.registrationDto, RegistrationConstants.UI_SCHEMA_SUBTYPE_PHONE)); - additionalInfo.put("name", String.join(" ", fullName)); - additionalInfo.put("email", getAdditionalInfo(emailObj)); - additionalInfo.put("phone", getAdditionalInfo(phoneObj)); - - registrationRepository.insertRegistration(this.registrationDto.getRId(), containerPath, - centerMachineDto.getCenterId(), this.registrationDto.getProcess(), additionalInfo); + additionalInfo.put("name", String.join(" ", fullName)); + additionalInfo.put("email", getAdditionalInfo(emailObj)); + additionalInfo.put("phone", getAdditionalInfo(phoneObj)); + + registrationRepository.insertRegistration(this.registrationDto.getPacketId(), containerPath, + centerMachineDto.getCenterId(), this.registrationDto.getProcess(), additionalInfo, this.registrationDto.getAdditionalInfoRequestId(), this.registrationDto.getRId(), this.registrationDto.getApplicationId()); + + // Delete pre-registration record after successful packet creation + if (this.registrationDto.getPreRegistrationId() != null + && !this.registrationDto.getPreRegistrationId().trim().isEmpty()) { + + ResponseDto responseDTO = new ResponseDto(); + List preRegistrationLists = new ArrayList<>(); + PreRegistrationList preRegistrationList = preRegistrationDataSyncServiceProvider.get() + .getPreRegistrationRecordForDeletion( + this.registrationDto.getPreRegistrationId()); + preRegistrationLists.add(preRegistrationList); + preRegistrationDataSyncServiceProvider.get().deletePreRegRecords(responseDTO, preRegistrationLists); + } // } finally { - clearRegistration(); + clearRegistration(); // } } @@ -411,7 +453,9 @@ private void addMetaInfoMap(String centerId, String machineId, String makerId) t metaData.put(PacketManagerConstant.META_CENTER_ID, centerId); metaData.put(PacketManagerConstant.META_KEYINDEX, this.clientCryptoManagerService.getClientKeyIndex()); metaData.put(PacketManagerConstant.META_REGISTRATION_ID, rid); - metaData.put(PacketManagerConstant.META_APPLICATION_ID, rid); + String appIdForMeta = this.registrationDto.getApplicationId() == null || this.registrationDto.getApplicationId().trim().isEmpty() + ? rid : this.registrationDto.getApplicationId(); + metaData.put(PacketManagerConstant.META_APPLICATION_ID, appIdForMeta); metaData.put(PacketManagerConstant.META_CREATION_DATE, DateUtils.formatToISOString(LocalDateTime.now(ZoneOffset.UTC))); metaData.put(PacketManagerConstant.META_CLIENT_VERSION, BuildConfig.CLIENT_VERSION); @@ -435,16 +479,26 @@ private void addMetaInfoMap(String centerId, String machineId, String makerId) t packetWriterService.addMetaInfo(rid, PacketManagerConstant.META_INFO_OPERATIONS_DATA, getLabelValueDTOListString(metaData)); //other metaInfo - packetWriterService.addMetaInfo(rid, PacketManagerConstant.META_LATITUDE, "null"); - packetWriterService.addMetaInfo(rid, PacketManagerConstant.META_LONGITUDE, "null"); + GeoLocationDto geoLocation = this.registrationDto.getGeoLocationDto(); + packetWriterService.addMetaInfo(rid, PacketManagerConstant.META_LATITUDE, + geoLocation != null ? String.valueOf(geoLocation.getLatitude()) : "null"); + packetWriterService.addMetaInfo(rid, PacketManagerConstant.META_LONGITUDE, + geoLocation != null ? String.valueOf(geoLocation.getLongitude()) : "null"); packetWriterService.addMetaInfo(rid, "checkSum", "{}"); packetWriterService.addMetaInfo(rid, PacketManagerConstant.REGISTRATIONID, rid); //biometric device details List> capturedRegisteredDevices = new ArrayList<>(); - for(Modality modality : this.registrationDto.BIO_DEVICES.keySet()) { - capturedRegisteredDevices.add((Map) this.registrationDto.BIO_DEVICES.get(modality)); + for(Modality modality : this.biometricService.BIO_DEVICES.keySet()) { + Map deviceInfo = (Map) this.biometricService.BIO_DEVICES.get(modality); + if (deviceInfo != null) { + capturedRegisteredDevices.add(deviceInfo); + } else { + Log.w(TAG, "Device info is null for modality: " + modality); + } } + + packetWriterService.addMetaInfo(rid, "capturedRegisteredDevices", capturedRegisteredDevices); } @@ -459,6 +513,89 @@ private List> getLabelValueDTOListString(Map return labelValueMap; } + /** + * Validate machine location against registration center + * @throws Exception if location is outside allowed distance + */ + private void validateLocation() throws Exception { + try { + + String enableFlag = globalParamRepository.getCachedStringGpsDeviceEnableFlag(); + boolean gpsValidationDisabled = "Y".equalsIgnoreCase(enableFlag); + if (gpsValidationDisabled) { + Log.w(TAG, "GPS distance validation disabled by config, skipping"); + return; + } + + GeoLocationDto geoLocation = this.registrationDto.getGeoLocationDto(); + if (geoLocation == null) { + Log.w(TAG, "Geo location not available, skipping validation"); + return; + } + + // Get center coordinates + CenterMachineDto centerMachineDto = masterDataService.getRegistrationCenterMachineDetails(); + if (centerMachineDto == null) { + Log.w(TAG, "Center details not found, skipping distance validation"); + return; + } + + List centers = registrationCenterRepository.getRegistrationCenter( + centerMachineDto.getCenterId()); + + if (centers == null || centers.isEmpty()) { + Log.w(TAG, "Center not found, skipping distance validation"); + return; + } + + RegistrationCenter center = centers.get(0); + String centerLatStr = center.getLatitude(); + String centerLonStr = center.getLongitude(); + + if (centerLatStr == null || centerLonStr == null || + centerLatStr.isEmpty() || centerLonStr.isEmpty()) { + Log.e(TAG, "Center coordinates not available"); + throw new ClientCheckedException(context, R.string.err_004); + } + + try { + double centerLatitude = Double.parseDouble(centerLatStr); + double centerLongitude = Double.parseDouble(centerLonStr); + + // Calculate distance + double distance = locationValidationService.getDistance( + geoLocation.getLongitude(), geoLocation.getLatitude(), + centerLongitude, centerLatitude); + + // Get max allowed distance from config + String maxDistanceStr = globalParamRepository.getCachedStringMachineToCenterDistance(); + if (maxDistanceStr == null || maxDistanceStr.isEmpty()) { + Log.e(TAG, "Max allowed distance configuration not found"); + throw new ClientCheckedException(context, R.string.err_004); + } + + double maxAllowedDistance = Double.parseDouble(maxDistanceStr); + + // Validate distance + if (distance > maxAllowedDistance) { + Log.e(TAG, "Distance not matched with allowed range"); + throw new ClientCheckedException(context, R.string.err_004); + } + + Log.i(TAG, "Location validated successfully"); + + } catch (NumberFormatException e) { + Log.e(TAG, "Invalid center coordinates format", e); + // Continue with submission even if coordinates are invalid + } + } catch (ClientCheckedException e) { + throw e; + } catch (Exception e) { + Log.e(TAG, "Location validation failed: " + e.getMessage(), e); + // Continue with submission even if validation fails + } + } + public List> getAudits() { List> audits = new ArrayList<>(); String savedPoint = globalParamRepository.getCachedStringGlobalParam(RegistrationConstants.AUDIT_EXPORTED_TILL); @@ -623,4 +760,57 @@ private int getAttemptsCount(Modality modality) { } return 0; } + + private void setBiometrics(RegistrationDto registrationDto) throws RegBaseCheckedException { + Map> capturedBiometrics = new HashMap<>(); + Map> capturedMetaInfo = new LinkedHashMap<>(); + Map> exceptionMetaInfo = new LinkedHashMap<>(); + + for(String key : registrationDto.getBiometrics().keySet()) { + String fieldId = key.split("_")[0]; + String bioAttribute = key.split("_")[1]; + BIR bir = buildBIR(registrationDto.getBiometrics().get(key)); + if (!capturedBiometrics.containsKey(fieldId)) { + capturedBiometrics.put(fieldId, new ArrayList<>()); + } + capturedBiometrics.get(fieldId).add(bir); + if (!capturedMetaInfo.containsKey(fieldId)) { + capturedMetaInfo.put(fieldId, new HashMap<>()); + } + Map metaInfo = new HashMap<>(); + metaInfo.put("numOfRetries", registrationDto.getBiometrics().get(key).getNumOfRetries()); + metaInfo.put("isForceCaptured", registrationDto.getBiometrics().get(key).isForceCaptured()); + metaInfo.put("index", bir.getBdbInfo().getIndex()); + capturedMetaInfo.get(fieldId).put(bioAttribute, metaInfo); + } + + for(String key : registrationDto.EXCEPTIONS.keySet()) { + String fieldId = key.split("_")[0]; + String bioAttribute = key.split("_")[1]; + registrationDto.EXCEPTIONS.get(key).forEach((v) -> { + Modality modality = Modality.getModality(v); + BiometricsDto exceptionBiometricDto = new BiometricsDto( + modality.getSingleType().value(), + Modality.getSpecBioSubType(v), + null, null, true, null, null, false, 0, 0.0, 0.0f); + BIR bir = buildBIR(exceptionBiometricDto); + capturedBiometrics.getOrDefault(fieldId, new ArrayList<>()).add(bir); + }); + exceptionMetaInfo.computeIfAbsent(fieldId, field -> new HashMap<>()).put(bioAttribute, + registrationDto.EXCEPTIONS.get(key)); + } + + capturedBiometrics.keySet().forEach(fieldId -> { + BiometricRecord biometricRecord = new BiometricRecord(); + biometricRecord.setOthers(new HashMap<>()); + biometricRecord.getOthers().put(OTHER_KEY_CONFIGURED, String.join(",", + registrationDto.CAPTURED_BIO_FIELDS.contains(fieldId) ? Collections.singletonList(fieldId) : Collections.EMPTY_LIST)); + biometricRecord.setSegments(capturedBiometrics.get(fieldId)); + Log.d(TAG, "Adding biometric to packet manager for field : " + fieldId); + packetWriterService.setBiometric(registrationDto.getRId(), fieldId, biometricRecord); + }); + + packetWriterService.addMetaInfo(registrationDto.getRId(), "biometrics", capturedMetaInfo); + packetWriterService.addMetaInfo(registrationDto.getRId(), "exceptionBiometrics", exceptionMetaInfo); + } } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/external/PreRegZipHandlingService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/external/PreRegZipHandlingService.java index 7e0da3658..1b53ae985 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/external/PreRegZipHandlingService.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/external/PreRegZipHandlingService.java @@ -19,7 +19,7 @@ public interface PreRegZipHandlingService { PreRegistrationDto encryptAndSavePreRegPacket(String preRegistrationId, String preRegPacket, CenterMachineDto centerMachineDto) ; - String storePreRegPacketToDisk(String preRegistrationId, byte[] encryptedPacket, CenterMachineDto centerMachineDto) throws RegBaseCheckedException, RegBaseUncheckedException; + String storePreRegPacketToDisk(String preRegistrationId, byte[] encryptedPacket, CenterMachineDto centerMachineDto) throws RegBaseUncheckedException; byte[] decryptPreRegPacket(String symmetricKey, byte[] encryptedPacket) throws Exception; diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/LocalConfigService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/LocalConfigService.java new file mode 100644 index 000000000..db3266aa3 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/LocalConfigService.java @@ -0,0 +1,25 @@ +package io.mosip.registration.clientmanager.spi; + +import java.util.List; +import java.util.Map; + +/** + * Service interface for managing local configurations + */ +public interface LocalConfigService { + + /** + * Get all local configurations as a map (name -> value) + */ + Map getLocalConfigurations(); + + /** + * Modify configurations by saving local preferences + */ + void modifyConfigurations(Map localPreferences); + + /** + * Get permitted configuration names + */ + List getPermittedConfiguration(); +} diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/LocationValidationService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/LocationValidationService.java new file mode 100644 index 000000000..86f937c14 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/LocationValidationService.java @@ -0,0 +1,22 @@ +package io.mosip.registration.clientmanager.spi; + +/** + * Service interface for location validation and distance calculation + * + * @author sachin sp + */ +public interface LocationValidationService { + + /** + * Calculate distance between two GPS coordinates using Haversine formula + * + * @param machineLongitude Machine's current longitude + * @param machineLatitude Machine's current latitude + * @param centerLongitude Registration center's longitude + * @param centerLatitude Registration center's latitude + * @return Distance in kilometers + */ + double getDistance(double machineLongitude, double machineLatitude, + double centerLongitude, double centerLatitude); +} + diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/MasterDataService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/MasterDataService.java index eebf857f3..85dce0d45 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/MasterDataService.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/MasterDataService.java @@ -8,6 +8,7 @@ import io.mosip.registration.clientmanager.entity.Location; import java.util.List; +import java.util.Map; public interface MasterDataService { @@ -23,21 +24,22 @@ public interface MasterDataService { /** * Fetches policy key */ - void syncCertificate(Runnable onFinish, String applicationId, String referenceId, String setApplicationId, String setReferenceId, boolean isManualSync); + void syncCertificate(Runnable onFinish, String applicationId, String referenceId, String setApplicationId, String setReferenceId, boolean isManualSync, String jobId); /** * Fetches all the master data * * @throws Exception */ - void syncMasterData(Runnable onFinish, int retryNo, boolean isManualSync) throws Exception; + void syncMasterData(Runnable onFinish, int retryNo, boolean isManualSync, String jobId) throws Exception; +// void syncMasterDataWithJob(Runnable onFinish, int retryNo, boolean isManualSync, String jobId) throws Exception; /** * Fetches all the global params from the server and sync locally. * * @throws Exception */ - void syncGlobalParamsData(Runnable onFinish, boolean isManualSync) throws Exception; + void syncGlobalParamsData(Runnable onFinish, boolean isManualSync, String jobId) throws Exception; /** * Fetches latest Id schema and UI specs @@ -51,12 +53,14 @@ public interface MasterDataService { * * @throws Exception */ - void syncUserDetails(Runnable onFinish, boolean isManualSync) throws Exception; + void syncUserDetails(Runnable onFinish, boolean isManualSync, String jobId) throws Exception; - void syncCACertificates(Runnable onFinish, boolean isManualSync); + void syncCACertificates(Runnable onFinish, boolean isManualSync, String jobId); String onResponseComplete(); + void logLastSyncCompletionDateTime(String jobId); + /** * @param hierarchyLevelName @@ -143,5 +147,7 @@ public interface MasterDataService { String getGlobalParamValue(String id); + Map getRegistrationParams(); + // void downloadUrlData(Path path, JSONObject jsonObject); } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreRegistrationDataSyncService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreRegistrationDataSyncService.java index 00231c6de..cc58d9f6f 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreRegistrationDataSyncService.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreRegistrationDataSyncService.java @@ -1,11 +1,17 @@ package io.mosip.registration.clientmanager.spi; +import java.sql.Timestamp; +import java.util.List; import java.util.Map; import io.mosip.registration.clientmanager.dto.ResponseDto; -import io.mosip.registration.clientmanager.exception.ClientCheckedException; +import io.mosip.registration.clientmanager.entity.PreRegistrationList; public interface PreRegistrationDataSyncService { - public Map getPreRegistration(String preRegistrationId, boolean forceDownload); - void fetchPreRegistrationIds(Runnable onFinish); + Map getPreRegistration(String preRegistrationId, boolean forceDownload); + void fetchPreRegistrationIds(Runnable onFinish, String jobId); + ResponseDto fetchAndDeleteRecords(); + void deletePreRegRecords(ResponseDto responseDTO, List preRegList); + PreRegistrationList getPreRegistrationRecordForDeletion(String preRegistrationId); + Timestamp getLastPreRegPacketDownloadedTime(); } \ No newline at end of file diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/util/CronExpressionParser.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/util/CronExpressionParser.java new file mode 100644 index 000000000..0699ab5e7 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/util/CronExpressionParser.java @@ -0,0 +1,65 @@ +package io.mosip.registration.clientmanager.util; + +import android.util.Log; + +import com.cronutils.model.Cron; +import com.cronutils.model.CronType; +import com.cronutils.model.definition.CronDefinitionBuilder; +import com.cronutils.model.time.ExecutionTime; +import com.cronutils.parser.CronParser; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Optional; + +/** + * Simple utility for cron expression parsing and next sync time calculation + */ +public class CronExpressionParser { + + private static final CronParser cronParser = new CronParser( + CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ) + ); + + /** + * Calculates next execution time from cron expression + * @param cronExpression e.g., "0 0 11 * * ?" (Every day at 11:00 AM) + * @return Next execution time or null if invalid + */ + public static Instant getNextExecutionTime(String cronExpression) { + try { + if (cronExpression == null || cronExpression.trim().isEmpty()) { + return null; + } + + Cron cron = cronParser.parse(cronExpression.trim()); + ExecutionTime executionTime = ExecutionTime.forCron(cron); + ZonedDateTime now = ZonedDateTime.now(ZoneId.systemDefault()); + + Optional next = executionTime.nextExecution(now); + return next.map(ZonedDateTime::toInstant).orElse(null); + } catch (Exception e) { + Log.e("CronExpressionParser", "Error parsing cron expression", e); + } + return null; + } + + /** + * Validates cron expression + * @param cronExpression The cron expression to validate + * @return true if valid + */ + public static boolean isValidCronExpression(String cronExpression) { + try { + if (cronExpression == null || cronExpression.trim().isEmpty()) { + return false; + } + cronParser.parse(cronExpression.trim()); + return true; + } catch (Exception e) { + Log.e("CronExpressionParser", "Invalid cron expression", e); + } + return false; + } +} \ No newline at end of file diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/util/DateUtil.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/util/DateUtil.java index e62801d50..6c6a5881e 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/util/DateUtil.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/util/DateUtil.java @@ -4,9 +4,14 @@ import java.text.DateFormat; import java.util.Date; +import java.text.SimpleDateFormat; +import java.util.Date; public class DateUtil { + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MMM-dd"); + private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss"); + DateFormat dateFormat; DateFormat timeFormat; @@ -17,8 +22,10 @@ public DateUtil(Context context) { public String getDateTime(long dateTimeMillis) { Date date = new Date(dateTimeMillis); - String dateStr = dateFormat.format(date); - String time = timeFormat.format(date); - return String.format("%s %s", dateStr, time); + + String dateStr = DATE_FORMAT.format(date); + String timeStr = TIME_FORMAT.format(date); + + return String.format("%s %s", dateStr, timeStr); } } diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/AppModuleTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/AppModuleTest.java index 67dee3a0d..58ac77e9d 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/AppModuleTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/AppModuleTest.java @@ -11,10 +11,12 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import javax.inject.Provider; import javax.inject.Singleton; import io.mosip.registration.clientmanager.dao.ApplicantValidDocumentDao; import io.mosip.registration.clientmanager.dao.FileSignatureDao; +import io.mosip.registration.clientmanager.dao.LocalConfigDAO; import io.mosip.registration.clientmanager.dao.PreRegistrationDataSyncDao; import io.mosip.registration.clientmanager.dao.PreRegistrationDataSyncRepositoryDao; import io.mosip.registration.clientmanager.entity.PreRegistrationList; @@ -75,6 +77,7 @@ public class AppModuleTest { @Mock SyncJobDefRepository syncJobDefRepository; @Mock UserDetailRepository userDetailRepository; @Mock LanguageRepository languageRepository; + @Mock UserRoleRepository userRoleRepository; @Mock JobManagerService jobManagerService; @Mock FileSignatureDao fileSignatureDao; @Mock UserBiometricRepository userBiometricRepository; @@ -90,6 +93,16 @@ public class AppModuleTest { @Mock PreRegistrationDataSyncRepositoryDao preRegistrationDataSyncRepositoryDao; @Mock PreRegZipHandlingService preRegZipHandlingService; @Mock PreRegistrationList preRegistrationList; + @Mock + Provider preRegistrationDataSyncServiceProvider; + + @Mock Biometrics095Service biometricService; + + @Mock JobTransactionService jobTransactionService; + + @Mock PermittedLocalConfigRepository permittedLocalConfigRepository; + + @Mock LocalConfigDAO localConfigDAO; private AppModule appModule; @@ -145,7 +158,7 @@ public void testProvideMasterDataService() { registrationCenterRepository, documentTypeRepository, applicantValidDocRepository, templateRepository, dynamicFieldRepository, locationRepository, globalParamRepository, identitySchemaRepository, blocklistedWordRepository, syncJobDefRepository, userDetailRepository, certificateManagerService, - languageRepository, jobManagerService, fileSignatureDao + languageRepository, jobManagerService, fileSignatureDao, jobTransactionService, permittedLocalConfigRepository, localConfigDAO ); assertNotNull(service); assertTrue(service instanceof MasterDataServiceImpl); @@ -159,7 +172,7 @@ public void testProvideSyncRestFactory() { @Test public void testProvideLoginService() { - LoginService service = appModule.provideLoginService(clientCryptoManagerService, userDetailRepository); + LoginService service = appModule.provideLoginService(clientCryptoManagerService, userDetailRepository,userRoleRepository); assertNotNull(service); } @@ -167,7 +180,7 @@ public void testProvideLoginService() { public void testProvideRegistrationService() { RegistrationService service = appModule.provideRegistrationService( packetWriterService, registrationRepository, mock(MasterDataService.class), identitySchemaRepository, - clientCryptoManagerService, keyStoreRepository, globalParamRepository, auditManagerService + clientCryptoManagerService, keyStoreRepository, globalParamRepository, auditManagerService,preRegistrationDataSyncServiceProvider,biometricService ); assertNotNull(service); assertTrue(service instanceof RegistrationServiceImpl); diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/ClientDatabaseTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/ClientDatabaseTest.java index 32851e1fa..325711ba4 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/ClientDatabaseTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/ClientDatabaseTest.java @@ -30,21 +30,6 @@ public void tearDown() { clientDatabase.close(); } - @Test - public void testGetDatabase_SingletonInstance() { - ClientDatabase instance1 = ClientDatabase.getDatabase(context); - ClientDatabase instance2 = ClientDatabase.getDatabase(context); - assertSame(instance1, instance2); - } - - @Test - public void testDestroyDB_CreatesNewInstance() { - ClientDatabase instance1 = ClientDatabase.getDatabase(context); - ClientDatabase.destroyDB(); - ClientDatabase instance2 = ClientDatabase.getDatabase(context); - assertNotSame(instance1, instance2); - } - @Test public void testDaos_NotNull() { assertNotNull(clientDatabase.userTokenDao()); diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/RoomModuleTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/RoomModuleTest.java index 6bf5c156a..531adf075 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/RoomModuleTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/config/RoomModuleTest.java @@ -8,6 +8,7 @@ import androidx.annotation.NonNull; import androidx.room.Room; import androidx.room.RoomDatabase; +import androidx.room.migration.Migration; import androidx.security.crypto.EncryptedSharedPreferences; import androidx.security.crypto.MasterKey; @@ -42,6 +43,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import io.mosip.registration.clientmanager.config.ClientDatabaseMigrations; + @RunWith(MockitoJUnitRunner.class) public class RoomModuleTest { @@ -98,10 +101,13 @@ public void setUp() throws Exception { .thenReturn(sharedPreferences); when(builder.openHelperFactory(any(SupportFactory.class))).thenReturn(builder); + when(builder.addMigrations(any(Migration[].class))).thenReturn(builder); when(builder.allowMainThreadQueries()).thenReturn(builder); when(builder.build()).thenReturn(clientDatabase); roomModule = new RoomModule(application, applicationInfo); + + verify(builder).addMigrations(ClientDatabaseMigrations.MIGRATION_1_2); } } @@ -125,6 +131,7 @@ public void testConstructor_DebugMode_UsesDebugPassword() { .thenReturn(sharedPreferences); when(builder.openHelperFactory(any(SupportFactory.class))).thenReturn(builder); + when(builder.addMigrations(any(Migration[].class))).thenReturn(builder); when(builder.allowMainThreadQueries()).thenReturn(builder); when(builder.build()).thenReturn(clientDatabase); when(sharedPreferences.edit()).thenReturn(editor); @@ -134,6 +141,7 @@ public void testConstructor_DebugMode_UsesDebugPassword() { verify(editor).putString("db_password", BuildConfig.DEBUG_PASSWORD); verify(editor).apply(); + verify(builder).addMigrations(ClientDatabaseMigrations.MIGRATION_1_2); } } @@ -175,6 +183,7 @@ public void testConstructor_EncryptExistingDb_Success() throws Exception { .thenReturn(sharedPreferences); when(builder.openHelperFactory(any(SupportFactory.class))).thenReturn(builder); + when(builder.addMigrations(any(Migration[].class))).thenReturn(builder); when(builder.allowMainThreadQueries()).thenReturn(builder); when(builder.build()).thenReturn(clientDatabase); doNothing().when(existingDb).rawExecSQL(anyString()); @@ -190,6 +199,7 @@ public void testConstructor_EncryptExistingDb_Success() throws Exception { verify(sharedPreferences).edit(); verify(editor).putString(eq("db_password"), anyString()); verify(editor).apply(); + verify(builder).addMigrations(ClientDatabaseMigrations.MIGRATION_1_2); } } diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/jobs/ConfigDataSyncJobTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/jobs/ConfigDataSyncJobTest.java index 3983e4dcd..41e8e4168 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/jobs/ConfigDataSyncJobTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/jobs/ConfigDataSyncJobTest.java @@ -43,12 +43,12 @@ public void testTriggerJob_SuccessfulSync_ReturnsTrue() throws Exception { Runnable callback = invocation.getArgument(0); callback.run(); // Simulate callback execution return null; - }).when(masterDataService).syncGlobalParamsData(any(), anyBoolean()); + }).when(masterDataService).syncGlobalParamsData(any(), anyBoolean(), ""); boolean result = configDataSyncJob.triggerJob(jobId); assertTrue(result); - verify(masterDataService).syncGlobalParamsData(any(), anyBoolean()); + verify(masterDataService).syncGlobalParamsData(any(), anyBoolean(), ""); verify(configDataSyncJob).logJobTransaction(jobId, TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis)); } @@ -56,12 +56,12 @@ public void testTriggerJob_SuccessfulSync_ReturnsTrue() throws Exception { public void testTriggerJob_SyncThrowsException_ReturnsFalse() throws Exception { int jobId = 1; doThrow(new RuntimeException("Sync failed")) - .when(masterDataService).syncGlobalParamsData(any(), anyBoolean()); + .when(masterDataService).syncGlobalParamsData(any(), anyBoolean(), ""); boolean result = configDataSyncJob.triggerJob(jobId); assertFalse(result); - verify(masterDataService).syncGlobalParamsData(any(), anyBoolean()); + verify(masterDataService).syncGlobalParamsData(any(), anyBoolean(), ""); } @Test @@ -71,10 +71,10 @@ public void testTriggerJob_LogsStartAndCompletion() throws Exception { Runnable callback = invocation.getArgument(0); callback.run(); return null; - }).when(masterDataService).syncGlobalParamsData(any(), anyBoolean()); + }).when(masterDataService).syncGlobalParamsData(any(), anyBoolean(), ""); configDataSyncJob.triggerJob(jobId); - verify(masterDataService).syncGlobalParamsData(any(), anyBoolean()); + verify(masterDataService).syncGlobalParamsData(any(), anyBoolean(), ""); } } \ No newline at end of file diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/repository/RegistrationRepositoryTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/repository/RegistrationRepositoryTest.java index ce316116d..c505e86d9 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/repository/RegistrationRepositoryTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/repository/RegistrationRepositoryTest.java @@ -121,7 +121,7 @@ public void testInsertRegistration() throws Exception { // Execute method Registration result = registrationRepository.insertRegistration("10001155851003120250220055513", "/path/to/container", - "10011", "NEW", mockJson); + "10011", "NEW", mockJson, "", "", "34259236291839"); // Capture inserted object ArgumentCaptor captor = ArgumentCaptor.forClass(Registration.class); diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/LoginServiceTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/LoginServiceTest.java index b6b34634f..b7191a7bf 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/LoginServiceTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/LoginServiceTest.java @@ -11,6 +11,7 @@ import com.auth0.android.jwt.DecodeException; import io.mosip.registration.clientmanager.config.SessionManager; import io.mosip.registration.clientmanager.entity.UserDetail; +import io.mosip.registration.clientmanager.repository.UserRoleRepository; import io.mosip.registration.keymanager.dto.CryptoRequestDto; import io.mosip.registration.keymanager.dto.CryptoResponseDto; import org.json.JSONObject; @@ -34,6 +35,7 @@ @RunWith(MockitoJUnitRunner.class) public class LoginServiceTest { @Mock UserDetailRepository userDetailRepository; + @Mock UserRoleRepository userRoleRepository; @Mock ClientCryptoManagerService clientCryptoManagerService; @Mock Context context; LoginService loginService; @@ -42,7 +44,7 @@ public class LoginServiceTest { @Before public void setUp() { MockitoAnnotations.openMocks(this); - loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository,userRoleRepository); } @Test(expected = InvalidMachineSpecIDException.class) @@ -71,7 +73,7 @@ public void isValidUserId_WithInactiveUser_Test() { @Test public void test_save_valid_auth_token_successfully() throws Exception { - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository,userRoleRepository); Field userDetailRepositoryField = LoginService.class.getDeclaredField("userDetailRepository"); userDetailRepositoryField.setAccessible(true); @@ -97,7 +99,7 @@ public void test_save_valid_auth_token_successfully() throws Exception { @Test public void test_handle_null_token_from_repository() throws Exception { - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository,userRoleRepository); Field userDetailRepositoryField = LoginService.class.getDeclaredField("userDetailRepository"); userDetailRepositoryField.setAccessible(true); @@ -120,7 +122,7 @@ public void test_handle_null_token_from_repository() throws Exception { @Test public void test_handles_empty_token_from_repository() throws Exception { - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository,userRoleRepository); String userId = "testUser"; when(userDetailRepository.getUserAuthToken(userId)).thenReturn(""); @@ -133,7 +135,7 @@ public void test_handles_empty_token_from_repository() throws Exception { @Test public void test_handles_exceptions_from_session_manager() { - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository,userRoleRepository); String userId = "testUser"; String token = "validToken"; @@ -146,7 +148,7 @@ public void test_handles_exceptions_from_session_manager() { @Test (expected = InvalidMachineSpecIDException.class) public void test_successful_auth_token_save() throws Exception { - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository,userRoleRepository); ClientCryptoManagerService mockCryptoService = mock(ClientCryptoManagerService.class); UserDetailRepository mockUserDetailRepository = mock(UserDetailRepository.class); @@ -174,7 +176,7 @@ public void test_successful_auth_token_save() throws Exception { @Test public void test_null_crypto_response_throws_exception() { - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository,userRoleRepository); String authResponse = "encryptedAuthResponse"; String userId = "testUser"; @@ -192,7 +194,7 @@ public void test_null_crypto_response_throws_exception() { @Test public void test_clear_auth_token_successful() { - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository,userRoleRepository); try { Field sessionManagerField = LoginService.class.getDeclaredField("sessionManager"); @@ -211,7 +213,7 @@ public void test_clear_auth_token_successful() { @Test public void test_clear_auth_token_throws_exception() { - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository,userRoleRepository); try { Field sessionManagerField = LoginService.class.getDeclaredField("sessionManager"); @@ -234,7 +236,7 @@ public void test_clear_auth_token_throws_exception() { @Test public void test_session_manager_null() { - LoginService loginService = new LoginService(context, null, null); + LoginService loginService = new LoginService(context, null, null,null); Exception exception = assertThrows(Exception.class, () -> { loginService.clearAuthToken(context); @@ -245,7 +247,7 @@ public void test_session_manager_null() { @Test public void test_session_manager_returns_non_null_value() { - LoginService loginService = new LoginService(context, null, null); + LoginService loginService = new LoginService(context, null, null,null); lenient().when(sessionManager.clearAuthToken()).thenReturn("non-null-token"); @@ -262,7 +264,7 @@ public void test_validate_password_returns_true_for_valid_credentials() { String password = "validPassword"; Mockito.when(userDetailRepository.isValidPassword(userId, password)).thenReturn(true); - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository, userRoleRepository); loginService.userDetailRepository = userDetailRepository; boolean result = loginService.validatePassword(userId, password); @@ -277,7 +279,7 @@ public void test_validate_password_handles_null_userid() { String password = "somePassword"; Mockito.when(userDetailRepository.isValidPassword(userId, password)).thenReturn(false); - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository, userRoleRepository); loginService.userDetailRepository = userDetailRepository; boolean result = loginService.validatePassword(userId, password); @@ -290,7 +292,7 @@ public void test_validate_password_handles_null_userid() { public void test_password_hash_stored_successfully() { String userId = "testUser"; String password = "testPassword"; - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository, userRoleRepository); loginService.setPasswordHash(userId, password); @@ -302,7 +304,7 @@ public void test_returns_true_when_password_exists_for_valid_user() { String validUserId = "validUser"; Mockito.when(userDetailRepository.isPasswordPresent(validUserId)).thenReturn(true); - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository, userRoleRepository); boolean result = loginService.isPasswordPresent(validUserId); @@ -315,7 +317,7 @@ public void test_returns_false_when_user_id_is_null() { String nullUserId = null; Mockito.when(userDetailRepository.isPasswordPresent(nullUserId)).thenReturn(false); - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository, userRoleRepository); boolean result = loginService.isPasswordPresent(nullUserId); @@ -333,7 +335,7 @@ public void test_get_user_details_returns_user_when_valid_userid() { Mockito.when(userDetailRepository.getUserDetailByUserId(userId)).thenReturn(expectedUserDetail); - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository, userRoleRepository); loginService.userDetailRepository = userDetailRepository; UserDetail result = loginService.getUserDetailsByUserId(userId); @@ -352,7 +354,7 @@ public void test_get_user_details_with_null_userid() { Mockito.when(userDetailRepository.getUserDetailByUserId(userId)).thenReturn(null); - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository, userRoleRepository); loginService.userDetailRepository = userDetailRepository; UserDetail result = loginService.getUserDetailsByUserId(userId); @@ -365,7 +367,7 @@ public void test_get_user_details_with_null_userid() { @Test public void test_returns_true_when_user_detail_count_is_zero() { Mockito.when(userDetailRepository.getUserDetailCount()).thenReturn(0); - LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository); + LoginService loginService = new LoginService(context, clientCryptoManagerService, userDetailRepository, userRoleRepository); boolean result = loginService.isValidUserId("anyUserId"); diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/MasterDataServiceImplTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/MasterDataServiceImplTest.java index 2eed3a543..5d8effceb 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/MasterDataServiceImplTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/MasterDataServiceImplTest.java @@ -89,6 +89,8 @@ public class MasterDataServiceImplTest { @Mock private Call> mockCACall; @Mock private Call> mockUserCall; @Mock private Call mockIdSchemaCall; + @Mock private PermittedLocalConfigRepository mockPermittedLocalConfigRepository; + @Mock private LocalConfigDAO mockLocalConfigDao; private final String TEST_APP_NAME = "MockAppName"; @@ -1762,7 +1764,7 @@ public void test_syncMasterData_success() { return null; }).when(mockMasterCall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncMasterData(onFinish, 0, false); + spyService.syncMasterData(onFinish, 0, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1786,7 +1788,7 @@ public void test_syncMasterData_error() { return null; }).when(mockMasterCall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncMasterData(onFinish, 0, false); + spyService.syncMasterData(onFinish, 0, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1808,7 +1810,7 @@ public void test_syncMasterData_failure() { return null; }).when(mockMasterCall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncMasterData(onFinish, 0, false); + spyService.syncMasterData(onFinish, 0, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1829,7 +1831,7 @@ public void test_syncGlobalParamsData_success() throws Exception { return null; }).when(mockGlobalCall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncGlobalParamsData(onFinish, false); + spyService.syncGlobalParamsData(onFinish, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1847,7 +1849,7 @@ public void test_syncGlobalParamsData_error() throws Exception { return null; }).when(mockGlobalCall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncGlobalParamsData(onFinish, false); + spyService.syncGlobalParamsData(onFinish, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1866,7 +1868,7 @@ public void test_syncGlobalParamsData_failure() throws Exception { return null; }).when(mockGlobalCall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncGlobalParamsData(onFinish, false); + spyService.syncGlobalParamsData(onFinish, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1886,7 +1888,7 @@ public void test_syncCACertificates_success() { return null; }).when(mockCACall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncCACertificates(onFinish, false); + spyService.syncCACertificates(onFinish, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1905,7 +1907,7 @@ public void test_syncCACertificates_error() { return null; }).when(mockCACall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncCACertificates(onFinish, false); + spyService.syncCACertificates(onFinish, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1922,7 +1924,7 @@ public void test_syncCACertificates_failure() { return null; }).when(mockCACall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncCACertificates(onFinish, false); + spyService.syncCACertificates(onFinish, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1946,7 +1948,7 @@ public void test_syncUserDetails_success() throws Exception { return null; }).when(mockUserCall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncUserDetails(onFinish, false); + spyService.syncUserDetails(onFinish, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1967,7 +1969,7 @@ public void test_syncUserDetails_error() throws Exception { return null; }).when(mockUserCall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncUserDetails(onFinish, false); + spyService.syncUserDetails(onFinish, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -1989,7 +1991,7 @@ public void test_syncUserDetails_failure() throws Exception { return null; }).when(mockUserCall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncUserDetails(onFinish, false); + spyService.syncUserDetails(onFinish, false, ""); verify(onFinish, atLeastOnce()).run(); } @@ -2076,7 +2078,7 @@ public void test_syncGlobalParamsData_onResponseNull() throws Exception { return null; }).when(mockGlobalCall).enqueue(any()); Runnable onFinish = mock(Runnable.class); - spyService.syncGlobalParamsData(onFinish, false); + spyService.syncGlobalParamsData(onFinish, false, ""); } /** @@ -2137,7 +2139,11 @@ public void test_public_constructor_with_all_args() { mockCertificateManagerService, mockLanguageRepository, mockJobManagerService, - mockFileSignatureDao + mockFileSignatureDao, + null + mockFileSignatureDao, + mockPermittedLocalConfigRepository, + mockLocalConfigDao ); assertNotNull(service); } diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncServiceImplTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncServiceImplTest.java index 8b27df71c..1fa932a96 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncServiceImplTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/PreRegistrationDataSyncServiceImplTest.java @@ -45,7 +45,6 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.sql.Ref; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -160,7 +159,7 @@ public void fetchPreRegistrationIds_success_Test() throws IOException { service.fetchPreRegistrationIds(() -> { verify(mockContext).getString(anyInt()); - }); + }, ""); verify(mockSyncRestService).getPreRegistrationIds(any()); } @@ -172,7 +171,7 @@ public void fetchPreRegistrationIds_noCenterMachineDetails_Test() { service.fetchPreRegistrationIds(() -> { verify(mockContext).getString(anyInt()); - }); + }, ""); verify(mockSyncRestService, never()).getPreRegistrationIds(any()); } @@ -463,7 +462,7 @@ public void fetchPreRegistrationIds_handlesServiceError() { }).when(mockCall).enqueue(any()); service.fetchPreRegistrationIds(() -> { - }); + }, ""); verify(mockSyncRestService).getPreRegistrationIds(any()); } } @@ -559,7 +558,7 @@ public void fetchPreRegistrationIds_handlesFailure() { }).when(mockCall).enqueue(any()); service.fetchPreRegistrationIds(() -> { - }); + }, ""); verify(mockSyncRestService).getPreRegistrationIds(any()); } @@ -579,7 +578,7 @@ public void fetchPreRegistrationIds_handlesUnsuccessfulResponse() { }).when(mockCall).enqueue(any()); service.fetchPreRegistrationIds(() -> { - }); + }, ""); verify(mockSyncRestService).getPreRegistrationIds(any()); } @@ -975,7 +974,7 @@ public void fetchPreRegistrationIds_successfulResponse_triggersToastAndOnFinish( Runnable onFinish = () -> onFinishCalled[0] = true; // Act - service.fetchPreRegistrationIds(onFinish); + service.fetchPreRegistrationIds(onFinish, ""); // Assert // Toast.makeText should be called with "Application Id Sync Completed" @@ -1034,7 +1033,7 @@ public void fetchPreRegistrationIds_onResponse_exceptionInTryBlock_triggersError }; // Act - service.fetchPreRegistrationIds(onFinish); + service.fetchPreRegistrationIds(onFinish, ""); // Wait for onFinish to be called (with timeout) synchronized (lock) { @@ -1089,7 +1088,7 @@ public void fetchPreRegistrationIds_onResponse_serviceError_triggersErrorToastAn Runnable onFinish = () -> onFinishCalled[0] = true; // Act - service.fetchPreRegistrationIds(onFinish); + service.fetchPreRegistrationIds(onFinish, ""); // Assert Toast.makeText(mockContext, "Application Id Sync failed service error message", Toast.LENGTH_LONG); @@ -1552,4 +1551,4 @@ public void test_preparePreRegistration_allNulls() { assertTrue(e.getMessage() == null || e.getMessage().contains("getPreRegId")); } } -} +} \ No newline at end of file diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java index b0c795438..bf39680e3 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java @@ -2,9 +2,7 @@ import android.content.Context; import android.content.SharedPreferences; -import android.content.res.Resources; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.mosip.registration.clientmanager.config.SessionManager; + import io.mosip.registration.clientmanager.constant.RegistrationConstants; import io.mosip.registration.clientmanager.dto.CenterMachineDto; import io.mosip.registration.clientmanager.dto.registration.RegistrationDto; @@ -12,14 +10,17 @@ import io.mosip.registration.clientmanager.exception.ClientCheckedException; import io.mosip.registration.clientmanager.repository.GlobalParamRepository; import io.mosip.registration.clientmanager.repository.IdentitySchemaRepository; +import io.mosip.registration.clientmanager.repository.RegistrationCenterRepository; import io.mosip.registration.clientmanager.repository.RegistrationRepository; import io.mosip.registration.clientmanager.spi.AuditManagerService; +import io.mosip.registration.clientmanager.spi.LocationValidationService; import io.mosip.registration.clientmanager.spi.MasterDataService; +import io.mosip.registration.clientmanager.spi.PreRegistrationDataSyncService; import io.mosip.registration.clientmanager.spi.RegistrationService; import io.mosip.registration.keymanager.repository.KeyStoreRepository; import io.mosip.registration.keymanager.spi.ClientCryptoManagerService; import io.mosip.registration.packetmanager.spi.PacketWriterService; -import org.junit.After; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -50,6 +51,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import javax.inject.Provider; + @RunWith(MockitoJUnitRunner.class) public class RegistrationServiceImplTest { @@ -74,6 +77,14 @@ public class RegistrationServiceImplTest { @Mock private ClientCryptoManagerService clientCryptoManagerService; private RegistrationService registrationService; + @Mock + private RegistrationCenterRepository registrationCenterRepository; + @Mock + private LocationValidationService locationValidationService; + @Mock + private Provider preRegistrationDataSyncServiceProvider; + @Mock + private Biometrics095Service biometricService; @Before public void setUp() { @@ -84,9 +95,9 @@ public void setUp() { when(mockApplicationContext.getSharedPreferences(anyString(), anyInt())).thenReturn(mockSharedPreferences); registrationService = new RegistrationServiceImpl(mockApplicationContext, packetWriterService, registrationRepository, masterDataService, identitySchemaRepository, clientCryptoManagerService, - keyStoreRepository, globalParamRepository, auditManagerService); + keyStoreRepository, globalParamRepository, auditManagerService,registrationCenterRepository,locationValidationService, preRegistrationDataSyncServiceProvider, biometricService); } - + @Test(expected = ClientCheckedException.class) // Test for getRegistrationDto without starting registration public void getRegistrationDtoWithoutStartingRegistration() throws Exception { @@ -453,10 +464,10 @@ public void testGetAttemptsCount_AllCases() throws Exception { for (Modality modality : Modality.values()) { int count = (int) getAttemptsCount.invoke(registrationService, modality); if (modality == Modality.FINGERPRINT_SLAB_LEFT || - modality == Modality.FINGERPRINT_SLAB_RIGHT || - modality == Modality.FINGERPRINT_SLAB_THUMBS || - modality == Modality.IRIS_DOUBLE || - modality == Modality.FACE) { + modality == Modality.FINGERPRINT_SLAB_RIGHT || + modality == Modality.FINGERPRINT_SLAB_THUMBS || + modality == Modality.IRIS_DOUBLE || + modality == Modality.FACE) { assertEquals(2, count); } else { assertEquals(0, count); @@ -673,7 +684,7 @@ public void testSubmitRegistrationDto_UpdateFlow_UINField() throws Exception { when(globalParamRepository.getSelectedHandles()).thenReturn(null); Mockito.when(globalParamRepository.getCachedStringGlobalParam(Mockito.eq(RegistrationConstants.AUDIT_EXPORTED_TILL))).thenReturn(null); Mockito.when(globalParamRepository.getCachedStringGlobalParam(Mockito.argThat(arg -> - !RegistrationConstants.AUDIT_EXPORTED_TILL.equals(arg)))).thenReturn("1.2.3"); + !RegistrationConstants.AUDIT_EXPORTED_TILL.equals(arg)))).thenReturn("1.2.3"); // Provide a fully initialized CenterMachineDto CenterMachineDto centerMachineDto = new CenterMachineDto(); centerMachineDto.setCenterId("centerId"); @@ -683,7 +694,7 @@ public void testSubmitRegistrationDto_UpdateFlow_UINField() throws Exception { when(identitySchemaRepository.getSchemaJson(Mockito.any(), Mockito.anyDouble())).thenReturn("{}"); when(packetWriterService.persistPacket(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyString())).thenReturn("containerPath123"); Registration mockRegistration = mock(Registration.class); - when(registrationRepository.insertRegistration(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(mockRegistration); + when(registrationRepository.insertRegistration(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(), "", "", "34259236291839")).thenReturn(mockRegistration); registrationService.submitRegistrationDto("makerName"); Mockito.verify(packetWriterService).setField("RID456", "UIN", "uinValue"); @@ -714,7 +725,7 @@ public void testSubmitRegistrationDto_NewFlow_AllFields() throws Exception { when(globalParamRepository.getSelectedHandles()).thenReturn(Collections.singletonList("handle1")); Mockito.when(globalParamRepository.getCachedStringGlobalParam(Mockito.eq(RegistrationConstants.AUDIT_EXPORTED_TILL))).thenReturn(null); Mockito.when(globalParamRepository.getCachedStringGlobalParam(Mockito.argThat(arg -> - !RegistrationConstants.AUDIT_EXPORTED_TILL.equals(arg)))).thenReturn("1.2.3"); + !RegistrationConstants.AUDIT_EXPORTED_TILL.equals(arg)))).thenReturn("1.2.3"); // Provide a fully initialized CenterMachineDto CenterMachineDto centerMachineDto = new CenterMachineDto(); centerMachineDto.setCenterId("centerId"); @@ -724,7 +735,7 @@ public void testSubmitRegistrationDto_NewFlow_AllFields() throws Exception { when(identitySchemaRepository.getSchemaJson(Mockito.any(), Mockito.anyDouble())).thenReturn("{}"); when(packetWriterService.persistPacket(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyString())).thenReturn("containerPath123"); Registration mockRegistration = mock(Registration.class); - when(registrationRepository.insertRegistration(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(mockRegistration); + when(registrationRepository.insertRegistration(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(), "", "", "34259236291839")).thenReturn(mockRegistration); registrationService.submitRegistrationDto("makerName"); Mockito.verify(packetWriterService).setField("RID789", "field1", "value1"); @@ -755,7 +766,7 @@ public void testSubmitRegistrationDto_CorrectionFlow() throws Exception { when(globalParamRepository.getSelectedHandles()).thenReturn(null); Mockito.when(globalParamRepository.getCachedStringGlobalParam(Mockito.eq(RegistrationConstants.AUDIT_EXPORTED_TILL))).thenReturn(null); Mockito.when(globalParamRepository.getCachedStringGlobalParam(Mockito.argThat(arg -> - !RegistrationConstants.AUDIT_EXPORTED_TILL.equals(arg)))).thenReturn("1.2.3"); + !RegistrationConstants.AUDIT_EXPORTED_TILL.equals(arg)))).thenReturn("1.2.3"); // Provide a fully initialized CenterMachineDto CenterMachineDto centerMachineDto = new CenterMachineDto(); centerMachineDto.setCenterId("centerId"); @@ -765,7 +776,7 @@ public void testSubmitRegistrationDto_CorrectionFlow() throws Exception { when(identitySchemaRepository.getSchemaJson(Mockito.any(), Mockito.anyDouble())).thenReturn("{}"); when(packetWriterService.persistPacket(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyString())).thenReturn("containerPath123"); Registration mockRegistration = mock(Registration.class); - when(registrationRepository.insertRegistration(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(mockRegistration); + when(registrationRepository.insertRegistration(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(), "", "", "34259236291839")).thenReturn(mockRegistration); registrationService.submitRegistrationDto("makerName"); Mockito.verify(packetWriterService).setField("RID999", "field2", "value2"); @@ -796,7 +807,7 @@ public void testSubmitRegistrationDto_LostFlow() throws Exception { when(globalParamRepository.getSelectedHandles()).thenReturn(null); Mockito.when(globalParamRepository.getCachedStringGlobalParam(Mockito.eq(RegistrationConstants.AUDIT_EXPORTED_TILL))).thenReturn(null); Mockito.when(globalParamRepository.getCachedStringGlobalParam(Mockito.argThat(arg -> - !RegistrationConstants.AUDIT_EXPORTED_TILL.equals(arg)))).thenReturn("1.2.3"); + !RegistrationConstants.AUDIT_EXPORTED_TILL.equals(arg)))).thenReturn("1.2.3"); // Provide a fully initialized CenterMachineDto CenterMachineDto centerMachineDto = new CenterMachineDto(); centerMachineDto.setCenterId("centerId"); @@ -806,7 +817,7 @@ public void testSubmitRegistrationDto_LostFlow() throws Exception { when(identitySchemaRepository.getSchemaJson(Mockito.any(), Mockito.anyDouble())).thenReturn("{}"); when(packetWriterService.persistPacket(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyString())).thenReturn("containerPath123"); Registration mockRegistration = mock(Registration.class); - when(registrationRepository.insertRegistration(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(mockRegistration); + when(registrationRepository.insertRegistration(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(), "", "", "34259236291839")).thenReturn(mockRegistration); registrationService.submitRegistrationDto("makerName"); Mockito.verify(packetWriterService).setField("RID888", "field3", "value3"); @@ -837,7 +848,7 @@ public void testSubmitRegistrationDto_EmptyContainerPath() throws Exception { when(globalParamRepository.getSelectedHandles()).thenReturn(null); Mockito.when(globalParamRepository.getCachedStringGlobalParam(Mockito.eq(RegistrationConstants.AUDIT_EXPORTED_TILL))).thenReturn(null); Mockito.when(globalParamRepository.getCachedStringGlobalParam(Mockito.argThat(arg -> - !RegistrationConstants.AUDIT_EXPORTED_TILL.equals(arg)))).thenReturn("1.2.3"); + !RegistrationConstants.AUDIT_EXPORTED_TILL.equals(arg)))).thenReturn("1.2.3"); // Provide a fully initialized CenterMachineDto CenterMachineDto centerMachineDto = new CenterMachineDto(); centerMachineDto.setCenterId("centerId"); @@ -1070,7 +1081,7 @@ public void testSubmitRegistrationDto_SelectedHandles_UpdateFlow() throws Except when(identitySchemaRepository.getSchemaJson(Mockito.any(), Mockito.anyDouble())).thenReturn("{}"); when(packetWriterService.persistPacket(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyString())).thenReturn("containerPath123"); Registration mockRegistration = mock(Registration.class); - when(registrationRepository.insertRegistration(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(mockRegistration); + when(registrationRepository.insertRegistration(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(), "", "", "34259236291839")).thenReturn(mockRegistration); registrationService.submitRegistrationDto("makerName"); Mockito.verify(packetWriterService).setField("RID123", "UIN", "uinValue"); @@ -1454,4 +1465,4 @@ public void testDoPreChecksBeforeRegistration_NullCenterMachineDto() throws Exce assertTrue(cause instanceof ClientCheckedException); } } -} +} \ No newline at end of file diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncDataServiceImplTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncDataServiceImplTest.java index e3fe66b6d..e2dad939e 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncDataServiceImplTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncDataServiceImplTest.java @@ -265,7 +265,7 @@ public void test_syncMasterData_successfulResponse_noError_centerMachinePresent( Runnable onFinish = mock(Runnable.class); - spyService.syncMasterData(onFinish, 0, true); + spyService.syncMasterData(onFinish, 0, true, ""); ArgumentCaptor>> callbackCaptor = ArgumentCaptor.forClass(Callback.class); verify(mockCallClientSetting).enqueue(callbackCaptor.capture()); @@ -294,7 +294,7 @@ public void test_syncMasterData_successfulResponse_withError() throws Exception Runnable onFinish = mock(Runnable.class); - spyService.syncMasterData(onFinish, 0, true); + spyService.syncMasterData(onFinish, 0, true, ""); ArgumentCaptor>> callbackCaptor = ArgumentCaptor.forClass(Callback.class); verify(mockCallClientSetting).enqueue(callbackCaptor.capture()); @@ -321,7 +321,7 @@ public void test_syncMasterData_unsuccessfulResponse() throws Exception { Runnable onFinish = mock(Runnable.class); - spyService.syncMasterData(onFinish, 0, true); + spyService.syncMasterData(onFinish, 0, true, ""); ArgumentCaptor>> callbackCaptor = ArgumentCaptor.forClass(Callback.class); verify(mockCallClientSetting).enqueue(callbackCaptor.capture()); @@ -340,7 +340,7 @@ public void test_syncMasterData_onFailure() throws Exception { Runnable onFinish = mock(Runnable.class); - spyService.syncMasterData(onFinish, 0, true); + spyService.syncMasterData(onFinish, 0, true, ""); ArgumentCaptor>> callbackCaptor = ArgumentCaptor.forClass(Callback.class); verify(mockCallClientSetting).enqueue(callbackCaptor.capture()); @@ -358,7 +358,7 @@ public void test_syncMasterData_recursiveRetry() throws Exception { Runnable onFinish = mock(Runnable.class); - spyService.syncMasterData(onFinish, 0, true); + spyService.syncMasterData(onFinish, 0, true,""); ArgumentCaptor>> callbackCaptor = ArgumentCaptor.forClass(Callback.class); verify(mockCallClientSetting).enqueue(callbackCaptor.capture()); @@ -390,7 +390,7 @@ public void test_syncGlobalParamsData_successful_noError() throws Exception { when(mockClientCryptoManagerService.getClientKeyIndex()).thenReturn("key1"); when(mockSyncRestService.getGlobalConfigs(anyString(), anyString())).thenReturn(mockCall); - spyService.syncGlobalParamsData(mockOnFinish, true); + spyService.syncGlobalParamsData(mockOnFinish, true, ""); ArgumentCaptor>>> callbackCaptor = ArgumentCaptor.forClass(Callback.class); verify(mockCall).enqueue(callbackCaptor.capture()); @@ -418,7 +418,7 @@ public void test_syncGlobalParamsData_successful_withError() throws Exception { when(mockClientCryptoManagerService.getClientKeyIndex()).thenReturn("key1"); when(mockSyncRestService.getGlobalConfigs(anyString(), anyString())).thenReturn(mockCall); - spyService.syncGlobalParamsData(mockOnFinish, true); + spyService.syncGlobalParamsData(mockOnFinish, true, ""); ArgumentCaptor>>> callbackCaptor = ArgumentCaptor.forClass(Callback.class); verify(mockCall).enqueue(callbackCaptor.capture()); @@ -450,7 +450,7 @@ public void test_syncGlobalParamsData_unsuccessfulResponse() throws Exception { when(mockClientCryptoManagerService.getClientKeyIndex()).thenReturn("key1"); when(mockSyncRestService.getGlobalConfigs(anyString(), anyString())).thenReturn(mockCall); - spyService.syncGlobalParamsData(mockOnFinish, true); + spyService.syncGlobalParamsData(mockOnFinish, true, ""); ArgumentCaptor>>> callbackCaptor = ArgumentCaptor.forClass(Callback.class); verify(mockCall).enqueue(callbackCaptor.capture()); @@ -473,7 +473,7 @@ public void test_syncGlobalParamsData_onFailure() throws Exception { when(mockClientCryptoManagerService.getClientKeyIndex()).thenReturn("key1"); when(mockSyncRestService.getGlobalConfigs(anyString(), anyString())).thenReturn(mockCall); - spyService.syncGlobalParamsData(mockOnFinish, true); + spyService.syncGlobalParamsData(mockOnFinish, true, ""); ArgumentCaptor>>> callbackCaptor = ArgumentCaptor.forClass(Callback.class); verify(mockCall).enqueue(callbackCaptor.capture()); @@ -540,13 +540,14 @@ public void test_handle_null_server_version() throws Exception { mockContext, mockObjectMapper, mockSyncRestService, mockClientCryptoManagerService, null, null, null, null, null, null, null, null, mockGlobalParamRepository, null, null, null, mockUserDetailRepository, - null, null, null, null + null, null, null, null, null + null, null, null, null,null,null ); Runnable mockOnFinish = mock(Runnable.class); assertThrows(NullPointerException.class, () -> { - masterDataService.syncUserDetails(mockOnFinish, true); + masterDataService.syncUserDetails(mockOnFinish, true, ""); }); } diff --git a/android/keymanager/build.gradle b/android/keymanager/build.gradle index 57401bc9c..7c8ea93c8 100644 --- a/android/keymanager/build.gradle +++ b/android/keymanager/build.gradle @@ -74,7 +74,7 @@ dependencies { // https://mvnrepository.com/artifact/commons-io/commons-io implementation group: 'commons-io', name: 'commons-io', version: '2.11.0' - implementation 'org.bouncycastle:bcprov-jdk15on:1.59' + implementation 'org.bouncycastle:bcprov-jdk15on:1.60' implementation 'org.bouncycastle:bcpkix-jdk15on:1.47' implementation 'com.madgag.spongycastle:core:1.58.0.0' diff --git a/android/packetmanager/build.gradle b/android/packetmanager/build.gradle index 344705ee9..8d4a6bfa7 100644 --- a/android/packetmanager/build.gradle +++ b/android/packetmanager/build.gradle @@ -78,7 +78,7 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3' - implementation 'commons-io:commons-io:2.11.0' + implementation 'commons-io:commons-io:2.14.0' implementation 'org.apache.commons:commons-lang3:3.12.0' diff --git a/android/packetmanager/src/main/java/io/mosip/registration/packetmanager/service/PacketWriterServiceImpl.java b/android/packetmanager/src/main/java/io/mosip/registration/packetmanager/service/PacketWriterServiceImpl.java index 734954ce7..672875e4e 100644 --- a/android/packetmanager/src/main/java/io/mosip/registration/packetmanager/service/PacketWriterServiceImpl.java +++ b/android/packetmanager/src/main/java/io/mosip/registration/packetmanager/service/PacketWriterServiceImpl.java @@ -13,7 +13,10 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.NoSuchAlgorithmException; +import java.time.LocalDateTime; import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -65,6 +68,7 @@ public class PacketWriterServiceImpl implements PacketWriterService { private String defaultProviderVersion; private Context context; private String timeFormat; + private String zipDatetimePattern = "yyyyMMddHHmmss"; @Inject public PacketWriterServiceImpl(Context appContext, PacketManagerHelper packetManagerHelper, @@ -72,6 +76,10 @@ public PacketWriterServiceImpl(Context appContext, PacketManagerHelper packetMan this.context = appContext; this.packetKeeper = packetKeeper; this.packetManagerHelper = packetManagerHelper; + String pattern = ConfigService.getProperty("packetmanager.zip.datetime.pattern", context); + if (pattern != null && !pattern.isEmpty()) { + this.zipDatetimePattern = pattern; + } } public RegistrationPacket initialize(String id) { @@ -129,11 +137,20 @@ public String persistPacket(String id, String version, String schemaJson, String return null; } + private String generatePacketId(String id, String refId) { + return id + "-" + refId + "-" + this.getCurrentTimeStamp(); + } + + private String getCurrentTimeStamp() { + DateTimeFormatter format = DateTimeFormatter.ofPattern(this.zipDatetimePattern); + return LocalDateTime.now(ZoneId.of("UTC")).format(format); + } + private String createPacket(String id, String version, String schemaJson, String source, String process, boolean offlineMode, String refId) throws Exception { - Log.i(TAG, "Started packet creation"); if (this.registrationPacket == null || !registrationPacket.getRegistrationId().equalsIgnoreCase(id)) throw new Exception("Registration packet is null or registration id does not exists"); + String packetId = generatePacketId(id, refId); String containerPath = null; Map> identityProperties = loadSchemaFields(schemaJson); @@ -141,7 +158,6 @@ private String createPacket(String id, String version, String schemaJson, String try { int counter = 1; for (String subPacketName : identityProperties.keySet()) { - Log.i(TAG, "Started Subpacket: " + subPacketName); List schemaFields = identityProperties.get(subPacketName); byte[] subpacketBytes = createSubpacket(Double.valueOf(version), schemaFields, defaultSubpacketName.equalsIgnoreCase(subPacketName), @@ -150,7 +166,7 @@ private String createPacket(String id, String version, String schemaJson, String PacketInfo packetInfo = new PacketInfo(); packetInfo.setProviderName(this.getClass().getSimpleName()); packetInfo.setSchemaVersion(new Double(version).toString()); - packetInfo.setId(id); + packetInfo.setId(packetId); packetInfo.setRefId(refId); packetInfo.setSource(source); packetInfo.setProcess(process); @@ -162,12 +178,11 @@ private String createPacket(String id, String version, String schemaJson, String packet.setPacketInfo(packetInfo); packet.setPacket(subpacketBytes); packetKeeper.putPacket(packet); - Log.i(TAG, "Completed SubPacket Creation"); if (counter == identityProperties.keySet().size()) { containerPath = packetKeeper.pack(packetInfo.getId(), packetInfo.getSource(), packetInfo.getProcess(), refId); if (containerPath == null) { - packetKeeper.deletePacket(id, source, process); + packetKeeper.deletePacket(packetId, source, process); throw new Exception("Failed to pack the created zip"); } } @@ -181,7 +196,6 @@ private String createPacket(String id, String version, String schemaJson, String } finally { this.registrationPacket = null; } - Log.i(TAG, "Exiting packet creation"); return containerPath; } diff --git a/assets/images/Group 57548@2x.png b/assets/images/Exception_Mark.png similarity index 100% rename from assets/images/Group 57548@2x.png rename to assets/images/Exception_Mark.png diff --git a/assets/images/Group 57745@2x.png b/assets/images/Green_Check.png similarity index 100% rename from assets/images/Group 57745@2x.png rename to assets/images/Green_Check.png diff --git a/assets/l10n/app_ar.arb b/assets/l10n/app_ar.arb index 83fc9b4e3..c2af4d6e1 100644 --- a/assets/l10n/app_ar.arb +++ b/assets/l10n/app_ar.arb @@ -56,6 +56,12 @@ "network_error": "لم يتم العثور على شبكة!", "delete": "يمسح", "newRegistrationProcess": "{language, select, eng{New Registration} ara{تسجيل جديد} fra{Nouvelle inscription} other{New Registration}}", + "inactive_logout_heading": "لقد كنت خاملاً", + "inactive_logout_description": "لأسباب أمنية، سيتم تسجيل خروجك في $TIMER", + "seconds": " ثواني.", + "minutes": " دقائق.", + "logout_button": "تسجيل الخروج", + "stay_logged_in_button": "البقاء مسجلاً الدخول", "@newRegistrationProcess": { "description": "New Registration Process Header", "placeholders": { @@ -169,6 +175,7 @@ }, "client_status": " حالة العميل", "server_status": "حالة الملقم", + "review_status": "حالة المراجعة", "clear_filter": "مرشح واضح", "serial_number": "س.ن", "application_id": "رقم الاستمارة", @@ -179,6 +186,7 @@ "system_storage_usage": "استخدام تخزين النظام", "download_pre_registration_data": "تنزيل بيانات التسجيل المسبق", "update_operator_biomterics": "تحديث القياسات الحيوية للمشغل", + "onboard_operator_biomterics": "تسجيل القياسات الحيوية للمشغل", "appliction_upload": "تحميل التطبيق", "check_updates": "تفقد التحديث", "center_remap_sync": "مركز إعادة رسم خريطة المزامنة.", @@ -292,6 +300,8 @@ "supervisor": "مشرف", "online": "متصل", "offline": "غير متصل على الانترنت", + "enabled": "ممكّن", + "disabled": "معطل", "logout_success": "لقد تم تسجيل بنجاح!", "logout_failure": "حدث خطأ ما، يرجى المحاولة مرة أخرى بعد مرور بعض الوقت", "go_to_home": "اذهب إلى المنزل", @@ -311,5 +321,16 @@ "okay": "تمام", "no_internet_connection": "لا يوجد اتصال بالإنترنت!", "connect_and_retry": "يرجى الاتصال بالإنترنت وإعادة المحاولة.", - "retry": "إعادة المحاولة" + "retry": "إعادة المحاولة", + "key": "مفتاح", + "server_value": "قيمة الخادم", + "local_value": "القيمة المحلية", + "no_configuration_parameters_found": "لم يتم العثور على معلمات التكوين", + "no_configurations_found": "لم يتم العثور على أي تكوين", + "confirm": "تأكيد", + "scan_now": "امسح الآن", + "enter_additional_info_req_id": "أدخل معرف طلب المعلومات الإضافية", + "additional_info_req_id": "معرف طلب المعلومات الإضافية", + "no_access_to_this_page": "ليست لديك صلاحية الوصول إلى هذه الصفحة", + "scheduled_job_settings": "إعدادات المهمة المجدولة" } \ No newline at end of file diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index 8c64c08e3..997e2b7e5 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -56,6 +56,12 @@ "network_error": "No network found!", "delete": "DELETE", "newRegistrationProcess": "{language, select, eng{New Registration} ara{تسجيل جديد} fra{Nouvelle inscription} other{New Registration}}", + "inactive_logout_heading": "You have been idle", + "inactive_logout_description": "For security reasons, you'll be logged out in $TIMER", + "seconds": " Seconds.", + "minutes": " Minutes.", + "logout_button": "LOG OUT", + "stay_logged_in_button": "STAY LOGGED IN", "@newRegistrationProcess": { "description": "New Registration Process Header", "placeholders": { @@ -180,6 +186,7 @@ "system_storage_usage": "System Storage Usage", "download_pre_registration_data": "Download Pre-Registration Data", "update_operator_biomterics": "Update Operator Biometrics", + "onboard_operator_biomterics":"Onboard Operator Biometrics", "appliction_upload": "Application Upload", "check_updates": "Check Updates", "center_remap_sync": "Center Remap Sync.", @@ -293,6 +300,8 @@ "supervisor": "Supervisor", "online": "Online", "offline": "Offline", + "enabled": "Enabled", + "disabled": "Disabled", "logout_success": "You have been successfully logged out!", "logout_failure": "Something went wrong, please try again after some time", "go_to_home": "Go To Home", @@ -312,5 +321,16 @@ "okay": "OKAY", "no_internet_connection": "No Internet Connection!", "connect_and_retry": "Please connect with internet and retry.", - "retry": "RETRY" + "retry": "RETRY", + "key": "Key", + "server_value": "Server Value", + "local_value": "Local Value", + "no_configuration_parameters_found": "No configuration parameters found", + "no_configurations_found": "No configurations found", + "confirm": "CONFIRM", + "scan_now": "Scan Now", + "enter_additional_info_req_id": "Enter Additional Info Request ID", + "additional_info_req_id": "Additional Info Request ID", + "scheduled_job_settings": "Scheduled Job Settings", + "no_access_to_this_page": "You don't have access to this page" } \ No newline at end of file diff --git a/assets/l10n/app_fr.arb b/assets/l10n/app_fr.arb index b00454270..d4bc2f01c 100644 --- a/assets/l10n/app_fr.arb +++ b/assets/l10n/app_fr.arb @@ -56,6 +56,12 @@ "network_error": "Aucun réseau trouvé!", "delete": "SUPPRIMER", "newRegistrationProcess": "{language, select, eng{New Registration} ara{تسجيل جديد} fra{Nouvelle inscription} other{New Registration}}", + "inactive_logout_heading": "Tu as été inactif", + "inactive_logout_description": "Pour des raisons de sécurité, vous serez déconnecté de $TIMER", + "seconds": " Seconds.", + "minutes": " Minutes.", + "logout_button": "LOG OUT", + "stay_logged_in_button": "DÉCONNEXION", "@newRegistrationProcess": { "description": "New Registration Process Header", "placeholders": { @@ -170,6 +176,7 @@ "client_status": "Statut du client", "server_status": "État du serveur", "clear_filter": "Effacer le filtre", + "review_status": "Statut d'examen", "serial_number": "S.non", "application_id": "ID d'application", "reg_date": "Rég. Date", @@ -254,6 +261,7 @@ "packets_created": "Paquets créés", "packets_synced": "Paquets synchronisés", "packets_uploaded": "Paquets téléchargés", + "onboard_operator_biomterics": "Enrôler les données biométriques de l'opérateur", "users": "Utilisatrices", "user_id": "ID de l'utilisateur", "user_name": "Nom d'utilisateur", @@ -292,6 +300,8 @@ "supervisor": "Superviseur", "online": "En ligne", "offline": "Hors ligne", + "enabled": "Activé", + "disabled": "Désactivé", "logout_success": "Vous avez été déconnecté avec succès!", "logout_failure": "Quelque chose s'est mal passé, veuillez réessayer après un certain temps", "go_to_home": "Aller à la maison", @@ -311,5 +321,16 @@ "okay": "D'ACCORD", "no_internet_connection": "Pas de connexion Internet!", "connect_and_retry": "Veuillez vous connecter à Internet et réessayer.", - "retry": "RECOMMENCEZ" + "retry": "RECOMMENCEZ", + "key": "Clé", + "server_value": "Valeur du serveur", + "local_value": "Valeur locale", + "no_configuration_parameters_found": "Aucun paramètre de configuration trouvé", + "no_configurations_found": "Aucune configuration trouvée", + "confirm": "Confirmer", + "scan_now": "Analyser maintenant", + "enter_additional_info_req_id": "Entrez l'identifiant de la demande d'information supplémentaire", + "additional_info_req_id": "Identifiant de la demande d'information supplémentaire", + "no_access_to_this_page": "Vous n'avez pas accès à cette page", + "scheduled_job_settings": "Paramètres des tâches planifiées" } \ No newline at end of file diff --git a/assets/l10n/app_hi.arb b/assets/l10n/app_hi.arb index eaf5db9b1..81f89da74 100644 --- a/assets/l10n/app_hi.arb +++ b/assets/l10n/app_hi.arb @@ -56,6 +56,12 @@ "network_error": "कोई नेटवर्क नहीं मिला!", "delete": "मिटाना", "newRegistrationProcess": "{language, select, eng{New Registration} ara{تسجيل جديد} fra{Nouvelle inscription} other{New Registration}}", + "inactive_logout_heading": "तुम निष्क्रिय हो गए हो", + "inactive_logout_description": "ससुरक्षा कारणों से, आपको $TIMER में लॉग आउट कर दिया जाएगा", + "seconds": " सेकंड.", + "minutes": " मिनट.", + "logout_button": "लॉग आउट", + "stay_logged_in_button": "लॉग इन रहें", "@newRegistrationProcess": { "description": "New Registration Process Header", "placeholders": { @@ -170,6 +176,7 @@ "client_status": "ग्राहक स्थिति", "server_status": "सर्वर की स्थिति", "clear_filter": "फ़िल्टर साफ़ करें", + "review_status": "समीक्षा स्थिति", "serial_number": "क्र.सं", "application_id": "आवेदन पहचान पत्र", "reg_date": "रजि. तारीख", @@ -236,6 +243,7 @@ "is_not_allowed": "अनुमति नहीं है", "capture": "कब्जा", "attempts_left": "प्रयास बाकी हैं", + "onboard_operator_biomterics": "ऑपरेटर बायोमेट्रिक्स ऑनबोर्ड करें", "left_hand": "बाएं हाथ", "left": "बाएं", "right": "दाएं", @@ -292,6 +300,8 @@ "supervisor": "पर्यवेक्षक", "online": "ऑनलाइन", "offline": "ऑफलाइन", + "enabled": "सक्षम", + "disabled": "अक्षम", "logout_success": "आप सफलता पूर्वक लॉगआउट कर चुके हैं!", "logout_failure": "कुछ गलती हो गई है, कृपया कुछ समय बाद पुनः प्रयास करें", "go_to_home": "होम पर जाएं", @@ -311,5 +321,16 @@ "okay": "ठीक है", "no_internet_connection": "कोई इंटरनेट कनेक्शन नहीं!", "connect_and_retry": "कृपया इंटरनेट से जुड़ें और पुनः प्रयास करें।", - "retry": "पुन: प्रयास" + "retry": "पुन: प्रयास", + "key": "कुंजी", + "server_value": "सर्वर मान", + "local_value": "स्थानीय मान", + "no_configuration_parameters_found": "कोई कॉन्फ़िगरेशन पैरामीटर नहीं मिला", + "no_configurations_found": "कोई कॉन्फ़िगरेशन नहीं मिला", + "confirm": "पुष्टि करें", + "scan_now": "अभी स्कैन करें", + "enter_additional_info_req_id": "अतिरिक्त जानकारी अनुरोध आईडी दर्ज करें", + "additional_info_req_id": "अतिरिक्त जानकारी अनुरोध आईडी", + "no_access_to_this_page": "आपको इस पृष्ठ तक पहुँच नहीं है", + "scheduled_job_settings": "अनुसूचित कार्य सेटिंग्स" } \ No newline at end of file diff --git a/assets/l10n/app_kn.arb b/assets/l10n/app_kn.arb index f82673aa0..8811d54b1 100644 --- a/assets/l10n/app_kn.arb +++ b/assets/l10n/app_kn.arb @@ -56,6 +56,12 @@ "network_error": "ಯಾವುದೇ ನೆಟ್ವರ್ಕ್ ಸಿಗಲಿಲ್ಲ!", "delete": "ಅಳಿಸಿ", "newRegistrationProcess": "{language, select, eng{New Registration} ara{تسجيل جديد} fra{Nouvelle inscription} other{New Registration}}", + "inactive_logout_heading": "ನೀವು ಸುಮ್ಮನಿದ್ದಿರಿ", + "inactive_logout_description": "ಭದ್ರತಾ ಕಾರಣಗಳಿಗಾಗಿ, ನೀವು $TIMER ನಲ್ಲಿ ಲಾಗ್ ಔಟ್ ಆಗುತ್ತೀರಿ", + "seconds": " ಸೆಕೆಂಡುಗಳು.", + "minutes": " ನಿಮಿಷಗಳು.", + "logout_button": "ಲಾಗ್ ಔಟ್ ಮಾಡಿ", + "stay_logged_in_button": "ಲಾಗಿನ್ ಆಗಿರಿ", "@newRegistrationProcess": { "description": "New Registration Process Header", "placeholders": { @@ -170,6 +176,7 @@ "client_status": "ಕ್ಲೈಂಟ್ ಸ್ಥಿತಿ", "server_status": "ಸರ್ವರ್ ಸ್ಥಿತಿ", "clear_filter": "Clear Filter", + "review_status": "ಪುನರ್ ಪರಿಶೀಲನೆ ಸ್ಥಿತಿ", "serial_number": "ಎಸ್.ಎನ್", "application_id": "ಅಪ್ಲಿಕೇಶನ್ ಐಡಿ", "reg_date": "ರೆಗ್. ದಿನಾಂಕ", @@ -266,6 +273,7 @@ "sync_alert_text": "ಸಿಂಕ್ ಮಾಡಿ", "onboarded_successfully": "ನೀವು ಯಶಸ್ವಿಯಾಗಿ ಆನ್‌ಬೋರ್ಡ್ ಮಾಡಿರುವಿರಿ.", "operator_biometric_updated_successfully": "ಆಪರೇಟರ್ ಬಯೋಮೆಟ್ರಿಕ್ಸ್ ಅನ್ನು ಯಶಸ್ವಿಯಾಗಿ ನವೀಕರಿಸಲಾಗಿದೆ.", + "onboard_operator_biomterics": "ಆಪರೇಟರ್ ಬಯೋಮೆಟ್ರಿಕ್ಸ್ ನೋಂದಣಿ", "home": "ಮನೆ", "verify_and_save": "ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಉಳಿಸಿ", "supervisors_biometric_onboard": "ನಿರೀಕ್ಷಕರ ಬಯೋಮೆಟ್ರಿಕ್ ಔನ್‌ಬೋರ್ಡಿಂಗ್", @@ -292,6 +300,8 @@ "supervisor": "ಮೇಲ್ವಿಚಾರಕ", "online": "ಆನ್ಲೈನ್", "offline": "ಆಫ್ಲೈನ್", + "enabled": "ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ", + "disabled": "ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ", "logout_success": "ನೀವು ಯಶಸ್ವಿಯಾಗಿ ಲಾಗ್ ಔಟ್ ಆಗಿರುವಿರಿ!", "logout_failure": "ಏನೋ ತಪ್ಪಾಗಿದೆ, ದಯವಿಟ್ಟು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ", "go_to_home": "ಮನೆಗೆ ಹೋಗು", @@ -311,5 +321,16 @@ "okay": "ಸರಿ", "no_internet_connection": "ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವಿಲ್ಲ!", "connect_and_retry": "ದಯವಿಟ್ಟು ಇಂಟರ್ನೆಟ್\u200Cನೊಂದಿಗೆ ಸಂಪರ್ಕಿಸಿ ಮತ್ತು ಮರುಪ್ರಯತ್ನಿಸಿ.", - "retry": "ಮರುಪ್ರಯತ್ನಿಸಿ" + "retry": "ಮರುಪ್ರಯತ್ನಿಸಿ", + "key": "ಕೀ", + "server_value": "ಸರ್ವರ್ ಮೌಲ್ಯ", + "local_value": "ಸ್ಥಳೀಯ ಮೌಲ್ಯ", + "no_configuration_parameters_found": "ಯಾವುದೇ ಸಂರಚನಾ ನಿಯತಾಂಕಗಳು ಕಂಡುಬರಲಿಲ್ಲ", + "no_configurations_found": "ಯಾವುದೇ ಸಂರಚನೆಗಳು ಕಂಡುಬರಲಿಲ್ಲ", + "confirm": "ದೃಢೀಕರಿಸಿ", + "scan_now": "ಈಗಸ್ಕ್ಯಾನ್ ಮಾಡಿ", + "enter_additional_info_req_id": "ಹೆಚ್ಚುವರಿ ಮಾಹಿತಿ ವಿನಂತಿ ಐಡಿ ನಮೂದಿಸಿ", + "additional_info_req_id": "ಹೆಚ್ಚುವರಿ ಮಾಹಿತಿ ವಿನಂತಿ ಐಡಿ", + "no_access_to_this_page": "ನಿಮಗೆ ಈ ಪುಟಕ್ಕೆ ಪ್ರವೇಶವಿಲ್ಲ", + "scheduled_job_settings": "ನಿಗದಿತ ಉದ್ಯೋಗ ಸೆಟ್ಟಿಂಗ್‌ಗಳು" } \ No newline at end of file diff --git a/assets/l10n/app_ta.arb b/assets/l10n/app_ta.arb index fc6093885..50db73239 100644 --- a/assets/l10n/app_ta.arb +++ b/assets/l10n/app_ta.arb @@ -41,6 +41,7 @@ "cred_expired": "நற்சான்றிதழ்கள் காணப்படவில்லை அல்லது காலாவதியாகவில்லை. ஆன்லைன் உள்நுழைவை முயற்சிக்கவும்!", "not_initialized": "இயந்திரத்தை துவக்க முடியவில்லை!", "invalid_user": "பயனர்பெயர் செல்லாது!", + "onboard_operator_biomterics": "ஆபரேட்டர் பயோமெட்ரிக் பதிவுசெய்தல்", "machine_inactive": "இயந்திரம் செயலில் இல்லை!", "center_inactive": "மையம் செயல்படவில்லை!", "sync_completed_succesfully": "ஒத்திசைவு வெற்றிகரமாக நிறைவடைந்தது", @@ -56,6 +57,12 @@ "network_error": "நெட்வொர்க் இல்லை!", "delete": "அழி", "newRegistrationProcess": "{language, select, eng{New Registration} ara{تسجيل جديد} fra{Nouvelle inscription} other{New Registration}}", + "inactive_logout_heading": "நீங்கள் சும்மா இருந்தீர்கள்", + "inactive_logout_description": "பாதுகாப்பு காரணங்களுக்காக, நீங்கள் $TIMER இல் வெளியேற்றப்படுவீர்கள்", + "seconds": " நொடிகள்.", + "minutes": " நிமிடங்கள்.", + "logout_button": "வெளியேறு", + "stay_logged_in_button": "உள்நுழைந்தே இருங்கள்", "@newRegistrationProcess": { "description": "New Registration Process Header", "placeholders": { @@ -160,6 +167,7 @@ }, "client_status": "வாடிக்கையாளர் நிலை", "server_status": "சேவையக நிலை", + "review_status": "மீளாய்வு நிலை", "clear_filter": "Clear Filter", "serial_number": "எஸ்.என்", "application_id": "விண்ணப்ப ஐடி", @@ -301,6 +309,8 @@ "supervisor": "மேற்பார்வையாளர்", "online": "நிகழ்நிலை", "offline": "ஆஃப்லைனில்", + "enabled": "இயக்கப்பட்டது", + "disabled": "முடக்கப்பட்டது", "logout_success": "நீங்கள் வெற்றிகரமாக வெளியேறிவிட்டீர்கள்!", "logout_failure": "ஏதோ தவறாகிவிட்டது, சிறிது நேரம் கழித்து மீண்டும் முயற்சிக்கவும்", "go_to_home": "வீட்டிற்கு போ", @@ -320,5 +330,16 @@ "okay": "சரி", "no_internet_connection": "இணைய இணைப்பு இல்லை!", "connect_and_retry": "இணையத்துடன் இணைத்து மீண்டும் முயற்சிக்கவும்.", - "retry": "மீண்டும் முயற்சிக்கவும்" + "retry": "மீண்டும் முயற்சிக்கவும்", + "key": "விசை", + "server_value": "சர்வர் மதிப்பு", + "local_value": "உள்ளூர் மதிப்பு", + "no_configuration_parameters_found": "எந்த கட்டமைப்பு அளவுருக்களும் கிடைக்கவில்லை", + "no_configurations_found": "எந்த கட்டமைப்பும் கிடைக்கவில்லை", + "confirm": "உறுதிப்படுத்தவும்", + "scan_now": "இப்போது ஸ்கேன் செய்யுங்கள்", + "enter_additional_info_req_id": "கூடுதல் தகவல் கோரிக்கை ஐடியை உள்ளிடவும்", + "additional_info_req_id": "கூடுதல் தகவல் கோரிக்கை ஐடி", + "no_access_to_this_page": "இந்தப் பக்கத்தை அணுக உங்களுக்கு அனுமதி இல்லை", + "scheduled_job_settings": "திட்டமிடப்பட்ட வேலை அமைப்புகள்" } \ No newline at end of file diff --git a/docs/HandlesUserFlow.png b/docs/HandlesUserFlow.png new file mode 100644 index 000000000..f711e6d18 Binary files /dev/null and b/docs/HandlesUserFlow.png differ diff --git a/docs/design/handle_flow.md b/docs/design/handle_flow.md new file mode 100644 index 000000000..a86099c72 --- /dev/null +++ b/docs/design/handle_flow.md @@ -0,0 +1,40 @@ +# Handles Feature +The Handles Feature is designed to streamline citizen registration and authentication. During registration, specific attributes such as email, phone number, or national ID—can be designated as a handle. This handle serves as a unique identifier that can later be used for authentication for various services. Handles can also be used to update data in case of data discrepancies. By allowing flexible and secure identification, the feature enhances the accuracy and integrity of citizen records while simplifying user interactions with government systems. + +# Configuration Guide + +It is important to acknowledge that all properties listed in this guide are automatically synchronized with the Android Registration Client. These properties are sourced from the `registration-default.properties` file. + +## Configuration Steps + +### 1. Update the Handle Fields in `registration-default.properties` + +In the `registration-default.properties` file, update the following property to specify the field values on which you want to enable the handle. Ensure that these field values match the field values in the IDSchema. + +```properties +mosip.registration.default-selected-handle-fields=email,phone +``` + +### 2. Update the Regex for Handle Validation in `id-authentication-default.properties` +In the `id-authentication-default.properties` file, update the Regex to validate handles with the provided key as the postfix: + +```properties +mosip.ida.handle-types.regex={ '@email' : '.*@email$', '@phonenumber' : '.*@phonenumber$' } +``` + +### 3. Map Postfix Values in `id-repository-default.properties` +In the `id-repository-default.properties` file, map the postfix values with the corresponding field values: + +```properties +mosip.identity.fieldid.handle-postfix.mapping={'email':'@email', 'phone':'@phonenumber'} +``` + +#### Configuration files + +* `application-default.properties` +* `registration-default.properties` + +## Sequence Diagram +The following sequence diagram illustrates the registration process, including the use of the Handles Feature: + +![HandlesUserFlow.png](../HandlesUserFlow.png) diff --git a/lib/app_router.dart b/lib/app_router.dart index f75c93e26..7a6775c42 100644 --- a/lib/app_router.dart +++ b/lib/app_router.dart @@ -6,25 +6,24 @@ */ import 'package:flutter/material.dart'; -import 'package:registration_client/ui/process_ui/lost_process.dart'; - -import 'package:registration_client/ui/process_ui/new_process.dart'; +import 'package:registration_client/ui/process_ui/generic_process.dart'; +import 'package:registration_client/ui/process_ui/process_type.dart'; import 'package:registration_client/ui/login_page.dart'; import 'package:registration_client/ui/onboard/onboard_landing_page.dart'; import 'package:registration_client/ui/onboard/home_page.dart'; -import 'package:registration_client/ui/process_ui/update_process.dart'; class AppRouter { AppRouter._(); static Map routes = { LoginPage.route: (context) => const LoginPage(), - NewProcess.routeName: (context) => const NewProcess(), - UpdateProcess.routeName: (context) => const UpdateProcess(), + '/new_process': (context) => const GenericProcess(processType: ProcessType.newProcess), + '/update_process': (context) => const GenericProcess(processType: ProcessType.updateProcess), + '/lost_process': (context) => const GenericProcess(processType: ProcessType.lostProcess), + '/correction_process': (context) => const GenericProcess(processType: ProcessType.correctionProcess), OnboardLandingPage.route: (context) => const OnboardLandingPage(), HomePage.route: (context) => const HomePage(), - LostProcess.routeName: (context) => const LostProcess(), }; static Route? onUnknownRoute(RouteSettings settings) { diff --git a/lib/main.dart b/lib/main.dart index 2e1d7741a..db4d4d40a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,7 +12,6 @@ import 'package:registration_client/app_router.dart'; import 'package:registration_client/provider/approve_packets_provider.dart'; import 'package:registration_client/provider/auth_provider.dart'; import 'package:registration_client/provider/connectivity_provider.dart'; - import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:registration_client/provider/global_provider.dart'; @@ -21,6 +20,11 @@ import 'package:registration_client/provider/sync_provider.dart'; import 'package:registration_client/ui/login_page.dart'; import 'package:registration_client/utils/app_config.dart'; import 'package:flutter_driver/driver_extension.dart'; +import 'package:registration_client/utils/inactivity_tracker.dart'; + +final GlobalKey rootNavigatorKey = GlobalKey(); +final GlobalKey rootScaffoldMessengerKey = +GlobalKey(); void main() async { enableFlutterDriverExtension(enableTextEntryEmulation: false); @@ -33,6 +37,29 @@ void main() async { ); } +Future _handleAutoLogout() async { + final ctx = rootNavigatorKey.currentContext; + if (ctx == null) return; // Safety guard + + // final syncProvider = ctx.read(); + final authProvider = ctx.read(); + final loc = AppLocalizations.of(ctx)!; + + final String result = await authProvider.logoutUser(); + + if (result.contains('Logout Success')) { + rootScaffoldMessengerKey.currentState?.showSnackBar( + SnackBar(content: Text(loc.logout_success)), + ); + rootNavigatorKey.currentState + ?.pushNamedAndRemoveUntil('/login-page', (_) => false); + } else { + rootScaffoldMessengerKey.currentState?.showSnackBar( + SnackBar(content: Text(loc.logout_failure)), + ); + } +} + class RegistrationClientApp extends StatelessWidget { const RegistrationClientApp({super.key}); @@ -70,44 +97,98 @@ class RegistrationClientApp extends StatelessWidget { } } -class BuildApp extends StatelessWidget { +class BuildApp extends StatefulWidget { const BuildApp({super.key}); + @override + State createState() => _BuildAppState(); +} + +class _BuildAppState extends State { + late AuthProvider authProvider; + /// Default to 5 minutes until server value arrives + Duration _idleDuration = const Duration(seconds: 900); + Duration _graceDuration = const Duration(seconds: 600); + + @override + void initState() { + super.initState(); + _loadIdleTimeFromServer(); + } + + Future _loadIdleTimeFromServer() async { + try { + final authProvider = Provider.of(context, listen: false); + + // Run both API calls in parallel + await Future.wait([ + authProvider.getIdleTime(), + authProvider.getRefreshedLoginTime(), + ]); + + final int idleSecs = int.tryParse(authProvider.idleTime) ?? 0; + final int graceSecs = int.tryParse(authProvider.refreshedLoginTime) ?? 0; + + if (mounted) { + setState(() { + if (idleSecs > 0) _idleDuration = Duration(seconds: idleSecs); + if (graceSecs > 0) _graceDuration = Duration(seconds: graceSecs); + }); + } + } catch (e) { + debugPrint('Failed to load idle time / grace period: $e'); + } + } + @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Registration Client', - routes: AppRouter.routes, - debugShowCheckedModeBanner: false, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - locale: Provider.of(context).appLocal, - theme: ThemeData( - colorScheme: ColorScheme.light(primary: solidPrimary), - primaryColor: solidPrimary, - textTheme: const TextTheme( - titleLarge: TextStyle(fontSize: 24), - bodyLarge: TextStyle(fontSize: 18), + return Consumer( + builder: (_, authProvider, __) { + return MaterialApp( + navigatorKey: rootNavigatorKey, + scaffoldMessengerKey: rootScaffoldMessengerKey, + title: 'Registration Client', + routes: AppRouter.routes, + debugShowCheckedModeBanner: false, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + locale: context.watch().appLocal, + theme: ThemeData( + colorScheme: ColorScheme.light(primary: solidPrimary), + primaryColor: solidPrimary, + textTheme: const TextTheme( + titleLarge: TextStyle(fontSize: 24), + bodyLarge: TextStyle(fontSize: 18), + ), + elevatedButtonTheme: + const ElevatedButtonThemeData(style: ButtonStyle()), ), - elevatedButtonTheme: - const ElevatedButtonThemeData(style: ButtonStyle())), - builder: (context, child) { - MediaQueryData mediaQueryData = MediaQuery.of(context); - Orientation orientation = mediaQueryData.orientation; - ScreenUtil.init( - context, - designSize: orientation == Orientation.portrait - ? mediaQueryData.size.width < 750 + builder: (context, child) { + MediaQueryData mediaQueryData = MediaQuery.of(context); + Orientation orientation = mediaQueryData.orientation; + ScreenUtil.init( + context, + designSize: orientation == Orientation.portrait + ? mediaQueryData.size.width < 750 ? const Size(390, 844) : const Size(800, 1280) - : const Size(1024, 768), - minTextAdapt: true, - splitScreenMode: true, - ); + : const Size(1024, 768), + minTextAdapt: true, + splitScreenMode: true, + ); - return child!; + /// Wrap entire app in the tracker with server-configured timeout + return InactivityTracker( + timeout: _idleDuration, + gracePeriod: _graceDuration,// ← dynamic duration + isUserLoggedIn: authProvider.isLoggedIn, + onTimeout: _handleAutoLogout, // global callback (already defined) + child: child!, + ); + }, + home: const LoginPage(), + ); }, - home: const LoginPage(), ); } } diff --git a/lib/model/registration.dart b/lib/model/registration.dart index a4009041a..add6962ac 100644 --- a/lib/model/registration.dart +++ b/lib/model/registration.dart @@ -36,6 +36,7 @@ class Registration with _$Registration { int? crDtime, String? updBy, int? updDtimes, + String? id }) = _Registration; factory Registration.fromJson(Map json) => _$RegistrationFromJson(json); diff --git a/lib/model/settings.dart b/lib/model/settings.dart new file mode 100644 index 000000000..2b274374d --- /dev/null +++ b/lib/model/settings.dart @@ -0,0 +1,22 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter/foundation.dart'; + +part 'settings.freezed.dart'; +part 'settings.g.dart'; + +@freezed +class Settings with _$Settings { + const factory Settings({ + Map? description, + String? fxml, + String? icon, + Map? label, + String? name, + String? order, + @JsonKey(name: 'access-control') List? accessControl, + @JsonKey(name: 'shortcut-icon') String? shortcutIcon, + }) = _Settings; + + factory Settings.fromJson(Map json) => + _$SettingsFromJson(json); +} diff --git a/lib/platform_android/auth_service_impl.dart b/lib/platform_android/auth_service_impl.dart index c8478ddc6..59c36731b 100644 --- a/lib/platform_android/auth_service_impl.dart +++ b/lib/platform_android/auth_service_impl.dart @@ -98,6 +98,44 @@ class AuthServiceImpl implements AuthService { return forgotPasswordResponse; } + @override + Future getIdleTime() async { + late String idleTime; + try { + idleTime = await AuthResponseApi().getIdleTime(); + } on PlatformException { + debugPrint('getIdleTime call failed!'); + } catch (e) { + debugPrint(e.toString()); + } + return idleTime; + } + + @override + Future getAutoLogoutPopupTimeout() async { + late String refreshLoginTime; + try { + refreshLoginTime = await AuthResponseApi().getAutoLogoutPopupTimeout(); + } on PlatformException { + debugPrint('getIdleTime call failed!'); + } catch (e) { + debugPrint(e.toString()); + } + return refreshLoginTime; + } + + @override + Future> getRolesByUserId(String userId) async { + List rolesList = []; + try { + rolesList = await AuthResponseApi().getRolesByUserId(userId); + } on PlatformException { + debugPrint('getRolesByUserId call failed!'); + } catch (e) { + debugPrint(e.toString()); + } + return rolesList; + } } diff --git a/lib/platform_android/biometrics_service_impl.dart b/lib/platform_android/biometrics_service_impl.dart index b8de0dd06..c7f87bc83 100644 --- a/lib/platform_android/biometrics_service_impl.dart +++ b/lib/platform_android/biometrics_service_impl.dart @@ -160,13 +160,26 @@ class BiometricsServiceImpl implements BiometricsService { try { response = await BiometricsApi().removeBioException(fieldId, modality, attribute); } on PlatformException { - debugPrint('BiomtericsApi call failed!'); + debugPrint('BiometricsApi call failed!'); } catch (e) { debugPrint('Remove Bio Exception failed: ${e.toString()}'); } return response; } + @override + Future> getListOfDevices(String modality) async { + List deviceList = List.empty(); + try { + deviceList = await BiometricsApi().getListOfDevices(modality); + } on PlatformException { + debugPrint('BiometricsApi call failed!'); + } catch (e) { + debugPrint('Fetch List of Device failed: ${e.toString()}'); + } + return deviceList; + } + } BiometricsService getBiometricsServiceImpl() => BiometricsServiceImpl(); diff --git a/lib/platform_android/global_config_service_impl.dart b/lib/platform_android/global_config_service_impl.dart new file mode 100644 index 000000000..b8d472285 --- /dev/null +++ b/lib/platform_android/global_config_service_impl.dart @@ -0,0 +1,76 @@ + + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:registration_client/pigeon/global_config_settings_pigeon.dart'; +import 'package:registration_client/platform_spi/global_config_service.dart'; + +class GlobalConfigServiceImpl implements GlobalConfigService { + @override + Future> getLocalConfigurations() async { + Map localPreferences = {}; + try { + localPreferences = await GlobalConfigSettingsApi().getLocalConfigurations(); + } on PlatformException { + debugPrint('GlobalConfigServiceImpl call failed!'); + } catch (e) { + debugPrint('Local Config value not fetched! ${e.toString()}'); + } + return localPreferences; + } + + @override + Future> getPermittedConfigurationNames() async { + List permittedConfiguration = []; + try { + permittedConfiguration = + await GlobalConfigSettingsApi().getPermittedConfigurationNames(); + } on PlatformException { + debugPrint('GlobalConfigServiceImpl call failed!'); + } catch (e) { + debugPrint( + 'Permitted Configuration value not fetched! ${e.toString()}'); + } + return permittedConfiguration; + } + + @override + Future> getRegistrationParams() async { + Map registrationParams = {}; + try { + registrationParams = await GlobalConfigSettingsApi().getRegistrationParams(); + } on PlatformException { + debugPrint('GlobalConfigServiceImpl call failed!'); + } catch (e) { + debugPrint('Registration Params value not fetched! ${e.toString()}'); + } + return registrationParams; + } + + @override + Future modifyConfigurations(Map localPreferences) async { + try { + await GlobalConfigSettingsApi().modifyConfigurations(localPreferences); + } on PlatformException { + debugPrint('GlobalConfigServiceImpl call failed!'); + } catch (e) { + debugPrint('Modify Configurations failed! ${e.toString()}'); + } + } + + @override + Future getGpsEnableFlag() async { + String gpsEnableFlag = ""; + try { + gpsEnableFlag = await GlobalConfigSettingsApi().getGpsEnableFlag(); + } on PlatformException { + debugPrint("Location Api failed!"); + } catch (e) { + debugPrint("Location fetch error: $e"); + } + return gpsEnableFlag; + } + +} + +GlobalConfigService getGlobalConfigServiceImpl() => GlobalConfigServiceImpl(); \ No newline at end of file diff --git a/lib/platform_android/process_spec_service_impl.dart b/lib/platform_android/process_spec_service_impl.dart index ffe005835..26b65dea6 100644 --- a/lib/platform_android/process_spec_service_impl.dart +++ b/lib/platform_android/process_spec_service_impl.dart @@ -106,6 +106,21 @@ class ProcessSpecServiceImpl implements ProcessSpecService { } return optionalLanguageCodes; } + + @override + Future> getSettingSpec() async { + List settingSpec; + try { + settingSpec = await ProcessSpecApi().getSettingSpec(); + } on PlatformException { + debugPrint("Settings Spec Api failed!"); + settingSpec = List.empty(); + } catch (e) { + settingSpec = List.empty(); + debugPrint("Settings spec fetch error: $e"); + } + return settingSpec; + } } ProcessSpecService getProcessSpecServiceImpl() => ProcessSpecServiceImpl(); \ No newline at end of file diff --git a/lib/platform_android/registration_service_impl.dart b/lib/platform_android/registration_service_impl.dart index 96de4a9f7..cbce62dea 100644 --- a/lib/platform_android/registration_service_impl.dart +++ b/lib/platform_android/registration_service_impl.dart @@ -96,6 +96,28 @@ class RegistrationServiceImpl implements RegistrationService { debugPrint('Application ID not added ${e.toString()}'); } } + + @override + Future setAdditionalReqId(String additionalReqId) async { + try { + await RegistrationDataApi().setAdditionalReqId(additionalReqId); + } on PlatformException { + debugPrint('RegistrationDataApi call failed'); + } catch (e) { + debugPrint('Additional info req ID not added ${e.toString()}'); + } + } + + @override + Future setMachineLocation(double latitude, double longitude) async { + try { + await RegistrationDataApi().setMachineLocation(latitude, longitude); + } on PlatformException { + debugPrint('RegistrationDataApi call failed'); + } catch (e) { + debugPrint('location not added ${e.toString()}'); + } + } } RegistrationService getRegistrationServiceImpl() => RegistrationServiceImpl(); diff --git a/lib/platform_android/sync_response_service_impl.dart b/lib/platform_android/sync_response_service_impl.dart index eb8215dbc..7bd0631ae 100644 --- a/lib/platform_android/sync_response_service_impl.dart +++ b/lib/platform_android/sync_response_service_impl.dart @@ -9,13 +9,14 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:registration_client/pigeon/master_data_sync_pigeon.dart'; import 'package:registration_client/platform_spi/sync_response_service.dart'; +import 'package:flutter/services.dart' show MethodChannel; class SyncResponseServiceImpl implements SyncResponseService { @override - Future getPolicyKeySync(bool isManualSync) async { + Future getPolicyKeySync(bool isManualSync, String jobId) async { late Sync syncResponse; try { - syncResponse = await SyncApi().getPolicyKeySync(isManualSync); + syncResponse = await SyncApi().getPolicyKeySync(isManualSync, jobId); } on PlatformException { debugPrint('PolicyKeySync Api call failed, PlatformException'); } catch (e) { @@ -25,10 +26,10 @@ class SyncResponseServiceImpl implements SyncResponseService { } @override - Future getGlobalParamsSync(bool isManualSync) async { + Future getGlobalParamsSync(bool isManualSync, String jobId) async { late Sync syncResponse; try { - syncResponse = await SyncApi().getGlobalParamsSync(isManualSync); + syncResponse = await SyncApi().getGlobalParamsSync(isManualSync, jobId); } on PlatformException { debugPrint('GlobalParamsSync Api call failed, PlatformException'); } catch (e) { @@ -38,10 +39,10 @@ class SyncResponseServiceImpl implements SyncResponseService { } @override - Future getUserDetailsSync(bool isManualSync) async { + Future getUserDetailsSync(bool isManualSync, String jobId) async { late Sync syncResponse; try { - syncResponse = await SyncApi().getUserDetailsSync(isManualSync); + syncResponse = await SyncApi().getUserDetailsSync(isManualSync, jobId); } on PlatformException { debugPrint('UserDetailsSync Api call failed, PlatformException'); } catch (e) { @@ -64,10 +65,10 @@ class SyncResponseServiceImpl implements SyncResponseService { } @override - Future getMasterDataSync(bool isManualSync) async { + Future getMasterDataSync(bool isManualSync, String jobId) async { late Sync syncResponse; try { - syncResponse = await SyncApi().getMasterDataSync(isManualSync); + syncResponse = await SyncApi().getMasterDataSync(isManualSync, jobId); } on PlatformException { debugPrint('MasterDataSync Api call failed, PlatformException'); } catch (e) { @@ -76,6 +77,8 @@ class SyncResponseServiceImpl implements SyncResponseService { return syncResponse; } + // Removed per request: use getMasterDataSync(bool) only + @override Future getLastSyncTime() async { late SyncTime syncTime; @@ -90,10 +93,10 @@ class SyncResponseServiceImpl implements SyncResponseService { } @override - Future getCaCertsSync(bool isManualSync) async { + Future getCaCertsSync(bool isManualSync, String jobId) async { late Sync syncResponse; try { - syncResponse = await SyncApi().getCaCertsSync(isManualSync); + syncResponse = await SyncApi().getCaCertsSync(isManualSync, jobId); } on PlatformException { debugPrint('CaCerts Api call failed, PlatformException'); } catch (e) { @@ -101,7 +104,7 @@ class SyncResponseServiceImpl implements SyncResponseService { } return syncResponse; } - + @override Future batchJob() async { String batchJobResponse = ""; @@ -116,10 +119,10 @@ class SyncResponseServiceImpl implements SyncResponseService { } @override - Future getPreRegIds() async { + Future getPreRegIds(String jobId) async { String preRegIdResponse = ""; try { - preRegIdResponse = await SyncApi().getPreRegIds(); + preRegIdResponse = await SyncApi().getPreRegIds(jobId); } on PlatformException { debugPrint('Application Id Api call failed, PlatformException'); } catch (e) { @@ -129,10 +132,10 @@ class SyncResponseServiceImpl implements SyncResponseService { } @override - Future getKernelCertsSync(bool isManualSync) async { + Future getKernelCertsSync(bool isManualSync, String jobId) async { late Sync syncResponse; try { - syncResponse = await SyncApi().getKernelCertsSync(isManualSync); + syncResponse = await SyncApi().getKernelCertsSync(isManualSync, jobId); } on PlatformException { debugPrint('KernelCerts Api call failed, PlatformException'); } catch (e) { @@ -158,7 +161,7 @@ class SyncResponseServiceImpl implements SyncResponseService { } @override - Future getSyncAndUploadInProgressStatus() async{ + Future getSyncAndUploadInProgressStatus() async { bool syncAndUploadResponse = false; try { syncAndUploadResponse = await SyncApi().getSyncAndUploadInProgressStatus(); @@ -169,6 +172,77 @@ class SyncResponseServiceImpl implements SyncResponseService { } return syncAndUploadResponse; } + + @override + Future> getActiveSyncJobs() async { + try { + final result = await SyncApi().getActiveSyncJobs(); + final list = result; + return list; + } on PlatformException catch (e) { + debugPrint('getActiveSyncJobs PlatformException: ${e.message}'); + return const []; + } catch (e) { + debugPrint('getActiveSyncJobs failed: $e'); + return const []; + } + } + + @override + Future deleteAuditLogs(String jobId) async { + try { + final deleteResponse = await SyncApi().deleteAuditLogs(jobId); + return deleteResponse; + } on PlatformException catch (e) { + debugPrint('deleteAuditLogs PlatformException: ${e.message}'); + return false; + } catch (e) { + debugPrint('deleteAuditLogs failed: $e'); + return false; + } + } + + @override + Future deletePreRegRecords(String jobId) async { + try { + final deleteResponse = await SyncApi().deletePreRegRecords(jobId); + return deleteResponse; + } on PlatformException catch (e) { + debugPrint('deleteAuditLogs PlatformException: ${e.message}'); + return false; + } catch (e) { + debugPrint('deleteAuditLogs failed: $e'); + return false; + } + } + + @override + Future getLastSyncTimeByJobId(String jobId) async{ + try { + final lastSyncTime = await SyncApi().getLastSyncTimeByJobId(jobId); + return lastSyncTime; + } on PlatformException catch (e) { + debugPrint('lastSync PlatformException: ${e.message}'); + return "false"; + } catch (e) { + debugPrint('lastSync failed: $e'); + return "false"; + } + } + + @override + Future getNextSyncTimeByJobId(String jobId) async{ + try { + final nextSyncTime = await SyncApi().getNextSyncTimeByJobId(jobId); + return nextSyncTime; + } on PlatformException catch (e) { + debugPrint('nextSync PlatformException: ${e.message}'); + return "false"; + } catch (e) { + debugPrint('nextSync failed: $e'); + return "false"; + } + } } SyncResponseService getSyncResponseServiceImpl() => SyncResponseServiceImpl(); diff --git a/lib/platform_spi/auth_service.dart b/lib/platform_spi/auth_service.dart index bf3fce5db..6b1f8bdb7 100644 --- a/lib/platform_spi/auth_service.dart +++ b/lib/platform_spi/auth_service.dart @@ -25,5 +25,11 @@ abstract class AuthService { Future forgotPasswordUrl(); + Future getIdleTime(); + + Future getAutoLogoutPopupTimeout(); + + Future> getRolesByUserId(String userId); + factory AuthService() => getAuthServiceImpl(); } diff --git a/lib/platform_spi/biometrics_service.dart b/lib/platform_spi/biometrics_service.dart index 2be3d25b8..49337280a 100644 --- a/lib/platform_spi/biometrics_service.dart +++ b/lib/platform_spi/biometrics_service.dart @@ -7,6 +7,7 @@ import 'dart:typed_data'; +import 'package:registration_client/pigeon/biometrics_pigeon.dart'; import 'package:registration_client/platform_android/biometrics_service_impl.dart'; abstract class BiometricsService { @@ -36,5 +37,7 @@ abstract class BiometricsService { Future conditionalBioAttributeValidation(String fieldId, String expression); + Future> getListOfDevices(String modality); + factory BiometricsService() => getBiometricsServiceImpl(); } diff --git a/lib/platform_spi/global_config_service.dart b/lib/platform_spi/global_config_service.dart new file mode 100644 index 000000000..eb481ff4f --- /dev/null +++ b/lib/platform_spi/global_config_service.dart @@ -0,0 +1,15 @@ +import 'package:registration_client/platform_android/global_config_service_impl.dart'; + +abstract class GlobalConfigService { + Future> getRegistrationParams(); + + Future> getLocalConfigurations(); + + Future> getPermittedConfigurationNames(); + + Future modifyConfigurations(Map localPreferences); + + Future getGpsEnableFlag(); + + factory GlobalConfigService() => getGlobalConfigServiceImpl(); +} \ No newline at end of file diff --git a/lib/platform_spi/process_spec_service.dart b/lib/platform_spi/process_spec_service.dart index 8cb440d02..665dbb0c9 100644 --- a/lib/platform_spi/process_spec_service.dart +++ b/lib/platform_spi/process_spec_service.dart @@ -15,6 +15,7 @@ abstract class ProcessSpecService { Future> getOptionalLanguageCodes(); Future getMinLanguageCount(); Future getMaxLanguageCount(); + Future> getSettingSpec(); factory ProcessSpecService() => getProcessSpecServiceImpl(); } \ No newline at end of file diff --git a/lib/platform_spi/registration_service.dart b/lib/platform_spi/registration_service.dart index 6eb231190..866db15ce 100644 --- a/lib/platform_spi/registration_service.dart +++ b/lib/platform_spi/registration_service.dart @@ -17,6 +17,8 @@ abstract class RegistrationService { bool isPreview, Map templateValues); Future submitRegistrationDto(String makerName); Future setApplicationId(String applicationId); + Future setAdditionalReqId(String additionalReqId); + Future setMachineLocation(double latitude, double longitude); factory RegistrationService() => getRegistrationServiceImpl(); } diff --git a/lib/platform_spi/sync_response_service.dart b/lib/platform_spi/sync_response_service.dart index 13123ac5e..4251e9376 100644 --- a/lib/platform_spi/sync_response_service.dart +++ b/lib/platform_spi/sync_response_service.dart @@ -10,19 +10,25 @@ import 'package:registration_client/platform_android/sync_response_service_impl. abstract class SyncResponseService { Future getLastSyncTime(); - Future getPolicyKeySync(bool isManualSync); - Future getGlobalParamsSync(bool isManualSync); - Future getUserDetailsSync(bool isManualSync); + Future getPolicyKeySync(bool isManualSync, String jobId); + Future getGlobalParamsSync(bool isManualSync, String jobId); + Future getUserDetailsSync(bool isManualSync, String jobId); Future getIDSchemaSync(bool isManualSync); - Future getMasterDataSync(bool isManualSync); - Future getCaCertsSync(bool isManualSync); + Future getMasterDataSync(bool isManualSync, String jobId); + Future getCaCertsSync(bool isManualSync, String jobId); Future batchJob(); - Future getPreRegIds(); + Future getPreRegIds(String jobId); Future> getReasonList(String langCode); - Future getKernelCertsSync(bool isManualSync); + Future getKernelCertsSync(bool isManualSync, String jobId); Future getSyncAndUploadInProgressStatus(); + Future deleteAuditLogs(String jobId); + Future deletePreRegRecords(String jobId); + + Future> getActiveSyncJobs(); + Future getLastSyncTimeByJobId(String jobId); + Future getNextSyncTimeByJobId(String jobId); factory SyncResponseService() => getSyncResponseServiceImpl(); } \ No newline at end of file diff --git a/lib/provider/auth_provider.dart b/lib/provider/auth_provider.dart index 3592d8342..c949f7e39 100644 --- a/lib/provider/auth_provider.dart +++ b/lib/provider/auth_provider.dart @@ -32,6 +32,8 @@ class AuthProvider with ChangeNotifier { String _userEmail = ""; bool _isNetworkPresent = false; String _forgotPasswordUrl = ""; + String _refreshedLoginTime = ""; + String _idleTime = ""; bool get isLoggedIn => _isLoggedIn; bool get isSyncing => _isSyncing; @@ -53,6 +55,8 @@ class AuthProvider with ChangeNotifier { String get userEmail => _userEmail; bool get isNetworkPresent => _isNetworkPresent; String get forgotPasswordUrl => _forgotPasswordUrl; + String get refreshedLoginTime => _refreshedLoginTime; + String get idleTime => _idleTime; setIsLoggedIn(bool value) { _isLoggedIn = value; @@ -223,4 +227,19 @@ class AuthProvider with ChangeNotifier { notifyListeners(); } + getIdleTime() async { + String idleTime = await auth.getIdleTime(); + _idleTime = idleTime; + notifyListeners(); + } + + getRefreshedLoginTime() async { + String refreshedLoginTime = await auth.getAutoLogoutPopupTimeout(); + _refreshedLoginTime = refreshedLoginTime; + notifyListeners(); + } + + Future> getUserRole(String userId) async { + return await auth.getRolesByUserId(userId); + } } diff --git a/lib/provider/connectivity_provider.dart b/lib/provider/connectivity_provider.dart index 3851aef02..6272a1b77 100644 --- a/lib/provider/connectivity_provider.dart +++ b/lib/provider/connectivity_provider.dart @@ -7,15 +7,18 @@ import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; import 'package:registration_client/platform_spi/network_service.dart'; class ConnectivityProvider extends ChangeNotifier { NetworkService networkService = NetworkService(); late ConnectivityResult _connectivityResult; bool _isConnected = false; + bool _isGPSEnabled = false; ConnectivityResult get connectivityResult => _connectivityResult; bool get isConnected => _isConnected; + bool get isGPSEnabled => _isGPSEnabled; checkNetworkConnection() async { String response = await networkService.checkInternetConnection(); @@ -26,4 +29,29 @@ class ConnectivityProvider extends ChangeNotifier { } notifyListeners(); } + + Future checkGPSStatus() async { + try { + bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); + + if (!serviceEnabled) { + _isGPSEnabled = false; + notifyListeners(); + return; + } + + LocationPermission permission = await Geolocator.checkPermission(); + + if (permission == LocationPermission.denied || + permission == LocationPermission.deniedForever || + permission == LocationPermission.unableToDetermine) { + _isGPSEnabled = false; + } else { + _isGPSEnabled = true; + } + } catch (e) { + _isGPSEnabled = false; + } + notifyListeners(); + } } diff --git a/lib/provider/global_provider.dart b/lib/provider/global_provider.dart index 1b00d052d..deac515f7 100644 --- a/lib/provider/global_provider.dart +++ b/lib/provider/global_provider.dart @@ -9,6 +9,7 @@ import 'dart:developer'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'package:geolocator/geolocator.dart'; import 'package:registration_client/model/field.dart'; import 'package:registration_client/model/process.dart'; import 'package:registration_client/pigeon/biometrics_pigeon.dart'; @@ -16,6 +17,7 @@ import 'package:registration_client/pigeon/common_details_pigeon.dart'; import 'package:registration_client/pigeon/dynamic_response_pigeon.dart'; import 'package:registration_client/platform_spi/audit_service.dart'; import 'package:registration_client/platform_spi/dynamic_response_service.dart'; +import 'package:registration_client/platform_spi/global_config_service.dart'; import 'package:registration_client/platform_spi/machine_key_service.dart'; import 'package:registration_client/platform_spi/network_service.dart'; @@ -32,6 +34,7 @@ class GlobalProvider with ChangeNotifier { DynamicResponseService(); final Audit audit = Audit(); final NetworkService networkService = NetworkService(); + final GlobalConfigService globalConfigService = GlobalConfigService(); //Variables int _currentIndex = 0; @@ -40,6 +43,7 @@ class GlobalProvider with ChangeNotifier { String _centerName = ""; String _machineName = ""; String _preRegId = ""; + String _additionalInfoReqId = ""; final formKey = GlobalKey(); final updateFieldKey = GlobalKey(); String _updateUINNumber = ""; @@ -168,6 +172,7 @@ class GlobalProvider with ChangeNotifier { Map get machineDetails => _machineDetails; String get regId => _regId; String get preRegId => _preRegId; + String get additionalInfoReqId => _additionalInfoReqId; String get operatorOnboardingAttributes => _operatorOnboardingAttributes; set operatorOnboardingAttributes(String value) { @@ -333,6 +338,11 @@ class GlobalProvider with ChangeNotifier { notifyListeners(); } + setAdditionalInfoReqId(String value) { + _additionalInfoReqId = value; + notifyListeners(); + } + setMachineDetails() async { final machine = await machineKeyService.getMachineKeys(); @@ -829,4 +839,40 @@ class GlobalProvider with ChangeNotifier { selectedUpdateFields = {}; updateUINNumber = ""; } + + // Fetch current GPS location + // Returns Position if successful, null if GPS disabled or permission denied + Future fetchLocation() async { + // Check if GPS is enabled in configuration + String gpsFlag = await globalConfigService.getGpsEnableFlag(); + + if (gpsFlag.isEmpty || gpsFlag != "Y") { + return null; + } + + // Check if location service is enabled on device + bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); + if (!serviceEnabled) { + return null; + } + + // Check and request permission + LocationPermission permission = await Geolocator.checkPermission(); + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + if (permission == LocationPermission.denied) { + // Permissions still denied + return null; + } + } + + if (permission == LocationPermission.deniedForever) { + return null; + } + + // Fetch location if permission is granted + return await Geolocator.getCurrentPosition( + desiredAccuracy: LocationAccuracy.high, + ); + } } diff --git a/lib/provider/registration_task_provider.dart b/lib/provider/registration_task_provider.dart index 60912808a..2eee264cc 100644 --- a/lib/provider/registration_task_provider.dart +++ b/lib/provider/registration_task_provider.dart @@ -39,6 +39,7 @@ class RegistrationTaskProvider with ChangeNotifier { ); List _listOfProcesses = List.empty(growable: true); + List _listOfSettings = List.empty(growable: true); String _stringValueGlobalParam = ""; String _uiSchema = ""; String _registrationStartError = ''; @@ -52,6 +53,7 @@ class RegistrationTaskProvider with ChangeNotifier { int _numberOfPackets = 0; List get listOfProcesses => _listOfProcesses; + List get listOfSettings => _listOfSettings; String get stringValueGlobalParam => _stringValueGlobalParam; String get uiSchema => _uiSchema; String get previewTemplate => _previewTemplate; @@ -67,6 +69,11 @@ class RegistrationTaskProvider with ChangeNotifier { notifyListeners(); } + set listOfSettings(List value) { + _listOfSettings = value; + notifyListeners(); + } + setNumberOfPackets(int value) { _numberOfPackets = value; notifyListeners(); @@ -117,6 +124,16 @@ class RegistrationTaskProvider with ChangeNotifier { notifyListeners(); } + getListOfSettings() async { + List result = await processSpecService.getSettingSpec(); + if (result.isEmpty) { + _listOfSettings = []; + } else { + _listOfSettings = result; + } + notifyListeners(); + } + startRegistration( List languages, String flowType, String process) async { _registrationStartError = await registrationService.startRegistration( @@ -332,4 +349,12 @@ class RegistrationTaskProvider with ChangeNotifier { setApplicationId(String appId) async { await registrationService.setApplicationId(appId); } + + setAdditionalReqId(String additionalReqId) async { + await registrationService.setAdditionalReqId(additionalReqId); + } + + setCurrentLocation(double latitude, double longitude) async { + await registrationService.setMachineLocation(latitude, longitude); + } } diff --git a/lib/provider/sync_provider.dart b/lib/provider/sync_provider.dart index b0283885c..2f85874a5 100644 --- a/lib/provider/sync_provider.dart +++ b/lib/provider/sync_provider.dart @@ -5,6 +5,7 @@ * */ +import 'dart:convert'; import 'dart:developer'; import 'package:flutter/widgets.dart'; @@ -12,6 +13,7 @@ import 'package:registration_client/pigeon/master_data_sync_pigeon.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:registration_client/platform_spi/sync_response_service.dart'; +import 'package:registration_client/utils/sync_job_def.dart'; class SyncProvider with ChangeNotifier { final SyncResponseService syncResponseService = SyncResponseService(); @@ -79,38 +81,69 @@ class SyncProvider with ChangeNotifier { setIsGlobalSyncInProgress(bool isGlobalSyncInProgress) { _isGlobalSyncInProgress = isGlobalSyncInProgress; } + + Future _getJobIdFinder() async { + List activeJobs = []; + try { + List activeJobJsonList = await syncResponseService.getActiveSyncJobs(); + activeJobs = activeJobJsonList + .whereType() + .map((jsonStr) { + try { + return SyncJobDef.fromJson(json.decode(jsonStr) as Map); + } catch (e) { + log("Failed to parse job JSON: $jsonStr, error: $e"); + return null; + } + }) + .whereType() + .toList(); + } catch (e) { + log("Failed to fetch active job IDs: $e"); + } + return (String apiName) { + + var job = activeJobs.where((job) => job.apiName == apiName).firstOrNull; + return job?.id ?? ""; + }; + } autoSync(BuildContext context) async { + // Get the job ID finder function + String Function(String) findJobIdByApiName = await _getJobIdFinder(); + await syncResponseService - .getGlobalParamsSync(false) + .getMasterDataSync(false, findJobIdByApiName("masterSyncJob")) .then((Sync getAutoSync) async { setCurrentProgressType(getAutoSync.syncType!); if (getAutoSync.errorCode == "") { - _policyKeySyncSuccess = true; + _globalParamsSyncSuccess = true; _currentSyncProgress = getAutoSync.syncProgress!; notifyListeners(); + findJobIdByApiName = await _getJobIdFinder(); + } else { - log(AppLocalizations.of(context)!.global_params_sync_failed); + log(AppLocalizations.of(context)!.master_data_sync_failed); } notifyListeners(); }); await syncResponseService - .getMasterDataSync(false) + .getGlobalParamsSync(false, findJobIdByApiName("synchConfigDataJob")) .then((Sync getAutoSync) async { setCurrentProgressType(getAutoSync.syncType!); if (getAutoSync.errorCode == "") { - _globalParamsSyncSuccess = true; + _policyKeySyncSuccess = true; _currentSyncProgress = getAutoSync.syncProgress!; notifyListeners(); } else { - log(AppLocalizations.of(context)!.master_data_sync_failed); + log(AppLocalizations.of(context)!.global_params_sync_failed); } notifyListeners(); }); await syncResponseService - .getUserDetailsSync(false) + .getUserDetailsSync(false, findJobIdByApiName("userDetailServiceJob")) .then((Sync getAutoSync) async { setCurrentProgressType(getAutoSync.syncType!); if (getAutoSync.errorCode == "") { @@ -138,7 +171,7 @@ class SyncProvider with ChangeNotifier { }); await syncResponseService - .getPolicyKeySync(false) + .getPolicyKeySync(false, findJobIdByApiName("keyPolicySyncJob")) .then((Sync getAutoSync) async { setCurrentProgressType(getAutoSync.syncType!); if (getAutoSync.errorCode == "") { @@ -151,7 +184,7 @@ class SyncProvider with ChangeNotifier { notifyListeners(); }); - await syncResponseService.getCaCertsSync(false).then((Sync getAutoSync) { + await syncResponseService.getCaCertsSync(false, findJobIdByApiName("syncCertificateJob")).then((Sync getAutoSync) { setCurrentProgressType(getAutoSync.syncType!); if (getAutoSync.errorCode == "") { _cacertsSyncSuccess = true; @@ -163,7 +196,7 @@ class SyncProvider with ChangeNotifier { notifyListeners(); }); - await syncResponseService.getKernelCertsSync(false).then((Sync getAutoSync) { + await syncResponseService.getKernelCertsSync(false, findJobIdByApiName("publicKeySyncJob")).then((Sync getAutoSync) { setCurrentProgressType(getAutoSync.syncType!); if (getAutoSync.errorCode == "") { _kernelCertsSyncSuccess = true; @@ -193,19 +226,22 @@ class SyncProvider with ChangeNotifier { manualSync() async { isSyncInProgress = true; - Sync syncResult = await syncResponseService.getMasterDataSync(true); + // Get the job ID finder function + String Function(String) findJobIdByApiName = await _getJobIdFinder(); + + Sync syncResult = await syncResponseService.getMasterDataSync(true, findJobIdByApiName("masterSyncJob")); if (syncResult.errorCode != null && syncResult.errorCode!.isEmpty) { syncResult = await syncResponseService.getIDSchemaSync(true); if (syncResult.errorCode != null && syncResult.errorCode!.isEmpty) { - syncResult = await syncResponseService.getUserDetailsSync(true); + syncResult = await syncResponseService.getUserDetailsSync(true, findJobIdByApiName("userDetailServiceJob")); if (syncResult.errorCode != null && syncResult.errorCode!.isEmpty) { - syncResult = await syncResponseService.getGlobalParamsSync(true); + syncResult = await syncResponseService.getGlobalParamsSync(true, findJobIdByApiName("synchConfigDataJob")); if (syncResult.errorCode != null && syncResult.errorCode!.isEmpty) { - syncResult = await syncResponseService.getKernelCertsSync(true); + syncResult = await syncResponseService.getKernelCertsSync(true, findJobIdByApiName("publicKeySyncJob")); if (syncResult.errorCode != null && syncResult.errorCode!.isEmpty) { - syncResult = await syncResponseService.getPolicyKeySync(true); + syncResult = await syncResponseService.getPolicyKeySync(true, findJobIdByApiName("keyPolicySyncJob")); if (syncResult.errorCode != null && syncResult.errorCode!.isEmpty) { - syncResult = await syncResponseService.getCaCertsSync(true); + syncResult = await syncResponseService.getCaCertsSync(true, findJobIdByApiName("syncCertificateJob")); await getLastSyncTime(); isSyncInProgress= false; } @@ -221,6 +257,27 @@ class SyncProvider with ChangeNotifier { } getPreRegistrationIds() async { - await syncResponseService.getPreRegIds(); + String Function(String) findJobIdByApiName = await _getJobIdFinder(); + await syncResponseService.getPreRegIds(findJobIdByApiName("preRegistrationDataSyncJob")); + } + + Future getLastSyncTimeByJobId(String jobId) async { + try { + final value = await syncResponseService.getLastSyncTimeByJobId(jobId); + return value; + } catch (e) { + log("Failed to get last sync time for job $jobId: $e"); + return null; + } + } + + Future getNextSyncTimeByJobId(String jobId) async { + try { + final value = await syncResponseService.getNextSyncTimeByJobId(jobId); + return value; + } catch (e) { + log("Failed to get next sync time for job $jobId: $e"); + return null; + } } } diff --git a/lib/ui/approve_packet/approve_packet_ui.dart b/lib/ui/approve_packet/approve_packet_ui.dart index bf8512b7e..dbfe58ad6 100644 --- a/lib/ui/approve_packet/approve_packet_ui.dart +++ b/lib/ui/approve_packet/approve_packet_ui.dart @@ -62,20 +62,20 @@ class _ApprovePacketsPageState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: solidPrimary, + minimumSize: const Size(double.infinity, 60), + padding: const EdgeInsets.symmetric(horizontal: 4), ), - child: SizedBox( - height: 60, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - AppLocalizations.of(context)!.submit, - style: Theme.of(context) - .textTheme - .titleLarge - ?.copyWith(color: Colors.white, fontSize: 17), - ), - ], + child: Center( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + appLocalizations.authenticate, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith(color: Colors.white, fontSize: 17), + ), ), ), ); diff --git a/lib/ui/approve_packet/widget/approve_table.dart b/lib/ui/approve_packet/widget/approve_table.dart index 5181d90d0..523718a45 100644 --- a/lib/ui/approve_packet/widget/approve_table.dart +++ b/lib/ui/approve_packet/widget/approve_table.dart @@ -181,7 +181,7 @@ class _ApproveTableState extends State { showTemplate(); log("show"); }, - child: Text(regCurrent.packetId, + child: Text(regCurrent.appId ?? regCurrent.id!, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, maxLines: 2), diff --git a/lib/ui/approve_packet/widget/template_bottom_sheet.dart b/lib/ui/approve_packet/widget/template_bottom_sheet.dart index 0781b09aa..9e6173cc4 100644 --- a/lib/ui/approve_packet/widget/template_bottom_sheet.dart +++ b/lib/ui/approve_packet/widget/template_bottom_sheet.dart @@ -1,6 +1,7 @@ import 'dart:developer'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:provider/provider.dart'; import 'package:registration_client/utils/app_config.dart'; @@ -66,7 +67,7 @@ class TemplateBottomSheet { context .read() .setWebViewPlusController(controller); - loadHtmlData(controller, regCurrent.packetId); + loadHtmlData(controller, regCurrent.id!); }, javascriptMode: JavascriptMode.unrestricted, ), @@ -79,7 +80,226 @@ class TemplateBottomSheet { const SizedBox( height: 18, ), - Row( + isMobileSize? + Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Back Arrow + Card( + elevation: 0, + shape: CircleBorder( + side: BorderSide( + color: (currentInd > 1) ? solidPrimary : Colors.grey, + ), + ), + child: IconButton( + icon: Icon( + Icons.arrow_back, + color: (currentInd > 1) ? solidPrimary : Colors.grey, + ), + onPressed: (currentInd > 1) + ? () { + context + .read() + .setCurrentInd(currentInd - 1); + Registration reg = context + .read().matchingPackets[(currentInd - 1) - 1]['packet'] as Registration; + log(reg.packetId); + loadHtmlData( + context + .read() + .webViewPlusController, + reg.packetId); + } + : () { + log("Out of range"); + }, + ), + ), + // Buttons Column + Column( + children: [ + // APPROVE BUTTON + SizedBox( + width: 190.sp, + child: ElevatedButton.icon( + style: ElevatedButton.styleFrom( + backgroundColor: solidPrimary, + padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 18), + ), + icon: const Icon(Icons.check_outlined, color: Colors.white), + label: Text( + AppLocalizations.of(context)!.approve, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + onPressed: reviewStatus == ReviewStatus.APPROVED.name + ? null + : () { + context + .read() + .approvePacket(regCurrent.packetId); + if (currentInd < + context.read().matchingPackets.length) { + context + .read() + .setCurrentInd(currentInd + 1); + Registration reg = context + .read() + .matchingPackets[(currentInd + 1) - 1]['packet'] as Registration; + loadHtmlData( + context.read().webViewPlusController, + reg.packetId); + } + }, + ), + ), + const SizedBox(height: 12), + + // REJECT BUTTON + SizedBox( + width: 190.sp, + child: OutlinedButton.icon( + icon: const Icon(Icons.close), + label: Text( + AppLocalizations.of(context)!.reject, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + style: OutlinedButton.styleFrom( + foregroundColor: Colors.red, + padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 18), + side: const BorderSide(color: Colors.red, width: 2), + ), + onPressed: reviewStatus == ReviewStatus.REJECTED.name + ? null + : () { + showDialog( + context: context, + builder: (BuildContext context) { + return dialogBox(() { + context + .read() + .rejectPacket(regCurrent.packetId); + if (currentInd < + context.read().matchingPackets.length) { + context + .read() + .setCurrentInd(currentInd + 1); + Registration reg = context + .read() + .matchingPackets[(currentInd + 1) - 1]['packet'] + as Registration; + loadHtmlData( + context + .read() + .webViewPlusController, + reg.packetId); + } + Navigator.of(context).pop(); + }, context); + }, + ); + }, + ), + ), + const SizedBox(height: 12), + // RESET BUTTON + MouseRegion( + cursor: SystemMouseCursors.click, + child: SizedBox( + width: 190.sp, + child: OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 18), + side: BorderSide(color: Colors.transparent, width: 2), + ).copyWith( + side: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.hovered)) { + return BorderSide(color: solidPrimary, width: 2); + } + return BorderSide(color: Colors.transparent, width: 2); + }), + ), + onPressed: reviewStatus == ReviewStatus.NOACTIONTAKEN.name + ? null + : () { + context.read().clearReview(regCurrent.packetId); + }, + child: Text( + 'RESET', + style: TextStyle( + fontWeight: FontWeight.bold, + color: solidPrimary, + ), + ), + ), + ), + ), + + ], + ), + + Card( + margin: const EdgeInsets.only(right: 24), + elevation: 0, + shape: CircleBorder( + side: BorderSide( + color: (currentInd < + context + .read() + .matchingPackets + .length) + ? solidPrimary + : Colors.grey, + )), + child: IconButton( + icon: Icon( + Icons.arrow_forward, + color: (currentInd < + context + .read() + .matchingPackets + .length) + ? solidPrimary + : Colors.grey, + size: 24, + ), + onPressed: currentInd < + context + .read() + .matchingPackets + .length + ? () { + context + .read() + .setCurrentInd(currentInd + 1); + Registration reg = context + .read() + .matchingPackets[(currentInd + 1) - 1] + ['packet'] as Registration; + log(reg.packetId); + loadHtmlData( + context + .read() + .webViewPlusController, + reg.packetId); + } + : () { + log("Out of range"); + }, + ), + ), + ], + ), + ), + ] + ) + :Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Card( @@ -87,8 +307,8 @@ class TemplateBottomSheet { elevation: 0, shape: CircleBorder( side: BorderSide( - color: (currentInd > 1) ? solidPrimary : Colors.grey, - )), + color: (currentInd > 1) ? solidPrimary : Colors.grey, + )), child: IconButton( icon: Icon( Icons.arrow_back, @@ -96,23 +316,23 @@ class TemplateBottomSheet { ), onPressed: (currentInd > 1) ? () { - context - .read() - .setCurrentInd(currentInd - 1); - Registration reg = context - .read() - .matchingPackets[(currentInd - 1) - 1] - ['packet'] as Registration; - log(reg.packetId); - loadHtmlData( - context - .read() - .webViewPlusController, - reg.packetId); - } + context + .read() + .setCurrentInd(currentInd - 1); + Registration reg = context + .read() + .matchingPackets[(currentInd - 1) - 1] + ['packet'] as Registration; + log(reg.packetId); + loadHtmlData( + context + .read() + .webViewPlusController, + reg.packetId); + } : () { - log("Out of range"); - }, + log("Out of range"); + }, ), ), Row( @@ -132,30 +352,30 @@ class TemplateBottomSheet { onPressed: reviewStatus == ReviewStatus.APPROVED.name ? null : () { + context + .read() + .approvePacket(regCurrent.packetId); + log(currentInd.toString()); + if (currentInd < + context + .read() + .matchingPackets + .length) { + context + .read() + .setCurrentInd(currentInd + 1); + Registration reg = context + .read() + .matchingPackets[(currentInd + 1) - 1] + ['packet'] as Registration; + log(reg.packetId); + loadHtmlData( context .read() - .approvePacket(regCurrent.packetId); - log(currentInd.toString()); - if (currentInd < - context - .read() - .matchingPackets - .length) { - context - .read() - .setCurrentInd(currentInd + 1); - Registration reg = context - .read() - .matchingPackets[(currentInd + 1) - 1] - ['packet'] as Registration; - log(reg.packetId); - loadHtmlData( - context - .read() - .webViewPlusController, - reg.packetId); - } - }, + .webViewPlusController, + reg.packetId); + } + }, label: Text(AppLocalizations.of(context)!.approve), ), const SizedBox( @@ -166,39 +386,39 @@ class TemplateBottomSheet { onPressed: reviewStatus == ReviewStatus.REJECTED.name ? null : () { - showDialog( - context: context, - builder: (BuildContext context) { - return dialogBox(() { - context - .read() - .rejectPacket(regCurrent.packetId); - if (currentInd < - context - .read() - .matchingPackets - .length) { - context - .read() - .setCurrentInd(currentInd + 1); + showDialog( + context: context, + builder: (BuildContext context) { + return dialogBox(() { + context + .read() + .rejectPacket(regCurrent.packetId); + if (currentInd < + context + .read() + .matchingPackets + .length) { + context + .read() + .setCurrentInd(currentInd + 1); - Registration reg = context - .read() - .matchingPackets[(currentInd + - 1) - - 1]['packet'] as Registration; - log(reg.packetId); - loadHtmlData( - context - .read< - ApprovePacketsProvider>() - .webViewPlusController, - reg.packetId); - } - Navigator.of(context).pop(); - }, context); - }); - }, + Registration reg = context + .read() + .matchingPackets[(currentInd + + 1) - + 1]['packet'] as Registration; + log(reg.packetId); + loadHtmlData( + context + .read< + ApprovePacketsProvider>() + .webViewPlusController, + reg.packetId); + } + Navigator.of(context).pop(); + }, context); + }); + }, style: OutlinedButton.styleFrom( disabledForegroundColor: Colors.white, disabledBackgroundColor: Colors.grey.withOpacity(0.5), @@ -224,25 +444,25 @@ class TemplateBottomSheet { elevation: 0, shape: CircleBorder( side: BorderSide( - color: reviewStatus == ReviewStatus.NOACTIONTAKEN.name - ? Colors.grey - : solidPrimary, - )), + color: reviewStatus == ReviewStatus.NOACTIONTAKEN.name + ? Colors.grey + : solidPrimary, + )), child: IconButton( onPressed: - reviewStatus == ReviewStatus.NOACTIONTAKEN.name - ? null - : () { - context - .read() - .clearReview(regCurrent.packetId); - }, + reviewStatus == ReviewStatus.NOACTIONTAKEN.name + ? null + : () { + context + .read() + .clearReview(regCurrent.packetId); + }, icon: Icon( Icons.refresh_outlined, color: - reviewStatus == ReviewStatus.NOACTIONTAKEN.name - ? Colors.grey - : solidPrimary, + reviewStatus == ReviewStatus.NOACTIONTAKEN.name + ? Colors.grey + : solidPrimary, size: 28, ), ), @@ -254,56 +474,55 @@ class TemplateBottomSheet { elevation: 0, shape: CircleBorder( side: BorderSide( - color: (currentInd < + color: (currentInd < context .read() .matchingPackets .length) - ? solidPrimary - : Colors.grey, - )), + ? solidPrimary + : Colors.grey, + )), child: IconButton( icon: Icon( Icons.arrow_forward, color: (currentInd < - context - .read() - .matchingPackets - .length) + context + .read() + .matchingPackets + .length) ? solidPrimary : Colors.grey, size: 24, ), onPressed: currentInd < - context - .read() - .matchingPackets - .length + context + .read() + .matchingPackets + .length ? () { - context - .read() - .setCurrentInd(currentInd + 1); - Registration reg = context - .read() - .matchingPackets[(currentInd + 1) - 1] - ['packet'] as Registration; - log(reg.packetId); - loadHtmlData( - context - .read() - .webViewPlusController, - reg.packetId); - } + context + .read() + .setCurrentInd(currentInd + 1); + Registration reg = context + .read() + .matchingPackets[(currentInd + 1) - 1] + ['packet'] as Registration; + log(reg.packetId); + loadHtmlData( + context + .read() + .webViewPlusController, + reg.packetId); + } : () { - log("Out of range"); - }, + log("Out of range"); + }, ), ), ], ), const SizedBox( - height: 18, - ), + height: 18), ], ), ); diff --git a/lib/ui/dashboard/user_dashboard.dart b/lib/ui/dashboard/user_dashboard.dart index 5194ece89..1641f1dae 100644 --- a/lib/ui/dashboard/user_dashboard.dart +++ b/lib/ui/dashboard/user_dashboard.dart @@ -209,11 +209,26 @@ class _UserDashBoardState extends State { ) ], ), - const SizedBox(height: 10), - Text(appLocalizations.packets_uploaded, - style: TextStyle( - fontSize: isMobileSize ? 15 : 20, - fontWeight: FontWeight.bold)), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 12), // pushes text slightly downward + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + appLocalizations.packets_uploaded, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: isMobileSize ? 15 : 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ) ], ), ); @@ -299,52 +314,56 @@ class _UserDashBoardState extends State { margin: isMobileSize ? const EdgeInsets.only(left: 10, right: 10, bottom: 10) : const EdgeInsets.only( - left: 40, right: 40, bottom: 20), + left: 40, right: 40, bottom: 20), child: SizedBox( width: MediaQuery.of(context).size.width, child: snapshot.hasData - ? DataTable( - dividerThickness: 2, - headingRowHeight: 60, - columns: [ - DataColumn( - label: Text(appLocalizations.user_id, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: isMobileSize ? 12 : 20, - color: appBlackShade2))), - DataColumn( - label: Text(appLocalizations.user_name, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: isMobileSize ? 12 : 20, - color: appBlackShade2))), - DataColumn( - label: Text(appLocalizations.status, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: isMobileSize ? 12 : 20, - color: appBlackShade2))), - ], - rows: snapshot.data! - .map((data) => DataRow(cells: [ - DataCell(Text(data!.userId.toString(), - style: TextStyle( - fontSize: - isMobileSize ? 10 : 17, - color: appBlackShade1, - fontWeight: FontWeight.w500))), - DataCell(Text(data.userName.toString(), - style: TextStyle( - fontSize: - isMobileSize ? 10 : 17, - color: appBlackShade2, - fontWeight: FontWeight.w500))), - DataCell(statusWidget(data.userStatus, - data.userIsOnboarded)) - ])) - .toList()) - : const SizedBox.shrink(), + ? FittedBox( + fit: BoxFit.scaleDown, // Shrinks table to fit in screen + alignment: Alignment.topLeft, + child:DataTable( + dividerThickness: 2, + headingRowHeight: 60, + columns: [ + DataColumn( + label: Text(appLocalizations.user_id, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: isMobileSize ? 12 : 20, + color: appBlackShade2))), + DataColumn( + label: Text(appLocalizations.user_name, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: isMobileSize ? 12 : 20, + color: appBlackShade2))), + DataColumn( + label: Text(appLocalizations.status, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: isMobileSize ? 12 : 20, + color: appBlackShade2))), + ], + rows: snapshot.data! + .map((data) => DataRow(cells: [ + DataCell(Text(data!.userId.toString(), + style: TextStyle( + fontSize: + isMobileSize ? 10 : 17, + color: appBlackShade1, + fontWeight: FontWeight.w500))), + DataCell(Text(data.userName.toString(), + style: TextStyle( + fontSize: + isMobileSize ? 10 : 17, + color: appBlackShade2, + fontWeight: FontWeight.w500))), + DataCell(statusWidget(data.userStatus, + data.userIsOnboarded)) + ])) + .toList()) + ) + : const SizedBox.shrink(), ), ); }) @@ -424,4 +443,4 @@ class _UserDashBoardState extends State { ], ); } -} +} \ No newline at end of file diff --git a/lib/ui/export_packet/export_packet_ui.dart b/lib/ui/export_packet/export_packet_ui.dart index f1156fea3..bb9f925e5 100644 --- a/lib/ui/export_packet/export_packet_ui.dart +++ b/lib/ui/export_packet/export_packet_ui.dart @@ -56,7 +56,36 @@ class ExportPacketsPage extends StatelessWidget { const SizedBox( height: 4, ), - const Row( + isMobileSize?Column( + children: [ + const Row( + children: [ + Expanded( + child: SearchBoxExport(), + ), + ], + ), + const SizedBox(height: 10), + Row( + children: [ + Expanded( + child: SizedBox( + height: 48, + child: UploadButton(), + ), + ), + const SizedBox(width: 10), + Expanded( + child: SizedBox( + height: 48, + child: ExportButton(), + ), + ), + ], + ), + ], + ) + :const Row( children: [ Flexible( flex: 2, @@ -74,6 +103,7 @@ class ExportPacketsPage extends StatelessWidget { ), ], ), + const SizedBox( height: 24, ), @@ -93,35 +123,35 @@ class ExportPacketsPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ context - .watch() - .countSelected > - 0 + .watch() + .countSelected > + 0 ? Text( - AppLocalizations.of(context)! - .total_selected_application( - context - .watch() - .countSelected, - context - .watch() - .matchingPackets - .length), - style: Theme.of(context) - .textTheme - .titleLarge - ?.copyWith(fontSize: 20), - ) + AppLocalizations.of(context)! + .total_selected_application( + context + .watch() + .countSelected, + context + .watch() + .matchingPackets + .length), + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith(fontSize: 20), + ) : Text( - AppLocalizations.of(context)! - .number_of_application(context - .watch() - .matchingPackets - .length), - style: Theme.of(context) - .textTheme - .titleLarge - ?.copyWith(fontSize: 20), - ), + AppLocalizations.of(context)! + .number_of_application(context + .watch() + .matchingPackets + .length), + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith(fontSize: 20), + ), const SizedBox( width: 50, ), diff --git a/lib/ui/export_packet/widgets/export_table.dart b/lib/ui/export_packet/widgets/export_table.dart index 833cc9838..5eaf7e35e 100644 --- a/lib/ui/export_packet/widgets/export_table.dart +++ b/lib/ui/export_packet/widgets/export_table.dart @@ -72,7 +72,7 @@ class ExportTable extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ SizedBox(width: tableWidth/35, child:Text("$index.", textAlign: TextAlign.center, )), - SizedBox(width: tableWidth/5.5, child: Text(context.watch().matchingPackets[index-1].packetId, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis,maxLines: 2,)), + SizedBox(width: tableWidth/5.5, child: Text(context.watch().matchingPackets[index-1].appId ?? context.watch().matchingPackets[index-1].id!, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis,maxLines: 2,)), SizedBox(width: tableWidth/9, child: Text(formattedDate,textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, maxLines: 2,)), SizedBox(width: tableWidth/15, child:Text(context.watch().matchingPackets[index-1].regType.toString(), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis,maxLines: 2,)), SizedBox(width: tableWidth/9, child: Text(context.watch().matchingPackets[index-1].clientStatus.toString(),textAlign: TextAlign.center, overflow: TextOverflow.ellipsis,maxLines: 2,)), diff --git a/lib/ui/machine_keys.dart b/lib/ui/machine_keys.dart index e87d2dc7d..9344f4953 100644 --- a/lib/ui/machine_keys.dart +++ b/lib/ui/machine_keys.dart @@ -108,15 +108,15 @@ class _MachineKeysState extends State { SizedBox( height: 20.h, ), - _downloadButton( - title: appLocalizations.download_json, - onTap: () { - FileStorage.writeCounter(machineDetails, "machine_details.txt") - .then((value) { - showInSnackBar(appLocalizations.download_message); - }); - }, - ), + // _downloadButton( + // title: appLocalizations.download_json, + // onTap: () { + // FileStorage.writeCounter(machineDetails, "machine_details.txt") + // .then((value) { + // showInSnackBar(appLocalizations.download_message); + // }); + // }, + // ), // SizedBox( // height: 20.h, // ), diff --git a/lib/ui/onboard/home_page.dart b/lib/ui/onboard/home_page.dart index b44145515..e68ef9335 100644 --- a/lib/ui/onboard/home_page.dart +++ b/lib/ui/onboard/home_page.dart @@ -13,6 +13,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:registration_client/model/process.dart'; +import 'package:registration_client/model/settings.dart'; import 'package:registration_client/pigeon/biometrics_pigeon.dart'; import 'package:registration_client/pigeon/dynamic_response_pigeon.dart'; import 'package:registration_client/provider/approve_packets_provider.dart'; @@ -62,6 +63,9 @@ class _HomePageState extends State { connectivityProvider = Provider.of(context, listen: false); _fetchProcessSpec(); + WidgetsBinding.instance.addPostFrameCallback((_) async { + await globalProvider.fetchLocation(); + }); super.initState(); } @@ -107,12 +111,18 @@ class _HomePageState extends State { globalProvider.setNotificationLanguages(fieldValues); } + Future> getSettingsUI(BuildContext context) async { + await registrationTaskProvider.getListOfSettings(); + return registrationTaskProvider.listOfSettings; + } + Widget getProcessUI(BuildContext context, Process process) { List sortedScreens; sortedScreens = process.screens!.toList()..sort((e1, e2) => e1!.order!.compareTo(e2!.order!)); - if (process.id == "NEW" || process.id == "UPDATE" || process.id == "LOST") { + if (process.flow == "NEW" || process.flow == "UPDATE" || process.flow == "LOST" || process.flow == "CORRECTION") { globalProvider.clearRegistrationProcessData(); globalProvider.setPreRegistrationId(""); + globalProvider.setAdditionalInfoReqId(""); for (var screen in sortedScreens) { for (var field in screen!.fields!) { if (field!.controlType == 'dropdown' && @@ -259,6 +269,9 @@ class _HomePageState extends State { syncData: (BuildContext context) { syncData(context); }, + getSettingsUI: (BuildContext context) async { + return await getSettingsUI(context); + }, ); } } diff --git a/lib/ui/onboard/onboarding_page.dart b/lib/ui/onboard/onboarding_page.dart index 2cb790038..a8f451fb4 100644 --- a/lib/ui/onboard/onboarding_page.dart +++ b/lib/ui/onboard/onboarding_page.dart @@ -115,20 +115,6 @@ class OnboardingPage extends StatelessWidget { color: appWhite, fontColor: appSolidPrimary, ), - SizedBox( - height: 40.h, - ), - _getButton( - title: appLocalizations.skip_to_home, - onTap: () { - globalProvider.setCurrentIndex(1); - }, - color: Colors.transparent, - fontColor: appWhite, - ), - SizedBox( - height: 40.h, - ), ], ), ), diff --git a/lib/ui/onboard/portrait/mobile_home_page.dart b/lib/ui/onboard/portrait/mobile_home_page.dart index eaa4cdabf..6d4899af2 100644 --- a/lib/ui/onboard/portrait/mobile_home_page.dart +++ b/lib/ui/onboard/portrait/mobile_home_page.dart @@ -9,6 +9,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; import 'package:registration_client/model/process.dart'; +import 'package:registration_client/model/settings.dart'; import 'package:registration_client/provider/global_provider.dart'; import 'package:registration_client/ui/dashboard/user_dashboard.dart'; import 'package:registration_client/ui/onboard/portrait/tasks_page.dart'; @@ -16,6 +17,7 @@ import 'package:registration_client/ui/onboard/widgets/bottom_navbar_widget.dart import 'package:registration_client/utils/app_config.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import '../../settings/settings_screen.dart'; import '../../profile/profile.dart'; class MobileHomePage extends StatefulWidget { @@ -24,20 +26,22 @@ class MobileHomePage extends StatefulWidget { required this.operationalTasks, required this.getProcessUI, required this.syncData, + required this.getSettingsUI, }); final List> operationalTasks; final Function getProcessUI; final Function syncData; + final Function getSettingsUI; @override State createState() => _MobileHomePageState(); } class _MobileHomePageState extends State { - int selectedTab = 1; + int selectedTab = 2; changeTab(int index) { - if (index == 0 || index == 1 || index == 2) { + if (index == 0 || index == 1 || index == 2 || index == 3) { setState(() { selectedTab = index; }); @@ -46,11 +50,13 @@ class _MobileHomePageState extends State { @override Widget build(BuildContext context) { - List bottomNavPages = [ + List bottomNavPages = [ const UserDashBoard(), - // const Center( - // child: Text("Settings"), - // ), + SettingsScreen( + getSettingsUI: (BuildContext context) async { + return await widget.getSettingsUI(context); + } + ), Container( height: ScreenUtil().screenHeight, width: ScreenUtil().screenWidth, @@ -81,30 +87,30 @@ class _MobileHomePageState extends State { return SafeArea( child: Scaffold( bottomNavigationBar: _getBottomNavigationBar(), - body: SingleChildScrollView( - child: Column( - children: [ - bottomNavPages[selectedTab], - selectedTab != 1 - ? Text( - "Community Registration - Client Version ${context.watch().versionNoApp}", - style: TextStyle( - color: const Color(0xff6F6E6E), - fontSize: 14, - fontWeight: regular), - ) - : const SizedBox(), - selectedTab != 1 - ? Text( - "Git Commit Id ${context.watch().commitIdApp}", - style: TextStyle( - color: const Color(0xff6F6E6E), - fontSize: 14, - fontWeight: regular), - ) - : const SizedBox(), + body: Column( + children: [ + Expanded( + child: bottomNavPages[selectedTab], + ), + if (selectedTab != 1) ...[ + Text( + "Community Registration - Client Version ${context.watch().versionNoApp}", + style: TextStyle( + color: const Color(0xff6F6E6E), + fontSize: 14, + fontWeight: regular, + ), + ), + Text( + "Git Commit Id ${context.watch().commitIdApp}", + style: TextStyle( + color: const Color(0xff6F6E6E), + fontSize: 14, + fontWeight: regular, + ), + ), ], - ), + ], ), ), ); @@ -127,16 +133,16 @@ class _MobileHomePageState extends State { title: AppLocalizations.of(context)!.dashboard, ), ), - // BottomNavigationBarItem( - // label: "", - // icon: BottomNavBarWidget( - // index: 1, - // selectedIndex: selectedTab, - // imagePath: settingsIcon, - // selectedImagePath: settingsSelectedIcon, - // title: AppLocalizations.of(context)!.settings, - // ), - // ), + BottomNavigationBarItem( + label: "", + icon: BottomNavBarWidget( + index: 1, + selectedIndex: selectedTab, + imagePath: settingsIcon, + selectedImagePath: settingsSelectedIcon, + title: AppLocalizations.of(context)!.settings, + ), + ), BottomNavigationBarItem( label: "", icon: SizedBox( @@ -162,7 +168,7 @@ class _MobileHomePageState extends State { BottomNavigationBarItem( label: "", icon: BottomNavBarWidget( - index: 2, + index: 3, selectedIndex: selectedTab, imagePath: profileIcon, selectedImagePath: profileSelectedIcon, diff --git a/lib/ui/onboard/portrait/registration_tasks.dart b/lib/ui/onboard/portrait/registration_tasks.dart index df8ae9aec..686c0aa28 100644 --- a/lib/ui/onboard/portrait/registration_tasks.dart +++ b/lib/ui/onboard/portrait/registration_tasks.dart @@ -45,30 +45,32 @@ class _RegistrationTasksState extends State { @override Widget build(BuildContext context) { isPortrait = MediaQuery.of(context).orientation == Orientation.portrait; - return Column( - children: [ - SizedBox( - height: 26.h, - ), - isMobileSize - ? Padding( - padding: EdgeInsets.symmetric(horizontal: 16.w), - child: HomePageCard( - index: 0, - icon: SvgPicture.asset( - syncDataIcon, - ), - title: AppLocalizations.of(context)!.synchronize_data, - ontap: () => widget.syncData(context), - subtitle: null, - ), - ) - : _getSyncDataProvider(), - SizedBox( - height: 16.h, - ), - _getTasks(), - ], + return SingleChildScrollView( + child: Column( + children: [ + SizedBox( + height: 26.h, + ), + isMobileSize + ? Padding( + padding: EdgeInsets.symmetric(horizontal: 16.w), + child: HomePageCard( + index: 0, + icon: SvgPicture.asset( + syncDataIcon, + ), + title: AppLocalizations.of(context)!.synchronize_data, + ontap: () => widget.syncData(context), + subtitle: null, + ), + ) + : _getSyncDataProvider(), + SizedBox( + height: 16.h, + ), + _getTasks(), + ], + ), ); } @@ -141,11 +143,11 @@ class _RegistrationTasksState extends State { Text( context.watch().lastSuccessfulSyncTime != "" ? DateFormat("EEEE d MMMM, hh:mma") - .format(DateTime.parse(context - .watch() - .lastSuccessfulSyncTime) - .toLocal()) - .toString() + .format(DateTime.parse(context + .watch() + .lastSuccessfulSyncTime) + .toLocal()) + .toString() : "Last Sync time not found", style: const TextStyle( fontSize: 18, @@ -163,8 +165,9 @@ class _RegistrationTasksState extends State { padding: EdgeInsets.symmetric(horizontal: 20.w), child: GridView.builder( itemCount: - context.watch().listOfProcesses.length, + context.watch().listOfProcesses.length, shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: (isPortrait) ? 2 : 4, mainAxisSpacing: (isPortrait) ? 8.h : 1.h, @@ -226,8 +229,8 @@ class _RegistrationTasksState extends State { ), Text( process.label![context - .read() - .selectedLanguage] ?? + .read() + .selectedLanguage] ?? process.label!["eng"] ?? process.label!.values.first, style: TextStyle( @@ -243,4 +246,4 @@ class _RegistrationTasksState extends State { }), ); } -} +} \ No newline at end of file diff --git a/lib/ui/onboard/widgets/operator_biometric_capture_scan_block_view.dart b/lib/ui/onboard/widgets/operator_biometric_capture_scan_block_view.dart index 28030db1a..def9f5812 100644 --- a/lib/ui/onboard/widgets/operator_biometric_capture_scan_block_view.dart +++ b/lib/ui/onboard/widgets/operator_biometric_capture_scan_block_view.dart @@ -101,14 +101,17 @@ class _OperatorBiometricCaptureScanBlockViewState ), Row( children: [ - const Spacer(), + //const Spacer(), const SizedBox( width: 28, ), Text( "${biometricAttributeData.title.replaceAll(" ", "")} ${AppLocalizations.of(context)!.capture}", + textAlign: TextAlign.start, + overflow: TextOverflow.visible, + maxLines: 1, style: Theme.of(context).textTheme.bodyLarge?.copyWith( - fontSize: 28, fontWeight: bold, color: blackShade1), + fontSize: 22, fontWeight: bold, color: blackShade1), ), const Spacer(), IconButton( @@ -131,34 +134,39 @@ class _OperatorBiometricCaptureScanBlockViewState Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - (biometricAttributeData.title == "Iris" && - biometricAttributeData.exceptions.contains(true)) - ? ((biometricAttributeData.exceptions.first == true) - ? SvgPicture.asset( + if (biometricAttributeData.title == "Iris" && + biometricAttributeData.exceptions.contains(true) && + biometricAttributeData.exceptions.first == true) + Expanded( + child:SvgPicture.asset( "assets/svg/Left Eye Exception.svg", height: (isMobileSize) ? 130.h : 260.h, - ) - : const SizedBox()) - : const SizedBox(), + fit: BoxFit.contain, + ), + ), + ...temp.map( - (e) => Image.memory( + (e) =>Expanded( + child: Image.memory( e!, height: (isMobileSize) ? 130.h : 260.h, + fit: BoxFit.contain, + ), ), ), - (biometricAttributeData.title == "Iris" && - biometricAttributeData.exceptions.contains(true)) - ? ((biometricAttributeData.exceptions.first == true) - ? const SizedBox() - : Transform.flip( + if (biometricAttributeData.title == "Iris" && + biometricAttributeData.exceptions.contains(true) && + biometricAttributeData.exceptions.first != true) + Expanded( + child: Transform.flip( flipX: true, child: SvgPicture.asset( "assets/svg/Left Eye Exception.svg", height: (isMobileSize) ? 130.h : 260.h, + fit: BoxFit.contain, ), )) - : const SizedBox(), - ], + ], ), // Divider( // height: 82, diff --git a/lib/ui/onboard/widgets/operator_biometrics_capture_view.dart b/lib/ui/onboard/widgets/operator_biometrics_capture_view.dart index d6123916f..35de245fc 100644 --- a/lib/ui/onboard/widgets/operator_biometrics_capture_view.dart +++ b/lib/ui/onboard/widgets/operator_biometrics_capture_view.dart @@ -12,6 +12,8 @@ import 'package:registration_client/utils/app_config.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:responsive_grid_list/responsive_grid_list.dart'; +import '../../../provider/auth_provider.dart'; + class OperatorBiometricsCaptureView extends StatefulWidget { const OperatorBiometricsCaptureView({super.key}); @@ -51,9 +53,9 @@ class _OperatorBiometricsCaptureState context, MaterialPageRoute( builder: (context) => ChangeNotifierProvider.value( - value: providerCopy, - child: OperatorBiometricCaptureScanBlockView(), - ))).then((value) { + value: providerCopy, + child: OperatorBiometricCaptureScanBlockView(), + ))).then((value) { setState(() {}); }); }, @@ -68,15 +70,15 @@ class _OperatorBiometricsCaptureState border: Border.all( color: (biometricAttributeData.isScanned == true) ? (biometricAttributeData.exceptions.contains(true)) - ? secondaryColors.elementAt(16) - : secondaryColors.elementAt(11) + ? secondaryColors.elementAt(16) + : secondaryColors.elementAt(11) : (context - .watch< - BiometricCaptureControlProvider>() - .biometricAttribute == - biometricAttributeData.title) - ? secondaryColors.elementAt(12) - : secondaryColors.elementAt(14)), + .watch< + BiometricCaptureControlProvider>() + .biometricAttribute == + biometricAttributeData.title) + ? secondaryColors.elementAt(12) + : secondaryColors.elementAt(14)), borderRadius: BorderRadius.circular(10)), child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -109,17 +111,17 @@ class _OperatorBiometricsCaptureState right: 15, child: (biometricAttributeData.exceptions.contains(true)) ? Image.asset( - "assets/images/Group 57548@2x.png", + "assets/images/Exception_Mark.png", ) : Image.asset( - "assets/images/Group 57745@2x.png", + "assets/images/Green_Check.png", )), if (!biometricAttributeData.exceptions.contains(false)) Positioned( top: 15, right: 15, child: Image.asset( - "assets/images/Group 57548@2x.png", + "assets/images/Exception_Mark.png", )), if (biometricAttributeData.isScanned == true) Positioned( @@ -130,9 +132,9 @@ class _OperatorBiometricsCaptureState horizontal: 10, vertical: 7), decoration: BoxDecoration( color: (biometricAttributeData.qualityPercentage - .toInt() < - int.parse(biometricAttributeData - .thresholdPercentage)) + .toInt() < + int.parse(biometricAttributeData + .thresholdPercentage)) ? secondaryColors.elementAt(26) : secondaryColors.elementAt(11), borderRadius: BorderRadius.circular(50)), @@ -148,9 +150,25 @@ class _OperatorBiometricsCaptureState ), )); } - @override Widget build(BuildContext context) { + globalProvider = context.watch(); + biometricCaptureControlProvider = context.watch(); + final authProvider = context.watch(); + final appLocalizations = AppLocalizations.of(context)!; + + String getBiometricTitle() { + if (authProvider.isOperator) { + return globalProvider.onboardingProcessName == "Onboarding" + ? appLocalizations.onboard_operator_biomterics + : appLocalizations.update_operator_biomterics; + } + + return globalProvider.onboardingProcessName == "Onboarding" + ? appLocalizations.supervisors_biometric_onboard + : appLocalizations.supervisors_biometric_update; + } + return SafeArea( child: Scaffold( backgroundColor: secondaryColors.elementAt(10), @@ -177,7 +195,7 @@ class _OperatorBiometricsCaptureState .contains("rightMiddle")&& globalProvider.operatorOnboardingAttributes .contains("rightIndex")) - ? ((biometricCaptureControlProvider.rightHand.isScanned || + ? ((biometricCaptureControlProvider.rightHand.isScanned || !biometricCaptureControlProvider.rightHand.exceptions .contains(false)) && (biometricCaptureControlProvider.rightHand.qualityPercentage >= int.parse(biometricCaptureControlProvider @@ -192,38 +210,35 @@ class _OperatorBiometricsCaptureState .contains("leftMiddle")&& globalProvider.operatorOnboardingAttributes .contains("leftIndex")) - ? ((biometricCaptureControlProvider.leftHand.isScanned || + ? ((biometricCaptureControlProvider.leftHand.isScanned || !biometricCaptureControlProvider.leftHand.exceptions .contains(false)) && - (biometricCaptureControlProvider.leftHand.qualityPercentage >= int.parse(biometricCaptureControlProvider.leftHand.thresholdPercentage) || - !biometricCaptureControlProvider.leftHand.exceptions - .contains(false))) : true) && + (biometricCaptureControlProvider.leftHand.qualityPercentage >= int.parse(biometricCaptureControlProvider.leftHand.thresholdPercentage) || + !biometricCaptureControlProvider.leftHand.exceptions + .contains(false))) : true) && ((globalProvider.operatorOnboardingAttributes .contains("leftThumb") && globalProvider.operatorOnboardingAttributes .contains("rightThumb")) - ? ((biometricCaptureControlProvider.thumbs.isScanned || + ? ((biometricCaptureControlProvider.thumbs.isScanned || !biometricCaptureControlProvider.thumbs.exceptions .contains(false)) && - (biometricCaptureControlProvider.thumbs.qualityPercentage >= + (biometricCaptureControlProvider.thumbs.qualityPercentage >= int.parse(biometricCaptureControlProvider.thumbs.thresholdPercentage) || - !biometricCaptureControlProvider.thumbs.exceptions.contains(false))) : true) && + !biometricCaptureControlProvider.thumbs.exceptions.contains(false))) : true) && ((globalProvider.operatorOnboardingAttributes .contains("face")) - ? ((biometricCaptureControlProvider.face.qualityPercentage >= int.parse(biometricCaptureControlProvider.face.thresholdPercentage)) && - biometricCaptureControlProvider.face.isScanned) : true)) { + ? ((biometricCaptureControlProvider.face.qualityPercentage >= int.parse(biometricCaptureControlProvider.face.thresholdPercentage)) && + biometricCaptureControlProvider.face.isScanned) : true)) { setState(() { isSavingBiometrics = true; }); String isOperatorBiometricSaved = ""; await BiometricsApi().saveOperatorBiometrics().timeout( - Duration(seconds: 10), + const Duration(seconds: 30), onTimeout: () { - setState(() { - isSavingBiometrics = false; - }); - return ""; + return "TIMEOUT"; }, ).then((value) { isOperatorBiometricSaved = value; @@ -232,7 +247,8 @@ class _OperatorBiometricsCaptureState setState(() { isSavingBiometrics = false; }); - if (isOperatorBiometricSaved != "") { + + if (isOperatorBiometricSaved == "OK") { await context.read().getLastUpdatedTime(); Navigator.pop(context); showDialog( @@ -251,12 +267,12 @@ class _OperatorBiometricsCaptureState "assets/svg/success_message_icon.svg"), Text( (context - .read() - .onboardingProcessName == - "Onboarding") + .read() + .onboardingProcessName == + "Onboarding") ? appLocalizations.onboarded_successfully : appLocalizations - .operator_biometric_updated_successfully, + .operator_biometric_updated_successfully, textAlign: TextAlign.center, style: TextStyle( fontSize: 28, @@ -293,17 +309,6 @@ class _OperatorBiometricsCaptureState } } }, - child: isSavingBiometrics - ? CircularProgressIndicator( - color: appWhite, - ) - : Text( - appLocalizations.verify_and_save, - style: Theme.of(context) - .textTheme - .bodyLarge - ?.copyWith(fontSize: 26.h, color: pureWhite), - ), style: OutlinedButton.styleFrom( backgroundColor: ( ((globalProvider.operatorOnboardingAttributes @@ -358,6 +363,17 @@ class _OperatorBiometricsCaptureState biometricCaptureControlProvider.face.isScanned) : true)) ? solidPrimary : secondaryColors.elementAt(22)), + child: isSavingBiometrics + ? const CircularProgressIndicator( + color: appWhite, + ) + : Text( + appLocalizations.verify_and_save, + style: Theme.of(context) + .textTheme + .bodyLarge + ?.copyWith(fontSize: 26.h, color: pureWhite), + ), ), ), ), @@ -377,14 +393,13 @@ class _OperatorBiometricsCaptureState mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - globalProvider.onboardingProcessName == "Onboarding" - ? appLocalizations.supervisors_biometric_onboard - : appLocalizations.supervisors_biometric_update, + getBiometricTitle(), // Call the function here style: Theme.of(context).textTheme.titleLarge?.copyWith( - fontSize: (isMobileSize) ? 16.w : 24.w, - color: blackShade1, - fontWeight: semiBold, - overflow: TextOverflow.ellipsis), + fontSize: (isMobileSize) ? 16.w : 24.w, + color: blackShade1, + fontWeight: semiBold, + overflow: TextOverflow.ellipsis, + ), ), // SizedBox( // height: (isMobileSize)?20.h:52.h, @@ -428,13 +443,13 @@ class _OperatorBiometricsCaptureState maxItemsPerRow: (isMobileSize) ? 1 : 2, children: [ if (globalProvider.operatorOnboardingAttributes - .contains("leftEye") && + .contains("leftEye") && globalProvider.operatorOnboardingAttributes .contains("rightEye")) _getBiometricCaptureSelectionBlockMobile( biometricCaptureControlProvider.iris), if (globalProvider.operatorOnboardingAttributes - .contains("rightLittle") && + .contains("rightLittle") && globalProvider.operatorOnboardingAttributes .contains("rightRing") && globalProvider.operatorOnboardingAttributes @@ -444,7 +459,7 @@ class _OperatorBiometricsCaptureState _getBiometricCaptureSelectionBlockMobile( biometricCaptureControlProvider.rightHand), if (globalProvider.operatorOnboardingAttributes - .contains("leftLittle") && + .contains("leftLittle") && globalProvider.operatorOnboardingAttributes .contains("leftRing") && globalProvider.operatorOnboardingAttributes @@ -454,7 +469,7 @@ class _OperatorBiometricsCaptureState _getBiometricCaptureSelectionBlockMobile( biometricCaptureControlProvider.leftHand), if (globalProvider.operatorOnboardingAttributes - .contains("rightThumb") && + .contains("rightThumb") && globalProvider.operatorOnboardingAttributes .contains("leftThumb")) _getBiometricCaptureSelectionBlockMobile( diff --git a/lib/ui/post_registration/acknowledgement_page.dart b/lib/ui/post_registration/acknowledgement_page.dart index bbe73b103..f9ccba188 100644 --- a/lib/ui/post_registration/acknowledgement_page.dart +++ b/lib/ui/post_registration/acknowledgement_page.dart @@ -69,7 +69,8 @@ class _AcknowledgementPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( + Expanded( + child:Text( AppLocalizations.of(context)!.registration_acknowledgement, style: const TextStyle( fontSize: 20, @@ -77,6 +78,8 @@ class _AcknowledgementPageState extends State { color: appBlackShade1, ), ), + ), + SizedBox(width: 10.w), InkWell( onTap: () { _printAcknowledgementAudit(); @@ -84,7 +87,7 @@ class _AcknowledgementPageState extends State { }, child: Container( height: 42.h, - width: 170.w, + padding: EdgeInsets.symmetric(horizontal: 12.w), decoration: BoxDecoration( color: appSolidPrimary, border: Border.all( diff --git a/lib/ui/process_ui/generic_process.dart b/lib/ui/process_ui/generic_process.dart new file mode 100644 index 000000000..9e5521a3c --- /dev/null +++ b/lib/ui/process_ui/generic_process.dart @@ -0,0 +1,1371 @@ +/* + * Copyright (c) Modular Open Source Identity Platform + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * +*/ + +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:registration_client/model/biometric_attribute_data.dart'; +import 'package:registration_client/model/field.dart'; +import 'package:registration_client/model/process.dart'; +import 'package:registration_client/model/screen.dart'; +import 'package:registration_client/ui/process_ui/process_type.dart'; +import 'package:registration_client/pigeon/biometrics_pigeon.dart'; +import 'package:registration_client/pigeon/demographics_data_pigeon.dart'; +import 'package:registration_client/pigeon/registration_data_pigeon.dart'; + +import 'package:registration_client/provider/auth_provider.dart'; +import 'package:registration_client/provider/connectivity_provider.dart'; +import 'package:registration_client/provider/global_provider.dart'; +import 'package:registration_client/provider/registration_task_provider.dart'; + +import 'package:registration_client/ui/post_registration/acknowledgement_page.dart'; +import 'package:registration_client/ui/post_registration/preview_page.dart'; +import 'package:registration_client/ui/process_ui/widgets/generic_process_screen_content.dart'; +import 'package:registration_client/ui/process_ui/widgets/update_field_selector.dart'; + +import 'package:registration_client/utils/app_config.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:registration_client/utils/app_style.dart'; + +import '../../utils/life_cycle_event_handler.dart'; + +class GenericProcess extends StatefulWidget { + final ProcessType processType; + + const GenericProcess({ + super.key, + required this.processType, + }); + + @override + State createState() => _GenericProcessState(); +} + +class _GenericProcessState extends State + with WidgetsBindingObserver { + late GlobalProvider globalProvider; + late RegistrationTaskProvider registrationTaskProvider; + late AuthProvider authProvider; + late ConnectivityProvider connectivityProvider; + late AppLocalizations appLocalizations = AppLocalizations.of(context)!; + bool isPortrait = true; + ScrollController scrollController = ScrollController(); + bool _isContinueProcessing = false; + bool fieldSelectionCompleted = false; + + List postRegistrationTabs = [ + 'Preview', + 'Authentication', + 'Acknowledgement', + ]; + + Map? templateTitleMap; + + String username = ''; + String password = ''; + + @override + void initState() { + globalProvider = Provider.of(context, listen: false); + registrationTaskProvider = + Provider.of(context, listen: false); + authProvider = Provider.of(context, listen: false); + connectivityProvider = + Provider.of(context, listen: false); + super.initState(); + WidgetsBinding.instance.addObserver(LifecycleEventHandler( + resumeCallBack: () async { + if (mounted) { + setState(() { + closeKeyboard(); + }); + } + }, + suspendingCallBack: () async { + if (mounted) { + setState(() { + closeKeyboard(); + }); + } + }, + )); + _registrationScreenLoadedAudit(); + WidgetsBinding.instance.addPostFrameCallback((_) async { + await _fetchLocation(); + }); + } + + bool _locationFetched = false; + + Future _fetchLocation() async { + if (_locationFetched) return; + _locationFetched = true; + + Position? position = await globalProvider.fetchLocation(); + if (position != null) { + registrationTaskProvider.setCurrentLocation(position.latitude, position.longitude); + } else { + debugPrint("Location unavailable — permission denied or service off."); + } + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + void closeKeyboard() { + FocusScope.of(context).unfocus(); + } + + void _showInSnackBar(String value) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(value), + ), + ); + } + + _authenticatePacket(BuildContext context) async { + if (!_validateUsername(context)) { + return false; + } + + if (!_validatePassword(context)) { + return false; + } + + if (authProvider.currentUser.userId != username) { + _showInSnackBar(appLocalizations.invalid_user); + return false; + } + + await authProvider.authenticatePacket(username, password); + + if (!authProvider.isPacketAuthenticated) { + _showErrorInSnackbar(); + return false; + } + return true; + } + + _showErrorInSnackbar() { + String errorMsg = authProvider.packetError; + String snackbarText = ""; + + switch (errorMsg) { + case "REG_TRY_AGAIN": + snackbarText = appLocalizations.login_failed; + break; + + case "REG_INVALID_REQUEST": + snackbarText = appLocalizations.password_incorrect; + break; + + case "REG_NETWORK_ERROR": + snackbarText = appLocalizations.network_error; + break; + + case "": + return; + + default: + snackbarText = errorMsg; + break; + } + + _showInSnackBar(snackbarText); + } + + bool _validateUsername(BuildContext context) { + if (username.trim().isEmpty) { + _showInSnackBar(appLocalizations.username_required); + return false; + } + + if (username.trim().length > 50) { + _showInSnackBar(appLocalizations.username_exceed); + return false; + } + + return true; + } + + bool _validatePassword(BuildContext context) { + if (password.trim().isEmpty) { + _showInSnackBar(appLocalizations.password_required); + return false; + } + + if (password.trim().length > 50) { + _showInSnackBar(appLocalizations.password_exceed); + return false; + } + + return true; + } + + _resetValuesOnRegistrationComplete() { + Navigator.of(context).pop(); + } + + void _registrationScreenLoadedAudit() async { + await globalProvider.getAudit("REG-EVT-002", "REG-MOD-103"); + } + + _nextButtonClickedAudit() async { + await globalProvider.getAudit("REG-EVT-003", "REG-MOD-103"); + } + + setScrollToTop() { + scrollController.animateTo( + scrollController.position.minScrollExtent, + duration: const Duration(milliseconds: 500), + curve: Curves.easeOut, + ); + globalProvider.isPageChanged = false; + } + + Future onWillPop(Process process) async { + if (globalProvider.newProcessTabIndex > 0 && + globalProvider.newProcessTabIndex < + process.screens!.length + postRegistrationTabs.length - 1) { + globalProvider.newProcessTabIndex = + globalProvider.newProcessTabIndex - 1; + } else if (globalProvider.newProcessTabIndex == 0 && + fieldSelectionCompleted) { + setState(() { + fieldSelectionCompleted = false; + }); + } else { + return true; + } + return false; + } + + bool isExceptionPresent(String id) { + bool isExceptionPresent = false; + for (BiometricAttributeData x in globalProvider.fieldInputValue[id]) { + if (x.exceptions.contains(true) || x.title == "Exception") { + isExceptionPresent = true; + break; + } + } + return isExceptionPresent; + } + + int returnBiometricListLength(List? list, String id) { + int i = 0; + if (list!.contains("leftEye") && list.contains("rightEye")) { + i++; + } + if (list.contains("rightIndex") && + list.contains("rightLittle") && + list.contains("rightRing") && + list.contains("rightMiddle")) { + i++; + } + if (list.contains("leftIndex") && + list.contains("leftLittle") && + list.contains("leftRing") && + list.contains("leftMiddle")) { + i++; + } + if (list.contains("rightThumb") && list.contains("rightThumb")) { + i++; + } + if (list.contains("face")) { + i++; + } + if (isExceptionPresent(id) == true) { + i++; + } + return i; + } + + Future ageDateChangeValidation(int currentIndex, Process process, int size) async { + if (globalProvider.newProcessTabIndex < size) { + Screen screen = process.screens!.elementAt(currentIndex)!; + for (int i = 0; i < screen.fields!.length; i++) { + if (screen.fields!.elementAt(i)!.controlType == "ageDate") { + if (globalProvider.checkAgeGroupChange == "") { + globalProvider.checkAgeGroupChange = globalProvider.ageGroup; + } else { + if (globalProvider.checkAgeGroupChange + .compareTo(globalProvider.ageGroup) == + 0) { + } else { + List screens = []; + for (int i = 0; + i < registrationTaskProvider.listOfProcesses.length; + i++) { + Process tempProcess = Process.fromJson( + jsonDecode( + context + .read() + .listOfProcesses + .elementAt(i) + .toString(), + ), + ); + if (tempProcess.id == widget.processType.id || + (widget.processType == ProcessType.updateProcess && + tempProcess.id == "NEW")) { + screens = tempProcess.screens!; + } + } + for (Screen? screen in screens) { + if (screen!.name! == "Documents" || + screen.name! == "BiometricDetails") { + for (Field? field in screen.fields!) { + if (globalProvider.fieldInputValue + .containsKey(field!.id!)) { + globalProvider.fieldInputValue.remove(field.id); + } + } + } + } + await BiometricsApi().clearBiometricAndDocumentHashmap(); + globalProvider.clearExceptions(); + globalProvider.checkAgeGroupChange = globalProvider.ageGroup; + } + } + } + } + } + } + + Future biometricRequiredFieldValidation(Field field) async { + if (field.controlType == "biometrics") { + int count = returnBiometricListLength(field.bioAttributes, field.id!); + if (globalProvider.completeException[field.id!] != null) { + int length = globalProvider.completeException[field.id!].length; + count = count - length; + } + + if (globalProvider.fieldInputValue[field.id!].length < count) { + return false; + } + } + + return true; + } + + Future biometricConditionalFieldValidation(Field field) async { + bool valid = await BiometricsApi().conditionalBioAttributeValidation( + field.id!, field.conditionalBioAttributes!.first!.validationExpr!); + if (field.exceptionPhotoRequired == true) { + List biometricAttributeDataList = + globalProvider.fieldInputValue[field.id!]; + bool isExceptionPresent = false; + bool isExceptionAttributePresent = false; + for (var biometricAttributeData in biometricAttributeDataList) { + if (globalProvider.exceptionAttributes + .contains(biometricAttributeData.title)) { + isExceptionPresent = true; + } + if (biometricAttributeData.title == "Exception") { + isExceptionAttributePresent = true; + } + } + + if (isExceptionPresent == true && + isExceptionAttributePresent == false) { + return false; + } + } + if (!valid) { + return false; + } + return true; + } + + Future biometricValidation(Field field) async { + if (field.conditionalBioAttributes != null && + field.conditionalBioAttributes!.isNotEmpty) { + String response = await BiometricsApi().getAgeGroup(); + if (response + .compareTo(field.conditionalBioAttributes!.first!.ageGroup!) != + 0) { + bool isValid = true; + if (field.conditionalBioAttributes!.first!.ageGroup! == "ALL") { + isValid = await biometricConditionalFieldValidation(field); + } else { + isValid = await biometricRequiredFieldValidation(field); + } + + if (!isValid) { + return false; + } + } + + if (response + .compareTo(field.conditionalBioAttributes!.first!.ageGroup!) == + 0) { + bool isValid = await biometricConditionalFieldValidation(field); + if (!isValid) { + return false; + } + } + } + + return true; + } + + Future evaluateMVELVisible(String fieldData) async { + bool visible = + await registrationTaskProvider.evaluateMVELVisible(fieldData); + return visible; + } + + Future evaluateMVELRequired(String fieldData) async { + bool required = + await registrationTaskProvider.evaluateMVELRequired(fieldData); + return required; + } + + Future customValidation(int currentIndex, Process process, int size) async { + // First screen validation + if (currentIndex == 0) { + if (widget.processType == ProcessType.updateProcess) { + if (globalProvider.updateUINNumber.isEmpty) { + return false; + } + } + return true; + } + + bool isValid = true; + if (globalProvider.newProcessTabIndex < size) { + Screen screen = process.screens!.elementAt(currentIndex)!; + for (int i = 0; i < screen.fields!.length; i++) { + Field field = screen.fields!.elementAt(i)!; + + // Update process specific validation + if (widget.processType == ProcessType.updateProcess) { + String group = field.group!; + String fieldId = field.id!; + if (globalProvider.selectedUpdateFields[group] != null) { + if (field.required! || + (globalProvider.mvelRequiredFields[fieldId] ?? false)) { + if (!(globalProvider.fieldInputValue.containsKey(field.id)) && + !(globalProvider.fieldInputValue.containsKey(field.subType)) && + !(globalProvider.fieldInputValue + .containsKey("${field.group}${field.subType}"))) { + return false; + } + + bool isValid = await biometricValidation(field); + if (!isValid) { + return false; + } + } + } else if (process.autoSelectedGroups!.contains(group)) { + if (globalProvider.mvelRequiredFields[fieldId] ?? false) { + if (!(globalProvider.fieldInputValue.containsKey(field.id)) && + !(globalProvider.fieldInputValue + .containsKey(field.subType)) && + !(globalProvider.fieldInputValue + .containsKey("${field.group}${field.subType}"))) { + return false; + } + + if (field.conditionalBioAttributes != null && + field.conditionalBioAttributes!.isNotEmpty) { + bool isValid = + await biometricConditionalFieldValidation(field); + if (!isValid) { + return false; + } + } + } + } + continue; + } + + // Common validation for other processes + if (field.inputRequired! && field.required!) { + if (!(globalProvider.fieldInputValue.containsKey(field.id)) && + !(globalProvider.fieldInputValue.containsKey(field.subType)) && + !(globalProvider.fieldInputValue + .containsKey("${field.group}${field.subType}"))) { + isValid = false; + break; + } + if (field.conditionalBioAttributes != null && + field.conditionalBioAttributes!.isNotEmpty) { + String response = await BiometricsApi().getAgeGroup(); + if (!(response.compareTo( + field.conditionalBioAttributes!.first!.ageGroup!) == + 0)) { + if (field.controlType == "biometrics") { + int count = + returnBiometricListLength(field.bioAttributes, field.id!); + if (globalProvider.completeException[field.id!] != null) { + int length = + globalProvider.completeException[field.id!].length; + count = count - length; + } + + if (globalProvider.fieldInputValue[field.id!].length < count) { + isValid = false; + break; + } + if (globalProvider.isValidBiometricCapture) { + isValid = false; + break; + } + } + } + } + } + if (field.requiredOn != null && field.requiredOn!.isNotEmpty) { + bool visible = + await evaluateMVELVisible(jsonEncode(field.toJson())); + bool required = + await evaluateMVELRequired(jsonEncode(field.toJson())); + if (visible && required) { + if (field.inputRequired!) { + if (!(globalProvider.fieldInputValue.containsKey(field.id)) && + !(globalProvider.fieldInputValue + .containsKey(field.subType)) && + !(globalProvider.fieldInputValue + .containsKey("${field.group}${field.subType}"))) { + isValid = false; + break; + } + if (field.conditionalBioAttributes != null && + field.conditionalBioAttributes!.isNotEmpty) { + String response = await BiometricsApi().getAgeGroup(); + if (!(response.compareTo( + field.conditionalBioAttributes!.first!.ageGroup!) == + 0)) { + if (field.controlType == "biometrics") { + int count = returnBiometricListLength( + field.bioAttributes, field.id!); + if (globalProvider.completeException[field.id!] != null) { + int length = + globalProvider.completeException[field.id!].length; + count = count - length; + } + if (globalProvider.fieldInputValue[field.id!].length < + count) { + isValid = false; + break; + } + if (globalProvider.isValidBiometricCapture) { + isValid = false; + break; + } + } + } + if (response.compareTo( + field.conditionalBioAttributes!.first!.ageGroup!) == + 0) { + bool valid = await BiometricsApi() + .conditionalBioAttributeValidation(field.id!, + field.conditionalBioAttributes!.first!.validationExpr!); + if (field.exceptionPhotoRequired == true) { + List biometricAttributeDataList = + globalProvider.fieldInputValue[field.id!]; + bool isExceptionPresent = false; + bool isExceptionAttributePresent = false; + for (var biometricAttributeData + in biometricAttributeDataList) { + if (globalProvider.exceptionAttributes + .contains(biometricAttributeData.title)) { + isExceptionPresent = true; + } + if (biometricAttributeData.title == "Exception") { + isExceptionAttributePresent = true; + } + } + if (isExceptionPresent == true && + isExceptionAttributePresent == false) { + isValid = false; + break; + } + } + if (!valid) { + isValid = false; + break; + } + } + } + } + } + } + } + } + return isValid; + } + + Future continueButtonTap(int size, Process process) async { + setState(() { + _isContinueProcessing = true; + }); + + try { + // Field selection validation (UPDATE process only) + if (globalProvider.newProcessTabIndex == 0 && + !fieldSelectionCompleted && + process.id == "UPDATE") { + if (globalProvider.selectedUpdateFields.isEmpty || + globalProvider.updateFieldKey.currentState == null || + !globalProvider.updateFieldKey.currentState!.validate()) { + return; + } + } + + // Field selection completion (UPDATE process only) + if (!fieldSelectionCompleted && + process.id == "UPDATE" && + globalProvider.selectedUpdateFields.isNotEmpty) { + globalProvider.clearMap(); + globalProvider.clearScannedPages(); + globalProvider.clearExceptions(); + await registrationTaskProvider.changeUpdatableFieldGroups(); + globalProvider.ageGroup = ""; + await BiometricsApi().clearBiometricAndDocumentHashmap(); + setState(() { + fieldSelectionCompleted = true; + }); + return; + } + + // Page change tracking for new process + if (widget.processType == ProcessType.newProcess) { + globalProvider.isPageChanged = true; + } + + if (globalProvider.newProcessTabIndex < size) { + await ageDateChangeValidation(globalProvider.newProcessTabIndex, process, size); + bool customValidator = + await customValidation(globalProvider.newProcessTabIndex, process, size); + if (customValidator) { + if (globalProvider.formKey.currentState!.validate()) { + // Additional info validation - prevent navigation if required but not filled + final screen = process.screens![globalProvider.newProcessTabIndex]!; + if (screen.additionalInfoRequestIdRequired == true && + (globalProvider.additionalInfoReqId == null || + globalProvider.additionalInfoReqId!.trim().isEmpty)) { + _showInSnackBar(appLocalizations.enter_additional_info_req_id); + return; + } + + if (globalProvider.newProcessTabIndex == + process.screens!.length - 1) { + templateTitleMap = { + 'demographicInfo': appLocalizations.demographic_information, + 'documents': appLocalizations.documents, + 'bioMetrics': appLocalizations.biometrics + }; + registrationTaskProvider.setPreviewTemplate(""); + registrationTaskProvider.setAcknowledgementTemplate(""); + await registrationTaskProvider.getPreviewTemplate( + true, templateTitleMap!); + await registrationTaskProvider.getAcknowledgementTemplate( + false, + templateTitleMap!, + ); + } + + globalProvider.newProcessTabIndex = + globalProvider.newProcessTabIndex + 1; + } + } + + _nextButtonClickedAudit(); + } else { + if (globalProvider.newProcessTabIndex == size + 1) { + bool isPacketAuthenticated = await _authenticatePacket(context); + if (!isPacketAuthenticated) { + return; + } + RegistrationSubmitResponse registrationSubmitResponse = + await registrationTaskProvider.submitRegistrationDto(username); + if (registrationSubmitResponse.errorCode != null && registrationSubmitResponse.errorCode!.isNotEmpty) { + _showInSnackBar(registrationSubmitResponse.errorCode!); + return; + } + globalProvider.setRegId(registrationSubmitResponse.rId); + + // Updating key to packetId after success creation of packet + registrationTaskProvider + .updateTemplateStorageKey(registrationSubmitResponse.rId); + registrationTaskProvider.deleteDefaultTemplateStored(); + + setState(() { + username = ''; + password = ''; + }); + } + if (globalProvider.newProcessTabIndex == size + 2) { + _resetValuesOnRegistrationComplete(); + return; + } + globalProvider.newProcessTabIndex = + globalProvider.newProcessTabIndex + 1; + } + } finally { + setState(() { + _isContinueProcessing = false; + }); + } + } + + bool continueButton = false; + bool authButton = false; + + @override + Widget build(BuildContext context) { + // Scroll to top when page changes + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + if (globalProvider.isPageChanged) { + setScrollToTop(); + } + }); + + postRegistrationTabs = [ + appLocalizations.preview_page, + appLocalizations.packet_auth_page, + appLocalizations.acknowledgement_page, + ]; + isPortrait = MediaQuery.of(context).orientation == Orientation.portrait; + bool isMobile = MediaQuery.of(context).size.width < 750; + double w = ScreenUtil().screenWidth; + Map arguments = + ModalRoute.of(context)!.settings.arguments! as Map; + final Process process = arguments["process"]; + int size = process.screens!.length; + + customValidation(globalProvider.newProcessTabIndex, process, size).then((value) { + setState(() { + // Field selection screen validation (UPDATE process before consent) + if (globalProvider.newProcessTabIndex == 0 && !fieldSelectionCompleted) { + continueButton = value && + globalProvider.updateFieldKey.currentState != null && + globalProvider.updateFieldKey.currentState!.validate(); + } + // Consent screen after field selection + else if (globalProvider.newProcessTabIndex == 0 && fieldSelectionCompleted) { + continueButton = true; + } + // Regular screen validation + else { + continueButton = value && + globalProvider.formKey.currentState != null && + globalProvider.formKey.currentState!.validate(); + + // Additional info validation + if (globalProvider.newProcessTabIndex < size) { + final screen = process.screens![globalProvider.newProcessTabIndex]!; + if (screen.additionalInfoRequestIdRequired == true && + (globalProvider.additionalInfoReqId == null || + globalProvider.additionalInfoReqId!.trim().isEmpty)) { + continueButton = false; + } + } + } + }); + if (globalProvider.newProcessTabIndex >= size) { + continueButton = true; + } + }); + + // Auth button validation for all processes + if (username.trim().isNotEmpty && password.trim().isNotEmpty) { + authButton = true; + } + + return WillPopScope( + onWillPop: () => onWillPop(process), + child: SafeArea( + child: Scaffold( + backgroundColor: secondaryColors.elementAt(10), + bottomNavigationBar: _buildBottomNavigationBar(size, process, isPortrait, isMobile), + body: _buildBody(size, process, isPortrait, isMobile, w), + ), + ), + ); + } + + Widget _buildBottomNavigationBar(int size, Process process, bool isPortrait, bool isMobile) { + return Container( + decoration: BoxDecoration( + border: const Border( + top: BorderSide( + color: dividerColor, + width: 1, + ), + ), + color: pureWhite, + ), + padding: EdgeInsets.symmetric( + horizontal: isPortrait ? 20.w : 60.w, + vertical: 16.h, + ), + child: globalProvider.newProcessTabIndex == 0 + ? _buildFirstScreenButtons(size, process, isPortrait, isMobile) + : _buildOtherScreenButtons(size, process, isPortrait, isMobile), + ); + } + + Widget _buildFirstScreenButtons(int size, Process process, bool isPortrait, bool isMobile) { + final bool isUpdate = process.id == "UPDATE"; + return Row( + children: [ + Expanded( + child: (isUpdate && fieldSelectionCompleted) + ? OutlinedButton( + child: SizedBox( + height: isPortrait && !isMobileSize ? 68.h : 52.h, + child: Center( + child: Text( + appLocalizations.go_back, + style: TextStyle( + fontSize: isPortrait && !isMobileSize ? 22 : 14, + ), + ), + ), + ), + onPressed: () { + setState(() { + fieldSelectionCompleted = false; + }); + }, + ) + : (!isUpdate + ? OutlinedButton( + child: SizedBox( + height: isPortrait && !isMobileSize ? 68.h : 52.h, + child: Center( + child: Text( + appLocalizations.go_back, + style: TextStyle( + fontSize: isPortrait && !isMobileSize ? 22 : 14, + ), + ), + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ) + : const SizedBox()), + ), + SizedBox( + width: 10.w, + ), + Expanded( + child: ElevatedButton( + style: isUpdate + ? ButtonStyle( + maximumSize: MaterialStateProperty.all( + const Size(209, 52)), + minimumSize: MaterialStateProperty.all( + const Size(209, 52)), + backgroundColor: MaterialStateProperty.all( + !fieldSelectionCompleted + ? (continueButton) + ? solidPrimary + : Colors.grey + : solidPrimary), + ) + : null, + onPressed: _isContinueProcessing + ? null + : () async { + if (isUpdate && fieldSelectionCompleted) { + registrationTaskProvider.addConsentField("Y"); + await DemographicsApi().addDemographicField( + "UIN", globalProvider.updateUINNumber); + await DemographicsApi() + .addDemographicField("consent", "true"); + } else { + registrationTaskProvider.addConsentField("Y"); + await DemographicsApi() + .addDemographicField("consent", "true"); + } + continueButtonTap(size, process); + }, + child: SizedBox( + height: isPortrait && !isMobileSize ? 68.h : 52.h, + child: Center( + child: Text( + isUpdate && fieldSelectionCompleted + ? appLocalizations.informed + : (isUpdate + ? appLocalizations.continue_text + : appLocalizations.informed), + style: TextStyle( + fontSize: isPortrait && !isMobileSize ? 22 : 14, + ), + ), + ), + ), + ), + ), + ], + ); + } + + Widget _buildOtherScreenButtons(int size, Process process, bool isPortrait, bool isMobile) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Expanded( + child: SizedBox(), + ), + ElevatedButton( + style: ButtonStyle( + maximumSize: MaterialStateProperty.all(const Size(209, 52)), + minimumSize: MaterialStateProperty.all(const Size(209, 52)), + backgroundColor: MaterialStateProperty.all( + (continueButton && + context.read().newProcessTabIndex <= size) + ? solidPrimary + : authButton + ? solidPrimary + : Colors.grey), + ), + onPressed: _isContinueProcessing + ? null + : () async { + await continueButtonTap(size, process); + }, + child: Text( + context.read().newProcessTabIndex <= size + ? appLocalizations.continue_text + : globalProvider.newProcessTabIndex == size + 1 + ? appLocalizations.authenticate + : appLocalizations.go_to_home, + style: const TextStyle(color: appWhite), + ), + ), + ], + ); + } + + Widget _buildBody(int size, Process process, bool isPortrait, bool isMobile, double w) { + return SingleChildScrollView( + controller: scrollController, + child: AnnotatedRegion( + value: const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + ), + child: Column( + children: [ + Container( + padding: isMobile && !isMobileSize + ? const EdgeInsets.fromLTRB(0, 46, 0, 0) + : const EdgeInsets.fromLTRB(0, 0, 0, 0), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0xff214FBF), Color(0xff1C43A1)], + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: w, + height: isPortrait ? 21.w : 30.w, + ), + Padding( + padding: isPortrait + ? EdgeInsets.fromLTRB(20.w, 0, 0, 0) + : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), + child: Text( + process.label![context + .read() + .selectedLanguage]!, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: pureWhite, + fontWeight: semiBold, + fontSize: isPortrait ? 24 : 21), + ), + ), + SizedBox( + height: 30.h, + ), + if (process.id != "UPDATE" || + fieldSelectionCompleted) + Divider( + height: 12.h, + thickness: 1, + color: secondaryColors.elementAt(2), + ), + if (process.id != "UPDATE" || + fieldSelectionCompleted) + _buildTabBar(size, process, isPortrait), + const SizedBox( + height: 5, + ), + ], + ), + ), + Padding( + padding: isPortrait + ? const EdgeInsets.all(0) + : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), + child: _buildMainContent(size, process), + ), + SizedBox( + height: 20.h, + ), + ], + ), + ), + ); + } + + Widget _buildTabBar(int size, Process process, bool isPortrait) { + return Padding( + padding: isPortrait + ? const EdgeInsets.all(0) + : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), + child: Stack( + alignment: FractionalOffset.centerRight, + children: [ + Padding( + padding: isPortrait + ? EdgeInsets.fromLTRB(20.w, 10.h, 0, 0) + : EdgeInsets.fromLTRB(0, 10.h, 0, 0), + child: SizedBox( + height: 36.h, + child: ListView.builder( + padding: const EdgeInsets.all(0), + scrollDirection: Axis.horizontal, + itemCount: process.screens!.length + 3, + itemBuilder: (BuildContext context, int index) { + return GestureDetector( + onTap: () { + if (context.read().newProcessTabIndex == + size + 2) { + return; + } + + if (index < + context + .read() + .newProcessTabIndex) { + context.read().newProcessTabIndex = + index; + } + }, + child: Row( + children: [ + Container( + padding: EdgeInsets.fromLTRB(0, 0, 0, 8.h), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: (context + .watch() + .newProcessTabIndex == + index) + ? pureWhite + : Colors.transparent, + width: 3), + ), + ), + child: Row( + children: [ + (index < + context + .watch() + .newProcessTabIndex) + ? Icon( + Icons.check_circle, + size: 17, + color: secondaryColors.elementAt(11), + ) + : (context + .watch() + .newProcessTabIndex == + index) + ? Icon( + Icons.circle, + color: pureWhite, + size: 17, + ) + : Icon( + Icons.circle_outlined, + size: 17, + color: secondaryColors.elementAt(9), + ), + SizedBox( + width: 6.w, + ), + Text( + index < size + ? process.screens![index]!.label![context + .read() + .selectedLanguage]! + : postRegistrationTabs[index - size], + style: Theme.of(context) + .textTheme + .titleSmall + ?.copyWith( + color: (context + .watch() + .newProcessTabIndex == + index) + ? pureWhite + : secondaryColors.elementAt(9), + fontWeight: semiBold, + fontSize: 14), + ), + ], + ), + ), + SizedBox( + width: 35.w, + ), + ], + ), + ); + }), + ), + ), + if (context.watch().newProcessTabIndex < size + 2) + Container( + height: 36.h, + width: 25.w, + padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), + color: solidPrimary, + child: Icon( + Icons.arrow_forward_ios_outlined, + color: pureWhite, + size: 17, + ), + ), + ], + ), + ); + } + + Widget _buildMainContent(int size, Process process) { + // Show field selector for UPDATE process before consent + if (!fieldSelectionCompleted && process.id == "UPDATE") { + return UpdateFieldSelector(process: process); + } + + if (context.watch().newProcessTabIndex < size) { + return GenericProcessScreenContent( + context: context, + screen: process.screens!.elementAt( + context.watch().newProcessTabIndex)!, + processType: widget.processType, + process: process, + ); + } + + if (context.watch().newProcessTabIndex == size) { + return const PreviewPage(); + } + + if (context.watch().newProcessTabIndex == size + 1) { + return _getPacketAuthComponent(); + } + + return const AcknowledgementPage(); + } + + Widget _getPacketAuthComponent() { + return Column( + children: [ + SizedBox( + height: 30.h, + ), + Container( + width: isPortrait && !isMobileSize ? 566.w : 376.w, + padding: EdgeInsets.only( + top: 24.h, + bottom: 28.h, + left: 20.w, + right: 20.w, + ), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(6), + ), + color: pureWhite, + ), + child: Column( + children: [ + _getAuthIcon(), + SizedBox( + height: 26.h, + ), + Text( + appLocalizations.authenticate_using_password, + style: TextStyle( + fontSize: isPortrait && !isMobileSize ? 24 : 18, + fontWeight: semiBold, + color: appBlack), + ), + SizedBox( + height: 35.h, + ), + Row( + children: [ + Text( + appLocalizations.username, + style: isPortrait + ? AppTextStyle.tabletPortraitTextfieldHeader + : AppTextStyle.mobileTextfieldHeader, + ), + const Text( + ' *', + style: TextStyle( + color: mandatoryField, + ), + ), + ], + ), + SizedBox( + height: 11.h, + ), + _getUsernameTextField(), + SizedBox( + height: 35.h, + ), + Row( + children: [ + Text( + appLocalizations.password, + style: isPortrait + ? AppTextStyle.tabletPortraitTextfieldHeader + : AppTextStyle.mobileTextfieldHeader, + ), + const Text( + ' *', + style: TextStyle(color: mandatoryField), + ), + ], + ), + SizedBox( + height: 11.h, + ), + _getPasswordTextField(), + ], + ), + ), + ], + ); + } + + _getAuthIcon() { + bool useImage = widget.processType == ProcessType.lostProcess || + widget.processType == ProcessType.updateProcess; + + return Container( + height: 80.w, + width: 80.w, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: authIconBorder, + width: 2, + ), + color: authIconBackground, + ), + child: Center( + child: useImage + ? Image.asset('assets/images/Registering an Individual@2x.png') + : SvgPicture.asset('assets/images/AuthenticationIcon.svg'), + ), + ); + } + + _getUsernameTextField() { + return Container( + height: isPortrait && !isMobileSize ? 82.h : 52.h, + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: 12.w, + ), + decoration: BoxDecoration( + border: Border.all( + width: 1.h, + color: appGreyShade, + ), + borderRadius: const BorderRadius.all( + Radius.circular(6), + ), + ), + child: TextField( + decoration: InputDecoration( + hintText: appLocalizations.enter_username, + hintStyle: isPortrait && !isMobileSize + ? AppTextStyle.tabletPortraitTextfieldHintText + : AppTextStyle.mobileTextfieldHintText, + border: InputBorder.none, + ), + style: TextStyle( + fontSize: isPortrait && !isMobileSize ? 22 : 14, + color: appBlack, + ), + onChanged: (v) { + setState(() { + username = v; + }); + }, + ), + ); + } + + _getPasswordTextField() { + return Container( + height: isPortrait && !isMobileSize ? 82.h : 52.h, + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: 12.w, + ), + decoration: BoxDecoration( + border: Border.all( + width: 1.h, + color: appGreyShade, + ), + borderRadius: const BorderRadius.all( + Radius.circular(6), + ), + ), + child: TextField( + obscureText: true, + decoration: InputDecoration( + hintText: appLocalizations.enter_password, + hintStyle: isPortrait && !isMobileSize + ? AppTextStyle.tabletPortraitTextfieldHintText + : AppTextStyle.mobileTextfieldHintText, + border: InputBorder.none, + ), + style: TextStyle( + fontSize: isPortrait && !isMobileSize ? 22 : 14, + color: appBlack, + ), + onChanged: (v) { + setState(() { + password = v; + }); + }, + ), + ); + } +} + diff --git a/lib/ui/process_ui/lost_process.dart b/lib/ui/process_ui/lost_process.dart deleted file mode 100644 index deb493f8e..000000000 --- a/lib/ui/process_ui/lost_process.dart +++ /dev/null @@ -1,1219 +0,0 @@ -/* - * Copyright (c) Modular Open Source Identity Platform - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * -*/ - -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:provider/provider.dart'; -import 'package:registration_client/model/biometric_attribute_data.dart'; -import 'package:registration_client/model/field.dart'; -import 'package:registration_client/model/process.dart'; -import 'package:registration_client/model/screen.dart'; -import 'package:registration_client/pigeon/biometrics_pigeon.dart'; -import 'package:registration_client/pigeon/demographics_data_pigeon.dart'; -import 'package:registration_client/pigeon/registration_data_pigeon.dart'; - -import 'package:registration_client/provider/auth_provider.dart'; -import 'package:registration_client/provider/connectivity_provider.dart'; -import 'package:registration_client/provider/global_provider.dart'; -import 'package:registration_client/provider/registration_task_provider.dart'; - -import 'package:registration_client/ui/post_registration/acknowledgement_page.dart'; - -import 'package:registration_client/ui/post_registration/preview_page.dart'; -import 'package:registration_client/ui/process_ui/widgets/lost_process_screen_content.dart'; -import 'package:registration_client/utils/app_config.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:registration_client/utils/app_style.dart'; - -import '../../utils/life_cycle_event_handler.dart'; - -class LostProcess extends StatefulWidget { - const LostProcess({ - super.key, - }); - - static const routeName = '/lost_process'; - - @override - State createState() => _LostProcessState(); -} - -class _LostProcessState extends State - with WidgetsBindingObserver { - late GlobalProvider globalProvider; - late RegistrationTaskProvider registrationTaskProvider; - late AuthProvider authProvider; - late ConnectivityProvider connectivityProvider; - late AppLocalizations appLocalizations = AppLocalizations.of(context)!; - bool isPortrait = true; - - List postRegistrationTabs = [ - 'Preview', - 'Authentication', - 'Acknowledgement', - ]; - - Map? templateTitleMap; - - String username = ''; - String password = ''; - - @override - void initState() { - globalProvider = Provider.of(context, listen: false); - registrationTaskProvider = - Provider.of(context, listen: false); - authProvider = Provider.of(context, listen: false); - connectivityProvider = - Provider.of(context, listen: false); - super.initState(); - WidgetsBinding.instance.addObserver(LifecycleEventHandler( - resumeCallBack: () async { - if (mounted) { - setState(() { - closeKeyboard(); - }); - } - }, - suspendingCallBack: () async { - if (mounted) { - setState(() { - closeKeyboard(); - }); - } - }, - )); - _registrationScreenLoadedAudit(); - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } - - void closeKeyboard() { - FocusScope.of(context).unfocus(); - } - - void _showInSnackBar(String value) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(value), - ), - ); - } - - _authenticatePacket(BuildContext context) async { - if (!_validateUsername(context)) { - return false; - } - - if (!_validatePassword(context)) { - return false; - } - - if (authProvider.currentUser.userId != username) { - _showInSnackBar(appLocalizations.invalid_user); - return false; - } - - await authProvider.authenticatePacket(username, password); - - if (!authProvider.isPacketAuthenticated) { - _showErrorInSnackbar(); - return false; - } - return true; - } - - _showErrorInSnackbar() { - String errorMsg = authProvider.packetError; - String snackbarText = ""; - - switch (errorMsg) { - case "REG_TRY_AGAIN": - snackbarText = appLocalizations.login_failed; - break; - - case "REG_INVALID_REQUEST": - snackbarText = appLocalizations.password_incorrect; - break; - - case "REG_NETWORK_ERROR": - snackbarText = appLocalizations.network_error; - break; - - case "": - return; - - default: - snackbarText = errorMsg; - break; - } - - _showInSnackBar(snackbarText); - } - - bool _validateUsername(BuildContext context) { - if (username.trim().isEmpty) { - _showInSnackBar(appLocalizations.username_required); - return false; - } - - if (username.trim().length > 50) { - _showInSnackBar(appLocalizations.username_exceed); - return false; - } - - return true; - } - - bool _validatePassword(BuildContext context) { - if (password.trim().isEmpty) { - _showInSnackBar(appLocalizations.password_required); - return false; - } - - if (password.trim().length > 50) { - _showInSnackBar(appLocalizations.password_exceed); - return false; - } - - return true; - } - - _resetValuesOnRegistrationComplete() { - Navigator.of(context).pop(); - } - - void _registrationScreenLoadedAudit() async { - await globalProvider.getAudit("REG-EVT-002", "REG-MOD-103"); - } - - _nextButtonClickedAudit() async { - await globalProvider.getAudit("REG-EVT-003", "REG-MOD-103"); - } - - bool continueButton = false; - - @override - Widget build(BuildContext context) { - postRegistrationTabs = [ - appLocalizations.preview_page, - appLocalizations.packet_auth_page, - appLocalizations.acknowledgement_page, - ]; - isPortrait = MediaQuery.of(context).orientation == Orientation.portrait; - bool isMobile = MediaQuery.of(context).size.width < 750; - double w = ScreenUtil().screenWidth; - Map arguments = - ModalRoute.of(context)!.settings.arguments! as Map; - final Process newProcess = arguments["process"]; - int size = newProcess.screens!.length; - - evaluateMVELVisible(String fieldData) async { - bool visible = - await registrationTaskProvider.evaluateMVELVisible(fieldData); - return visible; - } - - evaluateMVELRequired(String fieldData) async { - bool required = - await registrationTaskProvider.evaluateMVELRequired(fieldData); - return required; - } - - Future onWillPop() async { - if (globalProvider.newProcessTabIndex > 0 && - globalProvider.newProcessTabIndex < - newProcess.screens!.length + postRegistrationTabs.length - 1) { - globalProvider.newProcessTabIndex = - globalProvider.newProcessTabIndex - 1; - } else { - return true; - } - return false; - } - - isExceptionPresent(String id) { - bool isExceptionPresent = false; - for (BiometricAttributeData x in globalProvider.fieldInputValue[id]) { - if (x.exceptions.contains(true) || x.title == "Exception") { - isExceptionPresent = true; - break; - } - } - return isExceptionPresent; - } - - returnBiometricListLength(List? list, String id) { - int i = 0; - if (list!.contains("leftEye") && list.contains("rightEye")) { - i++; - } - if (list.contains("rightIndex") && - list.contains("rightLittle") && - list.contains("rightRing") && - list.contains("rightMiddle")) { - i++; - } - if (list.contains("leftIndex") && - list.contains("leftLittle") && - list.contains("leftRing") && - list.contains("leftMiddle")) { - i++; - } - if (list.contains("rightThumb") && list.contains("rightThumb")) { - i++; - } - if (list.contains("face")) { - i++; - } - if (isExceptionPresent(id) == true) { - i++; - } - return i; - } - - biometricRequiredFieldValidation(Field field) async { - if (field.controlType == "biometrics") { - int count = returnBiometricListLength(field.bioAttributes, field.id!); - if (globalProvider.completeException[field.id!] != null) { - int length = globalProvider.completeException[field.id!].length; - count = count - length; - } - - if (globalProvider.fieldInputValue[field.id!].length < count) { - return false; - } - } - - return true; - } - - biometricConditionalFieldValidation(Field field) async { - bool valid = await BiometricsApi().conditionalBioAttributeValidation( - field.id!, field.conditionalBioAttributes!.first!.validationExpr!); - if (field.exceptionPhotoRequired == true) { - List biometricAttributeDataList = - globalProvider.fieldInputValue[field.id!]; - bool isExceptionPresent = false; - bool isExceptionAttributePresent = false; - for (var biometricAttributeData in biometricAttributeDataList) { - if (globalProvider.exceptionAttributes - .contains(biometricAttributeData.title)) { - isExceptionPresent = true; - } - if (biometricAttributeData.title == "Exception") { - isExceptionAttributePresent = true; - } - } - - if (isExceptionPresent == true && - isExceptionAttributePresent == false) { - return false; - } - } - if (!valid) { - return false; - } - return true; - } - - biometricValidation(Field field) async { - if (field.conditionalBioAttributes != null && - field.conditionalBioAttributes!.isNotEmpty) { - String response = await BiometricsApi().getAgeGroup(); - if (response - .compareTo(field.conditionalBioAttributes!.first!.ageGroup!) != - 0) { - bool isValid = true; - if (field.conditionalBioAttributes!.first!.ageGroup! == "ALL") { - isValid = await biometricConditionalFieldValidation(field); - } else { - isValid = await biometricRequiredFieldValidation(field); - } - - if (!isValid) { - return false; - } - } - - if (response - .compareTo(field.conditionalBioAttributes!.first!.ageGroup!) == - 0) { - bool isValid = await biometricConditionalFieldValidation(field); - if (!isValid) { - return false; - } - } - } - - return true; - } - - ageDateChangeValidation(int currentIndex) async { - if (globalProvider.newProcessTabIndex < size) { - Screen screen = newProcess.screens!.elementAt(currentIndex)!; - for (int i = 0; i < screen.fields!.length; i++) { - if (screen.fields!.elementAt(i)!.id == "dateOfBirth") { - if (globalProvider.checkAgeGroupChange == "") { - globalProvider.checkAgeGroupChange = globalProvider.ageGroup; - } else { - if (globalProvider.checkAgeGroupChange - .compareTo(globalProvider.ageGroup) == - 0) { - } else { - List screens = []; - for (int i = 0; - i < registrationTaskProvider.listOfProcesses.length; - i++) { - Process process = Process.fromJson( - jsonDecode( - context - .read() - .listOfProcesses - .elementAt(i) - .toString(), - ), - ); - if (process.id == "NEW" || process.id == "UPDATE" || process.id == "LOST") { - screens = process.screens!; - } - } - for (Screen? screen in screens) { - if (screen!.name! == "Documents" || - screen.name! == "BiometricDetails") { - for (Field? field in screen.fields!) { - globalProvider.fieldInputValue.remove(field!.id); - } - } - } - await BiometricsApi().clearBiometricAndDocumentHashmap(); - globalProvider.clearExceptions(); - globalProvider.checkAgeGroupChange = globalProvider.ageGroup; - } - } - } - } - } - } - - customValidation(int currentIndex) async { - if (currentIndex == 0) { - return true; - } - bool isValid = true; - if (globalProvider.newProcessTabIndex < size) { - Screen screen = newProcess.screens!.elementAt(currentIndex)!; - for (int i = 0; i < screen.fields!.length; i++) { - if (screen.fields!.elementAt(i)!.inputRequired! && - screen.fields!.elementAt(i)!.required!) { - if (!(globalProvider.fieldInputValue - .containsKey(screen.fields!.elementAt(i)!.id)) && - !(globalProvider.fieldInputValue - .containsKey(screen.fields!.elementAt(i)!.subType)) && - !(globalProvider.fieldInputValue.containsKey( - "${screen.fields!.elementAt(i)!.group}${screen.fields!.elementAt(i)!.subType}"))) { - isValid = false; - - break; - } - if (screen.fields!.elementAt(i)!.conditionalBioAttributes != null && - screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .isNotEmpty) { - String response = await BiometricsApi().getAgeGroup(); - if (!(response.compareTo(screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .first! - .ageGroup!) == - 0)) { - if (screen.fields!.elementAt(i)!.controlType == "biometrics") { - int count = returnBiometricListLength( - screen.fields!.elementAt(i)!.bioAttributes, - screen.fields!.elementAt(i)!.id!); - if (globalProvider.completeException[ - screen.fields!.elementAt(i)!.id!] != - null) { - int length = globalProvider - .completeException[screen.fields!.elementAt(i)!.id!] - .length; - count = count - length; - } - - if (globalProvider - .fieldInputValue[screen.fields!.elementAt(i)!.id!] - .length < - count) { - isValid = false; - - break; - } - if(globalProvider.isValidBiometricCapture){ - isValid = false; - break; - } - } - } - } - } - if (screen.fields!.elementAt(i)!.requiredOn != null && - screen.fields!.elementAt(i)!.requiredOn!.isNotEmpty) { - bool visible = await evaluateMVELVisible( - jsonEncode(screen.fields!.elementAt(i)!.toJson())); - bool required = await evaluateMVELRequired( - jsonEncode(screen.fields!.elementAt(i)!.toJson())); - if (visible && required) { - if (screen.fields!.elementAt(i)!.inputRequired!) { - if (!(globalProvider.fieldInputValue - .containsKey(screen.fields!.elementAt(i)!.id)) && - !(globalProvider.fieldInputValue - .containsKey(screen.fields!.elementAt(i)!.subType)) && - !(globalProvider.fieldInputValue.containsKey( - "${screen.fields!.elementAt(i)!.group}${screen.fields!.elementAt(i)!.subType}"))) { - isValid = false; - - break; - } - if (screen.fields!.elementAt(i)!.conditionalBioAttributes != - null && - screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .isNotEmpty) { - String response = await BiometricsApi().getAgeGroup(); - if (!(response.compareTo(screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .first! - .ageGroup!) == - 0)) { - if (screen.fields!.elementAt(i)!.controlType == - "biometrics") { - int count = returnBiometricListLength( - screen.fields!.elementAt(i)!.bioAttributes, - screen.fields!.elementAt(i)!.id!); - if (globalProvider.completeException[ - screen.fields!.elementAt(i)!.id!] != - null) { - int length = globalProvider - .completeException[screen.fields!.elementAt(i)!.id!] - .length; - count = count - length; - } - if (globalProvider - .fieldInputValue[screen.fields!.elementAt(i)!.id!] - .length < - count) { - isValid = false; - - break; - } - if(globalProvider.isValidBiometricCapture){ - isValid = false; - break; - } - } - } - if (response.compareTo(screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .first! - .ageGroup!) == - 0) { - bool valid = await BiometricsApi() - .conditionalBioAttributeValidation( - screen.fields!.elementAt(i)!.id!, - screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .first! - .validationExpr!); - if (screen.fields!.elementAt(i)!.exceptionPhotoRequired == - true) { - List biometricAttributeDataList = - globalProvider.fieldInputValue[ - screen.fields!.elementAt(i)!.id!]; - bool isExceptionPresent = false; - bool isExceptionAttributePresent = false; - for (var biometricAttributeData - in biometricAttributeDataList) { - if (globalProvider.exceptionAttributes - .contains(biometricAttributeData.title)) { - isExceptionPresent = true; - } - if (biometricAttributeData.title == "Exception") { - isExceptionAttributePresent = true; - } - } - if (isExceptionPresent == true && - isExceptionAttributePresent == false) { - isValid = false; - break; - } - } - if (!valid) { - isValid = false; - break; - } - } - } - } - } - } - } - } - return isValid; - } - - continueButtonTap(int size, newProcess) async { - - if (globalProvider.newProcessTabIndex < size) { - ageDateChangeValidation(globalProvider.newProcessTabIndex); - bool customValidator = - await customValidation(globalProvider.newProcessTabIndex); - if (customValidator) { - if (globalProvider.formKey.currentState!.validate()) { - if (globalProvider.newProcessTabIndex == - newProcess.screens!.length - 1) { - templateTitleMap = { - 'demographicInfo': appLocalizations.demographic_information, - 'documents': appLocalizations.documents, - 'bioMetrics': appLocalizations.biometrics, - }; - registrationTaskProvider.setPreviewTemplate(""); - registrationTaskProvider.setAcknowledgementTemplate(""); - await registrationTaskProvider.getPreviewTemplate( - true, templateTitleMap!); - await registrationTaskProvider.getAcknowledgementTemplate( - false, templateTitleMap!); - } - - globalProvider.newProcessTabIndex = - globalProvider.newProcessTabIndex + 1; - } - } - - _nextButtonClickedAudit(); - } else { - if (globalProvider.newProcessTabIndex == size + 1) { - bool isPacketAuthenticated = await _authenticatePacket(context); - if (!isPacketAuthenticated) { - return; - } - RegistrationSubmitResponse registrationSubmitResponse = - await registrationTaskProvider.submitRegistrationDto(username); - if (registrationSubmitResponse.errorCode!.isNotEmpty) { - _showInSnackBar(registrationSubmitResponse.errorCode!); - return; - } - globalProvider.setRegId(registrationSubmitResponse.rId); - - // Updating key to packetId after success creation of packet - registrationTaskProvider - .updateTemplateStorageKey(registrationSubmitResponse.rId); - registrationTaskProvider.deleteDefaultTemplateStored(); - - setState(() { - username = ''; - password = ''; - }); - } - if (globalProvider.newProcessTabIndex == size + 2) { - _resetValuesOnRegistrationComplete(); - return; - } - globalProvider.newProcessTabIndex = - globalProvider.newProcessTabIndex + 1; - } - } - - customValidation(globalProvider.newProcessTabIndex).then((value) { - setState(() { - continueButton = value && - globalProvider.formKey.currentState != null && - globalProvider.formKey.currentState!.validate(); - }); - if (globalProvider.newProcessTabIndex >= size) { - continueButton = true; - } - }); - - return WillPopScope( - onWillPop: onWillPop, - child: SafeArea( - child: Scaffold( - backgroundColor: secondaryColors.elementAt(10), - bottomNavigationBar: Container( - decoration: BoxDecoration( - border: const Border( - top: BorderSide( - color: dividerColor, - width: 1, - ), - ), - color: pureWhite, - ), - padding: EdgeInsets.symmetric( - horizontal: isPortrait ? 20.w : 60.w, - vertical: 16.h, - ), - // height: isPortrait ? 94.h : 84.h, - child: context.watch().newProcessTabIndex == 0 - ? Row( - children: [ - Expanded( - child: OutlinedButton( - child: SizedBox( - height: - isPortrait && !isMobileSize ? 68.h : 52.h, - child: Center( - child: Text( - appLocalizations.go_back, - style: TextStyle( - fontSize: isPortrait && !isMobileSize - ? 22 - : 14, - ), - ), - ), - ), - onPressed: () { - Navigator.of(context).pop(); - }, - ) - ), - SizedBox( - width: 10.w, - ), - Expanded( - child: ElevatedButton( - child: SizedBox( - height: isPortrait && !isMobileSize ? 68.h : 52.h, - child: Center( - child: Text( - appLocalizations.informed, - style: TextStyle( - fontSize: - isPortrait && !isMobileSize ? 22 : 14, - ), - ), - ), - ), - onPressed: () async { - registrationTaskProvider.addConsentField("Y"); - await DemographicsApi() - .addDemographicField("consent", "true"); - continueButtonTap(size, newProcess); - }, - ), - ), - ], - ) - : Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - // globalProvider.newProcessTabIndex == size + 2 - // ? ElevatedButton( - // onPressed: () async { - // await connectivityProvider - // .checkNetworkConnection(); - // bool isConnected = - // connectivityProvider.isConnected; - // if (!isConnected) { - // _showInSnackBar( - // appLocalizations.network_error); - // return; - // } - // globalProvider.syncPacket(globalProvider.regId); - // }, - // child: Text(appLocalizations.sync_packet), - // ) - // : const SizedBox.shrink(), - // SizedBox( - // width: 10.w, - // ), - // globalProvider.newProcessTabIndex == size + 2 - // ? ElevatedButton( - // onPressed: () async { - // await connectivityProvider - // .checkNetworkConnection(); - // bool isConnected = - // connectivityProvider.isConnected; - // if (!isConnected) { - // _showInSnackBar( - // appLocalizations.network_error); - // return; - // } - // globalProvider - // .uploadPacket(globalProvider.regId); - // }, - // child: Text(appLocalizations.upload_packet), - // ) - // : const SizedBox.shrink(), - const Expanded( - child: SizedBox(), - ), - ElevatedButton( - style: ButtonStyle( - maximumSize: MaterialStateProperty.all( - const Size(209, 52)), - minimumSize: MaterialStateProperty.all( - const Size(209, 52)), - backgroundColor: MaterialStateProperty.all( - continueButton ? solidPrimary : Colors.grey), - ), - onPressed: () { - continueButtonTap(size, newProcess); - }, - child: Text( - context.read().newProcessTabIndex <= - size - ? appLocalizations.continue_text - : globalProvider.newProcessTabIndex == size + 1 - ? appLocalizations.authenticate - : appLocalizations.go_to_home, - style: const TextStyle(color: appWhite), - ), - ), - ], - ), - ), - body: SingleChildScrollView( - child: AnnotatedRegion( - value: const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - ), - child: Column( - children: [ - // isPortrait - // ? const SizedBox() - // : const Column( - // children: [ - // // TabletHeader(), - // TabletNavbar(), - // ], - // ), - Container( - padding: isMobile && !isMobileSize - ? const EdgeInsets.fromLTRB(0, 46, 0, 0) - : const EdgeInsets.fromLTRB(0, 0, 0, 0), - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0xff214FBF), Color(0xff1C43A1)], - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: w, - height: isPortrait ? 21.w : 30.w, - ), - Padding( - padding: isPortrait - ? EdgeInsets.fromLTRB(20.w, 0, 0, 0) - : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), - child: Text( - newProcess.label![globalProvider.selectedLanguage]!, - style: Theme.of(context) - .textTheme - .titleMedium - ?.copyWith( - color: pureWhite, - fontWeight: semiBold, - fontSize: isPortrait ? 24 : 21), - ), - ), - SizedBox( - height: 30.h, - ), - Divider( - height: 12.h, - thickness: 1, - color: secondaryColors.elementAt(2), - ), - Padding( - padding: isPortrait - ? const EdgeInsets.all(0) - : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), - child: Stack( - alignment: FractionalOffset.centerRight, - children: [ - Padding( - padding: isPortrait - ? EdgeInsets.fromLTRB( - 20.w, 10.h, 0, 0) - : EdgeInsets.fromLTRB(0, 10.h, 0, 0), - child: SizedBox( - height: 36.h, - child: ListView.builder( - padding: const EdgeInsets.all(0), - scrollDirection: Axis.horizontal, - itemCount: - newProcess.screens!.length + 3, - itemBuilder: (BuildContext context, - int index) { - return GestureDetector( - onTap: () { - if (globalProvider - .newProcessTabIndex == - size + 2) { - return; - } - - if (index < - globalProvider - .newProcessTabIndex) { - globalProvider - .newProcessTabIndex = - index; - } - }, - child: Row( - children: [ - Container( - padding: - EdgeInsets.fromLTRB( - 0, 0, 0, 8.h), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: (context - .watch< - GlobalProvider>() - .newProcessTabIndex == - index) - ? pureWhite - : Colors - .transparent, - width: 3), - ), - ), - child: Row( - children: [ - (index < - context - .watch< - GlobalProvider>() - .newProcessTabIndex) - ? Icon( - Icons - .check_circle, - size: 17, - color: secondaryColors - .elementAt( - 11), - ) - : (context - .watch< - GlobalProvider>() - .newProcessTabIndex == - index) - ? Icon( - Icons - .circle, - color: - pureWhite, - size: 17, - ) - : Icon( - Icons - .circle_outlined, - size: 17, - color: secondaryColors - .elementAt( - 9), - ), - SizedBox( - width: 6.w, - ), - Text( - index < size - ? newProcess - .screens![ - index]! - .label![ - context - .read< - GlobalProvider>() - .selectedLanguage]! - : postRegistrationTabs[ - index - - size], - style: Theme.of( - context) - .textTheme - .titleSmall - ?.copyWith( - color: (context.watch().newProcessTabIndex == - index) - ? pureWhite - : secondaryColors - .elementAt( - 9), - fontWeight: - semiBold, - fontSize: - 14), - ), - ], - ), - ), - SizedBox( - width: 35.w, - ), - ], - ), - ); - }), - ), - ), - Container( - height: 36.h, - width: 25.w, - padding: - const EdgeInsets.fromLTRB(0, 0, 0, 0), - color: solidPrimary, - child: Icon( - Icons.arrow_forward_ios_outlined, - color: pureWhite, - size: 17, - ), - ), - ], - ), - ), - const SizedBox( - height: 5, - ), - ], - ), - ), - Padding( - padding: isPortrait - ? const EdgeInsets.all(0) - : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), - child: context.watch().newProcessTabIndex < - size - ? LostProcessScreenContent( - context: context, - //process: newProcess, - screen: newProcess.screens!.elementAt(context - .watch() - .newProcessTabIndex)!) - : context - .watch() - .newProcessTabIndex == - size - ? const PreviewPage() - : context - .watch() - .newProcessTabIndex == - size + 1 - ? _getPacketAuthComponent() - : const AcknowledgementPage(), - ), - SizedBox( - height: 20.h, - ), - ], - ), - ), - ), - ), - ), - ); - } - - Widget _getPacketAuthComponent() { - return Column( - children: [ - SizedBox( - height: 30.h, - ), - Container( - width: isPortrait && !isMobileSize ? 566.w : 376.w, - padding: EdgeInsets.only( - top: 24.h, - bottom: 28.h, - left: 20.w, - right: 20.w, - ), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - color: pureWhite, - ), - child: Column( - children: [ - _getAuthIcon(), - SizedBox( - height: 26.h, - ), - Text( - appLocalizations.authenticate_using_password, - style: TextStyle( - fontSize: isPortrait && !isMobileSize ? 24 : 18, - fontWeight: semiBold, - color: appBlack), - ), - SizedBox( - height: 35.h, - ), - Row( - children: [ - Text( - appLocalizations.username, - style: isPortrait - ? AppTextStyle.tabletPortraitTextfieldHeader - : AppTextStyle.mobileTextfieldHeader, - ), - const Text( - ' *', - style: TextStyle( - color: mandatoryField, - ), - ), - ], - ), - SizedBox( - height: 11.h, - ), - _getUsernameTextField(), - SizedBox( - height: 35.h, - ), - Row( - children: [ - Text( - appLocalizations.password, - style: isPortrait - ? AppTextStyle.tabletPortraitTextfieldHeader - : AppTextStyle.mobileTextfieldHeader, - ), - const Text( - ' *', - style: TextStyle(color: mandatoryField), - ), - ], - ), - SizedBox( - height: 11.h, - ), - _getPasswordTextField(), - ], - ), - ), - ], - ); - } - - _getAuthIcon() { - return Container( - height: 80.w, - width: 80.w, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: authIconBorder, - width: 2, - ), - color: authIconBackground, - ), - child: Center( - child: Image.asset('assets/images/Registering an Individual@2x.png'), - ), - ); - } - - _getUsernameTextField() { - return Container( - height: isPortrait && !isMobileSize ? 82.h : 52.h, - alignment: Alignment.centerLeft, - padding: EdgeInsets.symmetric( - horizontal: 12.w, - ), - decoration: BoxDecoration( - border: Border.all( - width: 1.h, - color: appGreyShade, - ), - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - ), - child: TextField( - decoration: InputDecoration( - hintText: appLocalizations.enter_username, - hintStyle: isPortrait && !isMobileSize - ? AppTextStyle.tabletPortraitTextfieldHintText - : AppTextStyle.mobileTextfieldHintText, - border: InputBorder.none, - ), - style: TextStyle( - fontSize: isPortrait && !isMobileSize ? 22 : 14, - color: appBlack, - ), - onChanged: (v) { - setState(() { - username = v; - }); - }, - ), - ); - } - - _getPasswordTextField() { - return Container( - height: isPortrait && !isMobileSize ? 82.h : 52.h, - alignment: Alignment.centerLeft, - padding: EdgeInsets.symmetric( - horizontal: 12.w, - ), - decoration: BoxDecoration( - border: Border.all( - width: 1.h, - color: appGreyShade, - ), - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - ), - child: TextField( - obscureText: true, - decoration: InputDecoration( - hintText: appLocalizations.enter_password, - hintStyle: isPortrait && !isMobileSize - ? AppTextStyle.tabletPortraitTextfieldHintText - : AppTextStyle.mobileTextfieldHintText, - border: InputBorder.none, - ), - style: TextStyle( - fontSize: isPortrait && !isMobileSize ? 22 : 14, - color: appBlack, - ), - onChanged: (v) { - setState(() { - password = v; - }); - }, - ), - ); - } -} diff --git a/lib/ui/process_ui/new_process.dart b/lib/ui/process_ui/new_process.dart deleted file mode 100644 index dea2fadf9..000000000 --- a/lib/ui/process_ui/new_process.dart +++ /dev/null @@ -1,1169 +0,0 @@ -/* - * Copyright (c) Modular Open Source Identity Platform - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * -*/ - -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:provider/provider.dart'; -import 'package:registration_client/model/biometric_attribute_data.dart'; -import 'package:registration_client/model/field.dart'; -import 'package:registration_client/model/process.dart'; -import 'package:registration_client/model/screen.dart'; -import 'package:registration_client/pigeon/biometrics_pigeon.dart'; -import 'package:registration_client/pigeon/demographics_data_pigeon.dart'; -import 'package:registration_client/pigeon/registration_data_pigeon.dart'; - -import 'package:registration_client/provider/auth_provider.dart'; -import 'package:registration_client/provider/connectivity_provider.dart'; -import 'package:registration_client/provider/global_provider.dart'; -import 'package:registration_client/provider/registration_task_provider.dart'; - -import 'package:registration_client/ui/common/tablet_header.dart'; -import 'package:registration_client/ui/common/tablet_navbar.dart'; -import 'package:registration_client/ui/post_registration/acknowledgement_page.dart'; - -import 'package:registration_client/ui/post_registration/preview_page.dart'; -import 'package:registration_client/ui/process_ui/widgets/language_selector.dart'; - -import 'package:registration_client/ui/process_ui/widgets/new_process_screen_content.dart'; - -import 'package:registration_client/utils/app_config.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:registration_client/utils/app_style.dart'; - -import '../../utils/life_cycle_event_handler.dart'; - -class NewProcess extends StatefulWidget { - const NewProcess({super.key}); - - static const routeName = '/new_process'; - - @override - State createState() => _NewProcessState(); -} - -class _NewProcessState extends State with WidgetsBindingObserver { - late GlobalProvider globalProvider; - late RegistrationTaskProvider registrationTaskProvider; - late AuthProvider authProvider; - late ConnectivityProvider connectivityProvider; - late AppLocalizations appLocalizations = AppLocalizations.of(context)!; - bool isPortrait = true; - ScrollController scrollController = ScrollController(); - - List postRegistrationTabs = [ - 'Preview', - 'Authentication', - 'Acknowledgement', - ]; - - Map? templateTitleMap; - - String username = ''; - String password = ''; - - @override - void initState() { - globalProvider = Provider.of(context, listen: false); - registrationTaskProvider = - Provider.of(context, listen: false); - authProvider = Provider.of(context, listen: false); - connectivityProvider = - Provider.of(context, listen: false); - super.initState(); - WidgetsBinding.instance.addObserver(LifecycleEventHandler( - resumeCallBack: () async { - if (mounted) { - setState(() { - closeKeyboard(); - }); - } - }, - suspendingCallBack: () async { - if (mounted) { - setState(() { - closeKeyboard(); - }); - } - }, - )); - _registrationScreenLoadedAudit(); - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } - - void closeKeyboard() { - FocusScope.of(context).unfocus(); - } - - void _showInSnackBar(String value) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(value), - ), - ); - } - - _authenticatePacket(BuildContext context) async { - if (!_validateUsername(context)) { - return false; - } - - if (!_validatePassword(context)) { - return false; - } - - if (authProvider.currentUser.userId != username) { - _showInSnackBar(appLocalizations.invalid_user); - return false; - } - - await authProvider.authenticatePacket(username, password); - - if (!authProvider.isPacketAuthenticated) { - _showErrorInSnackbar(); - return false; - } - return true; - } - - _showErrorInSnackbar() { - String errorMsg = authProvider.packetError; - String snackbarText = ""; - - switch (errorMsg) { - case "REG_TRY_AGAIN": - snackbarText = appLocalizations.login_failed; - break; - - case "REG_INVALID_REQUEST": - snackbarText = appLocalizations.password_incorrect; - break; - - case "REG_NETWORK_ERROR": - snackbarText = appLocalizations.network_error; - break; - - case "": - return; - - default: - snackbarText = errorMsg; - break; - } - - _showInSnackBar(snackbarText); - } - - bool _validateUsername(BuildContext context) { - if (username.trim().isEmpty) { - _showInSnackBar(appLocalizations.username_required); - return false; - } - - if (username.trim().length > 50) { - _showInSnackBar(appLocalizations.username_exceed); - return false; - } - - return true; - } - - bool _validatePassword(BuildContext context) { - if (password.trim().isEmpty) { - _showInSnackBar(appLocalizations.password_required); - return false; - } - - if (password.trim().length > 50) { - _showInSnackBar(appLocalizations.password_exceed); - return false; - } - - return true; - } - - _resetValuesOnRegistrationComplete() { - Navigator.of(context).pop(); - } - - void _registrationScreenLoadedAudit() async { - await globalProvider.getAudit("REG-EVT-002", "REG-MOD-103"); - } - - _nextButtonClickedAudit() async { - await globalProvider.getAudit("REG-EVT-003", "REG-MOD-103"); - } - - setScrollToTop() { - scrollController.animateTo( - scrollController.position.minScrollExtent, - duration: const Duration(milliseconds: 500), - curve: Curves.easeOut, - ); - globalProvider.isPageChanged = false; - } - - bool continueButton = false; - bool authButton = false; - @override - Widget build(BuildContext context) { - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - if (globalProvider.isPageChanged) { - setScrollToTop(); - } - }); - postRegistrationTabs = [ - appLocalizations.preview_page, - appLocalizations.packet_auth_page, - appLocalizations.acknowledgement_page, - ]; - isPortrait = MediaQuery.of(context).orientation == Orientation.portrait; - bool isMobile = MediaQuery.of(context).size.width < 750; - double w = ScreenUtil().screenWidth; - Map arguments = - ModalRoute.of(context)!.settings.arguments! as Map; - final Process newProcess = arguments["process"]; - int size = newProcess.screens!.length; - - evaluateMVELVisible(String fieldData) async { - bool visible = - await registrationTaskProvider.evaluateMVELVisible(fieldData); - return visible; - } - - evaluateMVELRequired(String fieldData) async { - bool required = - await registrationTaskProvider.evaluateMVELRequired(fieldData); - return required; - } - - Future onWillPop() async { - if (globalProvider.newProcessTabIndex > 0 && - globalProvider.newProcessTabIndex < - newProcess.screens!.length + postRegistrationTabs.length - 1) { - globalProvider.newProcessTabIndex = - globalProvider.newProcessTabIndex - 1; - } else { - return true; - } - return false; - } - - isExceptionPresent(String id) { - bool isExceptionPresent = false; - for (BiometricAttributeData x in globalProvider.fieldInputValue[id]) { - if (x.exceptions.contains(true) || x.title == "Exception") { - isExceptionPresent = true; - break; - } - } - return isExceptionPresent; - } - - returnBiometricListLength(List? list, String id) { - int i = 0; - if (list!.contains("leftEye") && list.contains("rightEye")) { - i++; - } - if (list.contains("rightIndex") && - list.contains("rightLittle") && - list.contains("rightRing") && - list.contains("rightMiddle")) { - i++; - } - if (list.contains("leftIndex") && - list.contains("leftLittle") && - list.contains("leftRing") && - list.contains("leftMiddle")) { - i++; - } - if (list.contains("rightThumb") && list.contains("rightThumb")) { - i++; - } - if (list.contains("face")) { - i++; - } - if (isExceptionPresent(id) == true) { - i++; - } - return i; - } - - ageDateChangeValidation(int currentIndex) async { - if (globalProvider.newProcessTabIndex < size) { - Screen screen = newProcess.screens!.elementAt(currentIndex)!; - for (int i = 0; i < screen.fields!.length; i++) { - if (screen.fields!.elementAt(i)!.id == "dateOfBirth") { - if (globalProvider.checkAgeGroupChange == "") { - globalProvider.checkAgeGroupChange = globalProvider.ageGroup; - } else { - if (globalProvider.checkAgeGroupChange - .compareTo(globalProvider.ageGroup) == - 0) { - } else { - List screens = []; - for (int i = 0; - i < registrationTaskProvider.listOfProcesses.length; - i++) { - Process process = Process.fromJson( - jsonDecode( - context - .read() - .listOfProcesses - .elementAt(i) - .toString(), - ), - ); - if (process.id == "NEW") { - screens = process.screens!; - } - } - for (Screen? screen in screens) { - if (screen!.name! == "Documents" || - screen.name! == "BiometricDetails") { - for (Field? field in screen.fields!) { - if (globalProvider.fieldInputValue - .containsKey(field!.id!)) { - globalProvider.fieldInputValue.remove(field.id); - } - } - } - } - await BiometricsApi().clearBiometricAndDocumentHashmap(); - globalProvider.clearExceptions(); - globalProvider.checkAgeGroupChange = globalProvider.ageGroup; - } - } - } - } - } - } - - customValidation(int currentIndex) async { - if (currentIndex == 0) { - return true; - } - bool isValid = true; - if (globalProvider.newProcessTabIndex < size) { - Screen screen = newProcess.screens!.elementAt(currentIndex)!; - for (int i = 0; i < screen.fields!.length; i++) { - if (screen.fields!.elementAt(i)!.inputRequired! && - screen.fields!.elementAt(i)!.required!) { - if (!(globalProvider.fieldInputValue - .containsKey(screen.fields!.elementAt(i)!.id)) && - !(globalProvider.fieldInputValue - .containsKey(screen.fields!.elementAt(i)!.subType)) && - !(globalProvider.fieldInputValue.containsKey( - "${screen.fields!.elementAt(i)!.group}${screen.fields!.elementAt(i)!.subType}"))) { - // log("field: ${screen.fields!.elementAt(i)!.group}${screen.fields!.elementAt(i)!.subType}"); - - // if (screen.fields!.elementAt(i)!.controlType == "fileupload") { - // _showInSnackBar(appLocalizations.upload_document); - // } - isValid = false; - - break; - } - if (screen.fields!.elementAt(i)!.conditionalBioAttributes != null && - screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .isNotEmpty) { - String response = await BiometricsApi().getAgeGroup(); - if (!(response.compareTo(screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .first! - .ageGroup!) == - 0)) { - if (screen.fields!.elementAt(i)!.controlType == "biometrics") { - int count = returnBiometricListLength( - screen.fields!.elementAt(i)!.bioAttributes, - screen.fields!.elementAt(i)!.id!); - if (globalProvider.completeException[ - screen.fields!.elementAt(i)!.id!] != - null) { - int length = globalProvider - .completeException[screen.fields!.elementAt(i)!.id!] - .length; - count = count - length; - } - - if (globalProvider - .fieldInputValue[screen.fields!.elementAt(i)!.id!] - .length < - count) { - isValid = false; - - break; - } - if(globalProvider.isValidBiometricCapture){ - isValid = false; - break; - } - } - } - } - } - if (screen.fields!.elementAt(i)!.requiredOn != null && - screen.fields!.elementAt(i)!.requiredOn!.isNotEmpty) { - bool visible = await evaluateMVELVisible( - jsonEncode(screen.fields!.elementAt(i)!.toJson())); - bool required = await evaluateMVELRequired( - jsonEncode(screen.fields!.elementAt(i)!.toJson())); - if (visible && required) { - if (screen.fields!.elementAt(i)!.inputRequired!) { - if (!(globalProvider.fieldInputValue - .containsKey(screen.fields!.elementAt(i)!.id)) && - !(globalProvider.fieldInputValue - .containsKey(screen.fields!.elementAt(i)!.subType)) && - !(globalProvider.fieldInputValue.containsKey( - "${screen.fields!.elementAt(i)!.group}${screen.fields!.elementAt(i)!.subType}"))) { - isValid = false; - - break; - } - if (screen.fields!.elementAt(i)!.conditionalBioAttributes != - null && - screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .isNotEmpty) { - String response = await BiometricsApi().getAgeGroup(); - if (!(response.compareTo(screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .first! - .ageGroup!) == - 0)) { - if (screen.fields!.elementAt(i)!.controlType == - "biometrics") { - int count = returnBiometricListLength( - screen.fields!.elementAt(i)!.bioAttributes, - screen.fields!.elementAt(i)!.id!); - if (globalProvider.completeException[ - screen.fields!.elementAt(i)!.id!] != - null) { - int length = globalProvider - .completeException[screen.fields!.elementAt(i)!.id!] - .length; - count = count - length; - } - if (globalProvider - .fieldInputValue[screen.fields!.elementAt(i)!.id!] - .length < - count) { - isValid = false; - - break; - } - if(globalProvider.isValidBiometricCapture){ - isValid = false; - break; - } - } - } - if (response.compareTo(screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .first! - .ageGroup!) == - 0) { - bool valid = await BiometricsApi() - .conditionalBioAttributeValidation( - screen.fields!.elementAt(i)!.id!, - screen.fields! - .elementAt(i)! - .conditionalBioAttributes! - .first! - .validationExpr!); - if (screen.fields!.elementAt(i)!.exceptionPhotoRequired == - true) { - List biometricAttributeDataList = - globalProvider.fieldInputValue[ - screen.fields!.elementAt(i)!.id!]; - bool isExceptionPresent = false; - bool isExceptionAttributePresent = false; - for (var biometricAttributeData - in biometricAttributeDataList) { - if (globalProvider.exceptionAttributes - .contains(biometricAttributeData.title)) { - isExceptionPresent = true; - } - if (biometricAttributeData.title == "Exception") { - isExceptionAttributePresent = true; - } - } - if (isExceptionPresent == true && - isExceptionAttributePresent == false) { - isValid = false; - break; - } - } - if (!valid) { - isValid = false; - break; - } - } - } - } - } - } - } - } - return isValid; - } - - continueButtonTap(int size, newProcess) async { - globalProvider.isPageChanged = true; - if (globalProvider.newProcessTabIndex < size) { - ageDateChangeValidation(globalProvider.newProcessTabIndex); - bool customValidator = - await customValidation(globalProvider.newProcessTabIndex); - if (customValidator) { - if (globalProvider.formKey.currentState!.validate()) { - if (globalProvider.newProcessTabIndex == - newProcess.screens!.length - 1) { - templateTitleMap = { - 'demographicInfo': appLocalizations.demographic_information, - 'documents': appLocalizations.documents, - 'bioMetrics': appLocalizations.biometrics - }; - registrationTaskProvider.setPreviewTemplate(""); - registrationTaskProvider.setAcknowledgementTemplate(""); - await registrationTaskProvider.getPreviewTemplate( - true, templateTitleMap!); - await registrationTaskProvider.getAcknowledgementTemplate( - false, templateTitleMap!, - ); - } - - globalProvider.newProcessTabIndex = - globalProvider.newProcessTabIndex + 1; - } - } - - _nextButtonClickedAudit(); - } else { - if (globalProvider.newProcessTabIndex == size + 1) { - bool isPacketAuthenticated = await _authenticatePacket(context); - if (!isPacketAuthenticated) { - return; - } - RegistrationSubmitResponse registrationSubmitResponse = - await registrationTaskProvider.submitRegistrationDto(username); - if (registrationSubmitResponse.errorCode!.isNotEmpty) { - _showInSnackBar(registrationSubmitResponse.errorCode!); - return; - } - globalProvider.setRegId(registrationSubmitResponse.rId); - - // Updating key to packetId after success creation of packet - registrationTaskProvider - .updateTemplateStorageKey(registrationSubmitResponse.rId); - registrationTaskProvider.deleteDefaultTemplateStored(); - - setState(() { - username = ''; - password = ''; - }); - } - if (globalProvider.newProcessTabIndex == size + 2) { - _resetValuesOnRegistrationComplete(); - return; - } - globalProvider.newProcessTabIndex = - globalProvider.newProcessTabIndex + 1; - } - } - - customValidation(globalProvider.newProcessTabIndex).then((value) { - setState(() { - continueButton = value && - globalProvider.formKey.currentState != null && - globalProvider.formKey.currentState!.validate(); - }); - if (globalProvider.newProcessTabIndex >= size) { - continueButton = true; - } - }); - - //auth button validation - if(username.trim().isNotEmpty && password.trim().isNotEmpty){ - authButton = true; - } - - return WillPopScope( - onWillPop: onWillPop, - child: SafeArea( - child: Scaffold( - backgroundColor: secondaryColors.elementAt(10), - bottomNavigationBar: Container( - decoration: BoxDecoration( - border: const Border( - top: BorderSide( - color: dividerColor, - width: 1, - ), - ), - color: pureWhite, - ), - padding: EdgeInsets.symmetric( - horizontal: isPortrait ? 20.w : 60.w, - vertical: 16.h, - ), - // height: isPortrait ? 94.h : 84.h, - child: globalProvider.newProcessTabIndex == 0 - ? Row( - children: [ - Expanded( - child: OutlinedButton( - child: SizedBox( - height: isPortrait && !isMobileSize ? 68.h : 52.h, - child: Center( - child: Text( - appLocalizations.go_back, - style: TextStyle( - fontSize: - isPortrait && !isMobileSize ? 22 : 14, - ), - ), - ), - ), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ), - SizedBox( - width: 10.w, - ), - Expanded( - child: ElevatedButton( - child: SizedBox( - height: isPortrait && !isMobileSize ? 68.h : 52.h, - child: Center( - child: Text( - appLocalizations.informed, - style: TextStyle( - fontSize: - isPortrait && !isMobileSize ? 22 : 14, - ), - ), - ), - ), - onPressed: () async { - registrationTaskProvider.addConsentField("Y"); - await DemographicsApi() - .addDemographicField("consent", "true"); - continueButtonTap(size, newProcess); - }, - ), - ), - ], - ) - : Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - /*globalProvider.newProcessTabIndex == size + 2 - ? ElevatedButton( - onPressed: () async { - await connectivityProvider - .checkNetworkConnection(); - bool isConnected = - connectivityProvider.isConnected; - if (!isConnected) { - _showInSnackBar( - appLocalizations.network_error); - return; - } - globalProvider.syncPacket(globalProvider.regId); - }, - child: Text(appLocalizations.sync_packet), - ) - : const SizedBox.shrink(), - SizedBox( - width: 10.w, - ), - globalProvider.newProcessTabIndex == size + 2 - ? ElevatedButton( - onPressed: () async { - await connectivityProvider - .checkNetworkConnection(); - bool isConnected = - connectivityProvider.isConnected; - if (!isConnected) { - _showInSnackBar( - appLocalizations.network_error); - return; - } - globalProvider - .uploadPacket(globalProvider.regId); - }, - child: Text(appLocalizations.upload_packet), - ) - : const SizedBox.shrink(),*/ - const Expanded( - child: SizedBox(), - ), - ElevatedButton( - style: ButtonStyle( - maximumSize: MaterialStateProperty.all( - const Size(209, 52)), - minimumSize: MaterialStateProperty.all( - const Size(209, 52)), - backgroundColor: MaterialStateProperty.all( - (continueButton && context.read().newProcessTabIndex <= - size) ? solidPrimary : authButton ? solidPrimary: Colors.grey), - ), - onPressed: () { - continueButtonTap(size, newProcess); - }, - child: Text( - context.read().newProcessTabIndex <= - size - ? appLocalizations.continue_text - : globalProvider.newProcessTabIndex == size + 1 - ? appLocalizations.authenticate - : appLocalizations.go_to_home, - style: const TextStyle(color: appWhite), - ), - ), - ], - ), - ), - body: SingleChildScrollView( - controller: scrollController, - child: AnnotatedRegion( - value: const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - ), - child: Column( - children: [ - // isPortrait - // ? const SizedBox() - // : const Column( - // children: [ - // TabletHeader(), - // TabletNavbar(), - // ], - // ), - Container( - padding: isMobile && !isMobileSize - ? const EdgeInsets.fromLTRB(0, 46, 0, 0) - : const EdgeInsets.fromLTRB(0, 0, 0, 0), - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0xff214FBF), Color(0xff1C43A1)], - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: w, - height: isPortrait ? 21.w : 30.w, - ), - Padding( - padding: isPortrait - ? EdgeInsets.fromLTRB(20.w, 0, 0, 0) - : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), - child: Text( - newProcess.label![context - .read() - .selectedLanguage]!, - style: Theme.of(context) - .textTheme - .titleMedium - ?.copyWith( - color: pureWhite, - fontWeight: semiBold, - fontSize: isPortrait ? 24 : 21), - ), - ), - SizedBox( - height: 30.h, - ), - Divider( - height: 12.h, - thickness: 1, - color: secondaryColors.elementAt(2), - ), - Padding( - padding: isPortrait - ? const EdgeInsets.all(0) - : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), - child: Stack( - alignment: FractionalOffset.centerRight, - children: [ - Padding( - padding: isPortrait - ? EdgeInsets.fromLTRB(20.w, 10.h, 0, 0) - : EdgeInsets.fromLTRB(0, 10.h, 0, 0), - child: SizedBox( - height: 36.h, - child: ListView.builder( - padding: const EdgeInsets.all(0), - scrollDirection: Axis.horizontal, - itemCount: newProcess.screens!.length + 3, - itemBuilder: - (BuildContext context, int index) { - return GestureDetector( - onTap: () { - if (context - .read() - .newProcessTabIndex == - size + 2) { - return; - } - - if (index < - context - .read() - .newProcessTabIndex) { - context - .read() - .newProcessTabIndex = index; - } - }, - child: Row( - children: [ - Container( - padding: EdgeInsets.fromLTRB( - 0, 0, 0, 8.h), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: (context - .watch< - GlobalProvider>() - .newProcessTabIndex == - index) - ? pureWhite - : Colors - .transparent, - width: 3), - ), - ), - child: Row( - children: [ - (index < - context - .watch< - GlobalProvider>() - .newProcessTabIndex) - ? Icon( - Icons.check_circle, - size: 17, - color: - secondaryColors - .elementAt( - 11), - ) - : (context - .watch< - GlobalProvider>() - .newProcessTabIndex == - index) - ? Icon( - Icons.circle, - color: - pureWhite, - size: 17, - ) - : Icon( - Icons - .circle_outlined, - size: 17, - color: secondaryColors - .elementAt( - 9), - ), - SizedBox( - width: 6.w, - ), - Text( - index < size - ? newProcess - .screens![index]! - .label![ - context - .read< - GlobalProvider>() - .selectedLanguage]! - : postRegistrationTabs[ - index - size], - style: Theme.of(context) - .textTheme - .titleSmall - ?.copyWith( - color: (context - .watch< - GlobalProvider>() - .newProcessTabIndex == - index) - ? pureWhite - : secondaryColors - .elementAt( - 9), - fontWeight: - semiBold, - fontSize: 14), - ), - ], - ), - ), - SizedBox( - width: 35.w, - ), - ], - ), - ); - }), - ), - ), - Container( - height: 36.h, - width: 25.w, - padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), - color: solidPrimary, - child: Icon( - Icons.arrow_forward_ios_outlined, - color: pureWhite, - size: 17, - ), - ), - ], - ), - ), - const SizedBox( - height: 5, - ), - ], - ), - ), - Padding( - padding: isPortrait - ? const EdgeInsets.all(0) - : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), - child: context.watch().newProcessTabIndex < - size - ? NewProcessScreenContent( - context: context, - screen: newProcess.screens!.elementAt(context - .watch() - .newProcessTabIndex)!) - : context.watch().newProcessTabIndex == - size - ? const PreviewPage() - : context - .watch() - .newProcessTabIndex == - size + 1 - ? _getPacketAuthComponent() - : const AcknowledgementPage(), - ), - SizedBox( - height: 20.h, - ), - ], - ), - ), - ), - ), - ), - ); - } - - Widget _getPacketAuthComponent() { - return Column( - children: [ - SizedBox( - height: 30.h, - ), - Container( - width: isPortrait && !isMobileSize ? 566.w : 376.w, - padding: EdgeInsets.only( - top: 24.h, - bottom: 28.h, - left: 20.w, - right: 20.w, - ), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - color: pureWhite, - ), - child: Column( - children: [ - _getAuthIcon(), - SizedBox( - height: 26.h, - ), - Text( - appLocalizations.authenticate_using_password, - style: TextStyle( - fontSize: isPortrait && !isMobileSize ? 24 : 18, - fontWeight: semiBold, - color: appBlack), - ), - SizedBox( - height: 35.h, - ), - Row( - children: [ - Text( - appLocalizations.username, - style: isPortrait - ? AppTextStyle.tabletPortraitTextfieldHeader - : AppTextStyle.mobileTextfieldHeader, - ), - const Text( - ' *', - style: TextStyle( - color: mandatoryField, - ), - ), - ], - ), - SizedBox( - height: 11.h, - ), - _getUsernameTextField(), - SizedBox( - height: 35.h, - ), - Row( - children: [ - Text( - appLocalizations.password, - style: isPortrait - ? AppTextStyle.tabletPortraitTextfieldHeader - : AppTextStyle.mobileTextfieldHeader, - ), - const Text( - ' *', - style: TextStyle(color: mandatoryField), - ), - ], - ), - SizedBox( - height: 11.h, - ), - _getPasswordTextField(), - ], - ), - ), - ], - ); - } - - _getAuthIcon() { - return Container( - height: 80.w, - width: 80.w, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: authIconBorder, - width: 2, - ), - color: authIconBackground, - ), - child: Center( - child: SvgPicture.asset('assets/images/AuthenticationIcon.svg'), - ), - ); - } - - _getUsernameTextField() { - return Container( - height: isPortrait && !isMobileSize ? 82.h : 52.h, - alignment: Alignment.centerLeft, - padding: EdgeInsets.symmetric( - horizontal: 12.w, - ), - decoration: BoxDecoration( - border: Border.all( - width: 1.h, - color: appGreyShade, - ), - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - ), - child: TextField( - decoration: InputDecoration( - hintText: appLocalizations.enter_username, - hintStyle: isPortrait && !isMobileSize - ? AppTextStyle.tabletPortraitTextfieldHintText - : AppTextStyle.mobileTextfieldHintText, - border: InputBorder.none, - ), - style: TextStyle( - fontSize: isPortrait && !isMobileSize ? 22 : 14, - color: appBlack, - ), - onChanged: (v) { - setState(() { - username = v; - }); - }, - ), - ); - } - - _getPasswordTextField() { - return Container( - height: isPortrait && !isMobileSize ? 82.h : 52.h, - alignment: Alignment.centerLeft, - padding: EdgeInsets.symmetric( - horizontal: 12.w, - ), - decoration: BoxDecoration( - border: Border.all( - width: 1.h, - color: appGreyShade, - ), - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - ), - child: TextField( - obscureText: true, - decoration: InputDecoration( - hintText: appLocalizations.enter_password, - hintStyle: isPortrait && !isMobileSize - ? AppTextStyle.tabletPortraitTextfieldHintText - : AppTextStyle.mobileTextfieldHintText, - border: InputBorder.none, - ), - style: TextStyle( - fontSize: isPortrait && !isMobileSize ? 22 : 14, - color: appBlack, - ), - onChanged: (v) { - setState(() { - password = v; - }); - }, - ), - ); - } -} diff --git a/lib/ui/process_ui/process_type.dart b/lib/ui/process_ui/process_type.dart new file mode 100644 index 000000000..15fa1f18f --- /dev/null +++ b/lib/ui/process_ui/process_type.dart @@ -0,0 +1,42 @@ +/* + * Copyright (c) Modular Open Source Identity Platform + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * +*/ + +enum ProcessType { + newProcess, + lostProcess, + updateProcess, + correctionProcess, +} + +extension ProcessTypeExtension on ProcessType { + String get id { + switch (this) { + case ProcessType.newProcess: + return 'NEW'; + case ProcessType.lostProcess: + return 'LOST'; + case ProcessType.updateProcess: + return 'UPDATE'; + case ProcessType.correctionProcess: + return 'CORRECTION'; + } + } + + String get routeName { + switch (this) { + case ProcessType.newProcess: + return '/new_process'; + case ProcessType.lostProcess: + return '/lost_process'; + case ProcessType.updateProcess: + return '/update_process'; + case ProcessType.correctionProcess: + return '/correction_process'; + } + } +} + diff --git a/lib/ui/process_ui/update_process.dart b/lib/ui/process_ui/update_process.dart deleted file mode 100644 index 2312656d0..000000000 --- a/lib/ui/process_ui/update_process.dart +++ /dev/null @@ -1,1162 +0,0 @@ -/* - * Copyright (c) Modular Open Source Identity Platform - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * -*/ - -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:provider/provider.dart'; -import 'package:registration_client/model/biometric_attribute_data.dart'; -import 'package:registration_client/model/field.dart'; -import 'package:registration_client/model/process.dart'; -import 'package:registration_client/model/screen.dart'; -import 'package:registration_client/pigeon/biometrics_pigeon.dart'; -import 'package:registration_client/pigeon/demographics_data_pigeon.dart'; -import 'package:registration_client/pigeon/registration_data_pigeon.dart'; - -import 'package:registration_client/provider/auth_provider.dart'; -import 'package:registration_client/provider/connectivity_provider.dart'; -import 'package:registration_client/provider/global_provider.dart'; -import 'package:registration_client/provider/registration_task_provider.dart'; - -import 'package:registration_client/ui/post_registration/acknowledgement_page.dart'; - -import 'package:registration_client/ui/post_registration/preview_page.dart'; -import 'package:registration_client/ui/process_ui/widgets/update_field_selector.dart'; - -import 'package:registration_client/ui/process_ui/widgets/update_process_screen_content.dart'; - -import 'package:registration_client/utils/app_config.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:registration_client/utils/app_style.dart'; - -import '../../utils/life_cycle_event_handler.dart'; - -class UpdateProcess extends StatefulWidget { - const UpdateProcess({ - super.key, - }); - - static const routeName = '/update_process'; - - @override - State createState() => _UpdateProcessState(); -} - -class _UpdateProcessState extends State - with WidgetsBindingObserver { - late GlobalProvider globalProvider; - late RegistrationTaskProvider registrationTaskProvider; - late AuthProvider authProvider; - late ConnectivityProvider connectivityProvider; - late AppLocalizations appLocalizations = AppLocalizations.of(context)!; - bool isPortrait = true; - bool fieldSelectionCompleted = false; - - List postRegistrationTabs = [ - 'Preview', - 'Authentication', - 'Acknowledgement', - ]; - - Map? templateTitleMap; - - String username = ''; - String password = ''; - - @override - void initState() { - globalProvider = Provider.of(context, listen: false); - registrationTaskProvider = - Provider.of(context, listen: false); - authProvider = Provider.of(context, listen: false); - connectivityProvider = - Provider.of(context, listen: false); - super.initState(); - WidgetsBinding.instance.addObserver(LifecycleEventHandler( - resumeCallBack: () async { - if (mounted) { - setState(() { - closeKeyboard(); - }); - } - }, - suspendingCallBack: () async { - if (mounted) { - setState(() { - closeKeyboard(); - }); - } - }, - )); - _registrationScreenLoadedAudit(); - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } - - void closeKeyboard() { - FocusScope.of(context).unfocus(); - } - - void _showInSnackBar(String value) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(value), - ), - ); - } - - _authenticatePacket(BuildContext context) async { - if (!_validateUsername(context)) { - return false; - } - - if (!_validatePassword(context)) { - return false; - } - - if (authProvider.currentUser.userId != username) { - _showInSnackBar(appLocalizations.invalid_user); - return false; - } - - await authProvider.authenticatePacket(username, password); - - if (!authProvider.isPacketAuthenticated) { - _showErrorInSnackbar(); - return false; - } - return true; - } - - _showErrorInSnackbar() { - String errorMsg = authProvider.packetError; - String snackbarText = ""; - - switch (errorMsg) { - case "REG_TRY_AGAIN": - snackbarText = appLocalizations.login_failed; - break; - - case "REG_INVALID_REQUEST": - snackbarText = appLocalizations.password_incorrect; - break; - - case "REG_NETWORK_ERROR": - snackbarText = appLocalizations.network_error; - break; - - case "": - return; - - default: - snackbarText = errorMsg; - break; - } - - _showInSnackBar(snackbarText); - } - - bool _validateUsername(BuildContext context) { - if (username.trim().isEmpty) { - _showInSnackBar(appLocalizations.username_required); - return false; - } - - if (username.trim().length > 50) { - _showInSnackBar(appLocalizations.username_exceed); - return false; - } - - return true; - } - - bool _validatePassword(BuildContext context) { - if (password.trim().isEmpty) { - _showInSnackBar(appLocalizations.password_required); - return false; - } - - if (password.trim().length > 50) { - _showInSnackBar(appLocalizations.password_exceed); - return false; - } - - return true; - } - - _resetValuesOnRegistrationComplete() { - Navigator.of(context).pop(); - } - - void _registrationScreenLoadedAudit() async { - await globalProvider.getAudit("REG-EVT-002", "REG-MOD-103"); - } - - _nextButtonClickedAudit() async { - await globalProvider.getAudit("REG-EVT-003", "REG-MOD-103"); - } - - bool continueButton = false; - - @override - Widget build(BuildContext context) { - postRegistrationTabs = [ - appLocalizations.preview_page, - appLocalizations.packet_auth_page, - appLocalizations.acknowledgement_page, - ]; - isPortrait = MediaQuery.of(context).orientation == Orientation.portrait; - bool isMobile = MediaQuery.of(context).size.width < 750; - double w = ScreenUtil().screenWidth; - Map arguments = - ModalRoute.of(context)!.settings.arguments! as Map; - final Process newProcess = arguments["process"]; - int size = newProcess.screens!.length; - - Future onWillPop() async { - if (globalProvider.newProcessTabIndex > 0 && - globalProvider.newProcessTabIndex < - newProcess.screens!.length + postRegistrationTabs.length - 1) { - globalProvider.newProcessTabIndex = - globalProvider.newProcessTabIndex - 1; - } else if (globalProvider.newProcessTabIndex == 0 && - fieldSelectionCompleted) { - setState(() { - fieldSelectionCompleted = false; - }); - } else { - return true; - } - return false; - } - - isExceptionPresent(String id) { - bool isExceptionPresent = false; - for (BiometricAttributeData x in globalProvider.fieldInputValue[id]) { - if (x.exceptions.contains(true) || x.title == "Exception") { - isExceptionPresent = true; - break; - } - } - return isExceptionPresent; - } - - returnBiometricListLength(List? list, String id) { - int i = 0; - if (list!.contains("leftEye") && list.contains("rightEye")) { - i++; - } - if (list.contains("rightIndex") && - list.contains("rightLittle") && - list.contains("rightRing") && - list.contains("rightMiddle")) { - i++; - } - if (list.contains("leftIndex") && - list.contains("leftLittle") && - list.contains("leftRing") && - list.contains("leftMiddle")) { - i++; - } - if (list.contains("rightThumb") && list.contains("rightThumb")) { - i++; - } - if (list.contains("face")) { - i++; - } - if (isExceptionPresent(id) == true) { - i++; - } - return i; - } - - biometricRequiredFieldValidation(Field field) async { - if (field.controlType == "biometrics") { - int count = returnBiometricListLength(field.bioAttributes, field.id!); - if (globalProvider.completeException[field.id!] != null) { - int length = globalProvider.completeException[field.id!].length; - count = count - length; - } - - if (globalProvider.fieldInputValue[field.id!].length < count) { - return false; - } - } - - return true; - } - - biometricConditionalFieldValidation(Field field) async { - bool valid = await BiometricsApi().conditionalBioAttributeValidation( - field.id!, field.conditionalBioAttributes!.first!.validationExpr!); - if (field.exceptionPhotoRequired == true) { - List biometricAttributeDataList = - globalProvider.fieldInputValue[field.id!]; - bool isExceptionPresent = false; - bool isExceptionAttributePresent = false; - for (var biometricAttributeData in biometricAttributeDataList) { - if (globalProvider.exceptionAttributes - .contains(biometricAttributeData.title)) { - isExceptionPresent = true; - } - if (biometricAttributeData.title == "Exception") { - isExceptionAttributePresent = true; - } - } - - if (isExceptionPresent == true && - isExceptionAttributePresent == false) { - return false; - } - } - if (!valid) { - return false; - } - return true; - } - - biometricValidation(Field field) async { - if (field.conditionalBioAttributes != null && - field.conditionalBioAttributes!.isNotEmpty) { - String response = await BiometricsApi().getAgeGroup(); - if (response - .compareTo(field.conditionalBioAttributes!.first!.ageGroup!) != - 0) { - bool isValid = true; - if (field.conditionalBioAttributes!.first!.ageGroup! == "ALL") { - isValid = await biometricConditionalFieldValidation(field); - } else { - isValid = await biometricRequiredFieldValidation(field); - } - - if (!isValid) { - return false; - } - } - - if (response - .compareTo(field.conditionalBioAttributes!.first!.ageGroup!) == - 0) { - bool isValid = await biometricConditionalFieldValidation(field); - if (!isValid) { - return false; - } - } - } - - return true; - } - - ageDateChangeValidation(int currentIndex) async { - if (globalProvider.newProcessTabIndex < size) { - Screen screen = newProcess.screens!.elementAt(currentIndex)!; - for (int i = 0; i < screen.fields!.length; i++) { - if (screen.fields!.elementAt(i)!.id == "dateOfBirth") { - if (globalProvider.checkAgeGroupChange == "") { - globalProvider.checkAgeGroupChange = globalProvider.ageGroup; - } else { - if (globalProvider.checkAgeGroupChange - .compareTo(globalProvider.ageGroup) == - 0) { - } else { - List screens = []; - for (int i = 0; - i < registrationTaskProvider.listOfProcesses.length; - i++) { - Process process = Process.fromJson( - jsonDecode( - context - .read() - .listOfProcesses - .elementAt(i) - .toString(), - ), - ); - if (process.id == "NEW" || process.id == "UPDATE") { - screens = process.screens!; - } - } - for (Screen? screen in screens) { - if (screen!.name! == "Documents" || - screen.name! == "BiometricDetails") { - for (Field? field in screen.fields!) { - globalProvider.fieldInputValue.remove(field!.id); - } - } - } - await BiometricsApi().clearBiometricAndDocumentHashmap(); - globalProvider.clearExceptions(); - globalProvider.checkAgeGroupChange = globalProvider.ageGroup; - } - } - } - } - } - } - - customValidation(int currentIndex) async { - if (currentIndex == 0) { - if (globalProvider.updateUINNumber.isEmpty) { - return false; - } - return true; - } - if (globalProvider.newProcessTabIndex < size) { - Screen screen = newProcess.screens!.elementAt(currentIndex)!; - for (int i = 0; i < screen.fields!.length; i++) { - Field field = screen.fields!.elementAt(i)!; - String group = field.group!; - String fieldId = field.id!; - if (globalProvider.selectedUpdateFields[group] != null) { - if (field.required! || - (globalProvider.mvelRequiredFields[fieldId] ?? false)) { - if (!(globalProvider.fieldInputValue.containsKey(field.id)) && - !(globalProvider.fieldInputValue - .containsKey(field.subType)) && - !(globalProvider.fieldInputValue - .containsKey("${field.group}${field.subType}"))) { - return false; - } - - bool isValid = await biometricValidation(field); - if (!isValid) { - return false; - } - } - } else if (newProcess.autoSelectedGroups!.contains(group)) { - if (globalProvider.mvelRequiredFields[fieldId] ?? false) { - if (!(globalProvider.fieldInputValue.containsKey(field.id)) && - !(globalProvider.fieldInputValue - .containsKey(field.subType)) && - !(globalProvider.fieldInputValue - .containsKey("${field.group}${field.subType}"))) { - return false; - } - - if (field.conditionalBioAttributes != null && - field.conditionalBioAttributes!.isNotEmpty) { - bool isValid = await biometricConditionalFieldValidation(field); - if (!isValid) { - return false; - } - } - } - } - } - } - return true; - } - - continueButtonTap(int size, newProcess) async { - if (globalProvider.newProcessTabIndex == 0 && !fieldSelectionCompleted) { - if (globalProvider.selectedUpdateFields.isEmpty || - globalProvider.updateFieldKey.currentState == null || - !globalProvider.updateFieldKey.currentState!.validate()) { - return; - } - } - if (!fieldSelectionCompleted) { - globalProvider.clearMap(); - globalProvider.clearScannedPages(); - globalProvider.clearExceptions(); - await registrationTaskProvider.changeUpdatableFieldGroups(); - globalProvider.ageGroup = ""; - await BiometricsApi().clearBiometricAndDocumentHashmap(); - setState(() { - fieldSelectionCompleted = true; - }); - return; - } - if (globalProvider.newProcessTabIndex < size) { - ageDateChangeValidation(globalProvider.newProcessTabIndex); - bool customValidator = - await customValidation(globalProvider.newProcessTabIndex); - if (customValidator) { - if (globalProvider.formKey.currentState!.validate()) { - if (globalProvider.newProcessTabIndex == - newProcess.screens!.length - 1) { - templateTitleMap = { - 'demographicInfo': appLocalizations.demographic_information, - 'documents': appLocalizations.documents, - 'bioMetrics': appLocalizations.biometrics, - }; - registrationTaskProvider.setPreviewTemplate(""); - registrationTaskProvider.setAcknowledgementTemplate(""); - await registrationTaskProvider.getPreviewTemplate( - true, templateTitleMap!); - await registrationTaskProvider.getAcknowledgementTemplate( - false, templateTitleMap!); - } - - globalProvider.newProcessTabIndex = - globalProvider.newProcessTabIndex + 1; - } - } - - _nextButtonClickedAudit(); - } else { - if (globalProvider.newProcessTabIndex == size + 1) { - bool isPacketAuthenticated = await _authenticatePacket(context); - if (!isPacketAuthenticated) { - return; - } - RegistrationSubmitResponse registrationSubmitResponse = - await registrationTaskProvider.submitRegistrationDto(username); - if (registrationSubmitResponse.errorCode!.isNotEmpty) { - _showInSnackBar(registrationSubmitResponse.errorCode!); - return; - } - globalProvider.setRegId(registrationSubmitResponse.rId); - - // Updating key to packetId after success creation of packet - registrationTaskProvider - .updateTemplateStorageKey(registrationSubmitResponse.rId); - registrationTaskProvider.deleteDefaultTemplateStored(); - - setState(() { - username = ''; - password = ''; - }); - } - if (globalProvider.newProcessTabIndex == size + 2) { - _resetValuesOnRegistrationComplete(); - return; - } - globalProvider.newProcessTabIndex = - globalProvider.newProcessTabIndex + 1; - } - } - - customValidation(globalProvider.newProcessTabIndex).then((value) { - if (globalProvider.newProcessTabIndex == 0) { - if (!fieldSelectionCompleted) { - continueButton = value && - globalProvider.updateFieldKey.currentState != null && - globalProvider.updateFieldKey.currentState!.validate(); - } else { - continueButton = true; - } - } else { - continueButton = value && - globalProvider.formKey.currentState != null && - globalProvider.formKey.currentState!.validate(); - } - - if (globalProvider.newProcessTabIndex >= size) { - continueButton = true; - } - }); - - return WillPopScope( - onWillPop: onWillPop, - child: SafeArea( - child: Scaffold( - backgroundColor: secondaryColors.elementAt(10), - bottomNavigationBar: Container( - decoration: BoxDecoration( - border: const Border( - top: BorderSide( - color: dividerColor, - width: 1, - ), - ), - color: pureWhite, - ), - padding: EdgeInsets.symmetric( - horizontal: isPortrait ? 20.w : 60.w, - vertical: 16.h, - ), - // height: isPortrait ? 94.h : 84.h, - child: context.watch().newProcessTabIndex == 0 - ? Row( - children: [ - Expanded( - child: fieldSelectionCompleted - ? OutlinedButton( - child: SizedBox( - height: - isPortrait && !isMobileSize ? 68.h : 52.h, - child: Center( - child: Text( - appLocalizations.go_back, - style: TextStyle( - fontSize: isPortrait && !isMobileSize - ? 22 - : 14, - ), - ), - ), - ), - onPressed: () { - if (!fieldSelectionCompleted) { - Navigator.of(context).pop(); - } else { - setState(() { - fieldSelectionCompleted = false; - }); - } - }, - ) - : const SizedBox(), - ), - SizedBox( - width: 10.w, - ), - Expanded( - child: ElevatedButton( - style: ButtonStyle( - maximumSize: MaterialStateProperty.all( - const Size(209, 52)), - minimumSize: MaterialStateProperty.all( - const Size(209, 52)), - backgroundColor: MaterialStateProperty.all( - !fieldSelectionCompleted - ? (context - .watch() - .selectedUpdateFields - .isNotEmpty && - continueButton) - ? solidPrimary - : Colors.grey - : solidPrimary), - ), - child: SizedBox( - height: isPortrait && !isMobileSize ? 68.h : 52.h, - child: Center( - child: Text( - fieldSelectionCompleted - ? appLocalizations.informed - : appLocalizations.continue_text, - style: TextStyle( - fontSize: - isPortrait && !isMobileSize ? 22 : 14, - ), - ), - ), - ), - onPressed: () async { - if (fieldSelectionCompleted) { - registrationTaskProvider.addConsentField("Y"); - await DemographicsApi().addDemographicField( - "UIN", globalProvider.updateUINNumber); - await DemographicsApi() - .addDemographicField("consent", "true"); - } - continueButtonTap(size, newProcess); - }, - ), - ), - ], - ) - : Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - // globalProvider.newProcessTabIndex == size + 2 - // ? ElevatedButton( - // onPressed: () async { - // await connectivityProvider - // .checkNetworkConnection(); - // bool isConnected = - // connectivityProvider.isConnected; - // if (!isConnected) { - // _showInSnackBar( - // appLocalizations.network_error); - // return; - // } - // globalProvider.syncPacket(globalProvider.regId); - // }, - // child: Text(appLocalizations.sync_packet), - // ) - // : const SizedBox.shrink(), - // SizedBox( - // width: 10.w, - // ), - // globalProvider.newProcessTabIndex == size + 2 - // ? ElevatedButton( - // onPressed: () async { - // await connectivityProvider - // .checkNetworkConnection(); - // bool isConnected = - // connectivityProvider.isConnected; - // if (!isConnected) { - // _showInSnackBar( - // appLocalizations.network_error); - // return; - // } - // globalProvider - // .uploadPacket(globalProvider.regId); - // }, - // child: Text(appLocalizations.upload_packet), - // ) - // : const SizedBox.shrink(), - const Expanded( - child: SizedBox(), - ), - ElevatedButton( - style: ButtonStyle( - maximumSize: MaterialStateProperty.all( - const Size(209, 52)), - minimumSize: MaterialStateProperty.all( - const Size(209, 52)), - backgroundColor: MaterialStateProperty.all( - continueButton ? solidPrimary : Colors.grey), - ), - onPressed: () { - continueButtonTap(size, newProcess); - }, - child: Text( - context.read().newProcessTabIndex <= - size - ? appLocalizations.continue_text - : globalProvider.newProcessTabIndex == size + 1 - ? appLocalizations.authenticate - : appLocalizations.go_to_home, - style: const TextStyle(color: appWhite), - ), - ), - ], - ), - ), - body: SingleChildScrollView( - child: AnnotatedRegion( - value: const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - ), - child: Column( - children: [ - // isPortrait - // ? const SizedBox() - // : const Column( - // children: [ - // // TabletHeader(), - // TabletNavbar(), - // ], - // ), - Container( - padding: isMobile && !isMobileSize - ? const EdgeInsets.fromLTRB(0, 46, 0, 0) - : const EdgeInsets.fromLTRB(0, 0, 0, 0), - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0xff214FBF), Color(0xff1C43A1)], - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: w, - height: isPortrait ? 21.w : 30.w, - ), - Padding( - padding: isPortrait - ? EdgeInsets.fromLTRB(20.w, 0, 0, 0) - : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), - child: Text( - newProcess.label![globalProvider.selectedLanguage]!, - style: Theme.of(context) - .textTheme - .titleMedium - ?.copyWith( - color: pureWhite, - fontWeight: semiBold, - fontSize: isPortrait ? 24 : 21), - ), - ), - SizedBox( - height: 30.h, - ), - fieldSelectionCompleted - ? Divider( - height: 12.h, - thickness: 1, - color: secondaryColors.elementAt(2), - ) - : const SizedBox(), - fieldSelectionCompleted - ? Padding( - padding: isPortrait - ? const EdgeInsets.all(0) - : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), - child: Stack( - alignment: FractionalOffset.centerRight, - children: [ - Padding( - padding: isPortrait - ? EdgeInsets.fromLTRB( - 20.w, 10.h, 0, 0) - : EdgeInsets.fromLTRB(0, 10.h, 0, 0), - child: SizedBox( - height: 36.h, - child: ListView.builder( - padding: const EdgeInsets.all(0), - scrollDirection: Axis.horizontal, - itemCount: - newProcess.screens!.length + 3, - itemBuilder: (BuildContext context, - int index) { - return GestureDetector( - onTap: () { - if (globalProvider - .newProcessTabIndex == - size + 2) { - return; - } - - if (index < - globalProvider - .newProcessTabIndex) { - globalProvider - .newProcessTabIndex = - index; - } - }, - child: Row( - children: [ - Container( - padding: - EdgeInsets.fromLTRB( - 0, 0, 0, 8.h), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: (context - .watch< - GlobalProvider>() - .newProcessTabIndex == - index) - ? pureWhite - : Colors - .transparent, - width: 3), - ), - ), - child: Row( - children: [ - (index < - context - .watch< - GlobalProvider>() - .newProcessTabIndex) - ? Icon( - Icons - .check_circle, - size: 17, - color: secondaryColors - .elementAt( - 11), - ) - : (context - .watch< - GlobalProvider>() - .newProcessTabIndex == - index) - ? Icon( - Icons - .circle, - color: - pureWhite, - size: 17, - ) - : Icon( - Icons - .circle_outlined, - size: 17, - color: secondaryColors - .elementAt( - 9), - ), - SizedBox( - width: 6.w, - ), - Text( - index < size - ? newProcess - .screens![ - index]! - .label![ - context - .read< - GlobalProvider>() - .selectedLanguage]! - : postRegistrationTabs[ - index - - size], - style: Theme.of( - context) - .textTheme - .titleSmall - ?.copyWith( - color: (context.watch().newProcessTabIndex == - index) - ? pureWhite - : secondaryColors - .elementAt( - 9), - fontWeight: - semiBold, - fontSize: - 14), - ), - ], - ), - ), - SizedBox( - width: 35.w, - ), - ], - ), - ); - }), - ), - ), - Container( - height: 36.h, - width: 25.w, - padding: - const EdgeInsets.fromLTRB(0, 0, 0, 0), - color: solidPrimary, - child: Icon( - Icons.arrow_forward_ios_outlined, - color: pureWhite, - size: 17, - ), - ), - ], - ), - ) - : const SizedBox.shrink(), - const SizedBox( - height: 5, - ), - ], - ), - ), - Padding( - padding: isPortrait - ? const EdgeInsets.all(0) - : EdgeInsets.fromLTRB(60.w, 0, 60.w, 0), - child: !fieldSelectionCompleted - ? UpdateFieldSelector(process: newProcess) - : context.watch().newProcessTabIndex < - size - ? UpdateProcessScreenContent( - context: context, - process: newProcess, - screen: newProcess.screens!.elementAt(context - .watch() - .newProcessTabIndex)!) - : context - .watch() - .newProcessTabIndex == - size - ? const PreviewPage() - : context - .watch() - .newProcessTabIndex == - size + 1 - ? _getPacketAuthComponent() - : const AcknowledgementPage(), - ), - SizedBox( - height: 20.h, - ), - ], - ), - ), - ), - ), - ), - ); - } - - Widget _getPacketAuthComponent() { - return Column( - children: [ - SizedBox( - height: 30.h, - ), - Container( - width: isPortrait && !isMobileSize ? 566.w : 376.w, - padding: EdgeInsets.only( - top: 24.h, - bottom: 28.h, - left: 20.w, - right: 20.w, - ), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - color: pureWhite, - ), - child: Column( - children: [ - _getAuthIcon(), - SizedBox( - height: 26.h, - ), - Text( - appLocalizations.authenticate_using_password, - style: TextStyle( - fontSize: isPortrait && !isMobileSize ? 24 : 18, - fontWeight: semiBold, - color: appBlack), - ), - SizedBox( - height: 35.h, - ), - Row( - children: [ - Text( - appLocalizations.username, - style: isPortrait - ? AppTextStyle.tabletPortraitTextfieldHeader - : AppTextStyle.mobileTextfieldHeader, - ), - const Text( - ' *', - style: TextStyle( - color: mandatoryField, - ), - ), - ], - ), - SizedBox( - height: 11.h, - ), - _getUsernameTextField(), - SizedBox( - height: 35.h, - ), - Row( - children: [ - Text( - appLocalizations.password, - style: isPortrait - ? AppTextStyle.tabletPortraitTextfieldHeader - : AppTextStyle.mobileTextfieldHeader, - ), - const Text( - ' *', - style: TextStyle(color: mandatoryField), - ), - ], - ), - SizedBox( - height: 11.h, - ), - _getPasswordTextField(), - ], - ), - ), - ], - ); - } - - _getAuthIcon() { - return Container( - height: 80.w, - width: 80.w, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: authIconBorder, - width: 2, - ), - color: authIconBackground, - ), - child: Center( - child: Image.asset('assets/images/Registering an Individual@2x.png'), - ), - ); - } - - _getUsernameTextField() { - return Container( - height: isPortrait && !isMobileSize ? 82.h : 52.h, - alignment: Alignment.centerLeft, - padding: EdgeInsets.symmetric( - horizontal: 12.w, - ), - decoration: BoxDecoration( - border: Border.all( - width: 1.h, - color: appGreyShade, - ), - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - ), - child: TextField( - decoration: InputDecoration( - hintText: appLocalizations.enter_username, - hintStyle: isPortrait && !isMobileSize - ? AppTextStyle.tabletPortraitTextfieldHintText - : AppTextStyle.mobileTextfieldHintText, - border: InputBorder.none, - ), - style: TextStyle( - fontSize: isPortrait && !isMobileSize ? 22 : 14, - color: appBlack, - ), - onChanged: (v) { - setState(() { - username = v; - }); - }, - ), - ); - } - - _getPasswordTextField() { - return Container( - height: isPortrait && !isMobileSize ? 82.h : 52.h, - alignment: Alignment.centerLeft, - padding: EdgeInsets.symmetric( - horizontal: 12.w, - ), - decoration: BoxDecoration( - border: Border.all( - width: 1.h, - color: appGreyShade, - ), - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - ), - child: TextField( - obscureText: true, - decoration: InputDecoration( - hintText: appLocalizations.enter_password, - hintStyle: isPortrait && !isMobileSize - ? AppTextStyle.tabletPortraitTextfieldHintText - : AppTextStyle.mobileTextfieldHintText, - border: InputBorder.none, - ), - style: TextStyle( - fontSize: isPortrait && !isMobileSize ? 22 : 14, - color: appBlack, - ), - onChanged: (v) { - setState(() { - password = v; - }); - }, - ), - ); - } -} diff --git a/lib/ui/process_ui/widgets/additional_Info_ReqId_control.dart b/lib/ui/process_ui/widgets/additional_Info_ReqId_control.dart new file mode 100644 index 000000000..f51aed556 --- /dev/null +++ b/lib/ui/process_ui/widgets/additional_Info_ReqId_control.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:provider/provider.dart'; +import 'package:registration_client/provider/global_provider.dart'; +import 'package:registration_client/utils/app_config.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import '../../../provider/registration_task_provider.dart'; + +class AdditionalInfoReqIdControl extends StatefulWidget { + final VoidCallback? onFetched; + const AdditionalInfoReqIdControl({Key? key, this.onFetched}) : super(key: key); + + @override + State createState() => _AdditionalInfoReqIdControlState(); +} + +class _AdditionalInfoReqIdControlState extends State { + late GlobalProvider globalProvider; + late RegistrationTaskProvider registrationTaskProvider; + final TextEditingController reqIdController = TextEditingController(); + + @override + void initState() { + globalProvider = Provider.of(context, listen: false); + registrationTaskProvider = Provider.of(context, listen: false); + if (globalProvider.additionalInfoReqId != null && globalProvider.additionalInfoReqId!.isNotEmpty) { + reqIdController.text = globalProvider.additionalInfoReqId!; + } + super.initState(); + } + + @override + Widget build(BuildContext context) { + bool isPortrait = MediaQuery.of(context).orientation == Orientation.portrait; + return Card( + elevation: 5, + color: pureWhite, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6.0), + ), + margin: EdgeInsets.symmetric( + vertical: 20.h, horizontal: isPortrait ? 16.w : 0), + child: Padding( + padding: EdgeInsets.symmetric(vertical: 24.h, horizontal: 16.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.of(context)!.additional_info_req_id, + style: TextStyle( + fontSize: isPortrait ? 18 : 14), + ), + SizedBox(height: 10.h), + Row( + children: [ + Expanded( + flex: 3, + child: TextFormField( + controller: reqIdController, + onChanged: (value) { + globalProvider.setAdditionalInfoReqId(value); + registrationTaskProvider.setAdditionalReqId(value); + registrationTaskProvider.setApplicationId(value.split("-")[0]); + }, + textAlign: TextAlign.left, + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8.0), + borderSide: const BorderSide(color: appGreyShade, width: 1), + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + hintText: AppLocalizations.of(context)!.enter_additional_info_req_id, + hintStyle: const TextStyle(color: appBlackShade3, fontSize: 14), + ), + ), + ), + const Spacer(), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/ui/process_ui/widgets/custom_cupertino_picker.dart b/lib/ui/process_ui/widgets/custom_cupertino_picker.dart index 47d1f8dad..137e16c7b 100644 --- a/lib/ui/process_ui/widgets/custom_cupertino_picker.dart +++ b/lib/ui/process_ui/widgets/custom_cupertino_picker.dart @@ -135,11 +135,7 @@ class _CustomCupertinoDatePickerState extends State { } void _scrollList(FixedExtentScrollController controller, int index) { - controller.animateToItem( - index, - curve: Curves.easeIn, - duration: const Duration(milliseconds: 300), - ); + controller.jumpToItem(index); } @override diff --git a/lib/ui/process_ui/widgets/device_settings_tab.dart b/lib/ui/process_ui/widgets/device_settings_tab.dart new file mode 100644 index 000000000..6fd29f944 --- /dev/null +++ b/lib/ui/process_ui/widgets/device_settings_tab.dart @@ -0,0 +1,146 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:registration_client/pigeon/biometrics_pigeon.dart'; +import '../../../model/settings.dart'; +import '../../../platform_spi/biometrics_service.dart'; +import '../../../utils/app_config.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +class DeviceSettingsTab extends StatefulWidget { + DeviceSettingsTab( + {super.key, required this.settings, required this.selectedLan}); + Settings settings; + String selectedLan; + + @override + _DeviceSettingsTabState createState() => _DeviceSettingsTabState(); +} + +class _DeviceSettingsTabState extends State { + late Future> _devicesFuture; + + @override + void initState() { + super.initState(); + _devicesFuture = fetchDeviceDetails(); + } + + Future> fetchDeviceDetails() async { + List deviceDetails = []; + try { + await Future.delayed(const Duration(seconds: 1)); + List modality = ["Face", "Iris", "Thumbs"]; + + for (var modalityType in modality) { + List data = await BiometricsService().getListOfDevices(modalityType); + deviceDetails.addAll(data.whereType()); + } + return deviceDetails; + } catch (e) { + debugPrint('Error fetching device details: $e'); + return []; + } + } + + @override + Widget build(BuildContext context) { + return FutureBuilder>( + future: _devicesFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return const Center(child: Text("Error loading device details")); + } + + final devices = snapshot.data ?? []; + if (devices.isEmpty) { + return const Center(child: Text("No devices found")); + } + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.settings.label?[widget.selectedLan] ?? + widget.settings.label?['eng'] ?? + (widget.settings.label?.values.first ?? 'UnKnown'), + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: solidPrimary, + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 20), + ), + onPressed: () { + setState(() {}); + _devicesFuture = fetchDeviceDetails(); + }, + child: Text(AppLocalizations.of(context)!.scan_now), + ), + ], + ), + const SizedBox(height: 16), + Expanded( + child: GridView.builder( + itemCount: devices.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 16, + mainAxisSpacing: 16, + childAspectRatio: 3.5, + ), + itemBuilder: (context, index) { + final device = devices[index]; + return Container( + padding: const EdgeInsets.all(8), // even less padding + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.scanner, size: 25, color: solidPrimary), + const SizedBox(width: 10), + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text("ID: ${device.deviceId ?? ''}", + style: const TextStyle(fontSize: 12)), + Text( + "Name: ${device.deviceName?? ''}", + style: const TextStyle(fontSize: 12)), + Text( + "Status: ${device.connectionStatus ?? ''}", + style: const TextStyle(fontSize: 12)), + ], + ), + ), + ], + ), + ); + }, + ), + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/ui/process_ui/widgets/document_upload_control.dart b/lib/ui/process_ui/widgets/document_upload_control.dart index 5e3affb81..88295345a 100644 --- a/lib/ui/process_ui/widgets/document_upload_control.dart +++ b/lib/ui/process_ui/widgets/document_upload_control.dart @@ -42,6 +42,11 @@ class _DocumentUploadControlState extends State { late RegistrationTaskProvider registrationTaskProvider; Map transliterationLangMapper = {}; bool hasInteractedWithDropdown = false; + String getReadableFileSize(int bytes) { + if (bytes < 1024) return "$bytes B"; + if (bytes < 1024 * 1024) return "${(bytes / 1024).toStringAsFixed(2)} KB"; + return "${(bytes / (1024 * 1024)).toStringAsFixed(2)} MB"; + } FixedExtentScrollController scrollController = FixedExtentScrollController(); @@ -509,7 +514,7 @@ class _DocumentUploadControlState extends State { ), imageBytesList.isNotEmpty ? SizedBox( - height: 110.h, + height: 120.h, child: ListView( scrollDirection: Axis.horizontal, children: imageBytesList.map((item) { @@ -531,7 +536,12 @@ class _DocumentUploadControlState extends State { child: Image.memory(item!), ), ), - SizedBox(height: 10.h), + SizedBox(height: 2.h), + Text( + 'Size: ${getReadableFileSize(item.length)}', + style: TextStyle(fontSize: 12.sp, color: Colors.grey), + ), + SizedBox(height: 2.h), GestureDetector( onTap: () { _deleteImage(widget.field, item); @@ -771,7 +781,7 @@ class _DocumentUploadControlState extends State { ), imageBytesList.isNotEmpty ? SizedBox( - height: 110.h, + height: 130.h, child: ListView( scrollDirection: Axis.horizontal, children: imageBytesList.map((item) { @@ -794,6 +804,11 @@ class _DocumentUploadControlState extends State { ), ), SizedBox(height: 10.h), + Text( + 'Size: ${getReadableFileSize(item.length)}', + style: TextStyle(fontSize: 12.sp, color: Colors.grey), + ), + SizedBox(height: 10.h), GestureDetector( onTap: () { _deleteImage(widget.field, item); diff --git a/lib/ui/process_ui/widgets/dropdown_control.dart b/lib/ui/process_ui/widgets/dropdown_control.dart index 2b0095306..0ebede31b 100644 --- a/lib/ui/process_ui/widgets/dropdown_control.dart +++ b/lib/ui/process_ui/widgets/dropdown_control.dart @@ -249,6 +249,7 @@ class _CustomDropDownState extends State { height: 10, ), DropdownButtonFormField( + isExpanded: true, icon: const Icon(null), decoration: InputDecoration( contentPadding: @@ -270,8 +271,11 @@ class _CustomDropDownState extends State { items: list .map((option) => DropdownMenuItem( value: option, - child: - Text(option!.concatenatedName ?? option.name), + child: Text(option!.concatenatedName ?? option.name, + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), )) .toList(), autovalidateMode: AutovalidateMode.onUserInteraction, diff --git a/lib/ui/process_ui/widgets/new_process_screen_content.dart b/lib/ui/process_ui/widgets/generic_process_screen_content.dart similarity index 74% rename from lib/ui/process_ui/widgets/new_process_screen_content.dart rename to lib/ui/process_ui/widgets/generic_process_screen_content.dart index 8c3f68be6..4c9bdea5d 100644 --- a/lib/ui/process_ui/widgets/new_process_screen_content.dart +++ b/lib/ui/process_ui/widgets/generic_process_screen_content.dart @@ -11,9 +11,11 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:registration_client/model/field.dart'; +import 'package:registration_client/model/process.dart'; import 'package:registration_client/model/screen.dart'; import 'package:registration_client/provider/global_provider.dart'; import 'package:registration_client/provider/registration_task_provider.dart'; +import 'package:registration_client/ui/process_ui/process_type.dart'; import 'package:registration_client/ui/process_ui/widgets/age_date_control.dart'; import 'package:registration_client/ui/process_ui/widgets/biometric_capture_control.dart'; @@ -28,22 +30,31 @@ import 'package:registration_client/ui/process_ui/widgets/html_box_control.dart' import 'package:registration_client/ui/process_ui/widgets/button_control.dart'; import 'package:registration_client/ui/process_ui/widgets/terms_and_conditions.dart'; import 'package:registration_client/ui/process_ui/widgets/pre_reg_data_control.dart'; +import 'package:registration_client/ui/process_ui/widgets/additional_Info_ReqId_control.dart'; import 'package:registration_client/ui/process_ui/widgets/textbox_control.dart'; import 'radio_button_control.dart'; -class NewProcessScreenContent extends StatefulWidget { - const NewProcessScreenContent( - {super.key, required this.context, required this.screen}); +class GenericProcessScreenContent extends StatefulWidget { + const GenericProcessScreenContent({ + super.key, + required this.context, + required this.screen, + required this.processType, + required this.process, + }); + final BuildContext context; final Screen screen; + final ProcessType processType; + final Process process; @override - State createState() => - _NewProcessScreenContentState(); + State createState() => + _GenericProcessScreenContentState(); } -class _NewProcessScreenContentState extends State { +class _GenericProcessScreenContentState extends State { late GlobalProvider globalProvider; late RegistrationTaskProvider registrationTaskProvider; int refreshValue = 0; @@ -69,8 +80,9 @@ class _NewProcessScreenContentState extends State { if (e.id == "preferredLang") { return const SizedBox.shrink(); } - - if (e.inputRequired == false) { + if ((widget.processType == ProcessType.newProcess || + widget.processType == ProcessType.correctionProcess) && + e.inputRequired == false) { return const SizedBox.shrink(); } @@ -86,7 +98,7 @@ class _NewProcessScreenContentState extends State { case "html": return HtmlBoxControl(field: e); case "biometrics": - if (context.watch().mvelRequiredFields[e.id] ?? true) { + if (context.watch().mvelRequiredFields[e.id] ?? _getDefaultBiometricVisibility()) { return BiometricCaptureControl(e: e); } return Container(); @@ -132,8 +144,15 @@ class _NewProcessScreenContentState extends State { validation: regexPattern, ); default: - return (e.controlType!=null)? Text("${e.controlType}"): const SizedBox.shrink(); + return (e.controlType != null) ? Text("${e.controlType}") : const SizedBox.shrink(); + } + } + + bool _getDefaultBiometricVisibility() { + if (widget.processType == ProcessType.updateProcess) { + return false; } + return true; } evaluateMVELVisible(String fieldData, Field e) async { @@ -154,12 +173,37 @@ class _NewProcessScreenContentState extends State { } _checkMvelVisible(Field e) async { - if (e.required == false) { + if (widget.processType == ProcessType.updateProcess) { if (e.requiredOn != null && e.requiredOn!.isNotEmpty) { await evaluateMVELVisible(jsonEncode(e.toJson()), e); await evaluateMVELRequired(jsonEncode(e.toJson()), e); } + } else { + if (e.required == false) { + if (e.requiredOn != null && e.requiredOn!.isNotEmpty) { + await evaluateMVELVisible(jsonEncode(e.toJson()), e); + await evaluateMVELRequired(jsonEncode(e.toJson()), e); + } + } + } + } + + bool _shouldShowField(Field e) { + + if (widget.processType == ProcessType.updateProcess) { + if (widget.process.autoSelectedGroups!.contains(e.group)) { + return true; + } else if (globalProvider.selectedUpdateFields[e.group] != null) { + return true; + } + return false; + } + + if (context.watch().mvelVisibleFields[e.id] ?? true) { + return true; } + + return false; } @override @@ -175,6 +219,11 @@ class _NewProcessScreenContentState extends State { }); }), ], + + if (widget.screen.additionalInfoRequestIdRequired == true) ...[ + const AdditionalInfoReqIdControl(), + ], + (context.watch().preRegControllerRefresh) ? const CircularProgressIndicator() : Form( @@ -183,10 +232,7 @@ class _NewProcessScreenContentState extends State { children: [ ...widget.screen.fields!.map((e) { _checkMvelVisible(e!); - if (context - .watch() - .mvelVisibleFields[e.id] ?? - true) { + if (_shouldShowField(e)) { return widgetType(e); } return Container(); diff --git a/lib/ui/process_ui/widgets/language_selector.dart b/lib/ui/process_ui/widgets/language_selector.dart index 16c770571..ba8222693 100644 --- a/lib/ui/process_ui/widgets/language_selector.dart +++ b/lib/ui/process_ui/widgets/language_selector.dart @@ -13,10 +13,8 @@ import 'package:registration_client/model/process.dart'; import 'package:registration_client/provider/global_provider.dart'; import 'package:registration_client/provider/registration_task_provider.dart'; -import 'package:registration_client/ui/process_ui/lost_process.dart'; +import 'package:registration_client/ui/process_ui/process_type.dart'; -import 'package:registration_client/ui/process_ui/new_process.dart'; -import 'package:registration_client/ui/process_ui/update_process.dart'; import 'package:registration_client/ui/widgets/language_component.dart'; import 'package:registration_client/utils/app_config.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -38,19 +36,24 @@ class _LanguageSelectorState extends State { late RegistrationTaskProvider registrationTaskProvider; _triggerNavigation() { - if (widget.newProcess.id == "NEW") { - Navigator.pushNamed(context, NewProcess.routeName, + if (widget.newProcess.flow == "NEW") { + Navigator.pushNamed(context, ProcessType.newProcess.routeName, arguments: {"process": widget.newProcess}); } - if (widget.newProcess.id == "UPDATE") { - Navigator.pushNamed(context, UpdateProcess.routeName, + if (widget.newProcess.flow == "UPDATE") { + Navigator.pushNamed(context, ProcessType.updateProcess.routeName, arguments: {"process": widget.newProcess}); } - if (widget.newProcess.id == "LOST") { - Navigator.pushNamed(context, LostProcess.routeName, - arguments: {"process": widget.newProcess}); + if (widget.newProcess.flow == "LOST") { + Navigator.pushNamed(context, ProcessType.lostProcess.routeName, + arguments: {"process": widget.newProcess}); + } + + if (widget.newProcess.flow == "CORRECTION") { + Navigator.pushNamed(context, ProcessType.correctionProcess.routeName, + arguments: {"process": widget.newProcess}); } } @@ -86,6 +89,7 @@ class _LanguageSelectorState extends State { ? "Update" : widget.newProcess.flow!, widget.newProcess.id!); + registrationTaskProvider.addDemographicField("preferredLang", globalProvider.fieldInputValue["preferredLang"].toString()); String registrationStartError = diff --git a/lib/ui/process_ui/widgets/lost_process_screen_content.dart b/lib/ui/process_ui/widgets/lost_process_screen_content.dart deleted file mode 100644 index ec86e5872..000000000 --- a/lib/ui/process_ui/widgets/lost_process_screen_content.dart +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) Modular Open Source Identity Platform - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * -*/ - -import 'dart:convert'; - -import 'package:flutter/material.dart'; - -import 'package:provider/provider.dart'; -import 'package:registration_client/model/field.dart'; -import 'package:registration_client/model/screen.dart'; -import 'package:registration_client/provider/global_provider.dart'; -import 'package:registration_client/provider/registration_task_provider.dart'; -import 'package:registration_client/ui/process_ui/widgets/age_date_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/biometric_capture_control.dart'; - -import 'package:registration_client/ui/process_ui/widgets/checkbox_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/date_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/document_upload_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/dropdown_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/dynamic_dropdown_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/gender_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/html_box_control.dart'; - -import 'package:registration_client/ui/process_ui/widgets/button_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/terms_and_conditions.dart'; -import 'package:registration_client/ui/process_ui/widgets/pre_reg_data_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/textbox_control.dart'; - -import 'radio_button_control.dart'; - -class LostProcessScreenContent extends StatefulWidget { - const LostProcessScreenContent( - {super.key, required this.context, required this.screen}); - final BuildContext context; - final Screen screen; - - @override - State createState() => - _LostProcessScreenContentState(); -} - -class _LostProcessScreenContentState extends State { - late GlobalProvider globalProvider; - late RegistrationTaskProvider registrationTaskProvider; - int refreshValue = 0; - - @override - void initState() { - globalProvider = Provider.of(context, listen: false); - registrationTaskProvider = - Provider.of(context, listen: false); - super.initState(); - } - - Widget widgetType(Field e) { - RegExp regexPattern = RegExp(r'^.*$'); - - if (e.validators != null && e.validators!.isNotEmpty) { - final validation = e.validators?.first?.validator; - if (validation != null) { - regexPattern = RegExp(validation); - } - } - - if (e.id == "preferredLang") { - return const SizedBox.shrink(); - } - - switch (e.controlType) { - case "checkbox": - if (e.subType == "gender") { - return RadioButtonControl(field: e); - } - if (e.group!.toLowerCase() == "consent") { - return TermsAndConditions(field: e); - } - return CheckboxControl(field: e); - case "html": - return HtmlBoxControl(field: e); - case "biometrics": - if (context.watch().mvelRequiredFields[e.id] ?? true) { - return BiometricCaptureControl(e: e); - } - return Container(); - case "button": - if (e.subType == "preferredLang") { - return ButtonControl(field: e); - } - if (e.subType == "gender" || e.subType == "residenceStatus") { - return RadioButtonControl(field: e); - } - //feature will implement - if (e.subType == "selectedHandles") { - return const SizedBox.shrink(); - } - return Text("${e.controlType}"); - case "textbox": - return TextBoxControl(e: e, validation: regexPattern); - case "dropdown": - if (e.id == "gender") { - return GenderControl(field: e, validation: regexPattern); - } - if (e.fieldType == "dynamic") { - return DynamicDropDownControl(field: e, validation: regexPattern); - } - return DropDownControl( - validation: regexPattern, - field: e, - ); - - case "ageDate": - return AgeDateControl( - field: e, - validation: regexPattern, - ); - case "date": - return DateControl( - validation: regexPattern, - field: e, - ); - case "fileupload": - return DocumentUploadControl( - field: e, - validation: regexPattern, - ); - default: - return (e.controlType!=null)? Text("${e.controlType}"): const SizedBox.shrink(); - } - } - - evaluateMVELVisible(String fieldData, Field e) async { - registrationTaskProvider.evaluateMVELVisible(fieldData).then((value) { - if (!value) { - globalProvider.removeFieldFromMap( - e.id!, globalProvider.fieldInputValue); - registrationTaskProvider.removeDemographicField(e.id!); - } - globalProvider.setMvelVisibleFields(e.id!, value); - }); - } - - evaluateMVELRequired(String fieldData, Field e) async { - registrationTaskProvider.evaluateMVELRequired(fieldData).then((value) { - globalProvider.setMvelRequiredFields(e.id!, value); - }); - } - - _checkMvelVisible(Field e) async { - if (e.required == false) { - if (e.requiredOn != null && e.requiredOn!.isNotEmpty) { - await evaluateMVELVisible(jsonEncode(e.toJson()), e); - await evaluateMVELRequired(jsonEncode(e.toJson()), e); - } - } - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - if (widget.screen.preRegFetchRequired == true) ...[ - PreRegDataControl( - screen: widget.screen, - onFetched: () { - setState(() { - refreshValue = 1; - }); - }), - ], - (context.watch().preRegControllerRefresh) - ? const CircularProgressIndicator() - : Form( - key: context.watch().formKey, - child: Column( - children: [ - ...widget.screen.fields!.map((e) { - _checkMvelVisible(e!); - if (context - .watch() - .mvelVisibleFields[e.id] ?? - true) { - return widgetType(e); - } - return Container(); - }).toList(), - ], - ), - ), - ], - ); - } -} diff --git a/lib/ui/process_ui/widgets/pre_reg_data_control.dart b/lib/ui/process_ui/widgets/pre_reg_data_control.dart index 1d23d6419..466e72c87 100644 --- a/lib/ui/process_ui/widgets/pre_reg_data_control.dart +++ b/lib/ui/process_ui/widgets/pre_reg_data_control.dart @@ -241,26 +241,22 @@ class _PreRegDataControlState extends State { const BorderSide(color: appGreyShade, width: 1), ), contentPadding: const EdgeInsets.symmetric(horizontal: 16,vertical: 14), - hintText: "Enter Application ID", + hintText: AppLocalizations.of(context)!.enter_application_id, hintStyle: const TextStyle(color: appBlackShade3, fontSize: 14), ), ), ), - Expanded( - flex: 3, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w), - child: OutlinedButton( - style: OutlinedButton.styleFrom( - fixedSize: const Size(100, 50), - elevation: 0, - backgroundColor: Colors.white, - side: BorderSide(width: 1.0, color: solidPrimary), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(2)), - ), - ), + SizedBox(width: 8.w), + OutlinedButton( + style: OutlinedButton.styleFrom( + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 14.h), + backgroundColor: Colors.white, + side: BorderSide(width: 1.0, color: solidPrimary), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(2), + ), + ), onPressed: () async { widget.onFetched(); if(preRegIdController.text.isEmpty){ @@ -276,6 +272,13 @@ class _PreRegDataControlState extends State { context: context, builder: (BuildContext context) => ValidatorAlert(errorMessage: AppLocalizations.of(context)!.application_id_not_exist,subError: AppLocalizations.of(context)!.correct_application_id), ); + await context.read< + RegistrationTaskProvider>() + .fetchPreRegistrationDetail( + preRegIdController.text); + + globalProvider.clearMap(); + globalProvider.clearScannedPages(); globalProvider.preRegControllerRefresh = false; } else { globalProvider.preRegControllerRefresh = true; @@ -300,20 +303,17 @@ class _PreRegDataControlState extends State { style: TextStyle(fontSize: isPortrait && !isMobileSize ? 22 : 14, color: solidPrimary,fontWeight: FontWeight.bold), ), ), - ), - ), - Expanded( - flex: 1, - child: OutlinedButton( - style: OutlinedButton.styleFrom( - fixedSize: const Size(50, 50), - elevation: 0, - backgroundColor: Colors.white, - side: BorderSide(width: 1.0, color: solidPrimary), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(2)), - ), - ), + + SizedBox(width: 8.w), + OutlinedButton( + style: OutlinedButton.styleFrom( + minimumSize: Size(50.w, 50.h), + backgroundColor: Colors.white, + side: BorderSide(width: 1.0, color: solidPrimary), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(2), + ), + ), onPressed: () async { widget.onFetched(); var data = await Navigator.push( @@ -353,8 +353,8 @@ class _PreRegDataControlState extends State { }, child: Icon(Icons.crop_free,size: 32.6,color: solidPrimary), ), - ), - const Spacer(), + + //const Spacer(), ], ) ], diff --git a/lib/ui/process_ui/widgets/update_process_screen_content.dart b/lib/ui/process_ui/widgets/update_process_screen_content.dart deleted file mode 100644 index 6305b8b61..000000000 --- a/lib/ui/process_ui/widgets/update_process_screen_content.dart +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) Modular Open Source Identity Platform - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * -*/ - -import 'dart:convert'; - -import 'package:flutter/material.dart'; - -import 'package:provider/provider.dart'; -import 'package:registration_client/model/field.dart'; -import 'package:registration_client/model/process.dart'; -import 'package:registration_client/model/screen.dart'; -import 'package:registration_client/provider/global_provider.dart'; -import 'package:registration_client/provider/registration_task_provider.dart'; -import 'package:registration_client/ui/process_ui/widgets/age_date_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/biometric_capture_control.dart'; - -import 'package:registration_client/ui/process_ui/widgets/checkbox_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/date_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/document_upload_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/dropdown_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/dynamic_dropdown_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/gender_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/html_box_control.dart'; - -import 'package:registration_client/ui/process_ui/widgets/button_control.dart'; -import 'package:registration_client/ui/process_ui/widgets/terms_and_conditions.dart'; -import 'package:registration_client/ui/process_ui/widgets/textbox_control.dart'; - -import 'radio_button_control.dart'; - -class UpdateProcessScreenContent extends StatefulWidget { - const UpdateProcessScreenContent({ - super.key, - required this.context, - required this.screen, - required this.process, - }); - final BuildContext context; - final Screen screen; - final Process process; - - @override - State createState() => - _UpdateProcessScreenContentState(); -} - -class _UpdateProcessScreenContentState - extends State { - late GlobalProvider globalProvider; - late RegistrationTaskProvider registrationTaskProvider; - - @override - void initState() { - globalProvider = Provider.of(context, listen: false); - registrationTaskProvider = - Provider.of(context, listen: false); - - super.initState(); - } - - Widget widgetType(Field e) { - RegExp regexPattern = RegExp(r'^.*$'); - - if (e.validators != null && e.validators!.isNotEmpty) { - final validation = e.validators?.first?.validator; - if (validation != null) { - regexPattern = RegExp(validation); - } - } - - if (e.id == "preferredLang") { - return const SizedBox.shrink(); - } - - switch (e.controlType) { - case "checkbox": - if (e.subType == "gender") { - return RadioButtonControl(field: e); - } - if (e.group!.toLowerCase() == "consent") { - return TermsAndConditions(field: e); - } - return CheckboxControl(field: e); - case "html": - return HtmlBoxControl(field: e); - case "biometrics": - if (globalProvider.mvelRequiredFields[e.id] ?? false) { - return BiometricCaptureControl(e: e); - } - return Container(); - case "button": - if (e.subType == "preferredLang") { - return ButtonControl(field: e); - } - if (e.subType == "gender" || e.subType == "residenceStatus") { - return RadioButtonControl(field: e); - } - return Text("${e.controlType}"); - case "textbox": - return TextBoxControl(e: e, validation: regexPattern); - case "dropdown": - if (e.id == "gender") { - return GenderControl(field: e, validation: regexPattern); - } - if (e.fieldType == "dynamic") { - return DynamicDropDownControl(field: e, validation: regexPattern); - } - return DropDownControl( - validation: regexPattern, - field: e, - ); - - case "ageDate": - return AgeDateControl( - field: e, - validation: regexPattern, - ); - case "date": - return DateControl( - validation: regexPattern, - field: e, - ); - case "fileupload": - return DocumentUploadControl( - field: e, - validation: regexPattern, - ); - default: - return Text("${e.controlType}"); - } - } - - evaluateMVELVisible(String fieldData, Field e) async { - registrationTaskProvider.evaluateMVELVisible(fieldData).then((value) { - if (!value) { - globalProvider.removeFieldFromMap( - e.id!, globalProvider.fieldInputValue); - registrationTaskProvider.removeDemographicField(e.id!); - } - globalProvider.setMvelVisibleFields(e.id!, value); - }); - } - - evaluateMVELRequired(String fieldData, Field e) async { - registrationTaskProvider.evaluateMVELRequired(fieldData).then((value) { - if (!value && globalProvider.selectedUpdateFields[e.group] == null) { - globalProvider.removeFieldFromMap( - e.id!, globalProvider.fieldInputValue); - registrationTaskProvider.removeDemographicField(e.id!); - } - globalProvider.setMvelRequiredFields(e.id!, value); - }); - } - - checkMvelVisible(Field e) async { - if (e.requiredOn != null && e.requiredOn!.isNotEmpty) { - await evaluateMVELVisible(jsonEncode(e.toJson()), e); - await evaluateMVELRequired(jsonEncode(e.toJson()), e); - } - } - - @override - Widget build(BuildContext context) { - return Form( - key: context.watch().formKey, - child: Column( - children: [ - ...widget.screen.fields!.map((e) { - checkMvelVisible(e!); - if (widget.process.autoSelectedGroups!.contains(e.group)) { - return widgetType(e); - } else if (globalProvider.selectedUpdateFields[e.group] != null) { - return widgetType(e); - } - // else if (context - // .watch() - // .mvelRequiredFields[e.id] ?? - // false) { - // return widgetType(e); - // } - return Container(); - }).toList(), - ], - ), - ); - } -} diff --git a/lib/ui/process_ui/widgets_mobile/biometric_capture_control_portrait.dart b/lib/ui/process_ui/widgets_mobile/biometric_capture_control_portrait.dart index 262c37738..81f8c3269 100644 --- a/lib/ui/process_ui/widgets_mobile/biometric_capture_control_portrait.dart +++ b/lib/ui/process_ui/widgets_mobile/biometric_capture_control_portrait.dart @@ -110,6 +110,10 @@ class _BiometricCaptureControlPortraitState fontWeight: semiBold, color: blackShade1, ), + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + softWrap: true, ) ], ), @@ -119,18 +123,23 @@ class _BiometricCaptureControlPortraitState top: 15, right: 15, child: (biometricAttributeData.exceptions.contains(true)) + ? Image.asset( + "assets/images/Exception_Mark.png", + ) + : (biometricAttributeData.qualityPercentage < + ((int.tryParse(biometricAttributeData.thresholdPercentage) ?? 0).toDouble())) ? Image.asset( - "assets/images/Group 57548@2x.png", + "assets/images/Exception_Mark.png", ) : Image.asset( - "assets/images/Group 57745@2x.png", + "assets/images/Green_Check.png", )), if (!biometricAttributeData.exceptions.contains(false)) Positioned( top: 15, right: 15, child: Image.asset( - "assets/images/Group 57548@2x.png", + "assets/images/Exception_Mark.png", )), if (biometricAttributeData.isScanned == true) Positioned( @@ -144,7 +153,7 @@ class _BiometricCaptureControlPortraitState .toInt() < int.parse(biometricAttributeData .thresholdPercentage)) - ? secondaryColors.elementAt(26) + ? secondaryColors.elementAt(16) : secondaryColors.elementAt(11), borderRadius: BorderRadius.circular(50)), height: 40, @@ -179,7 +188,8 @@ class _BiometricCaptureControlPortraitState child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - (widget.field.inputRequired!) + Expanded( + child:(widget.field.inputRequired!) ? RichText( text: TextSpan( text: context @@ -225,6 +235,7 @@ class _BiometricCaptureControlPortraitState // SizedBox( // height: (isMobileSize)?20.h:52.h, // ), + ), ], ), ), diff --git a/lib/ui/process_ui/widgets_mobile/biometric_capture_scan_block_portrait.dart b/lib/ui/process_ui/widgets_mobile/biometric_capture_scan_block_portrait.dart index 827a17500..ebda09c44 100644 --- a/lib/ui/process_ui/widgets_mobile/biometric_capture_scan_block_portrait.dart +++ b/lib/ui/process_ui/widgets_mobile/biometric_capture_scan_block_portrait.dart @@ -96,22 +96,29 @@ class _BiometricCaptureScanBlockPortraitState height: (isMobileSize) ? 410.h : 610.h, width: 760.w, child: Column( + mainAxisSize: MainAxisSize.min, children: [ const SizedBox( height: 26, ), Row( children: [ - const Spacer(), + // const Spacer(), const SizedBox( width: 28, ), - Text( - "${biometricAttributeData.title.replaceAll(" ", "")} ${AppLocalizations.of(context)!.capture}", - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - fontSize: 28, fontWeight: bold, color: blackShade1), + Expanded( + child:Text( + "${biometricAttributeData.title.replaceAll(" ", "")} ${AppLocalizations.of(context)!.capture}", + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + fontSize: 28, fontWeight: bold, color: blackShade1), + maxLines: 2, + overflow: TextOverflow.visible, + softWrap: true, + + ), ), - const Spacer(), + // const Spacer(), IconButton( onPressed: () { Navigator.pop(context); @@ -129,60 +136,72 @@ class _BiometricCaptureScanBlockPortraitState thickness: 1, color: secondaryColors.elementAt(22), ), - Row( - mainAxisAlignment: (biometricAttributeData.title == "Iris") - ? MainAxisAlignment.spaceEvenly - : MainAxisAlignment.center, - children: [ - (biometricAttributeData.title == "Iris" && - biometricAttributeData.exceptions.contains(true)) - ? ((biometricAttributeData.exceptions.first == true) - ? SvgPicture.asset( - "assets/svg/Left Eye Exception.svg", - height: (isMobileSize) ? 130.h : 260.h, - ) - : const SizedBox()) - : const SizedBox(), - ...temp.map( - (e) => Image.memory( - e!, - height: (isMobileSize) ? 130.h : 260.h, + Expanded( + child: Row( + mainAxisAlignment: (biometricAttributeData.title == "Iris") + ? MainAxisAlignment.spaceEvenly + : MainAxisAlignment.center, + children: [ + // Left eye exception (if any) + if (biometricAttributeData.title == "Iris" && + biometricAttributeData.exceptions.contains(true) && + biometricAttributeData.exceptions.first == true) + SvgPicture.asset( + "assets/svg/Left Eye Exception.svg", + height: (isMobileSize) ? 130.h : 260.h, + ), + ...temp.map( + (e) => Flexible( + child: Container( + constraints: BoxConstraints( + maxHeight: (isMobileSize) ? 150.h : 280.h, + maxWidth: (isMobileSize) ? 150.w : 280.w, + ), + child: Image.memory( + e!, + fit: BoxFit.contain, + ), + ), + ), ), - ), - (biometricAttributeData.title == "Iris" && - biometricAttributeData.exceptions.contains(true)) - ? ((biometricAttributeData.exceptions.first == true) - ? const SizedBox() - : Transform.flip( - flipX: true, - child: SvgPicture.asset( - "assets/svg/Left Eye Exception.svg", - height: (isMobileSize) ? 130.h : 260.h, - ), - )) - : const SizedBox(), - ], + + if (biometricAttributeData.title == "Iris" && + biometricAttributeData.exceptions.contains(true) && + biometricAttributeData.exceptions.first != true) + Transform.flip( + flipX: true, + child: SvgPicture.asset( + "assets/svg/Left Eye Exception.svg", + height: (isMobileSize) ? 130.h : 260.h, + ), + ), + ], + ), ), Divider( height: 82, thickness: 1, color: secondaryColors.elementAt(22), ), - Container( - height: 96, - decoration: BoxDecoration( - color: secondaryColors.elementAt(23), - borderRadius: BorderRadius.circular(6)), - child: Center( - child: Text( - "${biometricAttributeData.noOfCapturesAllowed - currentAttemptNo} ${AppLocalizations.of(context)!.attempts_left}", - style: TextStyle( - fontSize: 25, - fontWeight: semiBold, - color: secondaryColors.elementAt(24)), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + //const Spacer(), + const SizedBox(width: 28), + Expanded( + child: Text( + "${biometricAttributeData.noOfCapturesAllowed - currentAttemptNo} ${AppLocalizations.of(context)!.attempts_left}", + style: TextStyle( + fontSize: 25, + fontWeight: semiBold, + color: secondaryColors.elementAt(24)), + overflow: TextOverflow.visible, + maxLines:2, + ), ), - ), - ) + ], + ), + ], ), ), @@ -198,28 +217,27 @@ class _BiometricCaptureScanBlockPortraitState _showCustomAlert(currentAttemptNo, temp); } - bool validateCaptureException(){ - return ( - (context.read().iris.isScanned) && - (context.read().iris.qualityPercentage <= - int.parse(context.read().iris.thresholdPercentage)) || + bool validateCaptureException() { + final biometricProvider = context.read(); + return (biometricProvider.iris.isScanned && + biometricProvider.iris.qualityPercentage <= int.parse(biometricProvider.iris.thresholdPercentage) && + biometricProvider.iris.attemptNo < biometricProvider.iris.noOfCapturesAllowed) || - (context.read().rightHand.isScanned) && - (context.read().rightHand.qualityPercentage <= - int.parse(context.read().rightHand.thresholdPercentage)) || + (biometricProvider.rightHand.isScanned && + biometricProvider.rightHand.qualityPercentage <= int.parse(biometricProvider.rightHand.thresholdPercentage) && + biometricProvider.rightHand.attemptNo < biometricProvider.rightHand.noOfCapturesAllowed) || - (context.read().leftHand.isScanned) && - (context.read().leftHand.qualityPercentage <= - int.parse(context.read().leftHand.thresholdPercentage)) || + (biometricProvider.leftHand.isScanned && + biometricProvider.leftHand.qualityPercentage <= int.parse(biometricProvider.leftHand.thresholdPercentage) && + biometricProvider.leftHand.attemptNo < biometricProvider.leftHand.noOfCapturesAllowed) || - (context.read().thumbs.isScanned) && - (context.read().thumbs.qualityPercentage <= - int.parse(context.read().thumbs.thresholdPercentage)) || + (biometricProvider.thumbs.isScanned && + biometricProvider.thumbs.qualityPercentage <= int.parse(biometricProvider.thumbs.thresholdPercentage) && + biometricProvider.thumbs.attemptNo < biometricProvider.thumbs.noOfCapturesAllowed) || - (context.read().face.isScanned) && - (context.read().face.qualityPercentage <= - int.parse(context.read().face.thresholdPercentage)) - ); + (biometricProvider.face.isScanned && + biometricProvider.face.qualityPercentage <= int.parse(biometricProvider.face.thresholdPercentage) && + biometricProvider.face.attemptNo < biometricProvider.face.noOfCapturesAllowed); } noOfTrue(List list) { @@ -237,16 +255,16 @@ class _BiometricCaptureScanBlockPortraitState if (context.read().fieldInputValue.containsKey(key)) { if (context.read().getElementPosition( - context.read().fieldInputValue[key], - data.title) == + context.read().fieldInputValue[key], + data.title) == -1) { context.read().fieldInputValue[key].add(data); } else { context.read().fieldInputValue[key].removeAt(context .read() .getElementPosition( - context.read().fieldInputValue[key], - data.title)); + context.read().fieldInputValue[key], + data.title)); context.read().fieldInputValue[key].add(data); } } else { @@ -255,6 +273,21 @@ class _BiometricCaptureScanBlockPortraitState } } + /// Returns true if the user can proceed to the next step for this biometric. + bool canProceedToNext() { + final int threshold = int.tryParse(biometricAttributeData.thresholdPercentage) ?? 0; + final int quality = biometricAttributeData.qualityPercentage.toInt(); + final int attempts = biometricAttributeData.attemptNo; + final int maxAttempts = biometricAttributeData.noOfCapturesAllowed; + + // If quality is above threshold, allow next + if (quality >= threshold) return true; + // If all attempts are completed, allow next (even if below threshold) + if (attempts >= maxAttempts) return true; + // Otherwise, do not allow + return false; + } + Widget _scanBlock() { return Column( children: [ @@ -267,70 +300,70 @@ class _BiometricCaptureScanBlockPortraitState width: (isPortrait) ? double.infinity : 760.w, child: (biometricAttributeData.isScanned == false) ? Directionality( - textDirection: TextDirection.ltr, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ...biometricAttributeData.listofImages - .map((e) => SvgPicture.asset( - "$e", - height: (isMobileSize) - ? biometricAttributeData.imageHeightMobile.h - : biometricAttributeData - .imageHeightTablet.h, - fit: BoxFit.fitHeight, - )) - ], - ), - ) + textDirection: TextDirection.ltr, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ...biometricAttributeData.listofImages + .map((e) => SvgPicture.asset( + "$e", + height: (isMobileSize) + ? biometricAttributeData.imageHeightMobile.h + : biometricAttributeData + .imageHeightTablet.h, + fit: BoxFit.fitHeight, + )) + ], + ), + ) : Directionality( - textDirection: TextDirection.ltr, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - (biometricAttributeData.title == "Iris" && - biometricAttributeData.exceptions.contains(true)) - ? ((biometricAttributeData.exceptions.first == true) - ? Row( - children: [ - SizedBox( - width: 115.h, - ), - SvgPicture.asset( - "assets/svg/Left Eye Exception.svg", - height: (isMobileSize) ? 70.h : 130.h, - ), - ], - ) - : const SizedBox()) - : const SizedBox(), - ...biometricAttributeData.listofImages - .map((e) => Image.memory( - e, - height: (isMobileSize) ? 70.h : 130.h, - )), - (biometricAttributeData.title == "Iris" && - biometricAttributeData.exceptions.contains(true)) - ? ((biometricAttributeData.exceptions.first == true) - ? const SizedBox() - : Row( - children: [ - Transform.flip( - flipX: true, - child: SvgPicture.asset( - "assets/svg/Left Eye Exception.svg", - height: (isMobileSize) ? 70.h : 130.h, - ), - ), - SizedBox( - width: 115.h, - ) - ], - )) - : const SizedBox(), - ], - ), - ), + textDirection: TextDirection.ltr, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + (biometricAttributeData.title == "Iris" && + biometricAttributeData.exceptions.contains(true)) + ? ((biometricAttributeData.exceptions.first == true) + ? Row( + children: [ + SizedBox( + width: 115.h, + ), + SvgPicture.asset( + "assets/svg/Left Eye Exception.svg", + height: (isMobileSize) ? 70.h : 130.h, + ), + ], + ) + : const SizedBox()) + : const SizedBox(), + ...biometricAttributeData.listofImages + .map((e) => Image.memory( + e, + height: (isMobileSize) ? 70.h : 130.h, + )), + (biometricAttributeData.title == "Iris" && + biometricAttributeData.exceptions.contains(true)) + ? ((biometricAttributeData.exceptions.first == true) + ? const SizedBox() + : Row( + children: [ + Transform.flip( + flipX: true, + child: SvgPicture.asset( + "assets/svg/Left Eye Exception.svg", + height: (isMobileSize) ? 70.h : 130.h, + ), + ), + SizedBox( + width: 115.h, + ) + ], + )) + : const SizedBox(), + ], + ), + ), ), const SizedBox( height: 30, @@ -357,10 +390,10 @@ class _BiometricCaptureScanBlockPortraitState Text( "${AppLocalizations.of(context)!.threshold} ${biometricAttributeData.thresholdPercentage}%", style: Theme.of(context).textTheme.bodyLarge?.copyWith( - fontSize: 23, - fontWeight: regular, - color: secondaryColors.elementAt(1), - ), + fontSize: 23, + fontWeight: regular, + color: secondaryColors.elementAt(1), + ), ), SizedBox( height: 21.h, @@ -374,11 +407,11 @@ class _BiometricCaptureScanBlockPortraitState percent: biometricAttributeData.qualityPercentage / 100, backgroundColor: Colors.grey, progressColor: - (biometricAttributeData.qualityPercentage.toInt() < - int.parse( - biometricAttributeData.thresholdPercentage)) - ? secondaryColors.elementAt(26) - : secondaryColors.elementAt(11), + (biometricAttributeData.qualityPercentage.toInt() < + int.parse( + biometricAttributeData.thresholdPercentage)) + ? secondaryColors.elementAt(26) + : secondaryColors.elementAt(11), ), SizedBox( width: (isMobileSize) ? 20.w : 43.w, @@ -386,10 +419,10 @@ class _BiometricCaptureScanBlockPortraitState Text( "${biometricAttributeData.qualityPercentage.toInt()}%", style: Theme.of(context).textTheme.bodyLarge?.copyWith( - fontSize: 23, - fontWeight: semiBold, - color: secondaryColors.elementAt(1), - ), + fontSize: 23, + fontWeight: semiBold, + color: secondaryColors.elementAt(1), + ), ), ], ), @@ -429,8 +462,8 @@ class _BiometricCaptureScanBlockPortraitState mainAxisAlignment: MainAxisAlignment.center, children: [ for (int i = 1; - i <= biometricAttributeData.noOfCapturesAllowed; - i++) + i <= biometricAttributeData.noOfCapturesAllowed; + i++) Padding( padding: const EdgeInsets.only(right: 16.3), child: InkWell( @@ -438,17 +471,17 @@ class _BiometricCaptureScanBlockPortraitState if (biometricAttributeData.attemptNo >= i) { await BiometricsApi() .getBiometrics( - widget.field.id!, - biometricAttributeData.title - .replaceAll(" ", ""), - i) + widget.field.id!, + biometricAttributeData.title + .replaceAll(" ", ""), + i) .then((value) { biometricAttributeData.listOfBiometricsDto .clear(); for (var e in value) { biometricAttributeData.listOfBiometricsDto .add(BiometricsDto.fromJson( - json.decode(e!))); + json.decode(e!))); } }); @@ -456,16 +489,16 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.qualityPercentage = context .read< - BiometricCaptureControlProvider>() + BiometricCaptureControlProvider>() .avgScore(biometricAttributeData - .listOfBiometricsDto); + .listOfBiometricsDto); }); await BiometricsApi() .extractImageValuesByAttempt( - widget.field.id!, - biometricAttributeData.title - .replaceAll(" ", ""), - i) + widget.field.id!, + biometricAttributeData.title + .replaceAll(" ", ""), + i) .then((value) { biometricAttributeData.listofImages = value; }); @@ -484,7 +517,9 @@ class _BiometricCaptureScanBlockPortraitState ), color: (biometricAttributeData.attemptNo < i) ? secondaryColors.elementAt(18) - : secondaryColors.elementAt(11), + : (biometricAttributeData.qualityPercentage < ((int.tryParse(biometricAttributeData.thresholdPercentage) ?? 0).toDouble()) + ? secondaryColors.elementAt(26) + : secondaryColors.elementAt(11)), ), child: Text( i.toString(), @@ -492,13 +527,13 @@ class _BiometricCaptureScanBlockPortraitState .textTheme .bodyLarge ?.copyWith( - fontSize: 21, - color: - (biometricAttributeData.attemptNo < - i) - ? secondaryColors.elementAt(19) - : pureWhite, - fontWeight: semiBold), + fontSize: 21, + color: + (biometricAttributeData.attemptNo < + i) + ? secondaryColors.elementAt(19) + : pureWhite, + fontWeight: semiBold), ), ), ), @@ -526,11 +561,11 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.title.replaceAll(" ", "")); await BiometricsApi() .getBestBiometrics(widget.field.id!, - biometricAttributeData.title.replaceAll(" ", "")) + biometricAttributeData.title.replaceAll(" ", "")) .then((value) {}); await BiometricsApi() .extractImageValues(widget.field.id!, - biometricAttributeData.title.replaceAll(" ", "")) + biometricAttributeData.title.replaceAll(" ", "")) .then((value) { tempImageList = value; }); @@ -540,10 +575,10 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.title.replaceAll(" ", "")); biometricAttributeData.attemptNo = await BiometricsApi() .getBioAttempt(widget.field.id!, - biometricAttributeData.title.replaceAll(" ", "")); + biometricAttributeData.title.replaceAll(" ", "")); await BiometricsApi() .getBestBiometrics(widget.field.id!, - biometricAttributeData.title.replaceAll(" ", "")) + biometricAttributeData.title.replaceAll(" ", "")) .then((value) async { for (var e in value) { biometricAttributeData.listOfBiometricsDto.add( @@ -558,7 +593,7 @@ class _BiometricCaptureScanBlockPortraitState .avgScore(biometricAttributeData.listOfBiometricsDto); await BiometricsApi() .extractImageValues(widget.field.id!, - biometricAttributeData.title.replaceAll(" ", "")) + biometricAttributeData.title.replaceAll(" ", "")) .then((value) { biometricAttributeData.listofImages = value; }); @@ -587,9 +622,9 @@ class _BiometricCaptureScanBlockPortraitState : const EdgeInsets.symmetric(horizontal: 46, vertical: 34), backgroundColor: (biometricAttributeData.exceptions.contains(false) ? biometricAttributeData.attemptNo < - biometricAttributeData.noOfCapturesAllowed - ? solidPrimary - : appGreyShade + biometricAttributeData.noOfCapturesAllowed + ? solidPrimary + : appGreyShade : secondaryColors.elementAt(22)), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), @@ -606,14 +641,14 @@ class _BiometricCaptureScanBlockPortraitState resetAfterException(String key, BiometricAttributeData data) { if (context.read().fieldInputValue.containsKey(key)) { if (context.read().getElementPosition( - context.read().fieldInputValue[key], - data.title) != + context.read().fieldInputValue[key], + data.title) != -1) { context.read().fieldInputValue[key].removeAt(context .read() .getElementPosition( - context.read().fieldInputValue[key], - data.title)); + context.read().fieldInputValue[key], + data.title)); } } } @@ -814,7 +849,7 @@ class _BiometricCaptureScanBlockPortraitState decoration: BoxDecoration( color: pureWhite, border: - Border.all(color: secondaryColors.elementAt(14), width: 1), + Border.all(color: secondaryColors.elementAt(14), width: 1), ), height: 353, width: (isPortrait) ? double.infinity : 760.w, @@ -825,215 +860,215 @@ class _BiometricCaptureScanBlockPortraitState height: 40, ), (biometricAttributeData.title != "Face" && - biometricAttributeData.title != "Exception") + biometricAttributeData.title != "Exception") ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Center( - child: Container( - decoration: BoxDecoration( - color: pureWhite, - border: Border.all( - color: secondaryColors.elementAt(14), width: 1), - ), - height: 134, - width: (isPortrait) ? double.infinity : 714.w, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - AppLocalizations.of(context)!.exceptions, - style: Theme.of(context) - .textTheme - .bodyLarge - ?.copyWith( - fontSize: 24, - color: blackShade1, - fontWeight: semiBold), - ), - const SizedBox( - height: 20, - ), - Padding( - padding: const EdgeInsets.only(right: 16.3), - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 7.3, - horizontal: 30, - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: secondaryColors.elementAt(17), - ), - color: secondaryColors.elementAt(18), - ), - child: Text( - noOfTrue(biometricAttributeData.exceptions) - .toString(), - style: Theme.of(context) - .textTheme - .bodyLarge - ?.copyWith( - fontSize: 21, - color: secondaryColors.elementAt(19), - fontWeight: semiBold), - ), - ), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + decoration: BoxDecoration( + color: pureWhite, + border: Border.all( + color: secondaryColors.elementAt(14), width: 1), + ), + height: 134, + width: (isPortrait) ? double.infinity : 714.w, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + AppLocalizations.of(context)!.exceptions, + style: Theme.of(context) + .textTheme + .bodyLarge + ?.copyWith( + fontSize: 24, + color: blackShade1, + fontWeight: semiBold), + ), + const SizedBox( + height: 20, + ), + Padding( + padding: const EdgeInsets.only(right: 16.3), + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 7.3, + horizontal: 30, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: secondaryColors.elementAt(17), ), - ], + color: secondaryColors.elementAt(18), + ), + child: Text( + noOfTrue(biometricAttributeData.exceptions) + .toString(), + style: Theme.of(context) + .textTheme + .bodyLarge + ?.copyWith( + fontSize: 21, + color: secondaryColors.elementAt(19), + fontWeight: semiBold), + ), ), ), - ), - const SizedBox( - height: 40, - ), - // Text( - // AppLocalizations.of(context)!.exception_type, - // style: TextStyle( - // fontSize: 25, fontWeight: semiBold, color: blackShade1), - // ), - // const SizedBox( - // height: 18, - // ), - // Row( - // children: [ - // Expanded( - // child: OutlinedButton( - // onPressed: () { - // if (biometricAttributeData.exceptions - // .contains(true)) { - // biometricAttributeData.exceptionType = - // "Permanent"; - // } - // }, - // style: ButtonStyle( - // backgroundColor: MaterialStateProperty.all( - // (biometricAttributeData.exceptionType == - // "Permanent") - // ? secondaryColors.elementAt(12) - // : pureWhite), - // shape: MaterialStateProperty.all( - // RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(36), - // ), - // ), - // side: MaterialStateProperty.all( - // BorderSide( - // color: secondaryColors.elementAt(12))), - // padding: - // MaterialStateProperty.all( - // EdgeInsets.symmetric( - // horizontal: (isMobileSize) ? 10.w : 109.w, - // vertical: (isMobileSize) ? 10.h : 20.h), - // ), - // ), - // child: Text( - // AppLocalizations.of(context)!.permanent, - // style: TextStyle( - // fontSize: (isMobileSize) ? 16.h : 24.h, - // fontWeight: FontWeight.w400, - // color: (biometricAttributeData.exceptionType == - // "Permanent") - // ? pureWhite - // : blackShade1), - // ), - // ), - // ), - // const SizedBox( - // width: 30, - // ), - // Expanded( - // child: OutlinedButton( - // onPressed: () { - // if (biometricAttributeData.exceptions - // .contains(true)) { - // biometricAttributeData.exceptionType = - // "Temporary"; - // } - // }, - // style: ButtonStyle( - // backgroundColor: MaterialStateProperty.all( - // (biometricAttributeData.exceptionType == - // "Temporary") - // ? secondaryColors.elementAt(12) - // : pureWhite), - // shape: MaterialStateProperty.all( - // RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(36), - // ), - // ), - // side: MaterialStateProperty.all( - // BorderSide( - // color: secondaryColors.elementAt(12))), - // padding: - // MaterialStateProperty.all( - // EdgeInsets.symmetric( - // horizontal: (isMobileSize) ? 10.w : 109.w, - // vertical: (isMobileSize) ? 10.h : 20.h), - // ), - // ), - // child: Text( - // AppLocalizations.of(context)!.temporary, - // style: TextStyle( - // fontSize: (isMobileSize) ? 16.h : 24.h, - // fontWeight: FontWeight.w400, - // color: (biometricAttributeData.exceptionType == - // "Temporary") - // ? pureWhite - // : blackShade1), - // ), - // ), - // ), - // ], - // ), - // const SizedBox( - // height: 40, - // ), - // Text( - // AppLocalizations.of(context)!.comments, - // style: TextStyle( - // fontSize: 25, fontWeight: semiBold, color: blackShade1), - // ), - // const SizedBox( - // height: 20, - // ), - // TextField( - // enabled: false, - // maxLines: 10, - // decoration: InputDecoration( - // fillColor: pureWhite, - // hintText: AppLocalizations.of(context)! - // .add_comments_for_marking_the_exception, - // hintStyle: TextStyle( - // fontSize: 28, - // fontWeight: regular, - // color: secondaryColors.elementAt(1)), - // border: OutlineInputBorder( - // borderRadius: BorderRadius.circular(6), - // borderSide: BorderSide( - // color: secondaryColors.elementAt(12), - // ), - // ), - // ), - // ), - ], - ) - : Container( - height: 96, - decoration: BoxDecoration( - color: secondaryColors.elementAt(23), - borderRadius: BorderRadius.circular(6)), - child: Center( - child: Text( - "${AppLocalizations.of(context)!.marking_exceptions_on} ${biometricAttributeData.viewTitle} ${AppLocalizations.of(context)!.is_not_allowed}", - style: TextStyle( - fontSize: 25, - fontWeight: semiBold, - color: secondaryColors.elementAt(24)), - ), + ], ), ), + ), + const SizedBox( + height: 40, + ), + // Text( + // AppLocalizations.of(context)!.exception_type, + // style: TextStyle( + // fontSize: 25, fontWeight: semiBold, color: blackShade1), + // ), + // const SizedBox( + // height: 18, + // ), + // Row( + // children: [ + // Expanded( + // child: OutlinedButton( + // onPressed: () { + // if (biometricAttributeData.exceptions + // .contains(true)) { + // biometricAttributeData.exceptionType = + // "Permanent"; + // } + // }, + // style: ButtonStyle( + // backgroundColor: MaterialStateProperty.all( + // (biometricAttributeData.exceptionType == + // "Permanent") + // ? secondaryColors.elementAt(12) + // : pureWhite), + // shape: MaterialStateProperty.all( + // RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(36), + // ), + // ), + // side: MaterialStateProperty.all( + // BorderSide( + // color: secondaryColors.elementAt(12))), + // padding: + // MaterialStateProperty.all( + // EdgeInsets.symmetric( + // horizontal: (isMobileSize) ? 10.w : 109.w, + // vertical: (isMobileSize) ? 10.h : 20.h), + // ), + // ), + // child: Text( + // AppLocalizations.of(context)!.permanent, + // style: TextStyle( + // fontSize: (isMobileSize) ? 16.h : 24.h, + // fontWeight: FontWeight.w400, + // color: (biometricAttributeData.exceptionType == + // "Permanent") + // ? pureWhite + // : blackShade1), + // ), + // ), + // ), + // const SizedBox( + // width: 30, + // ), + // Expanded( + // child: OutlinedButton( + // onPressed: () { + // if (biometricAttributeData.exceptions + // .contains(true)) { + // biometricAttributeData.exceptionType = + // "Temporary"; + // } + // }, + // style: ButtonStyle( + // backgroundColor: MaterialStateProperty.all( + // (biometricAttributeData.exceptionType == + // "Temporary") + // ? secondaryColors.elementAt(12) + // : pureWhite), + // shape: MaterialStateProperty.all( + // RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(36), + // ), + // ), + // side: MaterialStateProperty.all( + // BorderSide( + // color: secondaryColors.elementAt(12))), + // padding: + // MaterialStateProperty.all( + // EdgeInsets.symmetric( + // horizontal: (isMobileSize) ? 10.w : 109.w, + // vertical: (isMobileSize) ? 10.h : 20.h), + // ), + // ), + // child: Text( + // AppLocalizations.of(context)!.temporary, + // style: TextStyle( + // fontSize: (isMobileSize) ? 16.h : 24.h, + // fontWeight: FontWeight.w400, + // color: (biometricAttributeData.exceptionType == + // "Temporary") + // ? pureWhite + // : blackShade1), + // ), + // ), + // ), + // ], + // ), + // const SizedBox( + // height: 40, + // ), + // Text( + // AppLocalizations.of(context)!.comments, + // style: TextStyle( + // fontSize: 25, fontWeight: semiBold, color: blackShade1), + // ), + // const SizedBox( + // height: 20, + // ), + // TextField( + // enabled: false, + // maxLines: 10, + // decoration: InputDecoration( + // fillColor: pureWhite, + // hintText: AppLocalizations.of(context)! + // .add_comments_for_marking_the_exception, + // hintStyle: TextStyle( + // fontSize: 28, + // fontWeight: regular, + // color: secondaryColors.elementAt(1)), + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(6), + // borderSide: BorderSide( + // color: secondaryColors.elementAt(12), + // ), + // ), + // ), + // ), + ], + ) + : Container( + height: 96, + decoration: BoxDecoration( + color: secondaryColors.elementAt(23), + borderRadius: BorderRadius.circular(6)), + child: Center( + child: Text( + "${AppLocalizations.of(context)!.marking_exceptions_on} ${biometricAttributeData.viewTitle} ${AppLocalizations.of(context)!.is_not_allowed}", + style: TextStyle( + fontSize: 25, + fontWeight: semiBold, + color: secondaryColors.elementAt(24)), + ), + ), + ), SizedBox( height: 20.h, ), @@ -1048,7 +1083,7 @@ class _BiometricCaptureScanBlockPortraitState child: Directionality( textDirection: TextDirection.ltr, child: - Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Stack( children: [ SizedBox( @@ -1098,13 +1133,13 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[0] = - !(biometricAttributeData.exceptions[0]); + !(biometricAttributeData.exceptions[0]); if (biometricAttributeData.exceptions .contains(true)) { if (biometricAttributeData.exceptionType.isEmpty) { biometricAttributeData.exceptionType = - "Permanent"; + "Permanent"; } } if (!biometricAttributeData.exceptions @@ -1175,13 +1210,13 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[1] = - !(biometricAttributeData.exceptions[1]); + !(biometricAttributeData.exceptions[1]); if (biometricAttributeData.exceptions .contains(true)) { if (biometricAttributeData.exceptionType.isEmpty) { biometricAttributeData.exceptionType = - "Permanent"; + "Permanent"; } } if (!biometricAttributeData.exceptions @@ -1231,7 +1266,7 @@ class _BiometricCaptureScanBlockPortraitState child: InkWell( onTap: () async { if (!(biometricAttributeData.exceptions - .elementAt(0)) == + .elementAt(0)) == true) { await BiometricsApi().addBioException( widget.field.id!, "RightHand", "rightIndex"); @@ -1260,14 +1295,14 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[0] = - !(biometricAttributeData.exceptions[0]); + !(biometricAttributeData.exceptions[0]); if (biometricAttributeData.exceptions .contains(true)) { if (biometricAttributeData .exceptionType.isEmpty) { biometricAttributeData.exceptionType = - "Permanent"; + "Permanent"; } } if (!biometricAttributeData.exceptions @@ -1282,9 +1317,9 @@ class _BiometricCaptureScanBlockPortraitState "assets/svg/RH_1.svg", height: 165, color: - (biometricAttributeData.exceptions[0] == true) - ? secondaryColors.elementAt(25) - : Colors.transparent, + (biometricAttributeData.exceptions[0] == true) + ? secondaryColors.elementAt(25) + : Colors.transparent, ), )), Positioned( @@ -1293,7 +1328,7 @@ class _BiometricCaptureScanBlockPortraitState child: InkWell( onTap: () async { if (!(biometricAttributeData.exceptions - .elementAt(1)) == + .elementAt(1)) == true) { await BiometricsApi().addBioException( widget.field.id!, "RightHand", "rightMiddle"); @@ -1322,14 +1357,14 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[1] = - !(biometricAttributeData.exceptions[1]); + !(biometricAttributeData.exceptions[1]); if (biometricAttributeData.exceptions .contains(true)) { if (biometricAttributeData .exceptionType.isEmpty) { biometricAttributeData.exceptionType = - "Permanent"; + "Permanent"; } } if (!biometricAttributeData.exceptions @@ -1344,9 +1379,9 @@ class _BiometricCaptureScanBlockPortraitState "assets/svg/RH_2.svg", height: 205, color: - (biometricAttributeData.exceptions[1] == true) - ? secondaryColors.elementAt(25) - : Colors.transparent, + (biometricAttributeData.exceptions[1] == true) + ? secondaryColors.elementAt(25) + : Colors.transparent, ), )), Positioned( @@ -1355,7 +1390,7 @@ class _BiometricCaptureScanBlockPortraitState child: InkWell( onTap: () async { if (!(biometricAttributeData.exceptions - .elementAt(2)) == + .elementAt(2)) == true) { await BiometricsApi().addBioException( widget.field.id!, "RightHand", "rightRing"); @@ -1384,14 +1419,14 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[2] = - !(biometricAttributeData.exceptions[2]); + !(biometricAttributeData.exceptions[2]); if (biometricAttributeData.exceptions .contains(true)) { if (biometricAttributeData .exceptionType.isEmpty) { biometricAttributeData.exceptionType = - "Permanent"; + "Permanent"; } } if (!biometricAttributeData.exceptions @@ -1406,9 +1441,9 @@ class _BiometricCaptureScanBlockPortraitState "assets/svg/RH_3.svg", height: 165, color: - (biometricAttributeData.exceptions[2] == true) - ? secondaryColors.elementAt(25) - : const Color.fromARGB(0, 221, 210, 210), + (biometricAttributeData.exceptions[2] == true) + ? secondaryColors.elementAt(25) + : const Color.fromARGB(0, 221, 210, 210), ), )), Positioned( @@ -1417,7 +1452,7 @@ class _BiometricCaptureScanBlockPortraitState child: InkWell( onTap: () async { if (!(biometricAttributeData.exceptions - .elementAt(3)) == + .elementAt(3)) == true) { await BiometricsApi().addBioException( widget.field.id!, "RightHand", "rightLittle"); @@ -1446,14 +1481,14 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[3] = - !(biometricAttributeData.exceptions[3]); + !(biometricAttributeData.exceptions[3]); if (biometricAttributeData.exceptions .contains(true)) { if (biometricAttributeData .exceptionType.isEmpty) { biometricAttributeData.exceptionType = - "Permanent"; + "Permanent"; } } if (!biometricAttributeData.exceptions @@ -1468,9 +1503,9 @@ class _BiometricCaptureScanBlockPortraitState "assets/svg/RH_4.svg", height: 100, color: - (biometricAttributeData.exceptions[3] == true) - ? secondaryColors.elementAt(25) - : Colors.transparent, + (biometricAttributeData.exceptions[3] == true) + ? secondaryColors.elementAt(25) + : Colors.transparent, ), )), ], @@ -1487,422 +1522,422 @@ class _BiometricCaptureScanBlockPortraitState context: context, barrierDismissible: true, builder: (BuildContext context) => StatefulBuilder( - builder: (context, StateSetter setStateAlert) { - return Center( - child: AlertDialog( - insetPadding: EdgeInsets.symmetric( - vertical: (isMobileSize) ? 10 : 24, - horizontal: (isMobileSize) ? 10 : 40), - content: Container( - height: (isMobileSize) ? 500 : 720, - width: (isMobileSize) ? 404 : 760, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12)), - child: SingleChildScrollView( - child: Column( - children: [ - Row( - children: [ - const SizedBox( - width: 50, - ), - const Spacer(), - Text( - "${biometricAttributeData.viewTitle} ${AppLocalizations.of(context)!.scan}", - style: TextStyle( - fontSize: (isMobileSize) ? 20.h : 28.h, - fontWeight: bold, - color: blackShade1, - overflow: TextOverflow.ellipsis), - ), - const Spacer(), - IconButton( - onPressed: () { - setState(() { - - }); - Navigator.pop(context); - }, - icon: Icon( - Icons.close, - color: blackShade1, - weight: 25, - size: 28, - )), - ], - ), - Divider( - height: 30, - thickness: 1, - color: secondaryColors.elementAt(22), - ), - Stack( - children: [ - SizedBox( - height: (isMobileSize) ? 339 : 639, - width: (isMobileSize) ? 339 : 639, - child: SvgPicture.asset( - "assets/svg/Right Hand.svg", - fit: BoxFit.fitHeight, + builder: (context, StateSetter setStateAlert) { + return Center( + child: AlertDialog( + insetPadding: EdgeInsets.symmetric( + vertical: (isMobileSize) ? 10 : 24, + horizontal: (isMobileSize) ? 10 : 40), + content: Container( + height: (isMobileSize) ? 500 : 720, + width: (isMobileSize) ? 404 : 760, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12)), + child: SingleChildScrollView( + child: Column( + children: [ + Row( + children: [ + const SizedBox( + width: 50, ), - ), - Positioned( - top: (isMobileSize) ? 109 : 205, - left: (isMobileSize) ? 72 : 140, - child: InkWell( - onTap: () async { - if (!(biometricAttributeData - .exceptions - .elementAt(0)) == - true) { - await BiometricsApi() - .addBioException( - widget.field.id!, - "RightHand", - "rightIndex"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Right Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } else { - await BiometricsApi() - .removeBioException( - widget.field.id!, - "RightHand", - "rightIndex"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Right Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } - biometricAttributeData.exceptions[0] = - !(biometricAttributeData - .exceptions[0]); + const Spacer(), + Text( + "${biometricAttributeData.viewTitle} ${AppLocalizations.of(context)!.scan}", + style: TextStyle( + fontSize: (isMobileSize) ? 20.h : 28.h, + fontWeight: bold, + color: blackShade1, + overflow: TextOverflow.ellipsis), + ), + const Spacer(), + IconButton( + onPressed: () { + setState(() { - if (biometricAttributeData.exceptions - .contains(true)) { - if (biometricAttributeData - .exceptionType.isEmpty) { - biometricAttributeData - .exceptionType = "Permanent"; - } - } - if (!biometricAttributeData.exceptions - .contains(true)) { - biometricAttributeData - .exceptionType = ""; - } - updateExceptionList("Right Hand"); - proofOfExceptionList("Right Hand"); - setState(() {}); - setStateAlert(() {}); + }); + Navigator.pop(context); }, - child: SvgPicture.asset( - "assets/svg/RH_1.svg", - height: (isMobileSize) ? 204 : 385, - color: (biometricAttributeData - .exceptions[0] == - true) - ? secondaryColors.elementAt(25) - : Colors.transparent, - ), - )), - Positioned( - top: (isMobileSize) ? 66 : 125, - left: (isMobileSize) ? 127 : 245, - child: InkWell( - onTap: () async { - if (!(biometricAttributeData - .exceptions - .elementAt(1)) == - true) { - await BiometricsApi() - .addBioException( - widget.field.id!, - "RightHand", - "rightMiddle"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Right Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } else { - await BiometricsApi() - .removeBioException( - widget.field.id!, - "RightHand", - "rightMiddle"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Right Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } - biometricAttributeData.exceptions[1] = - !(biometricAttributeData - .exceptions[1]); - - if (biometricAttributeData.exceptions - .contains(true)) { - if (biometricAttributeData - .exceptionType.isEmpty) { + icon: Icon( + Icons.close, + color: blackShade1, + weight: 25, + size: 28, + )), + ], + ), + Divider( + height: 30, + thickness: 1, + color: secondaryColors.elementAt(22), + ), + Stack( + children: [ + SizedBox( + height: (isMobileSize) ? 339 : 639, + width: (isMobileSize) ? 339 : 639, + child: SvgPicture.asset( + "assets/svg/Right Hand.svg", + fit: BoxFit.fitHeight, + ), + ), + Positioned( + top: (isMobileSize) ? 109 : 205, + left: (isMobileSize) ? 72 : 140, + child: InkWell( + onTap: () async { + if (!(biometricAttributeData + .exceptions + .elementAt(0)) == + true) { + await BiometricsApi() + .addBioException( + widget.field.id!, + "RightHand", + "rightIndex"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; biometricAttributeData - .exceptionType = "Permanent"; - } - } - if (!biometricAttributeData.exceptions - .contains(true)) { - biometricAttributeData - .exceptionType = ""; - } - updateExceptionList("Right Hand"); - proofOfExceptionList("Right Hand"); - setState(() {}); - setStateAlert(() {}); - }, - child: SvgPicture.asset( - "assets/svg/RH_2.svg", - height: (isMobileSize) ? 247 : 465, - color: (biometricAttributeData - .exceptions[1] == - true) - ? secondaryColors.elementAt(25) - : Colors.transparent, - ), - )), - Positioned( - top: (isMobileSize) ? 114 : 215, - right: (isMobileSize) ? 104 : 203, - child: InkWell( - onTap: () async { - if (!(biometricAttributeData - .exceptions - .elementAt(2)) == - true) { - await BiometricsApi() - .addBioException( - widget.field.id!, - "RightHand", - "rightRing"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Right Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } else { - await BiometricsApi() - .removeBioException( - widget.field.id!, - "RightHand", - "rightRing"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Right Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } - biometricAttributeData.exceptions[2] = - !(biometricAttributeData - .exceptions[2]); + .listofImages = [ + "assets/svg/Right Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } else { + await BiometricsApi() + .removeBioException( + widget.field.id!, + "RightHand", + "rightIndex"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Right Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } + biometricAttributeData.exceptions[0] = + !(biometricAttributeData + .exceptions[0]); - if (biometricAttributeData.exceptions - .contains(true)) { - if (biometricAttributeData - .exceptionType.isEmpty) { + if (biometricAttributeData.exceptions + .contains(true)) { + if (biometricAttributeData + .exceptionType.isEmpty) { + biometricAttributeData + .exceptionType = "Permanent"; + } + } + if (!biometricAttributeData.exceptions + .contains(true)) { biometricAttributeData - .exceptionType = "Permanent"; + .exceptionType = ""; } - } - if (!biometricAttributeData.exceptions - .contains(true)) { - biometricAttributeData - .exceptionType = ""; - } - updateExceptionList("Right Hand"); - proofOfExceptionList("Right Hand"); - setState(() {}); - setStateAlert(() {}); - }, - child: SvgPicture.asset( - "assets/svg/RH_3.svg", - height: (isMobileSize) ? 204 : 385, - color: (biometricAttributeData - .exceptions[2] == - true) - ? secondaryColors.elementAt(25) - : Colors.transparent, - ), - )), - Positioned( - top: (isMobileSize) ? 189 : 357, - right: (isMobileSize) ? 56 : 110, - child: InkWell( - onTap: () async { - if (!(biometricAttributeData - .exceptions - .elementAt(3)) == - true) { - await BiometricsApi() - .addBioException( - widget.field.id!, - "RightHand", - "rightLittle"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Right Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } else { - await BiometricsApi() - .removeBioException( - widget.field.id!, - "RightHand", - "rightLittle"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Right Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } - biometricAttributeData.exceptions[3] = - !(biometricAttributeData - .exceptions[3]); + updateExceptionList("Right Hand"); + proofOfExceptionList("Right Hand"); + setState(() {}); + setStateAlert(() {}); + }, + child: SvgPicture.asset( + "assets/svg/RH_1.svg", + height: (isMobileSize) ? 204 : 385, + color: (biometricAttributeData + .exceptions[0] == + true) + ? secondaryColors.elementAt(25) + : Colors.transparent, + ), + )), + Positioned( + top: (isMobileSize) ? 66 : 125, + left: (isMobileSize) ? 127 : 245, + child: InkWell( + onTap: () async { + if (!(biometricAttributeData + .exceptions + .elementAt(1)) == + true) { + await BiometricsApi() + .addBioException( + widget.field.id!, + "RightHand", + "rightMiddle"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Right Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } else { + await BiometricsApi() + .removeBioException( + widget.field.id!, + "RightHand", + "rightMiddle"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Right Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } + biometricAttributeData.exceptions[1] = + !(biometricAttributeData + .exceptions[1]); - if (biometricAttributeData.exceptions - .contains(true)) { - if (biometricAttributeData - .exceptionType.isEmpty) { + if (biometricAttributeData.exceptions + .contains(true)) { + if (biometricAttributeData + .exceptionType.isEmpty) { + biometricAttributeData + .exceptionType = "Permanent"; + } + } + if (!biometricAttributeData.exceptions + .contains(true)) { biometricAttributeData - .exceptionType = "Permanent"; + .exceptionType = ""; } - } - if (!biometricAttributeData.exceptions - .contains(true)) { - biometricAttributeData - .exceptionType = ""; - } - updateExceptionList("Right Hand"); - proofOfExceptionList("Right Hand"); - setState(() {}); - setStateAlert(() {}); - }, - child: SvgPicture.asset( - "assets/svg/RH_4.svg", - height: (isMobileSize) ? 132 : 250, - color: (biometricAttributeData - .exceptions[3] == - true) - ? secondaryColors.elementAt(25) - : Colors.transparent, - ), - )), - ], - ), - ], + updateExceptionList("Right Hand"); + proofOfExceptionList("Right Hand"); + setState(() {}); + setStateAlert(() {}); + }, + child: SvgPicture.asset( + "assets/svg/RH_2.svg", + height: (isMobileSize) ? 247 : 465, + color: (biometricAttributeData + .exceptions[1] == + true) + ? secondaryColors.elementAt(25) + : Colors.transparent, + ), + )), + Positioned( + top: (isMobileSize) ? 114 : 215, + right: (isMobileSize) ? 104 : 203, + child: InkWell( + onTap: () async { + if (!(biometricAttributeData + .exceptions + .elementAt(2)) == + true) { + await BiometricsApi() + .addBioException( + widget.field.id!, + "RightHand", + "rightRing"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Right Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } else { + await BiometricsApi() + .removeBioException( + widget.field.id!, + "RightHand", + "rightRing"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Right Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } + biometricAttributeData.exceptions[2] = + !(biometricAttributeData + .exceptions[2]); + + if (biometricAttributeData.exceptions + .contains(true)) { + if (biometricAttributeData + .exceptionType.isEmpty) { + biometricAttributeData + .exceptionType = "Permanent"; + } + } + if (!biometricAttributeData.exceptions + .contains(true)) { + biometricAttributeData + .exceptionType = ""; + } + updateExceptionList("Right Hand"); + proofOfExceptionList("Right Hand"); + setState(() {}); + setStateAlert(() {}); + }, + child: SvgPicture.asset( + "assets/svg/RH_3.svg", + height: (isMobileSize) ? 204 : 385, + color: (biometricAttributeData + .exceptions[2] == + true) + ? secondaryColors.elementAt(25) + : Colors.transparent, + ), + )), + Positioned( + top: (isMobileSize) ? 189 : 357, + right: (isMobileSize) ? 56 : 110, + child: InkWell( + onTap: () async { + if (!(biometricAttributeData + .exceptions + .elementAt(3)) == + true) { + await BiometricsApi() + .addBioException( + widget.field.id!, + "RightHand", + "rightLittle"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Right Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } else { + await BiometricsApi() + .removeBioException( + widget.field.id!, + "RightHand", + "rightLittle"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Right Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } + biometricAttributeData.exceptions[3] = + !(biometricAttributeData + .exceptions[3]); + + if (biometricAttributeData.exceptions + .contains(true)) { + if (biometricAttributeData + .exceptionType.isEmpty) { + biometricAttributeData + .exceptionType = "Permanent"; + } + } + if (!biometricAttributeData.exceptions + .contains(true)) { + biometricAttributeData + .exceptionType = ""; + } + updateExceptionList("Right Hand"); + proofOfExceptionList("Right Hand"); + setState(() {}); + setStateAlert(() {}); + }, + child: SvgPicture.asset( + "assets/svg/RH_4.svg", + height: (isMobileSize) ? 132 : 250, + color: (biometricAttributeData + .exceptions[3] == + true) + ? secondaryColors.elementAt(25) + : Colors.transparent, + ), + )), + ], + ), + ], + ), ), ), ), - ), - ); - } + ); + } ), ); }, @@ -1947,7 +1982,7 @@ class _BiometricCaptureScanBlockPortraitState child: InkWell( onTap: () async { if (!(biometricAttributeData.exceptions - .elementAt(3)) == + .elementAt(3)) == true) { await BiometricsApi().addBioException( widget.field.id!, "LeftHand", "leftLittle"); @@ -1976,14 +2011,14 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[3] = - !(biometricAttributeData.exceptions[3]); + !(biometricAttributeData.exceptions[3]); if (biometricAttributeData.exceptions .contains(true)) { if (biometricAttributeData .exceptionType.isEmpty) { biometricAttributeData.exceptionType = - "Permanent"; + "Permanent"; } } if (!biometricAttributeData.exceptions @@ -1998,9 +2033,9 @@ class _BiometricCaptureScanBlockPortraitState "assets/svg/LH_1.svg", height: 100, color: - (biometricAttributeData.exceptions[3] == true) - ? secondaryColors.elementAt(25) - : Colors.transparent, + (biometricAttributeData.exceptions[3] == true) + ? secondaryColors.elementAt(25) + : Colors.transparent, ), )), Positioned( @@ -2009,7 +2044,7 @@ class _BiometricCaptureScanBlockPortraitState child: InkWell( onTap: () async { if (!(biometricAttributeData.exceptions - .elementAt(2)) == + .elementAt(2)) == true) { await BiometricsApi().addBioException( widget.field.id!, "LeftHand", "leftRing"); @@ -2038,14 +2073,14 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[2] = - !(biometricAttributeData.exceptions[2]); + !(biometricAttributeData.exceptions[2]); if (biometricAttributeData.exceptions .contains(true)) { if (biometricAttributeData .exceptionType.isEmpty) { biometricAttributeData.exceptionType = - "Permanent"; + "Permanent"; } } if (!biometricAttributeData.exceptions @@ -2060,9 +2095,9 @@ class _BiometricCaptureScanBlockPortraitState "assets/svg/LH_2.svg", height: 165, color: - (biometricAttributeData.exceptions[2] == true) - ? secondaryColors.elementAt(25) - : Colors.transparent, + (biometricAttributeData.exceptions[2] == true) + ? secondaryColors.elementAt(25) + : Colors.transparent, ), )), Positioned( @@ -2071,7 +2106,7 @@ class _BiometricCaptureScanBlockPortraitState child: InkWell( onTap: () async { if (!(biometricAttributeData.exceptions - .elementAt(1)) == + .elementAt(1)) == true) { await BiometricsApi().addBioException( widget.field.id!, "LeftHand", "leftMiddle"); @@ -2100,14 +2135,14 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[1] = - !(biometricAttributeData.exceptions[1]); + !(biometricAttributeData.exceptions[1]); if (biometricAttributeData.exceptions .contains(true)) { if (biometricAttributeData .exceptionType.isEmpty) { biometricAttributeData.exceptionType = - "Permanent"; + "Permanent"; } } if (!biometricAttributeData.exceptions @@ -2122,9 +2157,9 @@ class _BiometricCaptureScanBlockPortraitState "assets/svg/LH_3.svg", height: 205, color: - (biometricAttributeData.exceptions[1] == true) - ? secondaryColors.elementAt(25) - : Colors.transparent, + (biometricAttributeData.exceptions[1] == true) + ? secondaryColors.elementAt(25) + : Colors.transparent, ), )), Positioned( @@ -2133,7 +2168,7 @@ class _BiometricCaptureScanBlockPortraitState child: InkWell( onTap: () async { if (!(biometricAttributeData.exceptions - .elementAt(0)) == + .elementAt(0)) == true) { await BiometricsApi().addBioException( widget.field.id!, "LeftHand", "leftIndex"); @@ -2162,14 +2197,14 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[0] = - !(biometricAttributeData.exceptions[0]); + !(biometricAttributeData.exceptions[0]); if (biometricAttributeData.exceptions .contains(true)) { if (biometricAttributeData .exceptionType.isEmpty) { biometricAttributeData.exceptionType = - "Permanent"; + "Permanent"; } } if (!biometricAttributeData.exceptions @@ -2184,9 +2219,9 @@ class _BiometricCaptureScanBlockPortraitState "assets/svg/LH_4.svg", height: 165, color: - (biometricAttributeData.exceptions[0] == true) - ? secondaryColors.elementAt(25) - : Colors.transparent, + (biometricAttributeData.exceptions[0] == true) + ? secondaryColors.elementAt(25) + : Colors.transparent, ), )), ], @@ -2203,434 +2238,434 @@ class _BiometricCaptureScanBlockPortraitState context: context, builder: (context) => StatefulBuilder( builder: (context, StateSetter setStateAlert) { - return AlertDialog( - content: Container( - height: (isMobileSize) ? 500 : 720, - width: (isMobileSize) ? 404 : 760, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12)), - child: SingleChildScrollView( - child: Column( - children: [ - Row( - children: [ - const SizedBox( - width: 50, - ), - const Spacer(), - Text( - "${biometricAttributeData.viewTitle} ${AppLocalizations.of(context)!.scan}", - style: TextStyle( - fontSize: - (isMobileSize) ? 20.h : 28.h, - fontWeight: bold, - color: blackShade1, - overflow: TextOverflow.ellipsis), - ), - const Spacer(), - IconButton( - onPressed: () { - setState(() { - - }); - Navigator.pop(context); - }, - icon: Icon( - Icons.close, - color: blackShade1, - weight: 25, - size: 28, - )), - ], - ), - Divider( - height: 30, - thickness: 1, - color: secondaryColors.elementAt(22), - ), - Stack( - children: [ - SizedBox( - height: (isMobileSize) ? 339 : 639, - width: (isMobileSize) ? 339 : 639, - child: SvgPicture.asset( - "assets/svg/Left Hand.svg", - fit: BoxFit.fitHeight, + return AlertDialog( + content: Container( + height: (isMobileSize) ? 500 : 720, + width: (isMobileSize) ? 404 : 760, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12)), + child: SingleChildScrollView( + child: Column( + children: [ + Row( + children: [ + const SizedBox( + width: 50, ), - ), - Positioned( - top: (isMobileSize) ? 189 : 357, - left: (isMobileSize) ? 52 : 110, - child: InkWell( - onTap: () async { - if (!(biometricAttributeData - .exceptions - .elementAt(3)) == - true) { - await BiometricsApi() - .addBioException( - widget.field.id!, - "LeftHand", - "leftLittle"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Left Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } else { - await BiometricsApi() - .removeBioException( - widget.field.id!, - "LeftHand", - "leftLittle"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Left Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } - biometricAttributeData - .exceptions[3] = - !(biometricAttributeData - .exceptions[3]); + const Spacer(), + Text( + "${biometricAttributeData.viewTitle} ${AppLocalizations.of(context)!.scan}", + style: TextStyle( + fontSize: + (isMobileSize) ? 20.h : 28.h, + fontWeight: bold, + color: blackShade1, + overflow: TextOverflow.ellipsis), + ), + const Spacer(), + IconButton( + onPressed: () { + setState(() { - if (biometricAttributeData - .exceptions - .contains(true)) { - if (biometricAttributeData - .exceptionType.isEmpty) { - biometricAttributeData - .exceptionType = - "Permanent"; - } - } - if (!biometricAttributeData - .exceptions - .contains(true)) { - biometricAttributeData - .exceptionType = ""; - } - updateExceptionList("Left Hand"); - proofOfExceptionList("Left Hand"); - setState(() {}); - setStateAlert(() {}); + }); + Navigator.pop(context); }, - child: SvgPicture.asset( - "assets/svg/LH_1.svg", - height: - (isMobileSize) ? 132.h : 250.h, - color: (biometricAttributeData - .exceptions[3] == - true) - ? secondaryColors.elementAt(25) - : Colors.transparent, - ), - )), - Positioned( - top: (isMobileSize) ? 114 : 215, - left: (isMobileSize) ? 110 : 203, - child: InkWell( - onTap: () async { - if (!(biometricAttributeData - .exceptions - .elementAt(2)) == - true) { - await BiometricsApi() - .addBioException( - widget.field.id!, - "LeftHand", - "leftRing"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Left Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } else { - await BiometricsApi() - .removeBioException( - widget.field.id!, - "LeftHand", - "leftRing"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Left Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; + icon: Icon( + Icons.close, + color: blackShade1, + weight: 25, + size: 28, + )), + ], + ), + Divider( + height: 30, + thickness: 1, + color: secondaryColors.elementAt(22), + ), + Stack( + children: [ + SizedBox( + height: (isMobileSize) ? 339 : 639, + width: (isMobileSize) ? 339 : 639, + child: SvgPicture.asset( + "assets/svg/Left Hand.svg", + fit: BoxFit.fitHeight, + ), + ), + Positioned( + top: (isMobileSize) ? 189 : 357, + left: (isMobileSize) ? 52 : 110, + child: InkWell( + onTap: () async { + if (!(biometricAttributeData + .exceptions + .elementAt(3)) == + true) { + await BiometricsApi() + .addBioException( + widget.field.id!, + "LeftHand", + "leftLittle"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Left Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } else { + await BiometricsApi() + .removeBioException( + widget.field.id!, + "LeftHand", + "leftLittle"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Left Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } biometricAttributeData - .thresholdPercentage = "0"; - } - biometricAttributeData - .exceptions[2] = - !(biometricAttributeData - .exceptions[2]); + .exceptions[3] = + !(biometricAttributeData + .exceptions[3]); - if (biometricAttributeData - .exceptions - .contains(true)) { if (biometricAttributeData - .exceptionType.isEmpty) { + .exceptions + .contains(true)) { + if (biometricAttributeData + .exceptionType.isEmpty) { + biometricAttributeData + .exceptionType = + "Permanent"; + } + } + if (!biometricAttributeData + .exceptions + .contains(true)) { biometricAttributeData - .exceptionType = - "Permanent"; + .exceptionType = ""; + } + updateExceptionList("Left Hand"); + proofOfExceptionList("Left Hand"); + setState(() {}); + setStateAlert(() {}); + }, + child: SvgPicture.asset( + "assets/svg/LH_1.svg", + height: + (isMobileSize) ? 132.h : 250.h, + color: (biometricAttributeData + .exceptions[3] == + true) + ? secondaryColors.elementAt(25) + : Colors.transparent, + ), + )), + Positioned( + top: (isMobileSize) ? 114 : 215, + left: (isMobileSize) ? 110 : 203, + child: InkWell( + onTap: () async { + if (!(biometricAttributeData + .exceptions + .elementAt(2)) == + true) { + await BiometricsApi() + .addBioException( + widget.field.id!, + "LeftHand", + "leftRing"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Left Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } else { + await BiometricsApi() + .removeBioException( + widget.field.id!, + "LeftHand", + "leftRing"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Left Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; } - } - if (!biometricAttributeData - .exceptions - .contains(true)) { - biometricAttributeData - .exceptionType = ""; - } - updateExceptionList("Left Hand"); - proofOfExceptionList("Left Hand"); - setState(() {}); - setStateAlert(() {}); - }, - child: SvgPicture.asset( - "assets/svg/LH_2.svg", - height: (isMobileSize) ? 204 : 385, - color: (biometricAttributeData - .exceptions[2] == - true) - ? secondaryColors.elementAt(25) - : Colors.transparent, - ), - )), - Positioned( - top: (isMobileSize) ? 66 : 125, - right: (isMobileSize) ? 130 : 245, - child: InkWell( - onTap: () async { - if (!(biometricAttributeData - .exceptions - .elementAt(1)) == - true) { - await BiometricsApi() - .addBioException( - widget.field.id!, - "LeftHand", - "leftMiddle"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Left Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } else { - await BiometricsApi() - .removeBioException( - widget.field.id!, - "LeftHand", - "leftMiddle"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Left Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; biometricAttributeData - .thresholdPercentage = "0"; - } - biometricAttributeData - .exceptions[1] = - !(biometricAttributeData - .exceptions[1]); + .exceptions[2] = + !(biometricAttributeData + .exceptions[2]); - if (biometricAttributeData - .exceptions - .contains(true)) { if (biometricAttributeData - .exceptionType.isEmpty) { + .exceptions + .contains(true)) { + if (biometricAttributeData + .exceptionType.isEmpty) { + biometricAttributeData + .exceptionType = + "Permanent"; + } + } + if (!biometricAttributeData + .exceptions + .contains(true)) { biometricAttributeData - .exceptionType = - "Permanent"; + .exceptionType = ""; + } + updateExceptionList("Left Hand"); + proofOfExceptionList("Left Hand"); + setState(() {}); + setStateAlert(() {}); + }, + child: SvgPicture.asset( + "assets/svg/LH_2.svg", + height: (isMobileSize) ? 204 : 385, + color: (biometricAttributeData + .exceptions[2] == + true) + ? secondaryColors.elementAt(25) + : Colors.transparent, + ), + )), + Positioned( + top: (isMobileSize) ? 66 : 125, + right: (isMobileSize) ? 130 : 245, + child: InkWell( + onTap: () async { + if (!(biometricAttributeData + .exceptions + .elementAt(1)) == + true) { + await BiometricsApi() + .addBioException( + widget.field.id!, + "LeftHand", + "leftMiddle"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Left Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } else { + await BiometricsApi() + .removeBioException( + widget.field.id!, + "LeftHand", + "leftMiddle"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Left Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; } - } - if (!biometricAttributeData - .exceptions - .contains(true)) { - biometricAttributeData - .exceptionType = ""; - } - updateExceptionList("Left Hand"); - proofOfExceptionList("Left Hand"); - setState(() {}); - setStateAlert(() {}); - }, - child: SvgPicture.asset( - "assets/svg/LH_3.svg", - height: (isMobileSize) ? 247 : 465, - color: (biometricAttributeData - .exceptions[1] == - true) - ? secondaryColors.elementAt(25) - : Colors.transparent, - ), - )), - Positioned( - top: (isMobileSize) ? 109 : 205, - right: (isMobileSize) ? 73 : 140, - child: InkWell( - onTap: () async { - if (!(biometricAttributeData - .exceptions - .elementAt(0)) == - true) { - await BiometricsApi() - .addBioException( - widget.field.id!, - "LeftHand", - "leftIndex"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Left Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; - biometricAttributeData - .thresholdPercentage = "0"; - } else { - await BiometricsApi() - .removeBioException( - widget.field.id!, - "LeftHand", - "leftIndex"); - resetAfterException( - widget.field.id!, - biometricAttributeData); - biometricAttributeData.isScanned = - false; - biometricAttributeData.attemptNo = - 0; - biometricAttributeData - .listofImages = [ - "assets/svg/Left Hand.svg" - ]; - biometricAttributeData - .listOfBiometricsDto = []; - biometricAttributeData - .qualityPercentage = 0; biometricAttributeData - .thresholdPercentage = "0"; - } - biometricAttributeData - .exceptions[0] = - !(biometricAttributeData - .exceptions[0]); + .exceptions[1] = + !(biometricAttributeData + .exceptions[1]); - if (biometricAttributeData - .exceptions - .contains(true)) { if (biometricAttributeData - .exceptionType.isEmpty) { + .exceptions + .contains(true)) { + if (biometricAttributeData + .exceptionType.isEmpty) { + biometricAttributeData + .exceptionType = + "Permanent"; + } + } + if (!biometricAttributeData + .exceptions + .contains(true)) { biometricAttributeData - .exceptionType = - "Permanent"; + .exceptionType = ""; + } + updateExceptionList("Left Hand"); + proofOfExceptionList("Left Hand"); + setState(() {}); + setStateAlert(() {}); + }, + child: SvgPicture.asset( + "assets/svg/LH_3.svg", + height: (isMobileSize) ? 247 : 465, + color: (biometricAttributeData + .exceptions[1] == + true) + ? secondaryColors.elementAt(25) + : Colors.transparent, + ), + )), + Positioned( + top: (isMobileSize) ? 109 : 205, + right: (isMobileSize) ? 73 : 140, + child: InkWell( + onTap: () async { + if (!(biometricAttributeData + .exceptions + .elementAt(0)) == + true) { + await BiometricsApi() + .addBioException( + widget.field.id!, + "LeftHand", + "leftIndex"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Left Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; + } else { + await BiometricsApi() + .removeBioException( + widget.field.id!, + "LeftHand", + "leftIndex"); + resetAfterException( + widget.field.id!, + biometricAttributeData); + biometricAttributeData.isScanned = + false; + biometricAttributeData.attemptNo = + 0; + biometricAttributeData + .listofImages = [ + "assets/svg/Left Hand.svg" + ]; + biometricAttributeData + .listOfBiometricsDto = []; + biometricAttributeData + .qualityPercentage = 0; + biometricAttributeData + .thresholdPercentage = "0"; } - } - if (!biometricAttributeData - .exceptions - .contains(true)) { biometricAttributeData - .exceptionType = ""; - } - updateExceptionList("Left Hand"); - proofOfExceptionList("Left Hand"); - setState(() {}); - setStateAlert(() {}); - }, - child: SvgPicture.asset( - "assets/svg/LH_4.svg", - height: (isMobileSize) ? 204 : 385, - color: (biometricAttributeData - .exceptions[0] == - true) - ? secondaryColors.elementAt(25) - : Colors.transparent, - ), - )), - ], - ), - ], + .exceptions[0] = + !(biometricAttributeData + .exceptions[0]); + + if (biometricAttributeData + .exceptions + .contains(true)) { + if (biometricAttributeData + .exceptionType.isEmpty) { + biometricAttributeData + .exceptionType = + "Permanent"; + } + } + if (!biometricAttributeData + .exceptions + .contains(true)) { + biometricAttributeData + .exceptionType = ""; + } + updateExceptionList("Left Hand"); + proofOfExceptionList("Left Hand"); + setState(() {}); + setStateAlert(() {}); + }, + child: SvgPicture.asset( + "assets/svg/LH_4.svg", + height: (isMobileSize) ? 204 : 385, + color: (biometricAttributeData + .exceptions[0] == + true) + ? secondaryColors.elementAt(25) + : Colors.transparent, + ), + )), + ], + ), + ], + ), ), ), - ), - ); - } + ); + } ), ); }, @@ -2698,7 +2733,7 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[0] = - !(biometricAttributeData.exceptions[0]); + !(biometricAttributeData.exceptions[0]); if (biometricAttributeData.exceptions.contains(true)) { if (biometricAttributeData.exceptionType.isEmpty) { @@ -2754,7 +2789,7 @@ class _BiometricCaptureScanBlockPortraitState biometricAttributeData.thresholdPercentage = "0"; } biometricAttributeData.exceptions[1] = - !(biometricAttributeData.exceptions[1]); + !(biometricAttributeData.exceptions[1]); if (biometricAttributeData.exceptions.contains(true)) { if (biometricAttributeData.exceptionType.isEmpty) { @@ -2945,37 +2980,39 @@ class _BiometricCaptureScanBlockPortraitState ElevatedButton( style: ButtonStyle( maximumSize: - MaterialStateProperty.all(const Size(200, 68)), + MaterialStateProperty.all(const Size(200, 68)), minimumSize: - MaterialStateProperty.all(const Size(200, 68)), + MaterialStateProperty.all(const Size(200, 68)), ), - onPressed: () { - List bioAttributes = (widget - .field.conditionalBioAttributes!.first!.ageGroup! - .compareTo( - context.read().ageGroup) == - 0) - ? _returnBiometricList( - widget.field.conditionalBioAttributes!.first! - .bioAttributes!, - widget.field.id!) - : _returnBiometricList( - widget.field.bioAttributes!, widget.field.id!); - - var nextElement = _getNextElement( - bioAttributes, - context - .read() - .biometricAttribute); - setState(() {}); - if (nextElement != null) { - context - .read() - .biometricAttribute = nextElement; - } else { - Navigator.pop(context); - } - }, + onPressed: canProceedToNext() + ? () { + List bioAttributes = (widget + .field.conditionalBioAttributes!.first!.ageGroup! + .compareTo( + context.read().ageGroup) == + 0) + ? _returnBiometricList( + widget.field.conditionalBioAttributes!.first! + .bioAttributes!, + widget.field.id!) + : _returnBiometricList( + widget.field.bioAttributes!, widget.field.id!); + + var nextElement = _getNextElement( + bioAttributes, + context + .read() + .biometricAttribute); + setState(() {}); + if (nextElement != null) { + context + .read() + .biometricAttribute = nextElement; + } else { + Navigator.pop(context); + } + } + : null, // Disable if cannot proceed child: Text(AppLocalizations.of(context)!.next_button, style: TextStyle( fontSize: (isMobileSize) ? 20 : 24, fontWeight: bold)), @@ -2998,9 +3035,13 @@ class _BiometricCaptureScanBlockPortraitState child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - (widget.field.inputRequired!) - ? RichText( - text: TextSpan( + Expanded( + child: + (widget.field.inputRequired!) + ? RichText( + overflow: TextOverflow.ellipsis, + maxLines: 2, + text: TextSpan( text: context .read() .chooseLanguage(widget.field.label!), @@ -3008,10 +3049,11 @@ class _BiometricCaptureScanBlockPortraitState .textTheme .titleLarge ?.copyWith( - fontSize: (isMobileSize) ? 14.h : 24.h, - color: blackShade1, - fontWeight: semiBold, - overflow: TextOverflow.ellipsis), + fontSize: (isMobileSize) ? 14.h : 24.h, + color: blackShade1, + fontWeight: semiBold, + //overflow: TextOverflow.ellipsis + ), children: const [ TextSpan( text: " *", @@ -3020,19 +3062,23 @@ class _BiometricCaptureScanBlockPortraitState ) ], )) - : Text( - context - .read() - .chooseLanguage(widget.field.label!), - style: Theme.of(context) - .textTheme - .titleLarge - ?.copyWith( - fontSize: (isMobileSize) ? 14.h : 24.h, - color: blackShade1, - fontWeight: semiBold, - overflow: TextOverflow.ellipsis), - ), + : Text( + context + .read() + .chooseLanguage(widget.field.label!), + overflow: TextOverflow.ellipsis, + maxLines: 2, + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith( + fontSize: (isMobileSize) ? 14.h : 24.h, + color: blackShade1, + fontWeight: semiBold, + // overflow: TextOverflow.ellipsis, + ), + ), + ), Padding( padding: const EdgeInsets.only(right: 30), child: InkWell( @@ -3074,33 +3120,37 @@ class _BiometricCaptureScanBlockPortraitState child: Container( decoration: BoxDecoration( color: (context - .read() - .biometricCaptureScanBlockTabIndex == - 1) + .read() + .biometricCaptureScanBlockTabIndex == + 1) ? solidPrimary : pureWhite, border: (context - .read() - .biometricCaptureScanBlockTabIndex == - 1) + .read() + .biometricCaptureScanBlockTabIndex == + 1) ? const Border() : Border( - bottom: BorderSide( - color: solidPrimary, width: 3), - ), + bottom: BorderSide( + color: solidPrimary, width: 3), + ), ), height: 84, child: Center( child: Text( "${biometricAttributeData.viewTitle} ${AppLocalizations.of(context)!.scan}", + maxLines: 2, + overflow: TextOverflow.ellipsis, + softWrap: true, + textAlign: TextAlign.center, style: TextStyle( fontSize: (isMobileSize) ? 18 : 24, fontWeight: semiBold, color: (context - .read< - BiometricCaptureControlProvider>() - .biometricCaptureScanBlockTabIndex == - 1) + .read< + BiometricCaptureControlProvider>() + .biometricCaptureScanBlockTabIndex == + 1) ? pureWhite : blackShade1), ), @@ -3119,20 +3169,20 @@ class _BiometricCaptureScanBlockPortraitState child: Container( decoration: BoxDecoration( color: (context - .read() - .biometricCaptureScanBlockTabIndex == - 2) + .read() + .biometricCaptureScanBlockTabIndex == + 2) ? solidPrimary : pureWhite, border: (context - .read() - .biometricCaptureScanBlockTabIndex == - 2) + .read() + .biometricCaptureScanBlockTabIndex == + 2) ? const Border() : Border( - bottom: BorderSide( - color: solidPrimary, width: 3), - ), + bottom: BorderSide( + color: solidPrimary, width: 3), + ), ), height: 84, child: Center( @@ -3142,10 +3192,10 @@ class _BiometricCaptureScanBlockPortraitState fontSize: (isMobileSize) ? 18 : 24, fontWeight: semiBold, color: (context - .read< - BiometricCaptureControlProvider>() - .biometricCaptureScanBlockTabIndex == - 2) + .read< + BiometricCaptureControlProvider>() + .biometricCaptureScanBlockTabIndex == + 2) ? pureWhite : blackShade1), ), @@ -3159,9 +3209,9 @@ class _BiometricCaptureScanBlockPortraitState height: 40, ), (context - .read() - .biometricCaptureScanBlockTabIndex == - 1) + .read() + .biometricCaptureScanBlockTabIndex == + 1) ? _scanBlock() : _exceptionBlock() ], diff --git a/lib/ui/profile/profile.dart b/lib/ui/profile/profile.dart index 79e8e5ce0..f18ef8b34 100644 --- a/lib/ui/profile/profile.dart +++ b/lib/ui/profile/profile.dart @@ -30,6 +30,10 @@ class _ProfilePageState extends State { syncProvider = Provider.of(context, listen: false); connectivityProvider = Provider.of(context, listen: false); super.initState(); + // Check GPS status when profile page loads + WidgetsBinding.instance.addPostFrameCallback((_) { + connectivityProvider.checkGPSStatus(); + }); } goToUrl(String url) async { @@ -153,6 +157,50 @@ class _ProfilePageState extends State { ], ), ), + const Divider(color: Color(0xFFF5F8FF), height: 4), + const SizedBox(height: 15), + Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Icon( + Icons.location_on_outlined, + color: solidPrimary, + size: 16, + ), + const SizedBox(width: 10), + Text( + "GPS", + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: const Color(0xff333333), fontWeight: semiBold), + ), + ], + ), + Row( + children: [ + Icon( + Icons.circle, + color: context.watch().isGPSEnabled + ? const Color(0xff1A9B42) + : Colors.red, + size: 12, + ), + const SizedBox(width: 10), + Text( + context.watch().isGPSEnabled + ? appLocalizations.enabled + : appLocalizations.disabled, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: const Color(0xff333333), fontWeight: semiBold), + ), + ], + ), + ], + ), + ), const SizedBox(height: 15), const Divider(color: Color(0xFFF5F8FF), height: 4), const SizedBox(height: 15), diff --git a/lib/ui/settings/settings_screen.dart b/lib/ui/settings/settings_screen.dart new file mode 100644 index 000000000..3d7eb85b8 --- /dev/null +++ b/lib/ui/settings/settings_screen.dart @@ -0,0 +1,200 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:registration_client/model/settings.dart'; +import 'package:registration_client/provider/auth_provider.dart'; +import 'package:registration_client/provider/global_provider.dart'; +import 'package:registration_client/ui/process_ui/widgets/device_settings_tab.dart'; +import 'package:registration_client/utils/app_config.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:registration_client/platform_spi/sync_response_service.dart'; +import 'widgets/scheduled_jobs_settings.dart'; + +import 'widgets/global_config_settings_tab.dart'; + +class SettingsScreen extends StatefulWidget { + const SettingsScreen({ + super.key, + required this.getSettingsUI, + }); + + final Future> Function(BuildContext) getSettingsUI; + + @override + State createState() => _SettingsScreenState(); +} + +class _SettingsScreenState extends State { + List settingUiSpec = []; + List settingUiByRole = []; + bool isLoadingUiSpec = true; + late AuthProvider authProvider; + late SyncResponseService syncResponseService; + List activeJobs = const []; + + @override + void initState() { + super.initState(); + authProvider = Provider.of(context, listen: false); + syncResponseService = SyncResponseService(); + _loadUiSpec(); + _loadActiveJobs(); + } + + Future _loadUiSpec() async { + final specList = await widget.getSettingsUI(context); + settingUiSpec = specList + .whereType() + .map((e) => Settings.fromJson(json.decode(e) as Map)) + .toList(); + + // Get current user roles + final userId = authProvider.userId; + final List userRoles = await authProvider.getUserRole(userId); + + // Filter tabs accessible to the user based on roles + settingUiByRole = settingUiSpec.where((settings) { + final access = settings.accessControl ?? []; + return access.any((role) => userRoles.contains(role)); + }).toList(); + + setState(() => isLoadingUiSpec = false); + } + + Future _loadActiveJobs() async { + try { + final jobs = await syncResponseService.getActiveSyncJobs(); + setState(() { + activeJobs = jobs; + }); + } catch (e) { + debugPrint('Failed to load active sync jobs: $e'); + } + } + + @override + Widget build(BuildContext context) { + if (isLoadingUiSpec) { + return const Center(child: CircularProgressIndicator()); + } + + + if (settingUiByRole.isEmpty) { + return SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: Text( + AppLocalizations.of(context)!.no_access_to_this_page, + style: TextStyle(fontSize: 18, color: Colors.black), + ), + ), + ); + } + + return DefaultTabController( + length: settingUiByRole.length, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: MediaQuery.of(context).size.width, + color: solidPrimary, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0), + child: Text( + AppLocalizations.of(context)!.settings, + style: const TextStyle( + fontSize: 22, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + Container( + padding: const EdgeInsets.only(top: 8.0, bottom: 3.0), + decoration: const BoxDecoration( + border: Border( + top: BorderSide( + color: Color(0xFFE5EBFA), + width: .3, + ), + bottom: BorderSide( + color: Colors.blueAccent, + width: 1, + ), + ), + ), + child: TabBar( + labelColor: Colors.white, + unselectedLabelColor: Colors.white70, + indicatorColor: Colors.white, + indicatorWeight: 2.0, + indicatorSize: TabBarIndicatorSize.label, + tabs: [ + for (final settings in settingUiByRole) + Tab( + text: + settings.label?[context.read().selectedLanguage] ?? + settings.label?['eng'] ?? + (settings.label?.values.first ?? 'Unknown'), + ), + ], + ), + ), + ], + ), + ), + Expanded( + child: TabBarView( + children: [ + for (final settings in settingUiByRole) + _buildTabContent(settings), + ], + ), + ), + ], + ), + ); + } + + String _getControllerName(Settings settings) { + if (settings.fxml != null && settings.fxml!.isNotEmpty) { + return settings.fxml!.replaceAll('.fxml', 'Controller'); + } else { + return '${settings.name}Controller'; + } + } + + Widget _buildTabContent(Settings settings) { + final selectedLang = context.read().selectedLanguage; + + final controllerName = _getControllerName(settings); + + switch (controllerName) { + case 'ScheduledJobsSettingsController': + return ScheduledJobsSettings(jobJsonList: activeJobs); + case 'GlobalConfigSettingsController': + return GlobalConfigSettingsTab(settings: settings,selectedLan: selectedLang); + case 'DeviceSettingsController': + return DeviceSettingsTab(settings: settings, selectedLan: selectedLang); + default: + return _buildDescriptionOnlyTab(settings, selectedLang); + } + } + + Widget _buildDescriptionOnlyTab(Settings settings, String selectedLang) { + return Center( + child: Text( + settings.description?[selectedLang] ?? + settings.description?['eng'] ?? + (settings.description?.values.first ?? 'No description available'), + style: const TextStyle(color: Colors.black), + ), + ); + } +} \ No newline at end of file diff --git a/lib/ui/settings/widgets/global_config_settings_tab.dart b/lib/ui/settings/widgets/global_config_settings_tab.dart new file mode 100644 index 000000000..98eb3adfc --- /dev/null +++ b/lib/ui/settings/widgets/global_config_settings_tab.dart @@ -0,0 +1,513 @@ +import 'package:flutter/material.dart'; +import 'package:registration_client/utils/app_config.dart'; +import '../../../model/settings.dart'; +import '../../../pigeon/common_details_pigeon.dart'; +import '../../../pigeon/global_config_settings_pigeon.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../../../provider/global_provider.dart'; +import 'package:restart_app/restart_app.dart'; + +class GlobalConfigSettingsTab extends StatefulWidget { + final Settings settings; + final String selectedLan; + GlobalConfigSettingsTab({Key? key,required this.settings,required this.selectedLan,}) : super(key: key); + + @override + State createState() => + _GlobalConfigSettingsTabState(); +} + +class _GlobalConfigSettingsTabState extends State { + Map? serverValues; + Map localValues = {}; + Map localConfigurations = {}; + List permittedConfigurations = []; + final Map _controllers = {}; + bool isLoading = true; + String? errorMessage; + + @override + void initState() { + super.initState(); + _loadInitialData(); + } + + // Load initial data from the server and local storage + Future _loadInitialData() async { + setState(() { + isLoading = true; + errorMessage = null; + }); + try { + // Load registration params, local configurations, and permitted configurations in parallel + serverValues = (await GlobalConfigSettingsApi().getRegistrationParams()) + .cast(); + localConfigurations = + (await GlobalConfigSettingsApi().getLocalConfigurations()) + .cast(); + permittedConfigurations = + (await GlobalConfigSettingsApi().getPermittedConfigurationNames()) + .cast(); + + for (var key in serverValues!.keys) { + _controllers[key]?.dispose(); + _controllers[key] = TextEditingController( + text: _getLocalValue(key) == '-' ? '' : _getLocalValue(key), + ); + } + + setState(() { + isLoading = false; + }); + } catch (e) { + setState(() { + errorMessage = e.toString(); + isLoading = false; + }); + } + } + + @override + void dispose() { + for (var controller in _controllers.values) { + controller.dispose(); + } + super.dispose(); + } + + // Update _updateLocalValue to also update the controller's text if needed: + void _updateLocalValue(String key, String value) { + setState(() { + if (value.isEmpty) { + localValues.remove(key); + } else { + localValues[key] = value; + } + }); + } + + bool _isConfigurationPermitted(String configName) { + return permittedConfigurations.contains(configName); + } + + String _getLocalValue(String key) { + // First check if user has modified it in this session + if (localValues.containsKey(key)) { + return localValues[key]!; + } + // Then check if there's a saved local configuration + if (localConfigurations.containsKey(key)) { + return localConfigurations[key]!; + } + // Return empty if no local value + return ''; + } + + bool _hasChanges() { + if (localValues.isEmpty) { + return false; + } + + for (String key in localValues.keys) { + final String localValue = localValues[key]!; + final String? previousLocal = localConfigurations[key]; + + if (localValue.isEmpty) { + if (previousLocal != null) { + return true; + } + final String serverValue = serverValues?[key]?.toString() ?? ''; + if (serverValue.isNotEmpty) { + return true; + } + continue; + } + + if (previousLocal == null || previousLocal != localValue) { + return true; + } + } + + return false; + } + + void _onSaveChanges() { + if (!_hasChanges()) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('No changes to save')), + ); + return; + } + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Submit Changes'), + content: SizedBox( + width: 250, + height: 20, + child: Center( + child: Text('${localValues.length} configuration will be updated.'), + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(AppLocalizations.of(context)!.cancel), + ), + ElevatedButton( + onPressed: () async { + Navigator.pop(context); + await _saveChanges(); + }, + child: Text(AppLocalizations.of(context)!.confirm), + ), + ], + ), + ); + } + + Future _saveChanges() async { + try { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CircularProgressIndicator(), + SizedBox(height: 16), + Text('Saving configuration changes...'), + ], + ), + ), + ); + + // Save configuration changes + await GlobalConfigSettingsApi().modifyConfigurations(localValues); + + // Update local configurations with the saved values + setState(() { + localConfigurations.addAll(localValues); + localValues.clear(); + }); + + // Hide loading indicator + if (mounted) { + Navigator.of(context).pop(); + } + + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Configuration saved successfully. Restarting app...'), + duration: Duration(seconds: 2), + ), + ); + + // Wait a moment for the user to see the message, then restart the app + await Future.delayed(const Duration(seconds: 2)); + + // Restart the app to apply configuration changes + Restart.restartApp(); + } catch (e) { + if (mounted) { + Navigator.of(context).pop(); + } + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error saving changes: $e')), + ); + } + } + + List _getConfigurations() { + if (serverValues == null) return []; + + List globalConfigItems = []; + + for (String key in serverValues!.keys) { + String serverValue = serverValues![key]?.toString() ?? '-'; + String localValue = _getLocalValue(key); + bool isEditable = _isConfigurationPermitted(key); + bool isModified = localValues.containsKey(key); + + GlobalConfigItem item = GlobalConfigItem( + key: key, + serverValue: serverValue, + localValue: localValue, + editable: isEditable, + isModified: isModified, + ); + globalConfigItems.add(item); + } + + return globalConfigItems; + } + + @override + Widget build(BuildContext context) { + final heading = widget.settings.label?[widget.selectedLan] ?? + widget.settings.label?['eng'] ?? + (widget.settings.label?.values.first ?? 'Unknown'); + + return Scaffold( + body: Card( + margin: const EdgeInsets.all(5), + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.only(left: 12.0, right: 8.0), + child: Text( + heading, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + ), + const SizedBox(height: 12), + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: Colors.blue[50], + ), + child: Column( + children: [ + Row( + children: [ + Expanded( + flex: 2, + child: Text( + AppLocalizations.of(context)!.key, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + ), + Expanded( + flex: 1, + child: Text( + AppLocalizations.of(context)!.server_value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + ), + Expanded( + flex: 1, + child: Text( + AppLocalizations.of(context)!.local_value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ], + ), + ), + Expanded( + child: _buildContent(), + ), + ], + ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, + floatingActionButton: SafeArea( + top: false, + child: Padding( + padding: const EdgeInsets.only(right: 16), + child: ElevatedButton( + onPressed: _onSaveChanges, + style: ElevatedButton.styleFrom( + backgroundColor: solidPrimary, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric( + vertical: 18, + horizontal: 56, + ), + elevation: 4, + ), + child: Text(AppLocalizations.of(context)!.submit), + ), + ), + ), + ); + } + + Widget _buildContent() { + if (isLoading) { + return const Center(child: CircularProgressIndicator()); + } + if (errorMessage != null) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.error, size: 64, color: Colors.red[300]), + const SizedBox(height: 16), + Text( + 'Error: $errorMessage', + style: TextStyle(color: Colors.red[700]), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: _loadInitialData, + child: Text(AppLocalizations.of(context)!.retry), + ), + ], + ), + ); + } + if (serverValues == null || serverValues!.isEmpty) { + return Center( + child: Text( + AppLocalizations.of(context)!.no_configuration_parameters_found)); + } + + final configs = _getConfigurations(); + if (configs.isEmpty) { + return Center( + child: Text(AppLocalizations.of(context)!.no_configurations_found), + ); + } + + return SizedBox( + width: double.infinity, + child: ListView.separated( + padding: const EdgeInsets.only(top: 10, bottom: 90), + itemCount: configs.length, + separatorBuilder: (_, __) => + Divider(height: 1, color: Colors.grey[300]), + itemBuilder: (context, index) { + final config = configs[index]; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded( + flex: 2, + child: Text( + config.key, + style: TextStyle( + fontSize: 12, + fontWeight: config.isModified + ? FontWeight.bold + : FontWeight.normal, + color: config.isModified ? Colors.blue : Colors.black, + ), + ), + ), + const SizedBox(width: 10), + Expanded( + flex: 1, + child: Text( + config.serverValue, + style: const TextStyle( + fontSize: 12, + ), + ), + ), + Expanded( + flex: 1, + child: _buildEditableCell(config), + ), + ], + ), + ); + }, + ), + ); + } + + // Builds either an editable TextField or read-only text based on config permissions + Widget _buildEditableCell(GlobalConfigItem config) { + if (config.editable) { + return _buildEditableTextField(config); + } else { + return _buildReadOnlyText(config); + } + } + + // Builds an editable TextField for permitted configurations + Widget _buildEditableTextField(GlobalConfigItem config) { + final controller = _controllers[config.key]!; + return TextField( + controller: controller, + onChanged: (newValue) => _updateLocalValue(config.key, newValue), + style: TextStyle( + color: config.isModified ? Colors.blue : Colors.black, + fontSize: 12, + fontWeight: config.isModified ? FontWeight.bold : FontWeight.normal, + ), + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(4), + borderSide: BorderSide( + color: config.isModified ? Colors.blue : Colors.grey, + ), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + isDense: true, + hintText: config.serverValue, + ), + ); + } + + // Builds read-only styled text for non-permitted configurations + Widget _buildReadOnlyText(GlobalConfigItem config) { + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey[300]!), + borderRadius: BorderRadius.circular(4), + color: Colors.grey[100], + ), + child: Text( + config.localValue.isEmpty ? '-' : config.localValue, + style: TextStyle( + color: config.isModified ? Colors.blue : Colors.grey[600], + fontSize: 12, + fontWeight: config.isModified ? FontWeight.bold : FontWeight.normal, + ), + ), + ); + } +} + +class GlobalConfigItem { + final String key; + final String serverValue; + final String localValue; + final bool editable; + final bool isModified; + + GlobalConfigItem({ + required this.key, + required this.serverValue, + required this.localValue, + required this.editable, + required this.isModified, + }); +} diff --git a/lib/ui/settings/widgets/scheduled_jobs_settings.dart b/lib/ui/settings/widgets/scheduled_jobs_settings.dart new file mode 100644 index 000000000..e253d2707 --- /dev/null +++ b/lib/ui/settings/widgets/scheduled_jobs_settings.dart @@ -0,0 +1,262 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import 'package:registration_client/platform_spi/sync_response_service.dart'; +import 'package:registration_client/utils/sync_job_def.dart'; + +import '../../../provider/sync_provider.dart'; + +// Dart equivalent of the Java PACKET_JOBS constant +const List PACKET_JOBS = ['RPS_J00006', 'RSJ_J00014', 'PUJ_J00017', 'PVS_J00015']; + +class ScheduledJobsSettings extends StatelessWidget { + const ScheduledJobsSettings({ + super.key, + required this.jobJsonList, + this.onRefreshJob, + }); + + final List jobJsonList; + final void Function(String jobId)? onRefreshJob; + + @override + Widget build(BuildContext context) { + final jobs = jobJsonList + .whereType() + .map((e) => _ScheduledJob.fromJson(json.decode(e) as Map)) + .toList(); + + final bottomInset = MediaQuery.of(context).padding.bottom; + final bottomSpacer = bottomInset + kBottomNavigationBarHeight + 230; + final mediaSize = MediaQuery.of(context).size; + final bool isTablet = mediaSize.shortestSide <= 450; + final int crossAxisCount = isTablet ? 1 : 2; + final double childAspectRatio = MediaQuery.of(context).orientation == Orientation.landscape ? 5 : 3; + return SafeArea( + top: false, + bottom: true, + child: CustomScrollView( + slivers: [ + SliverPadding( + padding: const EdgeInsets.all(12.0), + sliver: SliverToBoxAdapter( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.of(context)!.scheduled_job_settings, + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w600), + ), + const SizedBox(height: 12), + ], + ), + ), + ), + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: crossAxisCount, + mainAxisSpacing: 8, + crossAxisSpacing: 12, + childAspectRatio: childAspectRatio, + ), + delegate: SliverChildBuilderDelegate( + (context, index) { + final job = jobs[index]; + return _JobCard(job: job, onRefresh: onRefreshJob); + }, + childCount: jobs.length, + ), + ), + ), + SliverToBoxAdapter(child: SizedBox(height: bottomSpacer)), + ], + ), + ); + } +} + +class _JobCard extends StatefulWidget { + const _JobCard({required this.job, this.onRefresh}); + final _ScheduledJob job; + final void Function(String jobId)? onRefresh; + + @override + State<_JobCard> createState() => _JobCardState(); +} + +class _JobCardState extends State<_JobCard> { + String? _lastSync; + String? _nextSync; + late SyncProvider syncProvider; + + + @override + void initState() { + super.initState(); + syncProvider = Provider.of(context, listen: false); + _loadLastSyncTime(); // Fetch last sync when widget loads + _loadNextSyncTime(); + } + + Future _loadLastSyncTime() async { + if (widget.job.id != null && widget.job.id!.isNotEmpty) { + final value = await syncProvider.getLastSyncTimeByJobId(widget.job.id!); + setState(() => _lastSync = value ?? '-'); + if (widget.job.apiName == "masterSyncJob" && _lastSync == "NA") { + _lastSync = formatDate(syncProvider.lastSuccessfulSyncTime); + setState(() {}); + } + } else { + setState(() => _lastSync = '-'); + } + } + + String formatDate(String dateString) { + // Parse the input UTC date string + DateTime dateTime = DateTime.parse(dateString).toLocal(); // Convert to local time + + // Format the date + String formattedDate = DateFormat("yyyy-MMM-dd HH:mm:ss").format(dateTime); + + return formattedDate; + } + + Future _loadNextSyncTime() async { + if (widget.job.id != null && widget.job.id!.isNotEmpty) { + final value = await syncProvider.getNextSyncTimeByJobId(widget.job.id!); + setState(() => _nextSync = value ?? '-'); + } else { + setState(() => _nextSync = '-'); + } + } + + Future _triggerJobSync(BuildContext context, String? apiName, String? jobId) async { + if (apiName == null || apiName.isEmpty) return; + final service = SyncResponseService(); + + try { + switch (apiName) { + case 'masterSyncJob': + await service.getMasterDataSync(true, jobId ?? ''); + break; + case 'keyPolicySyncJob': + await service.getPolicyKeySync(true, jobId ?? ''); + break; + case 'preRegistrationDataSyncJob': + await service.getPreRegIds(jobId ?? ''); + break; + case 'userDetailServiceJob': + await service.getUserDetailsSync(true, jobId ?? ''); + break; + case 'syncCertificateJob': + await service.getCaCertsSync(true, jobId ?? ''); + break; + case 'publicKeySyncJob': + await service.getKernelCertsSync(true, jobId ?? ''); + break; + case 'deleteAuditLogsJob': + await service.deleteAuditLogs(jobId ?? ''); + break; + case 'synchConfigDataJob': + await service.getGlobalParamsSync(true, jobId ?? ''); + break; + case 'preRegistrationPacketDeletionJob': + await service.deletePreRegRecords(jobId ?? ''); + break; + default: + debugPrint('No handler for sync job: $apiName'); + return; + } + + // Refresh last and next sync time after successful sync + await _loadLastSyncTime(); + await _loadNextSyncTime(); + + } catch (e) { + debugPrint('Sync failed for ${widget.job.id}: $e'); + } + } + + + @override + Widget build(BuildContext context) { + final job = widget.job; + + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: const Color(0xFFE5EBFA), width: 0.8), + boxShadow: const [BoxShadow(color: Color(0x11000000), blurRadius: 4, offset: Offset(0, 2))], + ), + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(job.name ?? job.apiName ?? 'Unknown Job', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)), + const SizedBox(height: 8), + _kv('Next Run', _nextSync ?? '-'), + _kv('Last Sync', _lastSync ?? '-'), + const SizedBox(height: 6), + _kv('Cron Expression', job.syncFreq ?? '-'), + ], + ), + ), + if (!PACKET_JOBS.contains(job.id)) + SizedBox( + width: 40, + child: OutlinedButton( + onPressed: () => _triggerJobSync(context, job.apiName, job.id), + style: OutlinedButton.styleFrom( + padding: EdgeInsets.zero, + minimumSize: const Size(40, 40), + side: const BorderSide(color: Color(0xFF2A4EA7)), + ), + child: const Icon(Icons.sync, size: 20, color: Color(0xFF2A4EA7)), + ), + ), + ], + ), + ), + ); + } + + Widget _kv(String k, String v) => Row( + children: [ + Text(k, style: const TextStyle(fontSize: 12, color: Colors.black54)), + const SizedBox(width: 8), + Flexible( + child: Text(v, style: const TextStyle(fontSize: 12, color: Colors.black87))), + ], + ); +} + +class _ScheduledJob { + _ScheduledJob({required this.syncJobDef, this.nextRun, this.lastRun}); + + final SyncJobDef syncJobDef; + final String? nextRun; + final String? lastRun; + + // Convenience getters to maintain compatibility + String? get id => syncJobDef.id; + String? get name => syncJobDef.name; + String? get apiName => syncJobDef.apiName; + String? get syncFreq => syncJobDef.syncFreq; + + factory _ScheduledJob.fromJson(Map json) => _ScheduledJob( + syncJobDef: SyncJobDef.fromJson(json), + nextRun: null, + lastRun: null, + ); +} \ No newline at end of file diff --git a/lib/ui/widgets/language_component.dart b/lib/ui/widgets/language_component.dart index 40dcb9cc5..53d9a9822 100644 --- a/lib/ui/widgets/language_component.dart +++ b/lib/ui/widgets/language_component.dart @@ -39,7 +39,9 @@ class _LanguageComponentState extends State { widget.onTap(); } }, + child: Container( + height: 60.h, padding: EdgeInsets.only( left: 25.w, right: 25.w, @@ -50,15 +52,15 @@ class _LanguageComponentState extends State { color: widget.isFreezed ? appButtonBorderText : widget.isSelected - ? appButtonBorderText - : Colors.transparent, + ? appButtonBorderText + : Colors.transparent, border: Border.all( width: 1, color: widget.isDisabled ? appBlackShade3 : widget.isSelected - ? appButtonBorderText - : languageSelectedColor, + ? appButtonBorderText + : languageSelectedColor, ), borderRadius: const BorderRadius.all( Radius.circular(36), @@ -69,24 +71,27 @@ class _LanguageComponentState extends State { children: [ widget.isFreezed || widget.isSelected ? Icon( - Icons.check, - color: widget.isFreezed ? appGreyShade : appWhite, - ) + Icons.check, + color: widget.isFreezed ? appGreyShade : appWhite, + ) : const SizedBox(), SizedBox( width: widget.isFreezed || widget.isSelected ? 15.02 : 0, ), - Text( - widget.title, - style: TextStyle( - fontSize: widget.isMobile && !isMobileSize ? 24 : 16, - color: widget.isDisabled - ? appBlackShade3 - : widget.isFreezed - ? appGreyShade - : widget.isSelected - ? appWhite - : appBlackShade1, + Directionality( + textDirection: TextDirection.ltr, + child: Text( + widget.title, + style: TextStyle( + fontSize: widget.isMobile && !isMobileSize ? 24 : 16, + color: widget.isDisabled + ? appBlackShade3 + : widget.isFreezed + ? appGreyShade + : widget.isSelected + ? appWhite + : appBlackShade1, + ), ), ), ], diff --git a/lib/utils/inactivity_tracker.dart b/lib/utils/inactivity_tracker.dart new file mode 100644 index 000000000..3c1bfee90 --- /dev/null +++ b/lib/utils/inactivity_tracker.dart @@ -0,0 +1,295 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import 'package:registration_client/provider/global_provider.dart'; +import 'package:registration_client/provider/sync_provider.dart'; + +import '../main.dart'; + +class InactivityTracker extends StatefulWidget { + final Widget child; + final Duration timeout; // inactivity before warning + final Duration gracePeriod; // countdown inside dialog + final bool isUserLoggedIn; + final Future Function() onTimeout; + + const InactivityTracker({ + Key? key, + required this.child, + required this.timeout, + required this.gracePeriod, + required this.isUserLoggedIn, + required this.onTimeout, + }) : super(key: key); + + @override + State createState() => _InactivityTrackerState(); +} + +class _InactivityTrackerState extends State with WidgetsBindingObserver { + Timer? _inactivityTimer; + Timer? _logoutTicker; + bool _warningShown = false; + final ValueNotifier _countdown = ValueNotifier(0); + DateTime? _pausedAt; + bool _dialogOpen = false; + late GlobalProvider globalProvider; + + @override + void initState() { + super.initState(); + globalProvider = Provider.of(context, listen: false); + WidgetsBinding.instance.addObserver(this); + _startInactivityTimer(); + } + + @override + void didUpdateWidget(covariant InactivityTracker oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.isUserLoggedIn != widget.isUserLoggedIn || + oldWidget.timeout != widget.timeout || + oldWidget.gracePeriod != widget.gracePeriod) { + _resetAllTimers(); + } + } + + @override + void dispose() { + _countdown.dispose(); + _cancelAllTimers(); + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + void _startInactivityTimer() { + if (!widget.isUserLoggedIn) return; + _inactivityTimer = Timer(widget.timeout, _showWarningDialog); + } + + void _resetAllTimers() { + _inactivityTimer?.cancel(); + _logoutTicker?.cancel(); + _warningShown = false; + _startInactivityTimer(); + } + + void _cancelAllTimers() { + _inactivityTimer?.cancel(); + _logoutTicker?.cancel(); + } + + void _showWarningDialog() async { + if (!widget.isUserLoggedIn) return; + + // Prevent warning dialog if sync is in progress + final syncProvider = Provider.of(context, listen: false); + final bool isAnySyncInProgress = syncProvider.isSyncInProgress || + syncProvider.isSyncAndUploadInProgress; + if (isAnySyncInProgress) { + _resetAllTimers(); + return; + } + + _warningShown = true; + _dialogOpen = true; + _countdown.value = widget.gracePeriod.inSeconds; + + _logoutTicker = Timer.periodic(const Duration(seconds: 1), (t) { + _countdown.value -= 1; + if (_countdown.value <= 0) { + t.cancel(); + // Clear registration process data on auto logout + globalProvider.clearRegistrationProcessData(); + widget.onTimeout(); + } + }); + + final dialogCtx = rootNavigatorKey.currentContext!; + final loc = AppLocalizations.of(dialogCtx)!; + + showDialog( + context: dialogCtx, + barrierDismissible: false, + builder: (_) => _IdleWarningDialog( + countdown: _countdown, + onStayLoggedIn: () { + _logoutTicker?.cancel(); + _warningShown = false; + _dialogOpen = false; + Navigator.of(dialogCtx, rootNavigator: true).pop(); + _resetAllTimers(); + }, + onLogOut: () async { + Navigator.of(dialogCtx, rootNavigator: true).pop(); + _dialogOpen = false; + await widget.onTimeout(); + globalProvider.clearRegistrationProcessData(); + }, + loc: loc, + ), + ); + } + + void _onUserInteraction() { + if (!widget.isUserLoggedIn) return; + if (_dialogOpen) return; + if (_warningShown) { + _logoutTicker?.cancel(); + Navigator.of(rootNavigatorKey.currentContext!, rootNavigator: true).maybePop(); + _warningShown = false; + } + _resetAllTimers(); + } + + void didChangeAppLifecycleState(AppLifecycleState state) { + switch (state) { + case AppLifecycleState.paused: + case AppLifecycleState.inactive: + case AppLifecycleState.detached: + _pausedAt = DateTime.now(); + _cancelAllTimers(); + break; + + case AppLifecycleState.resumed: + if (!widget.isUserLoggedIn) return; + + if (_pausedAt != null) { + final away = DateTime.now().difference(_pausedAt!); + + if (away >= widget.timeout + widget.gracePeriod) { + widget.onTimeout(); + _pausedAt = null; + return; + } + + if (away >= widget.timeout) { + final remainingGrace = widget.gracePeriod - (away - widget.timeout); + _countdown.value = remainingGrace.inSeconds; + _showWarningDialog(); + _pausedAt = null; + return; + } + } + + _pausedAt = null; + _resetAllTimers(); + break; + } + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: _onUserInteraction, + onPanDown: (_) => _onUserInteraction(), + onScaleStart: (_) => _onUserInteraction(), + child: widget.child, + ); + } +} + +class _IdleWarningDialog extends StatelessWidget { + const _IdleWarningDialog({ + required this.countdown, + required this.onStayLoggedIn, + required this.onLogOut, + required this.loc, + }); + + final ValueNotifier countdown; + final VoidCallback onStayLoggedIn; + final Future Function() onLogOut; + final AppLocalizations loc; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return Dialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 420), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + padding: const EdgeInsets.fromLTRB(24, 20, 24, 20), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide(color: Color(0xFFE5EBFA), width: 1), + ), + ), + child: Text( + loc.inactive_logout_heading, + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + fontSize: 22, + ), + ), + ), + Container( + padding: const EdgeInsets.fromLTRB(24, 20, 24, 20), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide(color: Color(0xFFE5EBFA), width: 1), + ), + ), + child: ValueListenableBuilder( + valueListenable: countdown, + builder: (_, secs, __) { + // Decide which unit to show + final String timeLeft = secs >= 60 + ? '${(secs / 60).ceil()}${loc.minutes}' // e.g. “2 minutes” + : '$secs${loc.seconds}'; // e.g. “45 seconds” + + // Replace $TIMER in the localized string with timeLeft, and style it + final parts = loc.inactive_logout_description.split('\$TIMER'); + return RichText( + text: TextSpan( + style: theme.textTheme.bodyMedium, + children: [ + TextSpan(text: parts[0]), + TextSpan( + text: timeLeft, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + if (parts.length > 1) TextSpan(text: parts[1]), + ], + ), + ); + }, + ), + ), + const SizedBox(height: 24), + Padding( + padding: const EdgeInsets.fromLTRB(24, 0, 24, 24), + child: Row( + children: [ + Expanded( + child: OutlinedButton( + onPressed: onLogOut, + child: Text(loc.logout_button), // You can localize this too + ), + ), + const SizedBox(width: 16), + Expanded( + child: ElevatedButton( + onPressed: onStayLoggedIn, + child: Text(loc.stay_logged_in_button), // And this + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/utils/sync_job_def.dart b/lib/utils/sync_job_def.dart new file mode 100644 index 000000000..977b5feea --- /dev/null +++ b/lib/utils/sync_job_def.dart @@ -0,0 +1,45 @@ +/* + * Copyright (c) Modular Open Source Identity Platform + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * +*/ + +/// Model for SyncJobDef entity from Android +class SyncJobDef { + final String? id; + final String? name; + final String? apiName; + final String? parentSyncJobId; + final String? syncFreq; + final String? lockDuration; + final String? langCode; + final bool? isDeleted; + final bool? isActive; + + SyncJobDef({ + this.id, + this.name, + this.apiName, + this.parentSyncJobId, + this.syncFreq, + this.lockDuration, + this.langCode, + this.isDeleted, + this.isActive, + }); + + factory SyncJobDef.fromJson(Map json) { + return SyncJobDef( + id: json['id'] as String?, + name: json['name'] as String?, + apiName: json['apiName'] as String?, + parentSyncJobId: json['parentSyncJobId'] as String?, + syncFreq: json['syncFreq'] as String?, + lockDuration: json['lockDuration'] as String?, + langCode: json['langCode'] as String?, + isDeleted: json['isDeleted'] as bool?, + isActive: json['isActive'] as bool?, + ); + } +} diff --git a/pigeon.sh b/pigeon.sh index 2d1e3f65f..3d9033560 100644 --- a/pigeon.sh +++ b/pigeon.sh @@ -56,4 +56,7 @@ dart run pigeon --input pigeon/transliteration.dart --dart_out lib/pigeon/transl dart run pigeon --input pigeon/document_category.dart --dart_out lib/pigeon/document_category_pigeon.dart --objc_header_out ios/Runner/pigeon.h --objc_source_out ios/Runner/pigeon.m --java_out ./android/app/src/main/java/io/mosip/registration_client/model/DocumentCategoryPigeon.java --java_package "io.mosip.registration_client.model" # Generate dash board files -dart run pigeon --input pigeon/dash_board.dart --dart_out lib/pigeon/dash_board_pigeon.dart --objc_header_out ios/Runner/pigeon.h --objc_source_out ios/Runner/pigeon.m --java_out ./android/app/src/main/java/io/mosip/registration_client/model/DashBoardPigeon.java --java_package "io.mosip.registration_client.model" \ No newline at end of file +dart run pigeon --input pigeon/dash_board.dart --dart_out lib/pigeon/dash_board_pigeon.dart --objc_header_out ios/Runner/pigeon.h --objc_source_out ios/Runner/pigeon.m --java_out ./android/app/src/main/java/io/mosip/registration_client/model/DashBoardPigeon.java --java_package "io.mosip.registration_client.model" + +# Generate global config settings files +dart run pigeon --input pigeon/global_config_settings.dart --dart_out lib/pigeon/global_config_settings_pigeon.dart --objc_header_out ios/Runner/pigeon.h --objc_source_out ios/Runner/pigeon.m --java_out ./android/app/src/main/java/io/mosip/registration_client/model/GlobalConfigSettingsPigeon.java --java_package "io.mosip.registration_client.model" \ No newline at end of file diff --git a/pigeon/auth_response.dart b/pigeon/auth_response.dart index 2c143718f..1c1587ffa 100644 --- a/pigeon/auth_response.dart +++ b/pigeon/auth_response.dart @@ -34,4 +34,10 @@ abstract class AuthResponseApi { String stopAlarmService(); @async String forgotPasswordUrl(); + @async + String getIdleTime(); + @async + String getAutoLogoutPopupTimeout(); + @async + List getRolesByUserId(String userId); } diff --git a/pigeon/biometrics.dart b/pigeon/biometrics.dart index 7a6bce709..e844ebbe8 100644 --- a/pigeon/biometrics.dart +++ b/pigeon/biometrics.dart @@ -1,5 +1,18 @@ import 'package:pigeon/pigeon.dart'; + +class DeviceInfo { + final String deviceName; + final String deviceId; + final String connectionStatus; + + DeviceInfo({ + required this.deviceName, + required this.deviceId, + required this.connectionStatus, + }); +} + @HostApi() abstract class BiometricsApi { @async @@ -47,4 +60,7 @@ abstract class BiometricsApi { @async bool conditionalBioAttributeValidation(String fieldId, String expression); + + @async + List getListOfDevices(String modality); } diff --git a/pigeon/global_config_settings.dart b/pigeon/global_config_settings.dart new file mode 100644 index 000000000..0c2e8f379 --- /dev/null +++ b/pigeon/global_config_settings.dart @@ -0,0 +1,15 @@ +import 'package:pigeon/pigeon.dart'; + +@HostApi() +abstract class GlobalConfigSettingsApi { + @async + Map getRegistrationParams(); + @async + Map getLocalConfigurations(); + @async + List getPermittedConfigurationNames(); + @async + void modifyConfigurations(Map localPreferences); + @async + String getGpsEnableFlag(); +} \ No newline at end of file diff --git a/pigeon/master_data_sync.dart b/pigeon/master_data_sync.dart index 95790a32a..1ed9e9830 100644 --- a/pigeon/master_data_sync.dart +++ b/pigeon/master_data_sync.dart @@ -20,22 +20,22 @@ abstract class SyncApi { SyncTime getLastSyncTime(); @async - Sync getPolicyKeySync(bool isManualSync); + Sync getPolicyKeySync(bool isManualSync, String jobId); @async - Sync getGlobalParamsSync(bool isManualSync); + Sync getGlobalParamsSync(bool isManualSync, String jobId); @async - Sync getUserDetailsSync(bool isManualSync); + Sync getUserDetailsSync(bool isManualSync, String jobId); @async Sync getIDSchemaSync(bool isManualSync); @async - Sync getMasterDataSync(bool isManualSync); + Sync getMasterDataSync(bool isManualSync, String jobId); @async - Sync getCaCertsSync(bool isManualSync); + Sync getCaCertsSync(bool isManualSync, String jobId); @async String batchJob(); @@ -44,9 +44,19 @@ abstract class SyncApi { List getReasonList(String langCode); @async - String getPreRegIds(); + String getPreRegIds(String jobId); @async - Sync getKernelCertsSync(bool isManualSync); + Sync getKernelCertsSync(bool isManualSync, String jobId); @async bool getSyncAndUploadInProgressStatus(); + @async + bool deleteAuditLogs(String jobId); + @async + bool deletePreRegRecords(String jobId); + @async + String getLastSyncTimeByJobId(String jobId); + @async + String getNextSyncTimeByJobId(String jobId); + @async + List getActiveSyncJobs(); } diff --git a/pigeon/process_spec.dart b/pigeon/process_spec.dart index e7793466b..76848983e 100644 --- a/pigeon/process_spec.dart +++ b/pigeon/process_spec.dart @@ -21,5 +21,8 @@ abstract class ProcessSpecApi { int getMinLanguageCount(); @async - int getMaxLanguageCount(); + int getMaxLanguageCount(); + + @async + List getSettingSpec(); } \ No newline at end of file diff --git a/pigeon/registration_data.dart b/pigeon/registration_data.dart index 4b541ddc0..0632b3379 100644 --- a/pigeon/registration_data.dart +++ b/pigeon/registration_data.dart @@ -30,4 +30,10 @@ abstract class RegistrationDataApi { @async void setApplicationId(String applicationId); + + @async + void setAdditionalReqId(String additionalReqId); + + @async + void setMachineLocation(double latitude, double longitude); } diff --git a/pubspec.lock b/pubspec.lock index 06ec3955a..a012c5009 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: _fe_analyzer_shared sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "61.0.0" analyzer: @@ -14,31 +14,23 @@ packages: description: name: analyzer sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "5.13.0" - android_alarm_manager_plus: - dependency: "direct main" - description: - name: android_alarm_manager_plus - sha256: "84720c8ad2758aabfbeafd24a8c355d8c8dd3aa52b01eaf3bb827c7210f61a91" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" - source: hosted - version: "3.0.4" archive: dependency: transitive description: name: archive - sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "4.0.7" args: dependency: transitive description: name: args sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.5.0" async: @@ -46,31 +38,31 @@ packages: description: name: async sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.11.0" barcode: dependency: transitive description: name: barcode - sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "7b6729c37e3b7f34233e2318d866e8c48ddb46c1f7ad01ff7bb2a8de1da2b9f4" + url: "https://pub.dev" source: hosted - version: "2.2.8" + version: "2.2.9" bidi: dependency: transitive description: name: bidi - sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "77f475165e94b261745cf1032c751e2032b8ed92ccb2bf5716036db79320637d" + url: "https://pub.dev" source: hosted - version: "2.0.10" + version: "2.0.13" boolean_selector: dependency: transitive description: name: boolean_selector sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.1.1" build: @@ -78,7 +70,7 @@ packages: description: name: build sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.4.1" build_config: @@ -86,63 +78,63 @@ packages: description: name: build_config sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + url: "https://pub.dev" source: hosted - version: "2.4.11" + version: "2.4.9" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + url: "https://pub.dev" source: hosted - version: "7.3.1" + version: "7.3.0" built_collection: dependency: transitive description: name: built_collection sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "5.1.1" built_value: dependency: transitive description: name: built_value - sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: ba95c961bafcd8686d1cf63be864eb59447e795e124d98d6a27d91fcd13602fb + url: "https://pub.dev" source: hosted - version: "8.9.2" + version: "8.11.1" characters: dependency: transitive description: name: characters sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.3.0" checked_yaml: @@ -150,7 +142,7 @@ packages: description: name: checked_yaml sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.0.3" cli_util: @@ -158,7 +150,7 @@ packages: description: name: cli_util sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.4.1" clock: @@ -166,7 +158,7 @@ packages: description: name: clock sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.1.1" code_builder: @@ -174,23 +166,23 @@ packages: description: name: code_builder sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "4.10.0" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.1" colorful_progress_indicators: dependency: "direct main" description: name: colorful_progress_indicators sha256: "5a37014775368b37ebfcd9a47e758785438b2bd7ccbf3d6ec8066cb37207d87f" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.0.2" connectivity_plus: @@ -198,7 +190,7 @@ packages: description: name: connectivity_plus sha256: "77a180d6938f78ca7d2382d2240eb626c0f6a735d0bfdce227d8ffb80f95c48b" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "4.0.2" connectivity_plus_platform_interface: @@ -206,7 +198,7 @@ packages: description: name: connectivity_plus_platform_interface sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.2.4" convert: @@ -214,23 +206,23 @@ packages: description: name: convert sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "3.1.1" cross_file: dependency: transitive description: name: cross_file - sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5" + url: "https://pub.dev" source: hosted - version: "0.3.4+2" + version: "0.3.3+7" crypto: dependency: transitive description: name: crypto sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "3.0.3" csslib: @@ -238,39 +230,39 @@ packages: description: name: csslib sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.17.3" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "1.0.6" dart_style: dependency: transitive description: name: dart_style sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.3.2" dbus: dependency: transitive description: name: dbus - sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.11" document_scanner: dependency: "direct main" description: name: document_scanner sha256: "6e0663f86cf7182a98d9a98f2dd8de82eeb3685dfa19282e5d3e1802f6b8b6e5" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.2.1" dotted_border: @@ -278,7 +270,7 @@ packages: description: name: dotted_border sha256: "108837e11848ca776c53b30bc870086f84b62ed6e01c503ed976e8f8c7df9c04" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.1.0" dropdown_button2: @@ -286,7 +278,7 @@ packages: description: name: dropdown_button2 sha256: b0fe8d49a030315e9eef6c7ac84ca964250155a6224d491c1365061bc974a9e1 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.3.9" fake_async: @@ -294,39 +286,39 @@ packages: description: name: fake_async sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.3.1" faker: dependency: "direct main" description: name: faker - sha256: "746e59f91d8b06a389e74cf76e909a05ed69c12691768e2f93557fdf29200fd0" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "544c34e9e1d322824156d5a8d451bc1bb778263b892aded24ec7ba77b0706624" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" ffi: dependency: transitive description: name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.0" file: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "6.1.4" file_picker: dependency: "direct main" description: name: file_picker sha256: be325344c1f3070354a1d84a231a1ba75ea85d413774ec4bdf444c023342e030 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "5.5.0" file_selector_linux: @@ -334,39 +326,39 @@ packages: description: name: file_selector_linux sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.9.2+1" file_selector_macos: dependency: transitive description: name: file_selector_macos - sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6 + url: "https://pub.dev" source: hosted - version: "0.9.4" + version: "0.9.3+3" file_selector_platform_interface: dependency: transitive description: name: file_selector_platform_interface sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.6.2" file_selector_windows: dependency: transitive description: name: file_selector_windows - sha256: "2ad726953f6e8affbc4df8dc78b77c3b4a060967a291e528ef72ae846c60fb69" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 + url: "https://pub.dev" source: hosted - version: "0.9.3+2" + version: "0.9.3+1" fixnum: dependency: transitive description: name: fixnum sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.1.0" flutter: @@ -379,7 +371,7 @@ packages: description: name: flutter_config sha256: a07e6156bb6e776e29c6357be433155acda87d1dab1a3f787a72091a1b71ffbf - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.0.2" flutter_driver: @@ -392,7 +384,7 @@ packages: description: name: flutter_html sha256: "02ad69e813ecfc0728a455e4bf892b9379983e050722b1dce00192ee2e41d1ee" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "3.0.0-beta.2" flutter_launcher_icons: @@ -400,7 +392,7 @@ packages: description: name: flutter_launcher_icons sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.13.1" flutter_lints: @@ -408,7 +400,7 @@ packages: description: name: flutter_lints sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.0.3" flutter_localizations: @@ -420,48 +412,48 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da + url: "https://pub.dev" source: hosted - version: "2.0.21" + version: "2.0.17" flutter_screenutil: dependency: "direct main" description: name: flutter_screenutil sha256: "8239210dd68bee6b0577aa4a090890342d04a136ce1c81f98ee513fc0ce891de" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "5.9.3" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage - sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" source: hosted - version: "9.2.2" + version: "9.2.4" flutter_secure_storage_linux: dependency: transitive description: name: flutter_secure_storage_linux - sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.3" flutter_secure_storage_macos: dependency: transitive description: name: flutter_secure_storage_macos - sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" flutter_secure_storage_platform_interface: dependency: transitive description: name: flutter_secure_storage_platform_interface sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.1.2" flutter_secure_storage_web: @@ -469,7 +461,7 @@ packages: description: name: flutter_secure_storage_web sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.2.1" flutter_secure_storage_windows: @@ -477,7 +469,7 @@ packages: description: name: flutter_secure_storage_windows sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "3.1.2" flutter_svg: @@ -485,7 +477,7 @@ packages: description: name: flutter_svg sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.0.9" flutter_test: @@ -503,7 +495,7 @@ packages: description: name: freezed sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.5.2" freezed_annotation: @@ -511,7 +503,7 @@ packages: description: name: freezed_annotation sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.4.4" frontend_server_client: @@ -519,7 +511,7 @@ packages: description: name: frontend_server_client sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "4.0.0" fuchsia_remote_debug_protocol: @@ -532,23 +524,23 @@ packages: description: name: glob sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.1.2" graphs: dependency: transitive description: name: graphs - sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.1" html: dependency: transitive description: name: html sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.15.4" http: @@ -556,7 +548,7 @@ packages: description: name: http sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.13.6" http_multi_server: @@ -564,7 +556,7 @@ packages: description: name: http_multi_server sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "3.2.1" http_parser: @@ -572,79 +564,55 @@ packages: description: name: http_parser sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "4.0.2" image: dependency: transitive description: name: image - sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" - source: hosted - version: "4.2.0" - image_cropper: - dependency: "direct main" - description: - name: image_cropper - sha256: "710ab4b7953e9ce1d27d833f741e5f8f3afb0b0ba3556dc0b844741b5f55c2b3" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" - source: hosted - version: "3.0.3" - image_cropper_for_web: - dependency: transitive - description: - name: image_cropper_for_web - sha256: "09e93a8ec0435adcaa23622ac090442872f18145d70b9ff605ffedcf97d56255" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" - source: hosted - version: "1.0.3" - image_cropper_platform_interface: - dependency: transitive - description: - name: image_cropper_platform_interface - sha256: "62349e3aab63873ea9b9ab9f69d036ab8a0d74b3004beec4303981386cb9273f" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" + url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "4.5.4" image_picker: dependency: "direct main" description: name: image_picker sha256: b6951e25b795d053a6ba03af5f710069c99349de9341af95155d52665cb4607c - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.8.9" image_picker_android: dependency: transitive description: name: image_picker_android - sha256: c0e72ecd170b00a5590bb71238d57dc8ad22ee14c60c6b0d1a4e05cafbc5db4b - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "39f2bfe497e495450c81abcd44b62f56c2a36a37a175da7d137b4454977b51b1" + url: "https://pub.dev" source: hosted - version: "0.8.12+11" + version: "0.8.9+3" image_picker_for_web: dependency: transitive description: name: image_picker_for_web sha256: "869fe8a64771b7afbc99fc433a5f7be2fea4d1cb3d7c11a48b6b579eb9c797f0" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.2.0" image_picker_ios: dependency: transitive description: name: image_picker_ios - sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: fadafce49e8569257a0cad56d24438a6fa1f0cbd7ee0af9b631f7492818a4ca3 + url: "https://pub.dev" source: hosted - version: "0.8.12" + version: "0.8.9+1" image_picker_linux: dependency: transitive description: name: image_picker_linux sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.2.1+1" image_picker_macos: @@ -652,39 +620,39 @@ packages: description: name: image_picker_macos sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.2.1+1" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface - sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b + url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.9.3" image_picker_windows: dependency: transitive description: name: image_picker_windows sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.2.1+1" intl: dependency: "direct main" description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.18.0" io: dependency: transitive description: name: io sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.0.4" js: @@ -692,7 +660,7 @@ packages: description: name: js sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.6.7" json_annotation: @@ -700,7 +668,7 @@ packages: description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "4.9.0" json_serializable: @@ -708,39 +676,15 @@ packages: description: name: json_serializable sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "6.8.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" - source: hosted - version: "10.0.4" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" - source: hosted - version: "3.0.3" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" - source: hosted - version: "3.0.1" lints: dependency: transitive description: name: lints sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.1.1" list_counter: @@ -748,7 +692,7 @@ packages: description: name: list_counter sha256: c447ae3dfcd1c55f0152867090e67e219d42fe6d4f2807db4bbe8b8d69912237 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.0.2" logging: @@ -756,71 +700,71 @@ packages: description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.2.0" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.2.0" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.9.1" mime: dependency: transitive description: name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.4" mockito: dependency: "direct main" description: name: mockito - sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "7d5b53bcd556c1bc7ffbe4e4d5a19c3e112b7e925e9e172dd7c6ad0630812616" + url: "https://pub.dev" source: hosted - version: "5.4.4" + version: "5.4.2" native_image_cropper: dependency: "direct main" description: name: native_image_cropper - sha256: "50b7b53d2757ee0b898d3d46a9a1ffa6cf5406641a9d3904be386e1ec6fda75f" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: d9cb7c7e5904e5dc28c37ebf0d68d2fed2d46b161960e7770c078e98c80e66b0 + url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.6.0" native_image_cropper_android: dependency: transitive description: name: native_image_cropper_android - sha256: "27ef9de059d195398795f862c2dd594c1a77a13168f7a97b9cbf70fe2e7e4677" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "77106960beee19fd080dc8bd609709da44e13824175cd6c1d330a8b7178a152e" + url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.3.1" native_image_cropper_ios: dependency: transitive description: name: native_image_cropper_ios sha256: "4c40ffc8ea1e52078384bba6adfae8a90bef064ebbb25a0d52211b828b3895aa" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.2.0" native_image_cropper_macos: @@ -828,7 +772,7 @@ packages: description: name: native_image_cropper_macos sha256: f3815264a7755047f0b38413c69f5b76ed4ffdd7af28edeacfc134b08b1d5a74 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.2.0" native_image_cropper_platform_interface: @@ -836,7 +780,7 @@ packages: description: name: native_image_cropper_platform_interface sha256: "3ac1484f83abd62be7ff1d74c475137c59008aa01e671d8678d5e25c46c98d59" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.2.0" native_image_cropper_web: @@ -844,7 +788,7 @@ packages: description: name: native_image_cropper_web sha256: "4767ca067ff2b5d1d3dd02c14eb00124da5d203ff917c9528b35f294bcf9227e" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.2.0" nested: @@ -852,7 +796,7 @@ packages: description: name: nested sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.0.0" nm: @@ -860,7 +804,7 @@ packages: description: name: nm sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.5.0" package_config: @@ -868,23 +812,23 @@ packages: description: name: package_config sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.1.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.8.3" path_drawing: dependency: transitive description: name: path_drawing sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.0.1" path_parsing: @@ -892,39 +836,39 @@ packages: description: name: path_parsing sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.0.1" path_provider: dependency: "direct main" description: name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" + url: "https://pub.dev" source: hosted - version: "2.2.9" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.3.2" path_provider_linux: dependency: transitive description: name: path_provider_linux sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.2.1" path_provider_platform_interface: @@ -932,47 +876,47 @@ packages: description: name: path_provider_platform_interface sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.1.2" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.2.1" pdf: dependency: transitive description: name: pdf - sha256: "05df53f8791587402493ac97b9869d3824eccbc77d97855f4545cf72df3cae07" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "28eacad99bffcce2e05bba24e50153890ad0255294f4dd78a17075a2ba5c8416" + url: "https://pub.dev" source: hosted - version: "3.11.1" + version: "3.11.3" pdf_widget_wrapper: dependency: transitive description: name: pdf_widget_wrapper - sha256: c930860d987213a3d58c7ec3b7ecf8085c3897f773e8dc23da9cae60a5d6d0f5 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: e9d31fd7782ce28ae346b127ea7d1cd748d799bddee379f31191693610e23749 + url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.1" percent_indicator: dependency: "direct main" description: name: percent_indicator - sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "157d29133bbc6ecb11f923d36e7960a96a3f28837549a20b65e5135729f0f9fd" + url: "https://pub.dev" source: hosted - version: "4.2.3" + version: "4.2.5" permission_handler: dependency: "direct main" description: name: permission_handler sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "10.4.5" permission_handler_android: @@ -980,7 +924,7 @@ packages: description: name: permission_handler_android sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "10.3.6" permission_handler_apple: @@ -988,7 +932,7 @@ packages: description: name: permission_handler_apple sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "9.1.4" permission_handler_platform_interface: @@ -996,7 +940,7 @@ packages: description: name: permission_handler_platform_interface sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "3.12.0" permission_handler_windows: @@ -1004,39 +948,39 @@ packages: description: name: permission_handler_windows sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.1.3" petitparser: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "5.4.0" pigeon: dependency: "direct main" description: name: pigeon sha256: d1ab184e028cfecd957d4de34f705b4f84b98661e6a4be1d9cac9dade278b937 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "10.1.6" platform: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.1.8" pool: @@ -1044,39 +988,47 @@ packages: description: name: pool sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.5.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" printing: dependency: "direct main" description: name: printing - sha256: de1889f30b34029fc46e5de6a9841498850b23d32942a9ee810ca36b0cb1b234 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "1c99cab90ebcc1fff65831d264627d5b529359d563e53f33ab9b8117f2d280bc" + url: "https://pub.dev" source: hosted - version: "5.13.2" + version: "5.12.0" process: dependency: transitive description: name: process - sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "4.2.4" provider: dependency: "direct main" description: name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "6.1.5+1" pub_semver: dependency: transitive description: name: pub_semver sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.1.4" pubspec_parse: @@ -1084,113 +1036,113 @@ packages: description: name: pubspec_parse sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.3.0" qr: dependency: transitive description: name: qr - sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" + url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.1" qr_code_scanner: dependency: "direct main" description: name: qr_code_scanner sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.0.1" quiver: dependency: transitive description: name: quiver - sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" responsive_grid_list: dependency: "direct main" description: name: responsive_grid_list sha256: e6cd1754240795cb8b08a4520c9eb5d28856ecf71de5fe6e64535a68c0913563 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.4.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: c272f9cabca5a81adc9b0894381e9c1def363e980f960fa903c604c471b22f68 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.2.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "041be4d9d2dc6079cf342bc8b761b03787e3b71192d658220a56cac9c04a0294" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.2.1" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.3.5" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.3.2" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.3.2" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "59dc807b94d29d52ddbb1b3c0d3b9d0a67fc535a64e62a5542c8db0513fcb6c2" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.2.1" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.3.2" shelf: dependency: transitive description: name: shelf sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.4.1" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -1201,7 +1153,7 @@ packages: description: name: source_gen sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.5.0" source_helper: @@ -1209,39 +1161,39 @@ packages: description: name: source_helper sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.3.4" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" stream_transform: dependency: transitive description: name: stream_transform sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.1.0" string_scanner: @@ -1249,7 +1201,7 @@ packages: description: name: string_scanner sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.2.0" sync_http: @@ -1257,7 +1209,7 @@ packages: description: name: sync_http sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.3.1" term_glyph: @@ -1265,23 +1217,23 @@ packages: description: name: term_glyph sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.5.1" timing: dependency: transitive description: name: timing sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.0.1" typed_data: @@ -1289,79 +1241,79 @@ packages: description: name: typed_data sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.3.2" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.1.14" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "94d8ad05f44c6d4e2ffe5567ab4d741b82d62e3c8e288cc1fcea45965edf47c9" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 + url: "https://pub.dev" source: hosted - version: "6.3.8" + version: "6.3.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" + url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "6.2.4" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 + url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.1.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 + url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f + url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.1" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: ba140138558fcc3eead51a1c42e92a9fb074a1b1149ed3c73e66035b2ccd94f2 + url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.0.19" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 + url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.1" vector_graphics: dependency: transitive description: name: vector_graphics sha256: "4ac59808bbfca6da38c99f415ff2d3a5d7ca0a6b4809c71d9cf30fba5daf9752" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.1.10+1" vector_graphics_codec: @@ -1369,7 +1321,7 @@ packages: description: name: vector_graphics_codec sha256: f3247e7ab0ec77dc759263e68394990edc608fb2b480b80db8aa86ed09279e33 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.1.10+1" vector_graphics_compiler: @@ -1377,7 +1329,7 @@ packages: description: name: vector_graphics_compiler sha256: "18489bdd8850de3dd7ca8a34e0c446f719ec63e2bab2e7a8cc66a9028dd76c5a" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.1.10+1" vector_math: @@ -1385,63 +1337,47 @@ packages: description: name: vector_math sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.1.4" vm_service: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe + url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "11.3.0" watcher: dependency: transitive description: name: watcher sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" - source: hosted - version: "1.0.0" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" - source: hosted - version: "0.1.6" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "2.4.0" webdriver: dependency: transitive description: name: webdriver - sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.2" webview_flutter: dependency: transitive description: name: webview_flutter sha256: "392c1d83b70fe2495de3ea2c84531268d5b8de2de3f01086a53334d8b6030a88" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "3.0.4" webview_flutter_android: @@ -1449,7 +1385,7 @@ packages: description: name: webview_flutter_android sha256: "8b3b2450e98876c70bfcead876d9390573b34b9418c19e28168b74f6cb252dbd" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.10.4" webview_flutter_platform_interface: @@ -1457,7 +1393,7 @@ packages: description: name: webview_flutter_platform_interface sha256: "812165e4e34ca677bdfbfa58c01e33b27fd03ab5fa75b70832d4b7d4ca1fa8cf" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.9.5" webview_flutter_plus: @@ -1465,7 +1401,7 @@ packages: description: name: webview_flutter_plus sha256: bea8756ae096529254725def7c4a633851a785c7d49206e0817125ab02b14307 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "0.3.0+2" webview_flutter_wkwebview: @@ -1473,41 +1409,41 @@ packages: description: name: webview_flutter_wkwebview sha256: a5364369c758892aa487cbf59ea41d9edd10f9d9baf06a94e80f1bd1b4c7bbc0 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "2.9.5" win32: dependency: transitive description: name: win32 - sha256: "015002c060f1ae9f41a818f2d5640389cc05283e368be19dc8d77cecb43c40c9" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + url: "https://pub.dev" source: hosted - version: "5.5.3" + version: "5.0.9" xdg_directories: dependency: transitive description: name: xdg_directories sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "1.0.4" xml: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.3.0" yaml: dependency: transitive description: name: yaml sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://infyartifactory.jfrog.io/artifactory/api/pub/pub.dev-remote/" + url: "https://pub.dev" source: hosted version: "3.1.2" sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.22.0" + dart: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 73071b751..66a428ffc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,7 +30,6 @@ environment: dependencies: flutter: sdk: flutter - freezed: ^2.1.0+1 freezed_annotation: ^2.1.0 json_annotation: ^4.8.0 flutter_svg: ^2.0.9 @@ -70,12 +69,15 @@ dependencies: flutter_config: ^2.0.2 native_image_cropper: ^0.6.0 qr_code_scanner: ^1.0.1 + restart_app: ^1.2.1 + geolocator: ^10.1.0 dev_dependencies: flutter_test: sdk: flutter flutter_driver: sdk: flutter + freezed: ^2.1.0+1 build_runner: ^2.3.3 json_serializable: ^6.1.5 flutter_launcher_icons: "^0.13.1" diff --git a/ui-test/pom.xml b/ui-test/pom.xml index dc38bc51d..b420ca776 100644 --- a/ui-test/pom.xml +++ b/ui-test/pom.xml @@ -1,4 +1,6 @@ - + 4.0.0 io.mosip.regclient uitest-regclient @@ -25,7 +27,6 @@ ${maven.compiler.target} - org.apache.maven.plugins maven-jar-plugin @@ -61,7 +62,8 @@ - + regclient.utils.TestRunner @@ -77,11 +79,32 @@ java-client 8.6.0 + + org.asynchttpclient + async-http-client + 2.12.4 + + + org.apache.commons + commons-lang3 + 3.18.0 + + + io.netty + netty-codec-http + 4.1.125.Final + com.googlecode.json-simple json-simple 1.1.1 + + com.github.javafaker + javafaker + 1.0.2 + + org.testng testng @@ -90,12 +113,12 @@ com.fasterxml.jackson.core jackson-databind - 2.13.3 + 2.13.4.1 org.keycloak keycloak-admin-client - 17.0.1 + 23.0.4 com.fasterxml.jackson.core @@ -110,24 +133,18 @@ org.json json - 20230227 + 20231013 commons-io commons-io - 2.13.0 + 2.14.0 com.aventstack extentreports 5.1.0 - - junit - junit - 4.13.2 - test - io.rest-assured rest-assured @@ -136,58 +153,84 @@ org.apache.logging.log4j log4j-api - 2.11.1 + 2.23.1 org.slf4j - slf4j-log4j12 - 1.6.2 + slf4j-api + 2.0.13 - javax.ws.rs - javax.ws.rs-api - 2.1.1 + org.apache.logging.log4j + log4j-slf4j2-impl + 2.23.1 org.yaml snakeyaml - 1.29 + 2.0 commons-beanutils commons-beanutils - 1.9.4 + 1.11.0 org.apache.logging.log4j log4j-core - 2.11.1 + 2.23.1 org.jboss.resteasy resteasy-multipart-provider - 3.15.0.Final + 4.7.8.Final + + + com.sun.mail + jakarta.mail + 2.0.2 + org.jboss.resteasy resteasy-client - 3.15.0.Final + 4.5.8.SP1 org.jboss.resteasy resteasy-client-microprofile - 3.15.0.Final + 4.5.8.SP1 org.jboss.resteasy resteasy-jackson2-provider 3.15.0.Final + + com.google.guava + guava + 32.0.0-jre + org.jboss.resteasy resteasy-jaxb-provider 3.15.0.Final + + io.mosip.testrig.apitest.commons + apitest-commons + 1.3.4 + + + org.slf4j + slf4j-api + + + com.google.guava + guava + + + diff --git a/ui-test/src/main/java/regclient/BaseTest/AndroidBaseTest.java b/ui-test/src/main/java/regclient/BaseTest/AndroidBaseTest.java index 51272ff11..604337fa4 100644 --- a/ui-test/src/main/java/regclient/BaseTest/AndroidBaseTest.java +++ b/ui-test/src/main/java/regclient/BaseTest/AndroidBaseTest.java @@ -11,6 +11,7 @@ public class AndroidBaseTest extends BaseTest { @BeforeMethod(alwaysRun = true) public void setup() { try { + DriverManager.startAppiumServer(); this.driver = DriverManager.getDriver(); } catch (Exception e) { throw new RuntimeException(); diff --git a/ui-test/src/main/java/regclient/androidTestCases/AddMachineDetails.java b/ui-test/src/main/java/regclient/androidTestCases/AddMachineDetails.java index f160e8389..cb1bed1da 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/AddMachineDetails.java +++ b/ui-test/src/main/java/regclient/androidTestCases/AddMachineDetails.java @@ -1,7 +1,6 @@ package regclient.androidTestCases; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.IOException; @@ -10,7 +9,7 @@ import regclient.BaseTest.AndroidBaseTest; import regclient.api.AdminTestUtil; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.KeycloakUserManager; import regclient.page.BasePage; import regclient.pages.english.LoginPageEnglish; @@ -27,7 +26,7 @@ public void addMachineDetails() throws UnsupportedFlavorException, IOException { loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); // assertTrue(loginPage.isMachineNotFoundMessageDisplayed(), "verify if the machine not found message displayed"); diff --git a/ui-test/src/main/java/regclient/androidTestCases/IntialLunch.java b/ui-test/src/main/java/regclient/androidTestCases/IntialLunch.java index 7c4b04eac..7d4fa844f 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/IntialLunch.java +++ b/ui-test/src/main/java/regclient/androidTestCases/IntialLunch.java @@ -5,7 +5,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.KeycloakUserManager; import regclient.page.BasePage; import regclient.pages.english.LoginPageEnglish; @@ -26,7 +26,7 @@ public void initallLaunch() { assertTrue(loginPage.isBackButtonDisplayed(), "Verify if back button is displayed"); assertTrue(loginPage.isForgetOptionDisplayed(), "Verify if forget password option is displayed"); assertTrue(loginPage.isPasswordHeaderDisplayed(), "Verify if the password input box header displayed"); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); assertTrue(loginPage.isLoginButtonEnabled(), "Verify if the login button enabled"); loginPage.clickOnloginButton(); diff --git a/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationAdult.java b/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationAdult.java index ee6b10092..2d3e505dc 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationAdult.java +++ b/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationAdult.java @@ -8,7 +8,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.FetchUiSpec; import regclient.api.KeycloakUserManager; import regclient.page.AcknowledgementPage; @@ -174,7 +174,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { @@ -457,7 +457,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ } assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Verify if authentication details page is displayed"); authenticationPage.enterUserName(KeycloakUserManager.moduleSpecificUser); - authenticationPage.enterPassword(ConfigManager.getIAMUsersPassword()); + authenticationPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); authenticationPage.clickOnAuthenticatenButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { acknowledgementPage=new AcknowledgementPageEnglish(driver); @@ -539,8 +539,16 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ pendingApproval.clickOnSubmitButton(); assertTrue(pendingApproval.isSupervisorAuthenticationTitleDisplayed(), "Verify if Supervisor Authentication page displayed"); + + pendingApproval.clickOnSubmitButton(); + assertTrue(pendingApproval.isInvalidemptyUsernameSumbitButtonEnbled(), "Verify if error empty username submit button enabled"); + + pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser+"123"); + + assertTrue(pendingApproval.isInvalidUsernameMessageDisplayed(), "Verify if invalid username messgae is displayed"); pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser); - pendingApproval.enterPassword(ConfigManager.getIAMUsersPassword()); + + pendingApproval.enterPassword(ArcConfigManager.getIAMUsersPassword()); pendingApproval.clickOnSubmitButton(); pendingApproval.clickOnBackButton(); assertTrue(operationalTaskPage.isApplicationUploadTitleDisplayed(), "Verify if application upload tite displayed"); @@ -610,7 +618,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(loginPage.isLoginPageLoaded(),"verify if login page is displayeded in Selected language"); } - @Test + public void newRegistrationAdultUploadMultipleDoccuments(){ FetchUiSpec.getUiSpec("newProcess"); FetchUiSpec.getBiometricDetails("individualBiometrics"); @@ -655,7 +663,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { registrationTasksPage=new RegistrationTasksPageEnglish(driver); @@ -1131,8 +1139,11 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ authenticationPage=new AuthenticationPageArabic(driver); } assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Verify if authentication details page is displayed"); + authenticationPage.clickOnAuthenticatenButton(); + assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Username/password required error should be displayed"); + authenticationPage.clickOnAuthenticatenButton(); authenticationPage.enterUserName(KeycloakUserManager.moduleSpecificUser); - authenticationPage.enterPassword(ConfigManager.getIAMUsersPassword()); + authenticationPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); authenticationPage.clickOnAuthenticatenButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { acknowledgementPage=new AcknowledgementPageEnglish(driver); @@ -1226,7 +1237,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(pendingApproval.isSupervisorAuthenticationTitleDisplayed(), "Verify if Supervisor Authentication page displayed"); pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser); - pendingApproval.enterPassword(ConfigManager.getIAMUsersPassword()); + pendingApproval.enterPassword(ArcConfigManager.getIAMUsersPassword()); pendingApproval.clickOnSubmitButton(); pendingApproval.clickOnBackButton(); assertTrue(operationalTaskPage.isApplicationUploadTitleDisplayed(), "Verify if application upload tite displayed"); diff --git a/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationAdultException.java b/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationAdultException.java index c305b101f..9fe3dad1d 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationAdultException.java +++ b/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationAdultException.java @@ -7,7 +7,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.FetchUiSpec; import regclient.api.KeycloakUserManager; import regclient.page.AcknowledgementPage; @@ -165,7 +165,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { registrationTasksPage=new RegistrationTasksPageEnglish(driver); @@ -502,7 +502,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ } assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Verify if authentication details page is displayed"); authenticationPage.enterUserName(KeycloakUserManager.moduleSpecificUser); - authenticationPage.enterPassword(ConfigManager.getIAMUsersPassword()); + authenticationPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); authenticationPage.clickOnAuthenticatenButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { acknowledgementPage=new AcknowledgementPageEnglish(driver); @@ -585,7 +585,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(pendingApproval.isSupervisorAuthenticationTitleDisplayed(), "Verify if Supervisor Authentication page displayed"); pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser); - pendingApproval.enterPassword(ConfigManager.getIAMUsersPassword()); + pendingApproval.enterPassword(ArcConfigManager.getIAMUsersPassword()); pendingApproval.clickOnSubmitButton(); pendingApproval.clickOnBackButton(); diff --git a/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationInfant.java b/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationInfant.java index c7524f302..453e9f155 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationInfant.java +++ b/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationInfant.java @@ -7,7 +7,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.FetchUiSpec; import regclient.api.KeycloakUserManager; import regclient.page.AcknowledgementPage; @@ -173,7 +173,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { registrationTasksPage=new RegistrationTasksPageEnglish(driver); @@ -468,7 +468,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ } assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Verify if authentication details page is displayed"); authenticationPage.enterUserName(KeycloakUserManager.moduleSpecificUser); - authenticationPage.enterPassword(ConfigManager.getIAMUsersPassword()); + authenticationPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); authenticationPage.clickOnAuthenticatenButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { acknowledgementPage=new AcknowledgementPageEnglish(driver); @@ -550,7 +550,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(pendingApproval.isSupervisorAuthenticationTitleDisplayed(), "Verify if Supervisor Authentication page displayed"); pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser); - pendingApproval.enterPassword(ConfigManager.getIAMUsersPassword()); + pendingApproval.enterPassword(ArcConfigManager.getIAMUsersPassword()); pendingApproval.clickOnSubmitButton(); pendingApproval.clickOnBackButton(); diff --git a/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationMinor.java b/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationMinor.java index a07a3ecf1..b010180f5 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationMinor.java +++ b/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationMinor.java @@ -7,7 +7,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.FetchUiSpec; import regclient.api.KeycloakUserManager; import regclient.page.AcknowledgementPage; @@ -172,7 +172,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { registrationTasksPage=new RegistrationTasksPageEnglish(driver); @@ -520,7 +520,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ } assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Verify if authentication details page is displayed"); authenticationPage.enterUserName(KeycloakUserManager.moduleSpecificUser); - authenticationPage.enterPassword(ConfigManager.getIAMUsersPassword()); + authenticationPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); authenticationPage.clickOnAuthenticatenButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { acknowledgementPage=new AcknowledgementPageEnglish(driver); @@ -607,7 +607,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(pendingApproval.isSupervisorAuthenticationTitleDisplayed(), "Verify if Supervisor Authentication page displayed"); pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser); - pendingApproval.enterPassword(ConfigManager.getIAMUsersPassword()); + pendingApproval.enterPassword(ArcConfigManager.getIAMUsersPassword()); pendingApproval.clickOnSubmitButton(); pendingApproval.clickOnBackButton(); assertTrue(operationalTaskPage.isApplicationUploadTitleDisplayed(), "Verify if application upload tite displayed"); diff --git a/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationMinorException.java b/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationMinorException.java index b3c7d08bc..ed44ca8e1 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationMinorException.java +++ b/ui-test/src/main/java/regclient/androidTestCases/NewRegistrationMinorException.java @@ -10,7 +10,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.FetchUiSpec; import regclient.api.KeycloakUserManager; import regclient.page.AcknowledgementPage; @@ -175,7 +175,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { registrationTasksPage=new RegistrationTasksPageEnglish(driver); @@ -359,7 +359,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ applicantBiometricsPage.clickOnMarkExceptionButton(); assertTrue(applicantBiometricsPage.isExceptionTypeTitleDisplyed(),"Verify if applicant biometric mark exception is displayed"); - applicantBiometricsPage.clickOnZoomButton(); + applicantBiometricsPage.clickOnZoomButton(); assertTrue(applicantBiometricsPage.isRightHandScanTitleDisplyed(),"Verify if applicant right hand scan is displayed"); applicantBiometricsPage.markFourFingureExceptionThenRemoveOne(); @@ -382,7 +382,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ applicantBiometricsPage.clickOnMarkExceptionButton(); assertTrue(applicantBiometricsPage.isExceptionTypeTitleDisplyed(),"Verify if applicant biometric mark exception is displayed"); - applicantBiometricsPage.clickOnZoomButton(); + applicantBiometricsPage.clickOnZoomButton(); assertTrue(applicantBiometricsPage.isLeftHandScanTitleDisplyed(),"Verify if applicant right hand scan is displayed"); applicantBiometricsPage.markOneFingureException(); @@ -583,7 +583,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ } assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Verify if authentication details page is displayed"); authenticationPage.enterUserName(KeycloakUserManager.moduleSpecificUser); - authenticationPage.enterPassword(ConfigManager.getIAMUsersPassword()); + authenticationPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); authenticationPage.clickOnAuthenticatenButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { acknowledgementPage=new AcknowledgementPageEnglish(driver); @@ -665,7 +665,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(pendingApproval.isSupervisorAuthenticationTitleDisplayed(), "Verify if Supervisor Authentication page displayed"); pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser); - pendingApproval.enterPassword(ConfigManager.getIAMUsersPassword()); + pendingApproval.enterPassword(ArcConfigManager.getIAMUsersPassword()); pendingApproval.clickOnSubmitButton(); pendingApproval.clickOnBackButton(); assertTrue(operationalTaskPage.isApplicationUploadTitleDisplayed(), "Verify if application upload tite displayed"); diff --git a/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUINUpdateDemographicDetails.java b/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUINUpdateDemographicDetails.java index f1ecf30b8..41c756254 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUINUpdateDemographicDetails.java +++ b/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUINUpdateDemographicDetails.java @@ -7,7 +7,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.FetchUiSpec; import regclient.api.KeycloakUserManager; import regclient.page.AcknowledgementPage; @@ -172,7 +172,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { registrationTasksPage=new RegistrationTasksPageEnglish(driver); @@ -439,7 +439,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ } assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Verify if authentication details page is displayed"); authenticationPage.enterUserName(KeycloakUserManager.moduleSpecificUser); - authenticationPage.enterPassword(ConfigManager.getIAMUsersPassword()); + authenticationPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); authenticationPage.clickOnAuthenticatenButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { acknowledgementPage=new AcknowledgementPageEnglish(driver); @@ -522,7 +522,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(pendingApproval.isSupervisorAuthenticationTitleDisplayed(), "Verify if Supervisor Authentication page displayed"); pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser); - pendingApproval.enterPassword(ConfigManager.getIAMUsersPassword()); + pendingApproval.enterPassword(ArcConfigManager.getIAMUsersPassword()); pendingApproval.clickOnSubmitButton(); pendingApproval.clickOnBackButton(); assertTrue(operationalTaskPage.isApplicationUploadTitleDisplayed(), "Verify if application upload tite displayed"); diff --git a/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinInfant.java b/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinInfant.java index 830d00757..a5b1414af 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinInfant.java +++ b/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinInfant.java @@ -7,7 +7,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.FetchUiSpec; import regclient.api.KeycloakUserManager; import regclient.page.AcknowledgementPage; @@ -180,7 +180,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { registrationTasksPage=new RegistrationTasksPageEnglish(driver); @@ -471,7 +471,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ } assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Verify if authentication details page is displayed"); authenticationPage.enterUserName(KeycloakUserManager.moduleSpecificUser); - authenticationPage.enterPassword(ConfigManager.getIAMUsersPassword()); + authenticationPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); authenticationPage.clickOnAuthenticatenButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { acknowledgementPage=new AcknowledgementPageEnglish(driver); @@ -558,7 +558,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(pendingApproval.isSupervisorAuthenticationTitleDisplayed(), "Verify if Supervisor Authentication page displayed"); pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser); - pendingApproval.enterPassword(ConfigManager.getIAMUsersPassword()); + pendingApproval.enterPassword(ArcConfigManager.getIAMUsersPassword()); pendingApproval.clickOnSubmitButton(); pendingApproval.clickOnBackButton(); diff --git a/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinMinor.java b/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinMinor.java index af11816bb..9ba1728b9 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinMinor.java +++ b/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinMinor.java @@ -7,7 +7,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.FetchUiSpec; import regclient.api.KeycloakUserManager; import regclient.page.AcknowledgementPage; @@ -181,7 +181,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { registrationTasksPage=new RegistrationTasksPageEnglish(driver); @@ -342,7 +342,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("tam")){ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ biometricDetailsPage=new BiometricDetailsPageArabic(driver); } - assertTrue(biometricDetailsPage.isBiometricDetailsPageDisplayed(),"Verify if biometric details page is displayed"); + assertTrue(biometricDetailsPage.isBiometricDetailsPageDisplayed(),"Verify if biometric details page is displayed"); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { applicantBiometricsPage=new ApplicantBiometricsPageEnglish(driver); } @@ -573,7 +573,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ } assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Verify if authentication details page is displayed"); authenticationPage.enterUserName(KeycloakUserManager.moduleSpecificUser); - authenticationPage.enterPassword(ConfigManager.getIAMUsersPassword()); + authenticationPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); authenticationPage.clickOnAuthenticatenButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { acknowledgementPage=new AcknowledgementPageEnglish(driver); @@ -657,7 +657,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(pendingApproval.isSupervisorAuthenticationTitleDisplayed(), "Verify if Supervisor Authentication page displayed"); pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser); - pendingApproval.enterPassword(ConfigManager.getIAMUsersPassword()); + pendingApproval.enterPassword(ArcConfigManager.getIAMUsersPassword()); pendingApproval.clickOnSubmitButton(); pendingApproval.clickOnBackButton(); assertTrue(operationalTaskPage.isApplicationUploadTitleDisplayed(), "Verify if application upload tite displayed"); diff --git a/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinUpdateBiometrics.java b/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinUpdateBiometrics.java index ac1fc8abe..9a93261e0 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinUpdateBiometrics.java +++ b/ui-test/src/main/java/regclient/androidTestCases/UpdateMyUinUpdateBiometrics.java @@ -7,7 +7,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.FetchUiSpec; import regclient.api.KeycloakUserManager; import regclient.page.AcknowledgementPage; @@ -172,7 +172,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ loginPage.enterUserName(KeycloakUserManager.moduleSpecificUser); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); loginPage.clickOnloginButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { registrationTasksPage=new RegistrationTasksPageEnglish(driver); @@ -469,7 +469,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ } assertTrue(authenticationPage.isAuthenticationPageDisplayed(),"Verify if authentication details page is displayed"); authenticationPage.enterUserName(KeycloakUserManager.moduleSpecificUser); - authenticationPage.enterPassword(ConfigManager.getIAMUsersPassword()); + authenticationPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); authenticationPage.clickOnAuthenticatenButton(); if(TestDataReader.readData("language").equalsIgnoreCase("eng")) { acknowledgementPage=new AcknowledgementPageEnglish(driver); @@ -552,7 +552,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(pendingApproval.isSupervisorAuthenticationTitleDisplayed(), "Verify if Supervisor Authentication page displayed"); pendingApproval.enterUserName(KeycloakUserManager.moduleSpecificUser); - pendingApproval.enterPassword(ConfigManager.getIAMUsersPassword()); + pendingApproval.enterPassword(ArcConfigManager.getIAMUsersPassword()); pendingApproval.clickOnSubmitButton(); pendingApproval.clickOnBackButton(); assertTrue(operationalTaskPage.isApplicationUploadTitleDisplayed(), "Verify if application upload tite displayed"); diff --git a/ui-test/src/main/java/regclient/androidTestCases/logintest.java b/ui-test/src/main/java/regclient/androidTestCases/logintest.java index b94abdcdc..ef6a3713d 100644 --- a/ui-test/src/main/java/regclient/androidTestCases/logintest.java +++ b/ui-test/src/main/java/regclient/androidTestCases/logintest.java @@ -6,7 +6,7 @@ import org.testng.annotations.Test; import regclient.BaseTest.AndroidBaseTest; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; import regclient.api.FetchUiSpec; import regclient.api.KeycloakUserManager; import regclient.page.BasePage; @@ -119,11 +119,11 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ //assertFalse(loginPage.isLoginButtonEnabled(),"verify if the login button is disable without entering password"); assertTrue(loginPage.isBackButtonDisplayed(), "Verify if back button is displayed"); - assertTrue(loginPage.isForgetOptionDisplayed(), "Verify if forget password option is displayed"); + //assertTrue(loginPage.isForgetOptionDisplayed(), "Verify if forget password option is displayed"); assertTrue(loginPage.isPasswordHeaderDisplayed(), "Verify if the password input box header displayed"); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()+"123"); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()+"123"); assertTrue(loginPage.isLoginButtonEnabled(),"Verify if the login button enabled"); loginPage.clickOnloginButton(); @@ -137,7 +137,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(loginPage.isNextButtonEnabled(),"verify if the next button enabled"); loginPage.clickOnNextButton(); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); assertTrue(loginPage.isLoginButtonEnabled(),"Verify if the login button enabled"); loginPage.clickOnloginButton(); @@ -285,7 +285,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(loginPage.isBackButtonDisplayed(), "Verify if back button is displayed"); assertTrue(loginPage.isForgetOptionDisplayed(), "Verify if forget password option is displayed"); assertTrue(loginPage.isPasswordHeaderDisplayed(), "Verify if the password input box header displayed"); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); assertTrue(loginPage.isLoginButtonEnabled(), "Verify if the login button enabled"); loginPage.clickOnloginButton(); @@ -341,7 +341,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ supervisorBiometricVerificationpage.markOneEyeException(); supervisorBiometricVerificationpage.clickOnExceptionTypeTemporaryButton(); - assertTrue(supervisorBiometricVerificationpage.isCommentHeaderDisplyed(),"Verify if Comments header is displayed"); + assertTrue(supervisorBiometricVerificationpage.isCommentHeaderDisplyed(),"Verify if Comments header is displayed"); supervisorBiometricVerificationpage.clickOnIrisScanTitle(); supervisorBiometricVerificationpage.clickOnScanButton(); @@ -485,7 +485,7 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(loginPage.isBackButtonDisplayed(), "Verify if back button is displayed"); assertTrue(loginPage.isForgetOptionDisplayed(), "Verify if forget password option is displayed"); assertTrue(loginPage.isPasswordHeaderDisplayed(), "Verify if the password input box header displayed"); - loginPage.enterPassword(ConfigManager.getIAMUsersPassword()); + loginPage.enterPassword(ArcConfigManager.getIAMUsersPassword()); assertTrue(loginPage.isLoginButtonEnabled(), "Verify if the login button enabled"); loginPage.clickOnloginButton(); @@ -560,9 +560,9 @@ else if(TestDataReader.readData("language").equalsIgnoreCase("ara")){ assertTrue(UpdateOperatorBiometricspage.isExceptionTypeTitleDisplyed(),"Verify if mark exception is displayed"); UpdateOperatorBiometricspage.markOneEyeException(); - UpdateOperatorBiometricspage.clickOnExceptionTypeTemporaryButton(); + UpdateOperatorBiometricspage.clickOnExceptionTypeTemporaryButton(); - assertTrue(UpdateOperatorBiometricspage.isCommentHeaderDisplyed(),"Verify if Comments header is displayed"); + assertTrue(UpdateOperatorBiometricspage.isCommentHeaderDisplyed(),"Verify if Comments header is displayed"); UpdateOperatorBiometricspage.clickOnIrisScanTitle(); UpdateOperatorBiometricspage.clickOnScanButton(); diff --git a/ui-test/src/main/java/regclient/api/AdminTestUtil.java b/ui-test/src/main/java/regclient/api/AdminTestUtil.java index a8640bfc8..3dacfd6b7 100644 --- a/ui-test/src/main/java/regclient/api/AdminTestUtil.java +++ b/ui-test/src/main/java/regclient/api/AdminTestUtil.java @@ -1,11 +1,15 @@ package regclient.api; +import io.mosip.testrig.apirig.testrunner.OTPListener; import io.restassured.response.Response; +import regclient.utils.TestDataReader; import org.apache.log4j.Logger; import org.json.JSONArray; import org.json.JSONObject; +import com.github.javafaker.Faker; + import javax.ws.rs.core.MediaType; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -87,7 +91,7 @@ public static String creteaMachine(String signPublicKey, String publicKey ,Strin public static void initialize() { if (!initialized) { - ConfigManager.init(); + ArcConfigManager.init(); BaseTestCase.initialize(); KeycloakUserManager.createUsers(); mapUserToZone(BaseTestCase.currentModule +"-"+propsKernel.getProperty("iam-users-to-create"),propsKernel.getProperty("zone")); @@ -123,9 +127,9 @@ public static void mapUserToZone(String user, String zone) { public static List getLanguageList() { logger.info("We have created a Config Manager. Beginning to read properties!"); - environment = ConfigManager.getiam_apienvuser(); + environment = ArcConfigManager.getiam_apienvuser(); logger.info("Environemnt is ==== :" + environment); - ApplnURI = ConfigManager.getiam_apiinternalendpoint(); + ApplnURI = ArcConfigManager.getiam_apiinternalendpoint(); logger.info("Application URI ======" + ApplnURI); logger.info("Configs from properties file are set."); @@ -177,6 +181,187 @@ public static void mapUserToCenter(String user, String center) { System.out.println("responseJson = " + responseJson); } + public static void sendOtp(String userId, String langCode) { + String token = kernelAuthLib.getTokenByRole("globalAdmin"); + JSONObject requestJson = new JSONObject(); + requestJson.put("id", "mosip.pre-registration.login.sendotp"); + requestJson.put("version", "1.0"); + requestJson.put("requesttime", AdminTestUtil.generateCurrentUTCTimeStamp()); + JSONObject innerRequest = new JSONObject(); + innerRequest.put("userId", userId); + innerRequest.put("langCode", langCode); + requestJson.put("request", innerRequest); + Response response = RestClient.postRequestWithCookie( + BaseTestCase.ApplnURI +"/preregistration/v1/login/sendOtp/langcode", + requestJson.toString(), + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_JSON, + BaseTestCase.COOKIENAME, token + ); + JSONObject responseJson = new JSONObject(response.asString()); + System.out.println("Response JSON = " + responseJson); + if (responseJson.has("response") + && responseJson.getJSONObject("response").has("status") + && "success".equalsIgnoreCase(responseJson.getJSONObject("response").getString("status"))) { + System.out.println("✅ OTP request sent successfully."); + } else { + throw new RuntimeException("❌ OTP request failed. Response: " + responseJson.toString()); + } + } + + public static Response validateOtp(String userId, String otp) { + String token = kernelAuthLib.getTokenByRole("globalAdmin"); + JSONObject requestJson = new JSONObject(); + requestJson.put("id", "mosip.pre-registration.login.useridotp"); + requestJson.put("version", "1.0"); + requestJson.put("requesttime", AdminTestUtil.generateCurrentUTCTimeStamp()); + + JSONObject innerRequest = new JSONObject(); + innerRequest.put("userId", userId); + innerRequest.put("otp", otp); + requestJson.put("request", innerRequest); + Response response = RestClient.postRequestWithCookie( + BaseTestCase.ApplnURI + "/preregistration/v1/login/validateOtp", + requestJson.toString(), + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_JSON, + BaseTestCase.COOKIENAME, token + ); + + if (response == null) { + throw new RuntimeException("No response from validateOtp API"); + } + + int statusCode = response.getStatusCode(); + if (statusCode < 200 || statusCode >= 300) { + throw new RuntimeException("HTTP error from validateOtp API. Status: " + statusCode + " Body: " + response.asString()); + } + + JSONObject responseJson; + try { + responseJson = new JSONObject(response.asString()); + } catch (Exception e) { + throw new RuntimeException("Invalid JSON returned from validateOtp API: " + response.asString(), e); + } + if (responseJson.has("response") + && responseJson.getJSONObject("response").has("status") + && "success".equalsIgnoreCase(responseJson.getJSONObject("response").getString("status"))) { + System.out.println("✅ OTP validation successful."); + return response; + } else { + throw new RuntimeException("❌ OTP validation failed. Response: " + responseJson.toString()); + } + } + + public static String createPreRegistration(String userId) { + String token = kernelAuthLib.getTokenByRole("globalAdmin"); + + // Generate base prereg JSON (as String) + String baseJson = io.mosip.testrig.apirig.utils.AdminTestUtil.generateHbsForPrereg(false); + + // Parse into JSONObject + JSONObject requestJson = new JSONObject(baseJson); + + // Faker for dynamic values + Faker faker = new Faker(); + String randomName = faker.name().fullName(); + String address1 = faker.address().streetAddress(); + String address2 = faker.address().secondaryAddress(); + String address3 = faker.address().buildingNumber(); + String phone = faker.number().digits(10); + String email = faker.internet().emailAddress(); + String dob = "1990/01/01"; // Or faker.date().birthday() + + // Navigate to identity node + JSONObject request = requestJson.getJSONObject("request"); + JSONObject identity = request.getJSONObject("demographicDetails").getJSONObject("identity"); + + // Fill only if empty or placeholder + if (identity.optJSONArray("fullName") != null) { + for (int i = 0; i < identity.getJSONArray("fullName").length(); i++) { + identity.getJSONArray("fullName").getJSONObject(i).put("value", randomName); + } + } + + if (!identity.has("dateOfBirth") || identity.get("dateOfBirth").toString().contains("{{")) { + identity.put("dateOfBirth", dob); + } + + if (!identity.has("phone") || identity.get("phone").toString().contains("{{")) { + identity.put("phone", phone); + } + + if (!identity.has("email") || identity.get("email").toString().contains("{{")) { + identity.put("email", email); + } + + if (identity.optJSONArray("addressLine1") != null) { + for (int i = 0; i < identity.getJSONArray("addressLine1").length(); i++) { + identity.getJSONArray("addressLine1").getJSONObject(i).put("value", address1); + } + } + + if (identity.optJSONArray("addressLine2") != null) { + for (int i = 0; i < identity.getJSONArray("addressLine2").length(); i++) { + identity.getJSONArray("addressLine2").getJSONObject(i).put("value", address2); + } + } + + if (identity.optJSONArray("addressLine3") != null) { + for (int i = 0; i < identity.getJSONArray("addressLine3").length(); i++) { + identity.getJSONArray("addressLine3").getJSONObject(i).put("value", address3); + } + } + + if (!identity.has("postalCode") || identity.get("postalCode").toString().contains("{{")) { + identity.put("postalCode", propsKernel.getProperty("regCenterId")); + } + + // Replace placeholders for langCode, id, and version + if (request.has("langCode") && request.getString("langCode").contains("{{")) { + request.put("langCode", "eng"); + } + if (requestJson.has("id") && requestJson.getString("id").contains("{{")) { + requestJson.put("id", "mosip.pre-registration.demographic.create"); + } + if (requestJson.has("version") && requestJson.getString("version").contains("{{")) { + requestJson.put("version", "1.0"); + } + + // update requesttime + requestJson.put("requesttime", AdminTestUtil.generateCurrentUTCTimeStamp()); + + // Hit API + Response response = RestClient.postRequestWithCookie( + BaseTestCase.ApplnURI + "/preregistration/v1/applications/prereg", + requestJson.toString(), + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_JSON, + BaseTestCase.COOKIENAME, token + ); + + String preRegId = null; + JSONObject responseJson = new JSONObject(response.asString()); + if (responseJson.has("response") && responseJson.getJSONObject("response").has("preRegistrationId")) { + preRegId = responseJson.getJSONObject("response").getString("preRegistrationId"); + System.out.println("✅ preRegistrationId = " + preRegId); + } else { + throw new RuntimeException("❌ preRegistrationId not found in response: " + responseJson.toString()); + } + return preRegId; + } + + + + public static String getPreRegistrationFlow() { + Faker faker = new Faker(); + String userId = faker.internet().emailAddress(); + sendOtp(userId, TestDataReader.readData("language")); + validateOtp(userId, OTPListener.getOtp(userId)); + String preRegId = createPreRegistration(userId); + return preRegId; + } + public static void mapCenter(String user) { String token = kernelAuthLib.getTokenByRole("globalAdmin"); String url = ApplnURI + "/v1/masterdata/usercentermapping"; diff --git a/ui-test/src/main/java/regclient/api/ArcConfigManager.java b/ui-test/src/main/java/regclient/api/ArcConfigManager.java new file mode 100644 index 000000000..270bae8d3 --- /dev/null +++ b/ui-test/src/main/java/regclient/api/ArcConfigManager.java @@ -0,0 +1,116 @@ +package regclient.api; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; + +import io.mosip.testrig.apirig.utils.ConfigManager; + +public class ArcConfigManager extends io.mosip.testrig.apirig.utils.ConfigManager { + + private static final Logger LOGGER = Logger.getLogger(ArcConfigManager.class); + + public static void init() { + Logger configManagerLogger = Logger.getLogger(ConfigManager.class); + configManagerLogger.setLevel(Level.WARN); + + Map moduleSpecificPropertiesMap = new HashMap<>(); + // Load scope specific properties + try { + Properties configProps = new Properties(); + try (InputStream inputStream = ArcConfigManager.class.getClassLoader() + .getResourceAsStream("config.properties")) { + if (inputStream == null) { + LOGGER.error("config.properties resource not found in classpath"); + throw new FileNotFoundException("config.properties not found"); + } + configProps.load(inputStream); + LOGGER.info("Config properties loaded successfully."); + } catch (IOException e) { + LOGGER.error("Failed to load config.properties", e); + throw new RuntimeException("Failed to load config.properties file", e); + } + + // Convert Properties to Map and add to moduleSpecificPropertiesMap + for (String key : configProps.stringPropertyNames()) { + moduleSpecificPropertiesMap.put(key, configProps.getProperty(key)); + } + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } + // Add module specific properties as well. + init(moduleSpecificPropertiesMap); + } + + public static String getDbUrl() { + return getProperty("db-server-es", ""); + } + + public static String getDbUser() { + return getProperty("db-su-user", ""); + } + + public static String getDbPassword() { + return getProperty("postgres-password", ""); + } + + public static String getDbSchema() { + return getProperty("es_db_schema", ""); + } + + public static String getiam_apienvuser() { + return getProperty("apiEnvUser", ""); + } + + public static String getiam_apiinternalendpoint() { + return getProperty("apiInternalEndPoint", ""); + } + + public static String getProperty(String key, String defaultValue) { + String value = propertiesMap.get(key) == null ? "" : propertiesMap.get(key).toString(); + return (value != null && !value.trim().isEmpty()) ? value : defaultValue; + } + + public static int getTimeout() { + try { + return Integer.parseInt(getProperty("explicitWaitTimeout", "10")); + } catch (NumberFormatException e) { + LOGGER.error("Invalid explicitWaitTimeout value in config.properties. Using default 10 seconds."); + return 10; + } + } + + public static String getIAMUrl() { + return getProperty("keycloak-external-url", "") + "/auth"; + } + + public static String getSignupPortalUrl() { + return getProperty("signup.portal.url", ""); + } + + public static String getIAMUsersPassword() { + return getProperty("iam-users-password", ""); + } + + public static String getEnv() { + return getProperty("db-server", ""); + } + + public static String getSmtpUrl() { + return getProperty("smtp.url", ""); + } + + public static String getHealthPortalUrl() { + return getProperty("baseurl", ""); + } + + public static String gettestcases() { + return getProperty("regclientScenariosToExecute", ""); + } +} \ No newline at end of file diff --git a/ui-test/src/main/java/regclient/api/BaseTestCase.java b/ui-test/src/main/java/regclient/api/BaseTestCase.java index 88bb7c017..c4b1bc71f 100644 --- a/ui-test/src/main/java/regclient/api/BaseTestCase.java +++ b/ui-test/src/main/java/regclient/api/BaseTestCase.java @@ -88,11 +88,11 @@ public static void initialize() { getOSType(); logger.info("We have created a Config Manager. Beginning to read properties!"); - environment = ConfigManager.getiam_apienvuser(); + environment = ArcConfigManager.getiam_apienvuser(); logger.info("Environemnt is ==== :" + environment); - ApplnURI = ConfigManager.getiam_apiinternalendpoint(); + ApplnURI = ArcConfigManager.getiam_apiinternalendpoint(); logger.info("Application URI ======" + ApplnURI); - ApplnURIForKeyCloak = ConfigManager.getIAMUrl(); + ApplnURIForKeyCloak = ArcConfigManager.getIAMUrl(); logger.info("Application URI ======" + ApplnURIForKeyCloak); testLevel = System.getProperty("env.testLevel"); logger.info("Test Level ======" + testLevel); diff --git a/ui-test/src/main/java/regclient/api/ConfigManager.java b/ui-test/src/main/java/regclient/api/ConfigManager.java deleted file mode 100644 index 104d783c4..000000000 --- a/ui-test/src/main/java/regclient/api/ConfigManager.java +++ /dev/null @@ -1,601 +0,0 @@ -package regclient.api; - - - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Properties; - -import regclient.utils.TestRunner; - - -public class ConfigManager { - - private static final org.slf4j.Logger LOGGER= org.slf4j.LoggerFactory.getLogger(ConfigManager.class); - - private static final String USEPRECONFIGOTP = "usePreConfiguredOtp"; - private static final String PRECONFIGOTP = "preconfiguredOtp"; - - private static final String MOSIP_IDREPO_CLIENT_SECRET = "mosip_idrepo_client_secret"; - private static final String MOSIP_IDREPO_CLIENT_ID = "mosip_idrepo_client_id"; - private static final String MOSIP_IDREPO_APP_ID = "mosip_idrepo_app_id"; - - private static final String MOSIP_ADMIN_CLIENT_SECRET = "mosip_admin_client_secret"; - private static final String MOSIP_ADMIN_CLIENT_ID = "mosip_admin_client_id"; - private static final String MOSIP_ADMIN_APP_ID = "mosip_admin_app_id"; - - private static final String MOSIP_REG_CLIENT_SECRET = "mosip_reg_client_secret"; - private static final String MOSIP_REG_CLIENT_ID = "mosip_reg_client_id"; - private static final String MOSIP_REGCLIENT_APP_ID = "mosip_regclient_app_id"; - - - - private static final String MOSIP_AUTOMATION_CLIENT_SECRET = "mosip_testrig_client_secret"; - private static final String MOSIP_AUTOMATION_CLIENT_ID = "mosip_testrig_client_id"; - private static final String MOSIP_AUTOMATION_APP_ID = "mosip_automation_app_id"; - - private static final String S3_HOST = "s3-host"; - private static String usePreConfiguredOtp; - private static final String S3_REGION = "s3-region"; - private static final String S3_USER_KEY = "s3-user-key"; - private static final String S3_SECRET_KEY = "s3-user-secret"; - private static final String S3_ACCOUNT = "s3-account"; - private static final String PUSH_TO_S3 = "push-reports-to-s3"; - private static final String ENABLE_DEBUG = "enableDebug"; - private static final String THREAD_COUNT = "threadCount"; - private static final String LANG_SELECT = "langselect"; - private static String Testcases = "regclientScenariosToExecute"; - private static String preconfiguredOtp; - - private static final String DB_PORT = "db-port"; - private static final String DB_DOMAIN = "db-server"; - private static final String HIBERNATE_CONNECTION_DRIVER_CLASS = "hibernate.connection.driver_class"; - private static final String HIBERNATE_CONNECTION_POOL_SIZE = "hibernate.connection.pool_size"; - private static final String HIBERNATE_DIALECT = "hibernate.dialect"; - private static final String HIBERNATE_SHOW_SQL = "hibernate.show_sql"; - private static final String HIBERNATE_CONTEXT_CLASS = "hibernate.current_session_context_class"; - - private static final String AUDIT_DB_USER = "db-su-user"; - private static final String AUDIT_DB_PASS = "postgresql-password"; - private static final String AUDIT_DB_SCHEMA = "audit_db_schema"; - - private static final String IDA_DB_USER = "db-su-user"; - private static final String IDA_DB_PASS = "postgresql-password"; - private static final String IDA_DB_SCHEMA = "ida_db_schema"; - - private static final String PMS_DB_USER = "db-su-user"; - private static final String PMS_DB_PASS = "postgresql-password"; - private static final String PMS_DB_SCHEMA = "pms_db_schema"; - - private static final String KM_DB_USER = "db-su-user"; - private static final String KM_DB_PASS = "postgresql-password"; - private static final String KM_DB_SCHEMA = "km_db_schema"; - - private static final String MASTER_DB_USER = "db-su-user"; - private static final String MASTER_DB_PASS = "postgresql-password"; - private static final String MASTER_DB_SCHEMA = "master_db_schema"; - - private static final String IAM_EXTERNAL_URL = "keycloak-external-url"; - private static final String IAM_ADMINPORTAL_PATH = "adminPortalPath"; - private static final String IAM_APIENVUSER = "apiEnvUser"; - private static final String IAM_APIINTERNALENDPOINT = "apiInternalEndPoint"; - private static final String IAM_REALM_ID = "keycloak-realm-id"; - private static final String IAM_USERS_TO_CREATE = "iam-users-to-create"; - private static final String IAM_USERS_PASSWORD = "iam-users-password"; - - private static final String AUTH_DEMO_SERVICE_PORT = "authDemoServicePort"; - private static final String AUTH_DEMO_SERVICE_BASE_URL = "authDemoServiceBaseURL"; - private static final String MOUNT_PATH = "mountPath"; - private static final String AUTHCERTS_PATH = "authCertsPath"; - private static final String MOUNT_PATH_FOR_SCENARIO = "mountPathForScenario"; - - private static final String PACKET_UTILITY_BASE_URL = "packetUtilityBaseUrl"; - - private static final String REPORT_EXPIRATION_IN_DAYS = "reportExpirationInDays"; - - private static String idrepo_client_secret; - private static String idrepo_client_id; - private static String idrepo_app_id; - - private static String admin_client_secret; - private static String admin_client_id; - private static String admin_app_id; - - private static String regproc_client_secret; - private static String regproc_client_id; - private static String regproc_app_id; - - private static String automation_client_secret; - private static String automation_client_id; - private static String automation_app_id; - - private static String s3_region; - private static String s3_host; - private static String s3_user_key; - private static String s3_account; - private static String s3_secret_key; - private static String push_reports_to_s3; - private static String enableDebug; - private static String threadCount; - private static String langselect; - private static String testcases; - - - private static String db_port; - private static String db_domain; - private static String hibernate_connection_driver_class; - private static String hibernate_connection_pool_size; - private static String hibernate_dialect; - private static String hibernate_show_sql; - private static String hibernate_current_session_context_class; - - private static String audit_db_user; - private static String audit_db_pass; - private static String audit_db_schema; - - private static String ida_db_user; - private static String ida_db_pass; - private static String ida_db_schema; - - private static String pms_db_user; - private static String pms_db_pass; - private static String pms_db_schema; - - private static String km_db_user; - private static String km_db_pass; - private static String km_db_schema; - - private static String master_db_user; - private static String master_db_pass; - private static String master_db_schema; - - private static String iam_external_url; - private static String iam_realm_id; - private static String iam_users_to_create; - private static String iam_adminportal_path; - private static String iam_apienvuser; - private static String iam_apiinternalendpoint; - private static String iam_users_password; - private static String authDemoServicePort; - private static String authDemoServiceBaseUrl; - - private static String mountPath; - private static String authCertsPath; - private static String mountPathForScenario; - private static String packetUtilityBaseUrl; - public static Properties propsKernel; - private static String reportExpirationInDays; - public static void setProperty(String key, String value) { - // Overwrite the value with only if the key exists - if (propsKernel.containsKey(key)) { - propsKernel.setProperty(key, value); - } - } - - public static String getValueForKey(String key) { - String value = System.getenv(key) == null ? propsKernel.getProperty(key) : System.getenv(key); - setProperty(key, value); - - return value; - } - - public static void init() { - propsKernel = getproperty(TestRunner.getResourcePath() + "/config/Kernel.properties"); - System.out.println(propsKernel); - - idrepo_client_secret = getValueForKey(MOSIP_IDREPO_CLIENT_SECRET); - idrepo_client_id = getValueForKey(MOSIP_IDREPO_CLIENT_ID); - idrepo_app_id = getValueForKey(MOSIP_IDREPO_APP_ID); - admin_client_secret = getValueForKey(MOSIP_ADMIN_CLIENT_SECRET); - admin_client_id = getValueForKey(MOSIP_ADMIN_CLIENT_ID); - admin_app_id = getValueForKey(MOSIP_ADMIN_APP_ID); - regproc_client_secret = getValueForKey(MOSIP_REG_CLIENT_SECRET); - regproc_client_id = getValueForKey(MOSIP_REG_CLIENT_ID); - regproc_app_id = getValueForKey(MOSIP_REGCLIENT_APP_ID); - automation_client_secret = getValueForKey(MOSIP_AUTOMATION_CLIENT_SECRET); - automation_client_id = getValueForKey(MOSIP_AUTOMATION_CLIENT_ID); - automation_app_id = getValueForKey(MOSIP_AUTOMATION_APP_ID); - s3_host = getValueForKey(S3_HOST); - s3_region = getValueForKey(S3_REGION); - s3_user_key = getValueForKey(S3_USER_KEY); - s3_secret_key = getValueForKey(S3_SECRET_KEY); - s3_account = getValueForKey(S3_ACCOUNT); - - iam_adminportal_path =System.getenv(IAM_ADMINPORTAL_PATH) == null - ? propsKernel.getProperty(IAM_ADMINPORTAL_PATH) - : System.getenv(IAM_ADMINPORTAL_PATH); - - LOGGER.info("adminportal_path from config manager::" + iam_adminportal_path); - iam_apienvuser = System.getenv(IAM_APIENVUSER) == null - ? propsKernel.getProperty(IAM_APIENVUSER) - : System.getenv(IAM_APIENVUSER); - LOGGER.info("apienvuser from config manager::" + iam_apienvuser); - iam_apiinternalendpoint = System.getenv(IAM_APIINTERNALENDPOINT) == null - ? propsKernel.getProperty(IAM_APIINTERNALENDPOINT) - : System.getenv(IAM_APIINTERNALENDPOINT); - LOGGER.info("apiinternalendpoint from config manager::" + iam_apiinternalendpoint); - // push_reports_to_s3 = getValueForKey(PUSH_TO_S3); - db_port = getValueForKey(DB_PORT); - db_domain = getValueForKey(DB_DOMAIN); - hibernate_connection_driver_class = getValueForKey(HIBERNATE_CONNECTION_DRIVER_CLASS); - hibernate_connection_pool_size = getValueForKey(HIBERNATE_CONNECTION_POOL_SIZE); - hibernate_dialect = getValueForKey(HIBERNATE_DIALECT); - hibernate_show_sql = getValueForKey(HIBERNATE_SHOW_SQL); - hibernate_current_session_context_class = getValueForKey(HIBERNATE_CONTEXT_CLASS); - audit_db_user = getValueForKey(AUDIT_DB_USER); - audit_db_pass = getValueForKey(AUDIT_DB_PASS); - audit_db_schema = getValueForKey(AUDIT_DB_SCHEMA); - ida_db_user = getValueForKey(IDA_DB_USER); - ida_db_pass = getValueForKey(IDA_DB_PASS); - ida_db_schema = getValueForKey(IDA_DB_SCHEMA); - pms_db_user = getValueForKey(PMS_DB_USER); - pms_db_pass = getValueForKey(PMS_DB_PASS); - pms_db_schema = getValueForKey(PMS_DB_SCHEMA); - km_db_user = getValueForKey(KM_DB_USER); - km_db_pass = getValueForKey(KM_DB_PASS); - km_db_schema = getValueForKey(KM_DB_SCHEMA); - master_db_user = getValueForKey(MASTER_DB_USER); - master_db_pass = getValueForKey(MASTER_DB_PASS); - master_db_schema = getValueForKey(MASTER_DB_SCHEMA); - iam_external_url = getValueForKey(IAM_EXTERNAL_URL); - System.out.println("keycloakendpoint from config manager::" + iam_external_url); - reportExpirationInDays = System.getenv(REPORT_EXPIRATION_IN_DAYS) == null - ? propsKernel.getProperty(REPORT_EXPIRATION_IN_DAYS) - : System.getenv(REPORT_EXPIRATION_IN_DAYS); - propsKernel.setProperty(REPORT_EXPIRATION_IN_DAYS, reportExpirationInDays); - iam_realm_id = getValueForKey(IAM_REALM_ID); - iam_users_to_create = getValueForKey(IAM_USERS_TO_CREATE); - iam_users_password = getValueForKey(IAM_USERS_PASSWORD); - - usePreConfiguredOtp = System.getenv(USEPRECONFIGOTP) == null ? propsKernel.getProperty(USEPRECONFIGOTP) - : System.getenv(USEPRECONFIGOTP); - propsKernel.setProperty(USEPRECONFIGOTP, usePreConfiguredOtp); - - preconfiguredOtp = System.getenv(PRECONFIGOTP) == null ? propsKernel.getProperty(PRECONFIGOTP) - : System.getenv(PRECONFIGOTP); - propsKernel.setProperty(PRECONFIGOTP, preconfiguredOtp); - - admin_client_secret = System.getenv(MOSIP_ADMIN_CLIENT_SECRET) == null - ? propsKernel.getProperty(MOSIP_ADMIN_CLIENT_SECRET) - : System.getenv(MOSIP_ADMIN_CLIENT_SECRET); - - propsKernel.setProperty(MOSIP_ADMIN_CLIENT_SECRET, admin_client_secret); - - authDemoServicePort = System.getenv(AUTH_DEMO_SERVICE_PORT) == null - ? propsKernel.getProperty(AUTH_DEMO_SERVICE_PORT) - : System.getenv(AUTH_DEMO_SERVICE_PORT); - propsKernel.setProperty(AUTH_DEMO_SERVICE_PORT, authDemoServicePort); - - authDemoServiceBaseUrl = System.getenv(AUTH_DEMO_SERVICE_BASE_URL) == null - ? propsKernel.getProperty(AUTH_DEMO_SERVICE_BASE_URL) - : System.getenv(AUTH_DEMO_SERVICE_BASE_URL); - propsKernel.setProperty(AUTH_DEMO_SERVICE_BASE_URL, authDemoServiceBaseUrl); - - mountPath = System.getenv(MOUNT_PATH) == null ? propsKernel.getProperty(MOUNT_PATH) : System.getenv(MOUNT_PATH); - propsKernel.setProperty(MOUNT_PATH, mountPath); - - authCertsPath = System.getenv(AUTHCERTS_PATH) == null ? propsKernel.getProperty(AUTHCERTS_PATH) : System.getenv(AUTHCERTS_PATH); - propsKernel.setProperty(AUTHCERTS_PATH, authCertsPath); - - mountPathForScenario = System.getenv(MOUNT_PATH_FOR_SCENARIO) == null ? propsKernel.getProperty(MOUNT_PATH_FOR_SCENARIO) : System.getenv(MOUNT_PATH_FOR_SCENARIO); - propsKernel.setProperty(MOUNT_PATH_FOR_SCENARIO, mountPathForScenario); - - packetUtilityBaseUrl = System.getenv(PACKET_UTILITY_BASE_URL) == null ? propsKernel.getProperty(PACKET_UTILITY_BASE_URL) : System.getenv(PACKET_UTILITY_BASE_URL); - propsKernel.setProperty(PACKET_UTILITY_BASE_URL, packetUtilityBaseUrl); - - push_reports_to_s3 =System.getenv(PUSH_TO_S3) == null ? propsKernel.getProperty(PUSH_TO_S3) : System.getenv(PUSH_TO_S3); - propsKernel.setProperty(PUSH_TO_S3, push_reports_to_s3); - - enableDebug =System.getenv(ENABLE_DEBUG) == null ? propsKernel.getProperty(ENABLE_DEBUG) : System.getenv(ENABLE_DEBUG); - propsKernel.setProperty(ENABLE_DEBUG, enableDebug); - - threadCount =System.getenv(THREAD_COUNT) == null ? propsKernel.getProperty(THREAD_COUNT) : System.getenv(THREAD_COUNT); - propsKernel.setProperty(THREAD_COUNT, threadCount); - - langselect =System.getenv(LANG_SELECT) == null ? propsKernel.getProperty(LANG_SELECT) : System.getenv(LANG_SELECT); - propsKernel.setProperty(LANG_SELECT, langselect); - - testcases =System.getenv(Testcases) == null ? propsKernel.getProperty(Testcases) : System.getenv(Testcases); - propsKernel.setProperty(Testcases, testcases); - } - - public static String gettestcases() { - return testcases; - } - - public static String getAuthDemoServicePort() { - return authDemoServicePort; - } - - public static String getAuthDemoServiceBaseUrl() { - return authDemoServiceBaseUrl; - - } - - - public static String getLangselect() { - return langselect; - - } - - public static String getThreadCount() { - return threadCount; - - } - - - - public static String getEnableDebug() { - return enableDebug; - - } - - public static String getmountPath() { - return mountPath; - } - - public static String getmountPathForScenario() { - return mountPathForScenario; - } - - public static String getpacketUtilityBaseUrl() { - return packetUtilityBaseUrl; - } - - public static String getauthCertsPath() { - return authCertsPath; - } - - public static Properties init(String abc) { - propsKernel = getproperty(TestRunner.getResourcePath() + "/" + "/config/Kernel.properties"); - - return propsKernel; - } - - - public static String getAdminClientSecret() { - return admin_client_secret; - } - - public static Boolean IsDebugEnabled() { - return enableDebug.equalsIgnoreCase("yes"); - } - - public static String getAdminClientId() { - return admin_client_id; - } - - public static String getAdminAppId() { - return admin_app_id; - } - - public static String getIdRepoClientSecret() { - return idrepo_client_secret; - } - - public static String getidRepoClientId() { - return idrepo_client_id; - } - - public static String getidRepoAppId() { - return idrepo_app_id; - } - - public static String getRegprocClientSecret() { - return regproc_client_secret; - } - - public static String getRegprocClientId() { - return regproc_client_id; - } - - public static String getRegprocAppId() { - return regproc_app_id; - } - - - public static String getAutomationClientSecret() { - return automation_client_secret; - } - - public static String getAutomationClientId() { - return automation_client_id; - } - - public static String getAutomationAppId() { - return automation_app_id; - } - - public static String getS3Host() { - return s3_host; - } - public static String getReportExpirationInDays() { - return reportExpirationInDays; - } - - public static String getS3Region() { - return s3_region; - } - - public static String getS3UserKey() { - return s3_user_key; - } - - public static String getS3SecretKey() { - return s3_secret_key; - } - - public static String getS3Account() { - return s3_account; - } - - public static String getPushReportsToS3() { - return push_reports_to_s3; - } - - public static String getIdaDbUrl() { - return "jdbc:postgresql://" + db_domain + ":" + db_port + "/mosip_ida"; - } - - public static String getAuditDbUrl() { - return "jdbc:postgresql://" + db_domain + ":" + db_port + "/mosip_audit"; - } - - public static String getDbDriverClass() { - return hibernate_connection_driver_class; - } - - public static String getDbConnectionPoolSize() { - return hibernate_connection_pool_size; - } - - public static String getDbDialect() { - return hibernate_dialect; - } - - public static String getShowSql() { - return hibernate_show_sql; - } - public static String getiam_adminportal_path() { - return iam_adminportal_path; - } - public static String getiam_apienvuser() { - return iam_apienvuser; - } - public static String getiam_apiinternalendpoint() { - return iam_apiinternalendpoint; - } - - public static String getDbSessionContext() { - return hibernate_current_session_context_class; - } - - public static String getAuditDbUser() { - return audit_db_user; - } - - public static String getAuditDbPass() { - System.out.println("DB Password from ENV::: " + System.getenv(AUDIT_DB_PASS)); - return audit_db_pass; - } - - public static String getAuditDbSchema() { - return audit_db_schema; - } - - public static String getIdaDbUser() { - return ida_db_user; - } - - public static String getIdaDbPass() { - return ida_db_pass; - } - - public static String getIdaDbSchema() { - return ida_db_schema; - } - - public static String getPMSDbUrl() { - return "jdbc:postgresql://" + db_domain + ":" + db_port + "/mosip_pms"; - } - - public static String getKMDbUrl() { - return "jdbc:postgresql://" + db_domain + ":" + db_port + "/mosip_keymgr"; - } - - public static String getMASTERDbUrl() { - return "jdbc:postgresql://" + db_domain + ":" + db_port + "/mosip_master"; - } - - public static String getPMSDbUser() { - return pms_db_user; - } - - public static String getPMSDbPass() { - return pms_db_pass; - } - - public static String getPMSDbSchema() { - return pms_db_schema; - } - - public static String getKMDbUser() { - return km_db_user; - } - - public static String getKMDbPass() { - return km_db_pass; - } - - public static String getKMDbSchema() { - return km_db_schema; - } - - public static String getMasterDbUser() { - return master_db_user; - } - - public static String getMasterDbPass() { - return master_db_pass; - } - - public static String getMasterDbSchema() { - return master_db_schema; - } - - // from docker env getting only host url - public static String getIAMUrl() { - System.out.println("keycloak url from ENV::: " + System.getenv(IAM_EXTERNAL_URL) + "/auth"); - System.out.println("keycloak url from Property::: " + System.getProperty(IAM_EXTERNAL_URL) + "/auth"); - System.out.println("keycloak url from Config::: " + propsKernel.getProperty(IAM_EXTERNAL_URL) + "/auth"); - System.out.println("keycloak url is:::" + iam_external_url + "/auth"); - return iam_external_url + "/auth"; - } - - public static String getIAMRealmId() { - return iam_realm_id; - } - - public static String getIAMUsersToCreate() { - return iam_users_to_create; - } - - public static String getIAMUsersPassword() { - return iam_users_password; - } - - public static String getUsePreConfiguredOtp() { - return usePreConfiguredOtp; - } - - public static String getPreConfiguredOtp() { - return preconfiguredOtp; - - } - - public static String getEnv() { - return db_domain; - } - - public static String getRolesForUser() { - propsKernel = getproperty(TestRunner.getResourcePath() + "/" + "/config/Kernel.properties"); - return propsKernel.getProperty("roles"); - } - - private static Properties getproperty(String path) { - Properties prop = new Properties(); - try { - File file = new File(path); - prop.load(new FileInputStream(file)); - } catch (IOException e) { - LOGGER.error("Exception " + e.getMessage()); - } - return prop; - } - - public static String getAuthDemoServiceUrl() { - return ConfigManager.getAuthDemoServiceBaseUrl() + ":" + ConfigManager.getAuthDemoServicePort(); - } - -} \ No newline at end of file diff --git a/ui-test/src/main/java/regclient/api/KernelAuthentication.java b/ui-test/src/main/java/regclient/api/KernelAuthentication.java index b25e574a7..30f4744f8 100644 --- a/ui-test/src/main/java/regclient/api/KernelAuthentication.java +++ b/ui-test/src/main/java/regclient/api/KernelAuthentication.java @@ -62,9 +62,9 @@ public String getAuthForIDREPO() { JSONObject actualrequest = getRequestJson(authRequest); JSONObject request=new JSONObject(); - request.put("appId", ConfigManager.getidRepoAppId()); - request.put("clientId", ConfigManager.getidRepoClientId()); - request.put("secretKey", ConfigManager.getIdRepoClientSecret()); + request.put("appId", ArcConfigManager.getidRepoAppId()); + request.put("clientId", ArcConfigManager.getidRepoClientId()); + request.put("secretKey", ArcConfigManager.getIdRepoClientSecret()); actualrequest.put("request", request); Response reponse=appl.postWithJson(props.get("authclientidsecretkeyURL"), actualrequest); @@ -78,14 +78,14 @@ public String getAuthForAdmin() { JSONObject actualrequest = getRequestJson(authInternalRequest); JSONObject request = new JSONObject(); - request.put("appId", ConfigManager.getAdminAppId()); + request.put("appId", ArcConfigManager.getAdminAppId()); request.put("password", admin_password); //if(BaseTestCase.currentModule==null) admin_userName= request.put("userName", BaseTestCase.currentModule +"-"+ admin_userName); - request.put("clientId", ConfigManager.getAdminClientId()); - request.put("clientSecret", ConfigManager.getAdminClientSecret()); + request.put("clientId", ArcConfigManager.getAdminClientId()); + request.put("clientSecret", ArcConfigManager.getAdminClientSecret()); actualrequest.put("request", request); Response reponse = appl.postWithJson(authenticationInternalEndpoint, actualrequest); @@ -100,11 +100,11 @@ public String getAuthForzoneMap() { JSONObject actualrequest = getRequestJson(authInternalRequest); JSONObject request = new JSONObject(); - request.put("appId", ConfigManager.getAdminAppId()); + request.put("appId", ArcConfigManager.getAdminAppId()); request.put("password", admin_password); request.put("userName", props.get("admin_zone_userName")); - request.put("clientId", ConfigManager.getAdminClientId()); - request.put("clientSecret", ConfigManager.getAdminClientSecret()); + request.put("clientId", ArcConfigManager.getAdminClientId()); + request.put("clientSecret", ArcConfigManager.getAdminClientSecret()); actualrequest.put("request", request); Response reponse = appl.postWithJson(authenticationInternalEndpoint, actualrequest); diff --git a/ui-test/src/main/java/regclient/api/KeycloakUserManager.java b/ui-test/src/main/java/regclient/api/KeycloakUserManager.java index bca9480da..28ce05a19 100644 --- a/ui-test/src/main/java/regclient/api/KeycloakUserManager.java +++ b/ui-test/src/main/java/regclient/api/KeycloakUserManager.java @@ -37,10 +37,10 @@ private static Keycloak getKeycloakInstance() { Keycloak key=null; try { - key=KeycloakBuilder.builder().serverUrl(ConfigManager.getIAMUrl()).realm(ConfigManager.getIAMRealmId()) - .grantType(OAuth2Constants.CLIENT_CREDENTIALS).clientId(ConfigManager.getAutomationClientId()).clientSecret(ConfigManager.getAutomationClientSecret()) + key=KeycloakBuilder.builder().serverUrl(ArcConfigManager.getIAMUrl()).realm(ArcConfigManager.getIAMRealmId()) + .grantType(OAuth2Constants.CLIENT_CREDENTIALS).clientId(ArcConfigManager.getAutomationClientId()).clientSecret(ArcConfigManager.getAutomationClientSecret()) .build(); - System.out.println(ConfigManager.getIAMUrl()); + System.out.println(ArcConfigManager.getIAMUrl()); System.out.println(key.toString() + key.realms()); }catch(Exception e) { @@ -62,7 +62,7 @@ public static Properties getproperty(String path) { } public static void createUsers() { - List needsToBeCreatedUsers = List.of(ConfigManager.getIAMUsersToCreate().split(",")); + List needsToBeCreatedUsers = List.of(ArcConfigManager.getIAMUsersToCreate().split(",")); Keycloak keycloakInstance = getKeycloakInstance(); for (String needsToBeCreatedUser : needsToBeCreatedUsers) { UserRepresentation user = new UserRepresentation(); @@ -86,7 +86,7 @@ else if(needsToBeCreatedUser.equals("masterdata-220005")){ user.setLastName(moduleSpecificUser); user.setEmail("automation" + moduleSpecificUser + "@automationlabs.com"); // Get realm - RealmResource realmResource = keycloakInstance.realm(ConfigManager.getIAMRealmId()); + RealmResource realmResource = keycloakInstance.realm(ArcConfigManager.getIAMRealmId()); UsersResource usersRessource = realmResource.users(); // Create user (requires manage-users role) Response response = null; @@ -107,7 +107,7 @@ else if(needsToBeCreatedUser.equals("masterdata-220005")){ passwordCred.setType(CredentialRepresentation.PASSWORD); //passwordCred.setValue(userPassword.get(passwordIndex)); - passwordCred.setValue(ConfigManager.getIAMUsersPassword()); + passwordCred.setValue(ArcConfigManager.getIAMUsersPassword()); UserResource userResource = usersRessource.get(userId); @@ -117,7 +117,7 @@ else if(needsToBeCreatedUser.equals("masterdata-220005")){ // Getting all the roles List allRoles = realmResource.roles().list(); List availableRoles = new ArrayList<>(); - List toBeAssignedRoles = List.of(ConfigManager.getRolesForUser().split(",")); + List toBeAssignedRoles = List.of(ArcConfigManager.getRolesForUser().split(",")); for(String role : toBeAssignedRoles) { if(allRoles.stream().anyMatch((r->r.getName().equalsIgnoreCase(role)))){ availableRoles.add(allRoles.stream().filter(r->r.getName().equals(role)).findFirst().get()); @@ -142,7 +142,7 @@ public static void createUsersWithOutDefaultRole() { user.setFirstName(onboardUser); user.setLastName(onboardUser); user.setEmail("automation" + onboardUser + "@automationlabs.com"); - RealmResource realmResource = keycloakInstance.realm(ConfigManager.getIAMRealmId()); + RealmResource realmResource = keycloakInstance.realm(ArcConfigManager.getIAMRealmId()); UsersResource usersRessource = realmResource.users(); Response response = null; response = usersRessource.create(user); @@ -156,7 +156,7 @@ public static void createUsersWithOutDefaultRole() { passwordCred.setTemporary(false); passwordCred.setType(CredentialRepresentation.PASSWORD); - passwordCred.setValue(ConfigManager.getIAMUsersPassword()); + passwordCred.setValue(ArcConfigManager.getIAMUsersPassword()); UserResource userResource = usersRessource.get(userId); @@ -164,7 +164,7 @@ public static void createUsersWithOutDefaultRole() { List allRoles = realmResource.roles().list(); List availableRoles = new ArrayList<>(); - List toBeAssignedRoles = List.of(ConfigManager.getRolesForUser().split(",")); + List toBeAssignedRoles = List.of(ArcConfigManager.getRolesForUser().split(",")); for(String role : toBeAssignedRoles) { if(!role.equalsIgnoreCase("Default")) { if(allRoles.stream().anyMatch((r->r.getName().equalsIgnoreCase(role)))){ diff --git a/ui-test/src/main/java/regclient/api/RestClient.java b/ui-test/src/main/java/regclient/api/RestClient.java index c2b5c4f19..07b4435fd 100644 --- a/ui-test/src/main/java/regclient/api/RestClient.java +++ b/ui-test/src/main/java/regclient/api/RestClient.java @@ -56,7 +56,7 @@ public static Response postRequestWithCookie(String url, Object body, String con // public static Response postReqestWithCookiesAndBody(String url, String body, String token, String opsToLog) { Response posttResponse = null; - if (ConfigManager.IsDebugEnabled()) { + if (ArcConfigManager.IsDebugEnabled()) { posttResponse = given().relaxedHTTPSValidation().body(body).contentType("application/json") .accept("*/*").log().all().when().cookie("Authorization", token).post(url).then().log().all() .extract().response(); diff --git a/ui-test/src/main/java/regclient/page/BasePage.java b/ui-test/src/main/java/regclient/page/BasePage.java index c8be843b1..f36da2d48 100644 --- a/ui-test/src/main/java/regclient/page/BasePage.java +++ b/ui-test/src/main/java/regclient/page/BasePage.java @@ -96,7 +96,7 @@ protected void clickAndsendKeysToTextBox(WebElement element, String text) { ((HidesKeyboard) driver).hideKeyboard(); } - protected void sendKeysToTextBox(WebElement element, String text) { + protected void clickAndsendKeysToTextBox2(WebElement element, String text) { this.waitForElementToBeVisible(element); element.click(); waitTime(1); @@ -106,6 +106,16 @@ protected void sendKeysToTextBox(WebElement element, String text) { waitTime(1); driver.navigate().back(); } + + protected void sendKeysToTextBox(WebElement element, String text) { + this.waitForElementToBeVisible(element); + waitTime(1); + element.clear(); + waitTime(1); + element.sendKeys(text); + waitTime(1); + driver.navigate().back(); + } protected String getTextFromLocator(WebElement element) { this.waitForElementToBeVisible(element); @@ -354,8 +364,8 @@ public static String generateData(String validator) { case "^(?=.{2,50}$).*": return generateStringOfLength(2, 30); - case "^[0-9]{6}[/][0-9]{2}[/][0-9]{1}$": - return generateSixDigitNumber() + "/" + generateTwoDigitNumber() + "/" + generateOneDigitNumber(); + case "^([0-9]{10})$": + return generateTenDigitNumber(); case "^(1869|18[7-9][0-9]|19[0-9][0-9]|20[0-9][0-9])/([0][1-9]|1[0-2])/([0][1-9]|[1-2][0-9]|3[01])$": return generateDateInRange(); @@ -371,6 +381,9 @@ public static String generateData(String validator) { case "^[A-Za-z0-9_\\-]+(\\.[A-Za-z0-9_]+)*@[A-Za-z0-9_-]+(\\.[A-Za-z0-9_]+)*(\\.[a-zA-Z]{2,})$": return generateEmail(); + + case "^([0-9]{10,30})$": + return generateTenDigitNumber(); default: return "abcd"; @@ -411,6 +424,15 @@ private static String generateDateInRange() { private static String generateNineDigitNumber() { return String.format("%09d", random.nextInt(1000000000)); } + + + private static final Random randomten = new Random(); + + private static String generateTenDigitNumber() { + long number = 1000000000L + (long)(randomten.nextDouble() * 9000000000L); + return String.valueOf(number); + } + private static String generateEmail() { String[] domains = {"example.com", "test.com", "email.com"}; diff --git a/ui-test/src/main/java/regclient/page/CameraPage.java b/ui-test/src/main/java/regclient/page/CameraPage.java index 04007c873..88755d7b8 100644 --- a/ui-test/src/main/java/regclient/page/CameraPage.java +++ b/ui-test/src/main/java/regclient/page/CameraPage.java @@ -10,7 +10,8 @@ public class CameraPage extends BasePage{ - @AndroidFindBy(xpath = "//android.widget.TextView[@text=\"OK\"]") + @AndroidFindBy(xpath = "//android.widget.TextView[@text=\"OK\"]") + private WebElement okButton; public CameraPage(AppiumDriver driver) { @@ -18,18 +19,17 @@ public CameraPage(AppiumDriver driver) { } public void clickOkButton() { -// if(isElementDisplayed(okButton)) -// clickOnElement(okButton); -// else { - waitTime(7); - clickAtCoordinates(633,2042); -// } + if(isElementDisplayed(okButton)) + clickOnElement(okButton); + else { + waitTime(7); + clickAtCoordinates(401,1123); + } } public void clickimage() { - waitTime(2); - isElementDisplayed(driver.findElement(By.id(TestDataReader.readData("id")))); - clickOnElement(driver.findElement(By.id(TestDataReader.readData("id")))); + waitTime(7); + clickAtCoordinates(401,1123); } diff --git a/ui-test/src/main/java/regclient/page/PendingApproval.java b/ui-test/src/main/java/regclient/page/PendingApproval.java index b75355816..bc5e76bb7 100644 --- a/ui-test/src/main/java/regclient/page/PendingApproval.java +++ b/ui-test/src/main/java/regclient/page/PendingApproval.java @@ -47,5 +47,11 @@ public PendingApproval(AppiumDriver driver) { public abstract boolean isRejectPacketTitleDisplayed(); public abstract boolean isSubmitButtonEnabled(); + + public abstract boolean isInvalidUsernameMessageDisplayed(); + + public abstract boolean isInvalidemptyUsernameSumbitButtonEnbled(); + + } diff --git a/ui-test/src/main/java/regclient/pages/arabic/PendingApprovalArabic.java b/ui-test/src/main/java/regclient/pages/arabic/PendingApprovalArabic.java index 42c76cb22..7b1cb4aa5 100644 --- a/ui-test/src/main/java/regclient/pages/arabic/PendingApprovalArabic.java +++ b/ui-test/src/main/java/regclient/pages/arabic/PendingApprovalArabic.java @@ -29,6 +29,12 @@ public class PendingApprovalArabic extends PendingApproval{ @AndroidFindBy(accessibility = "يُقدِّم") private WebElement submitButton; + + @AndroidFindBy(accessibility = "اسم المستخدم غير صالح!") + private WebElement invalidUsernameMessage; + + @AndroidFindBy(accessibility = "SUBMIT") + private WebElement invalidUsernameMessageForempty; @AndroidFindBy(accessibility = "مصادقة المشرف") private WebElement supervisorAuthenticationTitle; @@ -116,6 +122,11 @@ public boolean isRejectButtonDisplayed() { return isElementDisplayed(rejectButton); } + public boolean isInvalidUsernameMessageDisplayed() { + return isElementDisplayed(invalidUsernameMessage); + } + + public boolean isPageAttributesDisplayed() { return isElementDisplayed(pageAttributes); } @@ -157,5 +168,9 @@ public void selectRejectionReasonDropdown() { public boolean isSubmitButtonEnabled() { return isElementEnabled(submitButton); } + + public boolean isInvalidemptyUsernameSumbitButtonEnbled() { + return isElementEnabled(invalidUsernameMessageForempty); + } } diff --git a/ui-test/src/main/java/regclient/pages/english/AuthenticationPageEnglish.java b/ui-test/src/main/java/regclient/pages/english/AuthenticationPageEnglish.java index 2fc296d05..f76be7dcf 100644 --- a/ui-test/src/main/java/regclient/pages/english/AuthenticationPageEnglish.java +++ b/ui-test/src/main/java/regclient/pages/english/AuthenticationPageEnglish.java @@ -37,6 +37,10 @@ public boolean isAuthenticationPageDisplayed() { return isElementDisplayed(authenticationPageTitle); } + + + + public AcknowledgementPage clickOnAuthenticatenButton() { clickOnElement(authenticateButton); return new AcknowledgementPageEnglish(driver); diff --git a/ui-test/src/main/java/regclient/pages/english/DemographicDetailsPageEnglish.java b/ui-test/src/main/java/regclient/pages/english/DemographicDetailsPageEnglish.java index a9e27b4a1..dcf0d3df1 100644 --- a/ui-test/src/main/java/regclient/pages/english/DemographicDetailsPageEnglish.java +++ b/ui-test/src/main/java/regclient/pages/english/DemographicDetailsPageEnglish.java @@ -69,6 +69,7 @@ public DocumentUploadPage clickOnContinueButton() { public boolean isContinueButtonEnable() { return isElementEnabled(continueButton); + } diff --git a/ui-test/src/main/java/regclient/pages/english/LoginPageEnglish.java b/ui-test/src/main/java/regclient/pages/english/LoginPageEnglish.java index 7c6ab24c2..aa9fd84cc 100644 --- a/ui-test/src/main/java/regclient/pages/english/LoginPageEnglish.java +++ b/ui-test/src/main/java/regclient/pages/english/LoginPageEnglish.java @@ -31,6 +31,7 @@ public LoginPageEnglish(AppiumDriver driver) { @AndroidFindBy(xpath = "//android.widget.EditText") private WebElement passwordTextBox; + @AndroidFindBy(accessibility = "LOGIN") private WebElement loginButton; @@ -68,7 +69,7 @@ public LoginPageEnglish(AppiumDriver driver) { @AndroidFindBy(accessibility = "BACK") private WebElement backButton; - @AndroidFindBy(accessibility = "Forgot Password?") + @AndroidFindBy(accessibility = "FORGOT PASSWORD?") private WebElement forgetPasswordButton; @AndroidFindBy(accessibility = "User not found!") diff --git a/ui-test/src/main/java/regclient/pages/english/PendingApprovalEnglish.java b/ui-test/src/main/java/regclient/pages/english/PendingApprovalEnglish.java index 01cdc99cc..f2cce4bd6 100644 --- a/ui-test/src/main/java/regclient/pages/english/PendingApprovalEnglish.java +++ b/ui-test/src/main/java/regclient/pages/english/PendingApprovalEnglish.java @@ -32,6 +32,14 @@ public class PendingApprovalEnglish extends PendingApproval{ @AndroidFindBy(accessibility = "SUBMIT") private WebElement submitButton; + + @AndroidFindBy(accessibility = "Username invalid!") + private WebElement invalidUsernameMessage; + + + @AndroidFindBy(accessibility = "SUBMIT") + private WebElement invalidUsernameMessageForempty; + @AndroidFindBy(accessibility = "Supervisor's Authentication") private WebElement supervisorAuthenticationTitle; @@ -95,13 +103,21 @@ public void clickOnSubmitButton() { public boolean isSupervisorAuthenticationTitleDisplayed() { return isElementDisplayed(supervisorAuthenticationTitle); } + + public boolean isInvalidUsernameMessageDisplayed() { + return isElementDisplayed(invalidUsernameMessage); + } + + public boolean isInvalidemptyUsernameSumbitButtonEnbled() { + return isElementEnabled(invalidUsernameMessageForempty); + } public void enterUserName(String username) { - sendKeysToTextBox(userNameTextBox,username); + clickAndsendKeysToTextBox2(userNameTextBox,username); } public void enterPassword(String password) { - sendKeysToTextBox(passwordTextBox,password); + clickAndsendKeysToTextBox2(passwordTextBox,password); } public void clickOnBackButton() { diff --git a/ui-test/src/main/java/regclient/pages/french/PendingApprovalFrench.java b/ui-test/src/main/java/regclient/pages/french/PendingApprovalFrench.java index f747da797..3ca8b313f 100644 --- a/ui-test/src/main/java/regclient/pages/french/PendingApprovalFrench.java +++ b/ui-test/src/main/java/regclient/pages/french/PendingApprovalFrench.java @@ -62,7 +62,14 @@ public class PendingApprovalFrench extends PendingApproval{ @AndroidFindBy(accessibility = "Please select a value") private WebElement rejectReasonDropdown; + + @AndroidFindBy(accessibility = "اسم المستخدم غير صالح!") + private WebElement invalidUsernameMessage; + @AndroidFindBy(accessibility = "SUBMIT") + private WebElement invalidUsernameMessageForempty; + + public PendingApprovalFrench(AppiumDriver driver) { super(driver); } @@ -132,6 +139,10 @@ public void enterAID(String AID) { clickAndsendKeysToTextBox(applicationIdTextbox,AID); } + public boolean isInvalidUsernameMessageDisplayed() { + return isElementDisplayed(invalidUsernameMessage); + } + public void clickOnRejectButton() { clickOnElement(rejectButton); } @@ -157,5 +168,9 @@ public void selectRejectionReasonDropdown() { public boolean isSubmitButtonEnabled() { return isElementEnabled(submitButton); } + + public boolean isInvalidemptyUsernameSumbitButtonEnbled() { + return isElementEnabled(invalidUsernameMessageForempty); + } } diff --git a/ui-test/src/main/java/regclient/pages/hindi/PendingApprovalHindi.java b/ui-test/src/main/java/regclient/pages/hindi/PendingApprovalHindi.java index 1eed65cbe..5570bc5e2 100644 --- a/ui-test/src/main/java/regclient/pages/hindi/PendingApprovalHindi.java +++ b/ui-test/src/main/java/regclient/pages/hindi/PendingApprovalHindi.java @@ -23,6 +23,9 @@ public class PendingApprovalHindi extends PendingApproval{ @AndroidFindBy(accessibility = "स्क्रिम") private WebElement backGroundScreen; + + @AndroidFindBy(accessibility = "उपयोगकर्ता नाम अमान्य!") + private WebElement invalidUsernameMessage; @AndroidFindBy(uiAutomator = "UiSelector().className(\"android.widget.CheckBox\").instance(0)") private WebElement searchCheckBoxButton; @@ -63,6 +66,10 @@ public class PendingApprovalHindi extends PendingApproval{ @AndroidFindBy(accessibility = "Please select a value") private WebElement rejectReasonDropdown; + + @AndroidFindBy(accessibility = "SUBMIT") + private WebElement invalidUsernameMessageForempty; + public PendingApprovalHindi(AppiumDriver driver) { super(driver); } @@ -112,6 +119,14 @@ public boolean isApprovalButtonDisplayed() { return isElementDisplayed(approveButton); } + public boolean isInvalidUsernameMessageDisplayed() { + return isElementDisplayed(invalidUsernameMessage); + } + + public boolean isInvalidemptyUsernameSumbitButtonEnbled() { + return isElementEnabled(invalidUsernameMessageForempty); + } + public boolean isRejectButtonDisplayed() { return isElementDisplayed(rejectButton); } diff --git a/ui-test/src/main/java/regclient/pages/kannada/PendingApprovalKannada.java b/ui-test/src/main/java/regclient/pages/kannada/PendingApprovalKannada.java index 18df5507c..1e5427e60 100644 --- a/ui-test/src/main/java/regclient/pages/kannada/PendingApprovalKannada.java +++ b/ui-test/src/main/java/regclient/pages/kannada/PendingApprovalKannada.java @@ -23,6 +23,8 @@ public class PendingApprovalKannada extends PendingApproval{ @AndroidFindBy(accessibility = "ಸ್ಕ್ರಿಮ್") private WebElement backGroundScreen; + @AndroidFindBy(accessibility = "ಬಳಕೆದಾರ ಹೆಸರು ಅಮಾನ್ಯ!") + private WebElement invalidUsernameMessage; @AndroidFindBy(uiAutomator = "UiSelector().className(\"android.widget.CheckBox\").instance(0)") private WebElement searchCheckBoxButton; @@ -63,6 +65,9 @@ public class PendingApprovalKannada extends PendingApproval{ @AndroidFindBy(accessibility = "Please select a value") private WebElement rejectReasonDropdown; + @AndroidFindBy(accessibility = "SUBMIT") + private WebElement invalidUsernameMessageForempty; + public PendingApprovalKannada(AppiumDriver driver) { super(driver); } @@ -116,6 +121,7 @@ public boolean isRejectButtonDisplayed() { return isElementDisplayed(rejectButton); } + public boolean isPageAttributesDisplayed() { return isElementDisplayed(pageAttributes); } @@ -135,11 +141,15 @@ public void enterAID(String AID) { public void clickOnRejectButton() { clickOnElement(rejectButton); } + public boolean isInvalidUsernameMessageDisplayed() { + return isElementDisplayed(invalidUsernameMessage); + } public boolean isRejectPacketTitleDisplayed() { return isElementDisplayed(rejectPacketTitle); } + public void selectRejectionReasonDropdown() { boolean isdisplayed =isElementDisplayed(rejectReasonDropdown); assertTrue(isdisplayed,"Verify if "+rejectReasonDropdown+" header is displayed"); @@ -157,4 +167,8 @@ public void selectRejectionReasonDropdown() { public boolean isSubmitButtonEnabled() { return isElementEnabled(submitButton); } + + public boolean isInvalidemptyUsernameSumbitButtonEnbled() { + return isElementEnabled(invalidUsernameMessageForempty); + } } diff --git a/ui-test/src/main/java/regclient/pages/tamil/PendingApprovalTamil.java b/ui-test/src/main/java/regclient/pages/tamil/PendingApprovalTamil.java index e5136a7a3..f1f7db29e 100644 --- a/ui-test/src/main/java/regclient/pages/tamil/PendingApprovalTamil.java +++ b/ui-test/src/main/java/regclient/pages/tamil/PendingApprovalTamil.java @@ -20,6 +20,8 @@ public class PendingApprovalTamil extends PendingApproval{ @AndroidFindBy(accessibility = "ஒப்புதல்") private WebElement approveButton; + @AndroidFindBy(accessibility = "பயனர்பெயர் செல்லாது!") + private WebElement invalidUsernameMessage; @AndroidFindBy(accessibility = "ஸ்க்ரிம்") private WebElement backGroundScreen; @@ -63,6 +65,9 @@ public class PendingApprovalTamil extends PendingApproval{ @AndroidFindBy(accessibility = "Please select a value") private WebElement rejectReasonDropdown; + @AndroidFindBy(accessibility = "SUBMIT") + private WebElement invalidUsernameMessageForempty; + public PendingApprovalTamil(AppiumDriver driver) { super(driver); } @@ -70,6 +75,9 @@ public PendingApprovalTamil(AppiumDriver driver) { public boolean isPendingApprovalTitleDisplayed() { return isElementDisplayed(pendingApprovalTitle); } + public boolean isInvalidUsernameMessageDisplayed() { + return isElementDisplayed(invalidUsernameMessage); + } @SuppressWarnings("deprecation") public void clickOnAID(String AID) { @@ -157,5 +165,9 @@ public void selectRejectionReasonDropdown() { public boolean isSubmitButtonEnabled() { return isElementEnabled(submitButton); } + + public boolean isInvalidemptyUsernameSumbitButtonEnbled() { + return isElementEnabled(invalidUsernameMessageForempty); + } } diff --git a/ui-test/src/main/java/regclient/utils/EmailableReport.java b/ui-test/src/main/java/regclient/utils/EmailableReport.java index 1826aa3d8..91f2c5716 100644 --- a/ui-test/src/main/java/regclient/utils/EmailableReport.java +++ b/ui-test/src/main/java/regclient/utils/EmailableReport.java @@ -26,7 +26,7 @@ import org.testng.internal.Utils; import org.testng.xml.XmlSuite; -import regclient.api.ConfigManager; +import regclient.api.ArcConfigManager; @@ -170,7 +170,7 @@ protected void writeSuiteSummary() { for (SuiteResult suiteResult : suiteResults) { writer.print(""); - writer.print(Utils.escapeHtml("Android Regclient Ui Automation ------- Env - "+ConfigManager.getEnv() )); + writer.print(Utils.escapeHtml("Android Regclient Ui Automation ------- Env - "+ArcConfigManager.getEnv() )); writer.print(""); writer.print("
");
 			writer.print(Utils.escapeHtml("Date and Time  ")+printCurrentDateTime());
diff --git a/ui-test/src/main/java/regclient/utils/TestRunner.java b/ui-test/src/main/java/regclient/utils/TestRunner.java
index d18e805ff..050facd24 100644
--- a/ui-test/src/main/java/regclient/utils/TestRunner.java
+++ b/ui-test/src/main/java/regclient/utils/TestRunner.java
@@ -9,9 +9,10 @@
 import org.testng.xml.XmlSuite;
 import org.testng.xml.XmlTest;
 
+import io.mosip.testrig.apirig.testrunner.BaseTestCase;
+import io.mosip.testrig.apirig.testrunner.OTPListener;
 import regclient.api.AdminTestUtil;
-import regclient.api.BaseTestCase;
-import regclient.api.ConfigManager;
+import regclient.api.ArcConfigManager;
 import regclient.api.FetchUiSpec;
 
 public class TestRunner {
@@ -19,12 +20,22 @@ public class TestRunner {
 	public static String jarUrl = TestRunner.class.getProtectionDomain().getCodeSource().getLocation().getPath();
 	
 	public static void main(String[] args) {	
+		io.mosip.testrig.apirig.testrunner.BaseTestCase.currentModule = "androidregclient";
 		AdminTestUtil.initialize();
+		BaseTestCase.ApplnURI = ArcConfigManager.getiam_apiinternalendpoint();
+		OTPListener otpListener = new OTPListener();
+		otpListener.run();	
 		FetchUiSpec.getUiSpec("newProcess");
+		io.mosip.testrig.apirig.testrunner.BaseTestCase.setRunContext(checkRunType(), jarUrl);
+		io.mosip.testrig.apirig.testrunner.BaseTestCase.copymoduleSpecificAndConfigFile("config");
+		io.mosip.testrig.apirig.utils.AdminTestUtil.init();
 		FetchUiSpec.getBiometricDetails("individualBiometrics");
+		System.out.println("BaseTestCase.ApplnURI : " + BaseTestCase.ApplnURI);
+		AdminTestUtil.getPreRegistrationFlow();
+		
 		File homeDir = null;
 		TestNG runner = new TestNG();
-		if(!ConfigManager.gettestcases().equals("")) {
+		if(!ArcConfigManager.gettestcases().equals("")) {
 			XmlSuite suite = new XmlSuite();
 			suite.setName("MySuite");
 			suite.addListener("regclient.utils.EmailableReport");
@@ -44,7 +55,7 @@ public static void main(String[] args) {
 
 
 			List classes = new ArrayList<>();
-			String[] Scenarionames=ConfigManager.gettestcases().split(",");
+			String[] Scenarionames=ArcConfigManager.gettestcases().split(",");
 			for(String test:Scenarionames) {
 				String Scenarioname=test.toLowerCase();
 
@@ -106,6 +117,7 @@ public static void main(String[] args) {
 		System.getProperties().setProperty("emailable.report2.name", "AndroidRegClient-" + BaseTestCase.environment + 
 				 "-run-" + System.currentTimeMillis() + "-report.html");
 		runner.run();
+		otpListener.bTerminate=true;
 		System.exit(0);
 	}
 	
diff --git a/ui-test/src/main/resources/DesiredCapabilities.json b/ui-test/src/main/resources/DesiredCapabilities.json
index 59d1b2fdc..64e978cf8 100644
--- a/ui-test/src/main/resources/DesiredCapabilities.json
+++ b/ui-test/src/main/resources/DesiredCapabilities.json
@@ -2,9 +2,9 @@
   {
     "name": "androidDevice",
     "caps": {
-      "appium:udid": "ZD222L7BGN",
+      "appium:udid": "HA1TB4W4",
       "appium:automationName": "UiAutomator2",
-      "appium:app": "D:/app-release.apk",
+      "appium:app": "E:\app-release.apk",
       "platformName": "android",
       "appium:appPackage": "io.mosip.registration_client",
       "appium:appActivity": "io.mosip.registration_client.MainActivity",
diff --git a/ui-test/src/main/resources/config.properties b/ui-test/src/main/resources/config.properties
index 959f514f7..38f26270a 100644
--- a/ui-test/src/main/resources/config.properties
+++ b/ui-test/src/main/resources/config.properties
@@ -1,5 +1,5 @@
 ipAddress=127.0.0.1
 nodePath=C:/Program Files/nodejs/node
-appiumServerExecutable=C:/Users/jayesh.kharode/AppData/Roaming/npm/node_modules/appium/build/lib/main.js
+appiumServerExecutable=C:/Users/famuda.m/AppData/Roaming/npm/node_modules/appium/build/lib/main.js
 appiumLogFilePath=report/appiumLogs.txt
 startAppiumServer=true
\ No newline at end of file
diff --git a/ui-test/src/main/resources/config/Kernel.properties b/ui-test/src/main/resources/config/Kernel.properties
index bc715fb69..025e61b6e 100644
--- a/ui-test/src/main/resources/config/Kernel.properties
+++ b/ui-test/src/main/resources/config/Kernel.properties
@@ -11,6 +11,9 @@ leafzonesURL=/v1/masterdata/zones/leafzones/eng
 zoneNameUrl=/v1/masterdata/zones/zonename
 zoneMappingActivateUrl=/v1/masterdata/zoneuser
 userCenterMappingUrl=/v1/masterdata/usercentermapping
+actuatorAdminEndpoint=/v1/admin/actuator/env
+actuatorMasterDataEndpoint=/v1/masterdata/actuator/env
+actuatorIDAEndpoint=/idauthentication/v1/actuator/env
 # OTP Details
 OTPTimeOut = 181
 attempt = 10
diff --git a/ui-test/src/main/resources/config/application.properties b/ui-test/src/main/resources/config/application.properties
index 834c7cee0..8c187fd2c 100644
--- a/ui-test/src/main/resources/config/application.properties
+++ b/ui-test/src/main/resources/config/application.properties
@@ -5,4 +5,90 @@ masterSchemaURL=/v1/masterdata/idschema/latest
 addIdentityURL=/idrepository/v1/identity/
 idRepoGenVidURL=/idrepository/v1/vid
 locationHierarchyLevels=/v1/masterdata/locationHierarchyLevels/
-locationhierarchy=/v1/masterdata/locations/locationhierarchy/
\ No newline at end of file
+locationhierarchy=/v1/masterdata/locations/locationhierarchy/
+## End point(s) relative URLs
+encryptionPath=v1/identity/encrypt?isInternal=false
+internalEncryptionPath=v1/identity/encrypt?isInternal=true
+encodePath=v1/identity/encode
+decodePath=v1/identity/decode
+signRequest=v1/identity/signRequest
+decryptPath=/idauthentication/v1/internal/decrypt
+decryptkycdataurl = v1/identity/decryptEkycData
+encodeFilePath=v1/identity/encodeFile
+decodeFilePath=v1/identity/decodeFile/?fileName=cbeff
+validateSignaturePath=v1/identity/validateSign?signature=$signature$
+splitEncryptedData=v1/identity/splitEncryptedData
+bioValueEncryptionTemplate=config/bioValueEncryptionTemplate.json
+idaMappingPath=config/mapping.properties
+getIdaCertificateUrl=/idauthentication/v1/internal/getCertificate
+getPartnerCertificateUrl=/v1/partnermanager/partners/{partnerId}/certificate
+putPartnerRegistrationUrl=/v1/partnermanager/partners
+getPartnerCertURL=v1/identity/generatePartnerKeys
+uploadCACertificateUrl=/v1/partnermanager/partners/certificate/ca/upload
+uploadIntermediateCertificateUrl=/v1/partnermanager/partners/certificate/ca/upload
+uploadPartnerCertificateUrl=/v1/partnermanager/partners/certificate/upload
+uploadSignedCertificateUrl=v1/identity/updatePartnerCertificate
+getKeyCloakTokenUrl = /auth/realms/master/protocol/openid-connect/token
+masterSchemaURL=/v1/masterdata/idschema/latest
+preregLoginConfigUrl=/preregistration/v1/login/config
+uploadIdaFirurl=v1/identity/uploadIDACertificate?certificateType=IDA_FIR&moduleName=$MODULENAME$&certsDir=$CERTSDIR$
+uploadPartnerurl=v1/identity/uploadIDACertificate?certificateType=PARTNER&moduleName=$MODULENAME$&certsDir=$CERTSDIR$
+uploadInternalurl=v1/identity/uploadIDACertificate?certificateType=INTERNAL&moduleName=$MODULENAME$&certsDir=$CERTSDIR$
+authPolicyUrl=/v1/policymanager/policies
+policyGroupUrl=/v1/policymanager/policies/group/new
+publishPolicyurl=/v1/policymanager/policies/POLICYID/group/POLICYGROUPID/publish
+clearCertificateURL=v1/identity/clearKeys?moduleName=$MODULENAME$&certsDir=$CERTSDIR$
+fetchLocationData=/v1/masterdata/locations/all
+fetchLocationLevel=/v1/masterdata/locations/level/
+fetchTitle=/v1/masterdata/title
+fetchZoneCode=/v1/masterdata/zones/hierarchy/
+fetchZone=/v1/masterdata/zones/zonename
+decryptKycUrl=/v1/identity/decryptEkycData
+retrieveIdByUin=/idrepository/v1/identity/idvid/
+fetchLocationHierarchyLevels=/v1/masterdata/locationHierarchyLevels/
+fetchLocationHierarchy=/v1/masterdata/locations/locationhierarchy/
+generateArgon2HashURL=/v1/keymanager/generateArgon2Hash
+appointmentavailabilityurl=/preregistration/v1/appointment/availability/
+validateSignatureUrl=v1/identity/validateSign
+vciContextURL=https://www.w3.org/2018/credentials/v1
+
+## As below are non changble values, move these out from properties file
+appIdForCertificate=IDA
+partnerrefId=PARTNER
+internalrefId=INTERNAL
+idaFirRefId=IDA-FIR
+proxyOTP=111111
+wrongOtp=123455
+
+## 
+regcentretobookappointment=10003
+keysToValidateInKYC=phoneNumber,emailId,age,dob,name_eng
+#partner certificate refId, used for getting the partner certificate
+partner=9998
+signatureheaderKey=response-signature
+uinGenMaxLoopCount=20
+uinGenDelayTime=10000
+Delaytime=90000
+
+
+## Remove this from properties file
+picturevalue=iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAABCSURBVChTbYtBEgAgCAL7/6eNhBy09qDi6gpjXZSxUU8o/jrfpDmcmY1QAOWhgTswv6sSm8zVhULlgst++8T51IjYNUHdI+4XZHoAAAAASUVORK5CYII=
+
+
+## Check are we using these properties. If not remove them
+zoneCode_to_beMapped=NTH
+expireOtpTime=180000
+demoAppVersion=1.2.1-java21-SNAPSHOT
+AttributetoBeUpdate:Name
+ValuetoBeUpdate:Sohan
+
+## As these will be based on regEx, move these out from proprties file
+passwordForAddIdentity=12341234_Aa
+passwordToReset=12341234_AaB
+
+## Need to revisit these propeties
+XSRFTOKEN=7d01b2a8-b89d-41ad-9361-d7f6294021d1
+codeChallenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
+codeVerifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
+policyNumberForSunBirdRC=654321
+challengeValueForSunBirdRC=eyJmdWxsTmFtZSI6IkthaWYgU2lkZGlxdWUiLCJkb2IiOiIyMDAwLTA3LTI2In0=
\ No newline at end of file
diff --git a/ui-test/src/main/resources/config/valueMapping.properties b/ui-test/src/main/resources/config/valueMapping.properties
new file mode 100644
index 000000000..c9b1c8ebb
--- /dev/null
+++ b/ui-test/src/main/resources/config/valueMapping.properties
@@ -0,0 +1,35 @@
+# common properties
+residenceStatus=NFR
+fullName=TEST_FULLNAME
+firstName=TEST_FIRSTNAME
+dateOfBirth=1996/01/01
+gender=MLE
+pobCountry=POB_COUNTRY
+pobProvince=POB_PROVINCE
+pobCity=POB_CITY
+bloodType=BLOOD_TYPE
+permanentCountry=PERMANENT_COUNTRY
+permanentAddressLine1=PERMANENT_ADDRESS_LINE_1
+presentAddressLine1=PRESENT_ADDRESS_LINE_1
+modeOfClaim=MODE_OF_CLAIM
+presentCountry=PRESENT_COUNTRY
+addressLine1=TEST_ADDRESSLINE1
+addressLine2=TEST_ADDRESSLINE2
+addressLine3=TEST_ADDRESSLINE3
+postalCode=14022
+phone=8249742850
+email=test@mosip.net
+region=TEST_REGION
+fullName1=Lisa.GN
+firstName1=Lisa.GN
+province=TEST_PROVINCE
+city=TEST_CITY
+zone=TEST_ZONE
+introducerName=TEST_INTRODUCERNAME
+bloodGroup=O
+Miss-TitleFromServer=MIS
+Mr-TitleFromServer=MIR
+Mrs-TitleFromServer=MRS
+Miss-TitleFromServer.=MIS
+addressCopy=TEST_ADDRESSLINE3
+proofOfAddress=TEST_ADDRESSLINE2
\ No newline at end of file