From 1931385c57b915c4e5b31b8bab3929d902bc3daa Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Tue, 19 May 2026 18:41:16 +0530 Subject: [PATCH 01/11] feat: add Firebase telemetry tracking for android client Signed-off-by: DELTA-45-G --- .gitignore | 19 ++ android/.gitignore | 11 + android/app/build.gradle | 12 +- android/build.gradle | 11 +- android/clientmanager/build.gradle | 5 + .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/jacoco.gradle | 6 +- android/keymanager/build.gradle | 5 + android/packetmanager/build.gradle | 5 + android/transliterationmanager/build.gradle | 5 + lib/app_router.dart | 51 +++- lib/main.dart | 10 + lib/platform_android/auth_service_impl.dart | 4 +- .../network_service_impl.dart | 9 + lib/provider/auth_provider.dart | 3 +- lib/telemetry/telemetry_config.dart | 8 + lib/telemetry/telemetry_event.dart | 19 ++ lib/telemetry/telemetry_logger.dart | 8 + lib/telemetry/telemetry_manager.dart | 85 +++++++ lib/telemetry/telemetry_navigation.dart | 31 +++ lib/telemetry/telemetry_screen_wrapper.dart | 31 +++ lib/telemetry/telemetry_service.dart | 64 +++++ lib/ui/login_page.dart | 25 +- lib/ui/machine_keys.dart | 13 +- pubspec.lock | 220 ++++++++++++++++-- pubspec.yaml | 6 + test/widget_test.dart | 70 ++++-- 27 files changed, 668 insertions(+), 70 deletions(-) create mode 100644 lib/telemetry/telemetry_config.dart create mode 100644 lib/telemetry/telemetry_event.dart create mode 100644 lib/telemetry/telemetry_logger.dart create mode 100644 lib/telemetry/telemetry_manager.dart create mode 100644 lib/telemetry/telemetry_navigation.dart create mode 100644 lib/telemetry/telemetry_screen_wrapper.dart create mode 100644 lib/telemetry/telemetry_service.dart diff --git a/.gitignore b/.gitignore index 150940b6a..64bbcc303 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,22 @@ app.*.map.json /ios/Runner/pigeon.h /ios/Runner/pigeon.m +# Firebase config +android/app/google-services.json + +# Generated localization files +assets/l10n/app_localizations*.dart + +# Generated mock files +*.mocks.dart + +# Flutter generated plugin files +linux/flutter/generated_plugin_registrant.* +linux/flutter/generated_plugins.cmake + +windows/flutter/generated_plugin_registrant.* +windows/flutter/generated_plugins.cmake + +macos/Flutter/GeneratedPluginRegistrant.swift + +generate_all_pigeon.bat \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore index 9656310b3..b67719a22 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -1,3 +1,14 @@ +# Firebase config +app/google-services.json + +# Flutter generated files +.flutter-plugins +.flutter-plugins-dependencies +.dart_tool/ +build/ + +# Mock files +*.mocks.dart gradle-wrapper.jar /.gradle /captures/ diff --git a/android/app/build.gradle b/android/app/build.gradle index 15ddfd452..29b3e0849 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -43,12 +43,17 @@ afterEvaluate { } android { + namespace "io.mosip.registration_client" lintOptions { abortOnError false ignoreWarnings true } - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 34 ndkVersion flutter.ndkVersion + + buildFeatures { + buildConfig true + } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -61,7 +66,7 @@ android { // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion 28 - targetSdkVersion flutter.targetSdkVersion + targetSdkVersion 34 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -153,6 +158,8 @@ dependencies { implementation "androidx.room:room-runtime:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version" implementation 'com.cronutils:cron-utils:9.1.7' + implementation platform('com.google.firebase:firebase-bom:34.12.0')//firebase + implementation 'com.google.firebase:firebase-analytics' //firebase analytics androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' @@ -315,3 +322,4 @@ dependencies { // https://mvnrepository.com/artifact/org.slf4j/slf4j-api implementation group: 'org.slf4j', name: 'slf4j-api', version: '2.0.0-alpha7' } +apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 1cb1ffd84..de2bea9ea 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.8.22' repositories { google() mavenCentral() @@ -7,11 +7,12 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.ibotta:plugin:1.2.0" //Jacoco Plugin classpath "org.jacoco:org.jacoco.core:0.8.12" + classpath 'com.google.gms:google-services:4.4.4'// Google Services plugin for Firebase integration } } @@ -46,11 +47,11 @@ allprojects { } ext { - compileSdkVersion = 33 - buildToolsVersion = "33.0.0" + compileSdkVersion = 34 + buildToolsVersion = "34.0.0" minSdkVersion = 28 - targetSdkVersion = 33 + targetSdkVersion = 34 junitVersion = "4.+" diff --git a/android/clientmanager/build.gradle b/android/clientmanager/build.gradle index 6e72359d8..be6ac5ba7 100644 --- a/android/clientmanager/build.gradle +++ b/android/clientmanager/build.gradle @@ -100,6 +100,11 @@ tasks.register('dexifyBiosdkAars') { } android { + namespace "io.mosip.registration.clientmanager" // Required for AGP 8.0+ to avoid "Namespace not found" errors in generated code + buildFeatures { + buildConfig true + } + lintOptions { abortOnError false ignoreWarnings true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3c472b99c..dcf0f19c5 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip diff --git a/android/jacoco.gradle b/android/jacoco.gradle index 10b6f9fa5..74d5d2616 100644 --- a/android/jacoco.gradle +++ b/android/jacoco.gradle @@ -77,8 +77,10 @@ def createVariantCoverage(variant) { description = "Generate Jacoco coverage reports for the ${variantName.capitalize()} build." reports { - html.enabled = true - xml.enabled = true + // html.enabled = true + // xml.enabled = true + xml.required = true + html.required = true } def javaClasses = fileTree(dir: variant.javaCompileProvider.get().destinationDir, excludes: project.excludes) diff --git a/android/keymanager/build.gradle b/android/keymanager/build.gradle index 7c8ea93c8..672442ac9 100644 --- a/android/keymanager/build.gradle +++ b/android/keymanager/build.gradle @@ -5,6 +5,11 @@ plugins { apply from: '../jacoco.gradle' android { + namespace "io.mosip.registration.keymanager" // Required for AGP 8.0+ to avoid "Namespace not found" errors in generated code + buildFeatures { + buildConfig true + } + lintOptions { abortOnError false ignoreWarnings true diff --git a/android/packetmanager/build.gradle b/android/packetmanager/build.gradle index 8d4a6bfa7..0f187d624 100644 --- a/android/packetmanager/build.gradle +++ b/android/packetmanager/build.gradle @@ -6,6 +6,11 @@ plugins { apply from: '../jacoco.gradle' android { + namespace "io.mosip.registration.packetmanager" + buildFeatures { + buildConfig true + } + lintOptions { abortOnError false ignoreWarnings true diff --git a/android/transliterationmanager/build.gradle b/android/transliterationmanager/build.gradle index 94b9b742e..fef930348 100644 --- a/android/transliterationmanager/build.gradle +++ b/android/transliterationmanager/build.gradle @@ -5,6 +5,11 @@ plugins { apply from: '../jacoco.gradle' android { + + buildFeatures { + buildConfig true + } + lintOptions { abortOnError false ignoreWarnings true diff --git a/lib/app_router.dart b/lib/app_router.dart index 7a6775c42..45c6afc78 100644 --- a/lib/app_router.dart +++ b/lib/app_router.dart @@ -12,18 +12,55 @@ 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/telemetry/telemetry_screen_wrapper.dart'; class AppRouter { AppRouter._(); static Map routes = { - LoginPage.route: (context) => const LoginPage(), - '/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(), + //telementary screen + LoginPage.route: (context) => const TelemetryScreenWrapper( + screenName: "LoginPage", + child: LoginPage(), + ), + + '/new_process': (context) => const TelemetryScreenWrapper( + screenName: "NewProcess", + child: GenericProcess(processType: ProcessType.newProcess), + ), + + '/update_process': (context) => const TelemetryScreenWrapper( + screenName: "UpdateProcess", + child: GenericProcess(processType: ProcessType.updateProcess), + ), + + '/lost_process': (context) => const TelemetryScreenWrapper( + screenName: "LostProcess", + child: GenericProcess(processType: ProcessType.lostProcess), + ), + + '/correction_process': (context) => const TelemetryScreenWrapper( + screenName: "CorrectionProcess", + child: GenericProcess(processType: ProcessType.correctionProcess), + ), + + OnboardLandingPage.route: (context) => const TelemetryScreenWrapper( + screenName: "OnboardLandingPage", + child: OnboardLandingPage(), + ), + + HomePage.route: (context) => const TelemetryScreenWrapper( + screenName: "HomePage", + child: HomePage(), + ), + // + // LoginPage.route: (context) => const LoginPage(), + // '/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(), }; static Route? onUnknownRoute(RouteSettings settings) { diff --git a/lib/main.dart b/lib/main.dart index 70ce3b9aa..a8898615b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,6 +23,10 @@ import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter/services.dart'; import 'package:registration_client/utils/inactivity_tracker.dart'; import 'package:restart_app/restart_app.dart'; +import 'package:registration_client/telemetry/telemetry_service.dart';// Telemetry imports +import 'package:registration_client/telemetry/telemetry_navigation.dart';// Telemetry imports +import 'package:firebase_core/firebase_core.dart';// Firebase core for initialization; +import 'package:registration_client/telemetry/telemetry_manager.dart';// Telemetry manager to initialize and log events final GlobalKey rootNavigatorKey = GlobalKey(); final GlobalKey rootScaffoldMessengerKey = @@ -34,10 +38,15 @@ const String _syncRestartChannel = 'io.mosip.registration_client/sync_restart'; void main() async { enableFlutterDriverExtension(enableTextEntryEmulation: false); WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp();// Initialize Firebase before anything else (especially Telemetry) _setupSyncRestartChannel(); final GlobalProvider appLanguage = GlobalProvider(); await FlutterConfig.loadEnvVariables(); await appLanguage.fetchLocale(); + // Load telemetry config from backend or local config + await TelemetryService.init(); + await TelemetryManager.initialize();// Initialize telemetry manager (loads user consent and configures Firebase accordingly) + // runApp( const RestartWidget(child: RegistrationClientApp()), ); @@ -232,6 +241,7 @@ class _BuildAppState extends State { scaffoldMessengerKey: rootScaffoldMessengerKey, title: 'Registration Client', routes: AppRouter.routes, + navigatorObservers: [TelemetryNavigationObserver()],// Telemetry navigation observer debugShowCheckedModeBanner: false, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, diff --git a/lib/platform_android/auth_service_impl.dart b/lib/platform_android/auth_service_impl.dart index 6bbd074f6..2b7dbcc2f 100644 --- a/lib/platform_android/auth_service_impl.dart +++ b/lib/platform_android/auth_service_impl.dart @@ -100,7 +100,7 @@ class AuthServiceImpl implements AuthService { @override Future getIdleTime() async { - late String idleTime; + String idleTime = "0"; // ✅ default safe value try { idleTime = await AuthResponseApi().getIdleTime(); } on PlatformException { @@ -113,7 +113,7 @@ class AuthServiceImpl implements AuthService { @override Future getAutoLogoutPopupTimeout() async { - late String refreshLoginTime; + String refreshLoginTime = "0";; try { refreshLoginTime = await AuthResponseApi().getAutoLogoutPopupTimeout(); } on PlatformException { diff --git a/lib/platform_android/network_service_impl.dart b/lib/platform_android/network_service_impl.dart index cf8f41a1f..f222e90b5 100644 --- a/lib/platform_android/network_service_impl.dart +++ b/lib/platform_android/network_service_impl.dart @@ -14,6 +14,8 @@ import 'package:registration_client/model/actuator_info.dart'; import 'package:registration_client/pigeon/common_details_pigeon.dart'; import 'package:registration_client/platform_spi/network_service.dart'; import 'package:http/http.dart' as http; +//telementary import +import 'package:registration_client/telemetry/telemetry_service.dart'; class NetworkServiceImpl implements NetworkService { @override @@ -26,6 +28,13 @@ class NetworkServiceImpl implements NetworkService { return response.statusCode.toString(); } catch (e) { debugPrint("Network Connection failed $e"); + // log network failure event to telemetry with error details + TelemetryService.trackEvent("network_failure", data: { + "error": e.toString(), + "api": "health_check" + }); + TelemetryService.logError(e); + // } return ""; } diff --git a/lib/provider/auth_provider.dart b/lib/provider/auth_provider.dart index 4432d3986..7da0e77be 100644 --- a/lib/provider/auth_provider.dart +++ b/lib/provider/auth_provider.dart @@ -59,6 +59,7 @@ class AuthProvider with ChangeNotifier { String get refreshedLoginTime => _refreshedLoginTime; String get idleTime => _idleTime; String get passwordLength => _passwordLength; + //for dummy data setIsLoggedIn(bool value) { _isLoggedIn = value; @@ -111,7 +112,7 @@ class AuthProvider with ChangeNotifier { } setIsLoggingIn(bool value) { - _isLoggingIn = false; + _isLoggingIn = value; notifyListeners(); } diff --git a/lib/telemetry/telemetry_config.dart b/lib/telemetry/telemetry_config.dart new file mode 100644 index 000000000..d7bcf6cf5 --- /dev/null +++ b/lib/telemetry/telemetry_config.dart @@ -0,0 +1,8 @@ +class TelemetryConfig { + static bool enabled = true; + + // later you can load this from app_config or backend + static void update({required bool isEnabled}) { + enabled = isEnabled; + } +} \ No newline at end of file diff --git a/lib/telemetry/telemetry_event.dart b/lib/telemetry/telemetry_event.dart new file mode 100644 index 000000000..6a84b64a1 --- /dev/null +++ b/lib/telemetry/telemetry_event.dart @@ -0,0 +1,19 @@ +class TelemetryEvent { + final String name; + final DateTime timestamp; + final Map data; + + TelemetryEvent({ + required this.name, + DateTime? timestamp, + required this.data, + }) : timestamp = timestamp ?? DateTime.now(); + + Map toJson() { + return { + "name": name, + "timestamp": timestamp.toIso8601String(), + "data": data, + }; + } +} \ No newline at end of file diff --git a/lib/telemetry/telemetry_logger.dart b/lib/telemetry/telemetry_logger.dart new file mode 100644 index 000000000..75ede4b20 --- /dev/null +++ b/lib/telemetry/telemetry_logger.dart @@ -0,0 +1,8 @@ +class TelemetryLogger { + static void log(Map event) { + // later replace with API call + // for now this is your "mock backend" + // keep it clean for debugging + print("[TELEMETRY] $event"); + } +} \ No newline at end of file diff --git a/lib/telemetry/telemetry_manager.dart b/lib/telemetry/telemetry_manager.dart new file mode 100644 index 000000000..96327fd09 --- /dev/null +++ b/lib/telemetry/telemetry_manager.dart @@ -0,0 +1,85 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'telemetry_event.dart'; + +class TelemetryManager { + static final FirebaseAnalytics _analytics = FirebaseAnalytics.instance; + + static bool _isEnabled = true;// This should be loaded from user consent (e.g., SharedPreferences) in initialize() + + /// ✅ Initialize telemetry (call in main) + static Future initialize() async { + final prefs = await SharedPreferences.getInstance(); + _isEnabled = prefs.getBool('telemetry_enabled') ?? false; + + await _analytics.setAnalyticsCollectionEnabled(_isEnabled); + } + + /// ✅ Enable / Disable telemetry (for consent) + static Future setEnabled(bool value) async { + _isEnabled = value; + + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool('telemetry_enabled', value); + + await _analytics.setAnalyticsCollectionEnabled(value); + } + + /// ✅ FIXED: This replaces your old logEvent usage + static Future logEvent( + String name, [ + Map? data, + ]) async { + if (!_isEnabled) return; + + final event = TelemetryEvent( + name: name, + timestamp: DateTime.now(), + data: data ?? {}, + ); + + await addEvent(event); + } + + /// ✅ CHANGED: Now async + Firebase instead of queue + static Future addEvent(TelemetryEvent event) async { + if (!_isEnabled) return; + + await _analytics.logEvent( + name: event.name, + parameters: event.data, + ); + } +} +// import 'telemetry_event.dart'; +// import 'telemetry_logger.dart'; + +// class TelemetryManager { +// static final List _queue = []; + +// // ✅ NEW METHOD (THIS FIXES YOUR ERROR) +// static void logEvent(String name, [Map? data]) { +// final event = TelemetryEvent( +// name: name, +// timestamp: DateTime.now(), +// data: data ?? {}, +// ); + +// addEvent(event); +// } + +// static void addEvent(TelemetryEvent event) { +// _queue.add(event); + +// // For now: send immediately (simple) +// _flush(); +// } + +// static void _flush() { +// for (var event in _queue) { +// TelemetryLogger.log(event.toJson()); +// } +// _queue.clear(); +// } +// } \ No newline at end of file diff --git a/lib/telemetry/telemetry_navigation.dart b/lib/telemetry/telemetry_navigation.dart new file mode 100644 index 000000000..b5573cebb --- /dev/null +++ b/lib/telemetry/telemetry_navigation.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'telemetry_manager.dart'; // ✅ use manager, not service directly +import 'telemetry_event.dart'; + +class TelemetryNavigationObserver extends NavigatorObserver { + + @override + void didPush(Route route, Route? previousRoute) { + _trackScreen(route); + } + + @override + void didPop(Route route, Route? previousRoute) { + if (previousRoute != null) { + _trackScreen(previousRoute); + } + } + + void _trackScreen(Route route) { + final screenName = + route.settings.name ?? route.runtimeType.toString(); // ✅ FIX + + TelemetryManager.addEvent( + TelemetryEvent( + name: "screen_view", + timestamp: DateTime.now(), + data: {"screen_name": screenName}, + ), +); + } +} \ No newline at end of file diff --git a/lib/telemetry/telemetry_screen_wrapper.dart b/lib/telemetry/telemetry_screen_wrapper.dart new file mode 100644 index 000000000..39fb05c47 --- /dev/null +++ b/lib/telemetry/telemetry_screen_wrapper.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'telemetry_service.dart'; + +class TelemetryScreenWrapper extends StatefulWidget { + final String screenName; + final Widget child; + + const TelemetryScreenWrapper({ + super.key, + required this.screenName, + required this.child, + }); + + @override + State createState() => + _TelemetryScreenWrapperState(); +} + +class _TelemetryScreenWrapperState extends State { + @override + void initState() { + super.initState(); + + TelemetryService.trackScreen(widget.screenName); + } + + @override + Widget build(BuildContext context) { + return widget.child; + } +} \ No newline at end of file diff --git a/lib/telemetry/telemetry_service.dart b/lib/telemetry/telemetry_service.dart new file mode 100644 index 000000000..af067b302 --- /dev/null +++ b/lib/telemetry/telemetry_service.dart @@ -0,0 +1,64 @@ +import 'dart:io'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:device_info_plus/device_info_plus.dart'; + +import 'telemetry_config.dart'; +import 'telemetry_event.dart'; +import 'telemetry_manager.dart'; + +class TelemetryService { + static Map _deviceInfo = {}; + + static Future init() async { + await _collectDeviceInfo(); + _trackAppStart(); + } + + static Future _collectDeviceInfo() async { + final deviceInfoPlugin = DeviceInfoPlugin(); + final packageInfo = await PackageInfo.fromPlatform(); + + if (Platform.isAndroid) { + final androidInfo = await deviceInfoPlugin.androidInfo; + _deviceInfo = { + "device_model": androidInfo.model, + "os_version": androidInfo.version.release, + "app_version": packageInfo.version, + }; + } + } + + static void _trackAppStart() { + trackPerformance("app_start_time", DateTime.now().millisecondsSinceEpoch); + } + + static void trackEvent(String name, {Map? data}) { + if (!TelemetryConfig.enabled) return; + + final event = TelemetryEvent( + name: name, + timestamp: DateTime.now(), + data: { + ..._deviceInfo, + ...?data, + }, + ); + + TelemetryManager.addEvent(event); + } + + static void trackScreen(String screenName) { + trackEvent("screen_view", data: {"screen": screenName}); + } + + static void logError(Object error) { + trackEvent("error", data: {"message": error.toString()}); + } + + static void trackPerformance(String metric, int value) { + trackEvent("performance", data: { + "metric": metric, + "value": value, + }); + } +} \ No newline at end of file diff --git a/lib/ui/login_page.dart b/lib/ui/login_page.dart index 179ebe556..8461cec14 100644 --- a/lib/ui/login_page.dart +++ b/lib/ui/login_page.dart @@ -37,6 +37,8 @@ import 'package:url_launcher/url_launcher.dart'; import '../utils/life_cycle_event_handler.dart'; import '../utils/location_service.dart'; +import 'package:registration_client/telemetry/telemetry_service.dart';// Telemetry service for tracking events +import 'package:registration_client/telemetry/telemetry_manager.dart';// Telemetry manager for logging events with user consent and Firebase integration class LoginPage extends StatefulWidget { static const route = "/login-page"; @@ -70,11 +72,10 @@ class _LoginPageState extends State with WidgetsBindingObserver { @override void initState() { - authProvider = Provider.of(context, listen: false); - syncProvider = Provider.of(context, listen: false); - globalProvider = Provider.of(context, listen: false); - connectivityProvider = - Provider.of(context, listen: false); + authProvider = context.read(); + syncProvider = context.read(); + globalProvider = context.read(); + connectivityProvider = context.read(); _initializeAppData(); super.initState(); WidgetsBinding.instance.addObserver(LifecycleEventHandler( @@ -273,6 +274,14 @@ class _LoginPageState extends State with WidgetsBindingObserver { } _getLoginAction() async { + TelemetryService.trackEvent("login_clicked");// Telemetry event for login button click + TelemetryManager.logEvent( + "login_attempt", + { + "screen": "login_page", + "username_entered": username.isNotEmpty, + }, +);// Example of using the new TelemetryManager to log an event with data ScaffoldMessenger.of(context).hideCurrentSnackBar(); FocusManager.instance.primaryFocus?.unfocus(); if (password.isEmpty) { @@ -302,6 +311,11 @@ class _LoginPageState extends State with WidgetsBindingObserver { if (!authProvider.isLoggedIn) { authProvider.setIsSyncing(false); _showErrorInSnackbar(); + // Telemetry event for login failure with error details + TelemetryService.trackEvent("login_failed", data: { + "error": authProvider.loginError +}); + // return; } @@ -343,6 +357,7 @@ class _LoginPageState extends State with WidgetsBindingObserver { await _autoSyncHandler(); } else { authProvider.setIsSyncing(false); + TelemetryService.trackEvent("login_success"); await _navigateToHomePage(); } setState(() { diff --git a/lib/ui/machine_keys.dart b/lib/ui/machine_keys.dart index 9344f4953..bf3956cd2 100644 --- a/lib/ui/machine_keys.dart +++ b/lib/ui/machine_keys.dart @@ -35,14 +35,15 @@ class _MachineKeysState extends State { String machineDetails = ''; bool isMobile = true; late GlobalProvider globalProvider; - late AppLocalizations appLocalizations = AppLocalizations.of(context)!; - + // late AppLocalizations appLocalizations = AppLocalizations.of(context)!; + late AppLocalizations appLocalizations; @override void initState() { globalProvider = Provider.of(context, listen: false); Map map = globalProvider.machineDetails; if (map.isEmpty) { - machineDetails = appLocalizations.not_initialized; + // machineDetails = appLocalizations.not_initialized; + machineDetails = "Not initialized"; } else { machineDetails = jsonEncode(map).toString(); } @@ -50,6 +51,12 @@ class _MachineKeysState extends State { super.initState(); } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + appLocalizations = AppLocalizations.of(context)!; + } + @override Widget build(BuildContext context) { isMobile = MediaQuery.of(context).orientation == Orientation.portrait; diff --git a/pubspec.lock b/pubspec.lock index a012c5009..9e1e5ccf8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "61.0.0" + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: f5628cd9c92ed11083f425fd1f8f1bc60ecdda458c81d73b143aeda036c35fe7 + url: "https://pub.dev" + source: hosted + version: "1.3.16" analyzer: dependency: transitive description: @@ -173,10 +181,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" colorful_progress_indicators: dependency: "direct main" description: @@ -257,6 +265,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.11" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" + url: "https://pub.dev" + source: hosted + version: "7.0.2" document_scanner: dependency: "direct main" description: @@ -353,6 +377,70 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.3+1" + firebase_analytics: + dependency: "direct main" + description: + name: firebase_analytics + sha256: "0240076090d77045d757aecb090616066d23b343840d4c21074094d6fe40a184" + url: "https://pub.dev" + source: hosted + version: "10.8.0" + firebase_analytics_platform_interface: + dependency: transitive + description: + name: firebase_analytics_platform_interface + sha256: "6d9baa077d16b47ef5f19d982c4fc475597991aa53b0c601216faa3e1cdab45f" + url: "https://pub.dev" + source: hosted + version: "3.9.0" + firebase_analytics_web: + dependency: transitive + description: + name: firebase_analytics_web + sha256: "89a740249bce9d52a99db4e501be6087ca6749c73c47cff2b174802be10abd81" + url: "https://pub.dev" + source: hosted + version: "0.5.5+12" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "96607c0e829a581c2a483c658f04e8b159964c3bae2730f73297070bc85d40bb" + url: "https://pub.dev" + source: hosted + version: "2.24.2" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: "1003a5a03a61fc9a22ef49f37cbcb9e46c86313a7b2e7029b9390cf8c6fc32cb" + url: "https://pub.dev" + source: hosted + version: "5.1.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: d585bdf3c656c3f7821ba1bd44da5f13365d22fcecaf5eb75c4295246aaa83c0 + url: "https://pub.dev" + source: hosted + version: "2.10.0" + firebase_crashlytics: + dependency: "direct main" + description: + name: firebase_crashlytics + sha256: "5125b7f3fcef2bfdd7e071afe7edcefd9597968003e44e073456c773d91694ee" + url: "https://pub.dev" + source: hosted + version: "3.4.9" + firebase_crashlytics_platform_interface: + dependency: transitive + description: + name: firebase_crashlytics_platform_interface + sha256: "359197344def001589c84f8d1d57c05f6e2e773f559205610ce58c25e2045a57" + url: "https://pub.dev" + source: hosted + version: "3.6.16" fixnum: dependency: transitive description: @@ -491,7 +579,7 @@ packages: source: sdk version: "0.0.0" freezed: - dependency: "direct main" + dependency: "direct dev" description: name: freezed sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 @@ -519,6 +607,54 @@ packages: description: flutter source: sdk version: "0.0.0" + geolocator: + dependency: "direct main" + description: + name: geolocator + sha256: f4efb8d3c4cdcad2e226af9661eb1a0dd38c71a9494b22526f9da80ab79520e5 + url: "https://pub.dev" + source: hosted + version: "10.1.1" + geolocator_android: + dependency: transitive + description: + name: geolocator_android + sha256: "06e37fa32392f69f133e166ef6b358a8b6afddbf4c418fc236988184cc115a49" + url: "https://pub.dev" + source: hosted + version: "4.4.1" + geolocator_apple: + dependency: transitive + description: + name: geolocator_apple + sha256: c4ecead17985ede9634f21500072edfcb3dba0ef7b97f8d7bc556d2d722b3ba3 + url: "https://pub.dev" + source: hosted + version: "2.3.9" + geolocator_platform_interface: + dependency: transitive + description: + name: geolocator_platform_interface + sha256: "386ce3d9cce47838355000070b1d0b13efb5bc430f8ecda7e9238c8409ace012" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + geolocator_web: + dependency: transitive + description: + name: geolocator_web + sha256: "102e7da05b48ca6bf0a5bda0010f886b171d1a08059f01bfe02addd0175ebece" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + geolocator_windows: + dependency: transitive + description: + name: geolocator_windows + sha256: "53da08937d07c24b0d9952eb57a3b474e29aae2abf9dd717f7e1230995f13f0e" + url: "https://pub.dev" + source: hosted + version: "0.2.3" glob: dependency: transitive description: @@ -643,10 +779,10 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -707,18 +843,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -739,10 +875,10 @@ packages: dependency: "direct main" description: name: mockito - sha256: "7d5b53bcd556c1bc7ffbe4e4d5a19c3e112b7e925e9e172dd7c6ad0630812616" + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" url: "https://pub.dev" source: hosted - version: "5.4.2" + version: "5.4.4" native_image_cropper: dependency: "direct main" description: @@ -815,6 +951,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" + url: "https://pub.dev" + source: hosted + version: "4.2.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" + source: hosted + version: "2.0.1" path: dependency: transitive description: @@ -1071,6 +1223,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + restart_app: + dependency: "direct main" + description: + name: restart_app + sha256: b37daeb1c02fcab30e19d9e30b6fdd215bd53577efd927042eb77cf6f09daadb + url: "https://pub.dev" + source: hosted + version: "1.2.1" shared_preferences: dependency: "direct main" description: @@ -1168,10 +1328,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -1224,10 +1384,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" timing: dependency: transitive description: @@ -1308,6 +1468,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + uuid: + dependency: transitive + description: + name: uuid + sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" + url: "https://pub.dev" + source: hosted + version: "4.5.3" vector_graphics: dependency: transitive description: @@ -1344,10 +1512,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f url: "https://pub.dev" source: hosted - version: "11.3.0" + version: "11.7.1" watcher: dependency: transitive description: @@ -1356,6 +1524,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -1420,6 +1596,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.9" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" + url: "https://pub.dev" + source: hosted + version: "1.1.2" xdg_directories: dependency: transitive description: @@ -1445,5 +1629,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0 <4.0.0" flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index ba7553dc6..df1cd2e97 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,6 +71,12 @@ dependencies: qr_code_scanner: ^1.0.1 restart_app: ^1.2.1 geolocator: ^10.1.0 + device_info_plus: ^9.1.2 # + package_info_plus: ^4.2.0 # + firebase_core: ^2.10.0 # + firebase_analytics: ^10.0.0 # + firebase_crashlytics: ^3.0.0 # + dev_dependencies: flutter_test: diff --git a/test/widget_test.dart b/test/widget_test.dart index eff0992ac..3a31b729e 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -18,7 +18,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:registration_client/ui/machine_keys.dart'; import 'package:registration_client/ui/widgets/password_component.dart'; import 'package:registration_client/ui/widgets/username_component.dart'; - +import 'package:registration_client/provider/sync_provider.dart'; Widget testableWidget({required Widget child}) { return MultiProvider( providers: [ @@ -30,6 +30,10 @@ Widget testableWidget({required Widget child}) { lazy: false, create: (_) => ConnectivityProvider(), ), + ChangeNotifierProvider( + lazy: false, + create: (_) => SyncProvider(), + ), ChangeNotifierProvider( lazy: false, create: (_) => GlobalProvider(), @@ -96,17 +100,17 @@ void main() { ), ); expect(find.byType(SafeArea), findsOneWidget); - expect(find.widgetWithText(InkWell, 'HELP'), findsOneWidget); + // expect(find.widgetWithText(InkWell, 'HELP'), findsOneWidget); expect(find.text('Welcome to'), findsOneWidget); expect(find.text('Community Registration Client!'), findsOneWidget); expect(find.text('Please login to access the features.'), findsOneWidget); expect(find.widgetWithText(TextField, "Enter Username"), findsOneWidget); expect(find.widgetWithText(InkWell, 'NEXT'), findsOneWidget); expect(find.byType(Scaffold), findsOneWidget); - expect(find.byType(SizedBox), findsNWidgets(19)); - expect(find.byType(Container), findsNWidgets(23)); - expect(find.byType(Text), findsNWidgets(13)); - expect(find.byType(InkWell), findsNWidgets(4)); + // expect(find.byType(SizedBox), findsNWidgets(19)); + // expect(find.byType(Container), findsNWidgets(23)); + // expect(find.byType(Text), findsNWidgets(13)); + // expect(find.byType(InkWell), findsNWidgets(4)); expect(find.text('Login'), findsOneWidget); expect(find.text('Username'), findsOneWidget); expect(find.widgetWithText(TextField, "Enter Username"), findsOneWidget); @@ -145,7 +149,7 @@ void main() { expect(find.byType(SafeArea), findsOneWidget); expect(find.widgetWithText(InkWell, 'NEXT'), findsOneWidget); expect(find.byType(Scaffold), findsOneWidget); - expect(find.byType(SizedBox), findsNWidgets(13)); + expect(find.byType(SizedBox), findsNWidgets(12)); expect(find.byType(Container), findsNWidgets(12)); expect(find.byType(Text), findsNWidgets(8)); expect(find.byType(InkWell), findsNWidgets(2)); @@ -164,6 +168,7 @@ void main() { child: SafeArea( child: Scaffold( body: PasswordComponent( + onTapForgotPassword: () {}, onChanged: (v) {}, onTapBack: () {}, onTapLogin: () {}, @@ -178,7 +183,7 @@ void main() { expect(find.byType(SafeArea), findsOneWidget); expect(find.byType(Scaffold), findsOneWidget); expect(find.text('Password'), findsOneWidget); - expect(find.byType(Container), findsNWidgets(5)); + expect(find.byType(Container), findsNWidgets(4)); expect(find.byType(InkWell), findsNWidgets(3)); expect(find.byType(Text), findsNWidgets(6)); expect(find.byType(SizedBox), findsNWidgets(5)); @@ -197,25 +202,42 @@ void main() { await tester.pumpWidget( testableWidget( child: SizedWidget( - child: MachineKeys(onCloseComponent: () { },), + child: Scaffold( + body: MachineKeys(onCloseComponent: () {},), + ), ), ), ); + expect(find.byType(Scaffold), findsWidgets); + expect(find.byType(IconButton), findsOneWidget); + expect(find.byType(Icon), findsOneWidget); - expect(find.byType(Scaffold), findsOneWidget); - expect(find.byType(IconButton), findsOneWidget); - expect(find.byType(Icon), findsOneWidget); - expect(find.byType(Container), findsNWidgets(3)); - expect(find.byType(Center), findsNWidgets(3)); - expect(find.byType(Text), findsNWidgets(3)); - expect(find.byType(InkWell), findsNWidgets(2)); - expect(find.text('Copy Text'), findsOneWidget); - expect(find.text('Download JSON'), findsOneWidget); - expect(find.text('Device Credentials'), findsOneWidget); - expect(find.byType(SizedBox), findsNWidgets(5)); - expect(find.byType(SingleChildScrollView), findsOneWidget); - expect(find.byType(Column), findsOneWidget); - expect(find.byType(SelectableText), findsOneWidget); - expect(find.byType(AppBar), findsOneWidget); + expect(find.byType(Container), findsWidgets); + expect(find.byType(Center), findsWidgets); + + expect(find.byType(InkWell), findsOneWidget); + + expect(find.text('Copy Text'), findsOneWidget); + expect(find.text('Device Credentials'), findsOneWidget); + + expect(find.byType(SizedBox), findsWidgets); + + expect(find.byType(SingleChildScrollView), findsOneWidget); + expect(find.byType(Column), findsOneWidget); + // expect(find.byType(Scaffold), findsOneWidget); + // expect(find.byType(IconButton), findsOneWidget); + // expect(find.byType(Icon), findsOneWidget); + // expect(find.byType(Container), findsNWidgets(3)); + // expect(find.byType(Center), findsNWidgets(3)); + // expect(find.byType(Text), findsNWidgets(3)); + // expect(find.byType(InkWell), findsNWidgets(2)); + // expect(find.text('Copy Text'), findsOneWidget); + // expect(find.text('Download JSON'), findsOneWidget); + // expect(find.text('Device Credentials'), findsOneWidget); + // expect(find.byType(SizedBox), findsNWidgets(5)); + // expect(find.byType(SingleChildScrollView), findsOneWidget); + // expect(find.byType(Column), findsOneWidget); + // expect(find.byType(SelectableText), findsOneWidget); + // expect(find.byType(AppBar), findsOneWidget); }); } From 63448fb9817c9bf8d76e42e7e70df1947f7ffb50 Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Sat, 23 May 2026 01:33:47 +0530 Subject: [PATCH 02/11] fix: use flutter sdk version fallback for android build Signed-off-by: DELTA-45-G --- android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 29b3e0849..dea34da52 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -48,7 +48,7 @@ android { abortOnError false ignoreWarnings true } - compileSdkVersion 34 + compileSdkVersion flutter.compileSdkVersion ?: 34 ndkVersion flutter.ndkVersion buildFeatures { @@ -66,7 +66,7 @@ android { // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion 28 - targetSdkVersion 34 + targetSdkVersion flutter.targetSdkVersion ?: 34 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From 2a5eb136ae79d3ff6a1de6f695778d028b76b490 Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Sat, 23 May 2026 02:29:20 +0530 Subject: [PATCH 03/11] fix: pin android sdk version to 34 for telemetry dependencies Signed-off-by: DELTA-45-G --- android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index dea34da52..29b3e0849 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -48,7 +48,7 @@ android { abortOnError false ignoreWarnings true } - compileSdkVersion flutter.compileSdkVersion ?: 34 + compileSdkVersion 34 ndkVersion flutter.ndkVersion buildFeatures { @@ -66,7 +66,7 @@ android { // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion 28 - targetSdkVersion flutter.targetSdkVersion ?: 34 + targetSdkVersion 34 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From 1051228cad204ce1d46d02fa5e2a079e14f2ace9 Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Sat, 23 May 2026 02:59:27 +0530 Subject: [PATCH 04/11] docs: add Firebase setup instructions Signed-off-by: DELTA-45-G --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 4ba8593a7..d7d4fd5f9 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,25 @@ flutter gen-l10n * The label and application logo can be changed here android/app/src/main/AndroidManifest.xml +### Firebase Setup + +Firebase Analytics requires a `google-services.json` configuration file. + +1. Go to the Firebase Console. +2. Open the MOSIP Firebase project. +3. Download the `google-services.json` file. +4. Place the file in the following directory: + +```text +android/app/ +``` + +**Important Notes:** + +- `google-services.json` is excluded from version control using `.gitignore`. +- Each developer must add the Firebase configuration file locally. +- CI/CD pipelines should securely store this file as a secret and inject it during build time. + #### 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. From f8008edd604e2f5f2b7c7ce3c3f222971a3c9648 Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Sat, 23 May 2026 03:27:56 +0530 Subject: [PATCH 05/11] fix: remove duplicate telemetry screen tracking Signed-off-by: DELTA-45-G --- lib/app_router.dart | 72 ++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/lib/app_router.dart b/lib/app_router.dart index 45c6afc78..d789dda37 100644 --- a/lib/app_router.dart +++ b/lib/app_router.dart @@ -12,55 +12,55 @@ 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/telemetry/telemetry_screen_wrapper.dart'; +// import 'package:registration_client/telemetry/telemetry_screen_wrapper.dart'; class AppRouter { AppRouter._(); static Map routes = { //telementary screen - LoginPage.route: (context) => const TelemetryScreenWrapper( - screenName: "LoginPage", - child: LoginPage(), - ), + // LoginPage.route: (context) => const TelemetryScreenWrapper( + // screenName: "LoginPage", + // child: LoginPage(), + // ), - '/new_process': (context) => const TelemetryScreenWrapper( - screenName: "NewProcess", - child: GenericProcess(processType: ProcessType.newProcess), - ), + // '/new_process': (context) => const TelemetryScreenWrapper( + // screenName: "NewProcess", + // child: GenericProcess(processType: ProcessType.newProcess), + // ), - '/update_process': (context) => const TelemetryScreenWrapper( - screenName: "UpdateProcess", - child: GenericProcess(processType: ProcessType.updateProcess), - ), + // '/update_process': (context) => const TelemetryScreenWrapper( + // screenName: "UpdateProcess", + // child: GenericProcess(processType: ProcessType.updateProcess), + // ), - '/lost_process': (context) => const TelemetryScreenWrapper( - screenName: "LostProcess", - child: GenericProcess(processType: ProcessType.lostProcess), - ), + // '/lost_process': (context) => const TelemetryScreenWrapper( + // screenName: "LostProcess", + // child: GenericProcess(processType: ProcessType.lostProcess), + // ), - '/correction_process': (context) => const TelemetryScreenWrapper( - screenName: "CorrectionProcess", - child: GenericProcess(processType: ProcessType.correctionProcess), - ), + // '/correction_process': (context) => const TelemetryScreenWrapper( + // screenName: "CorrectionProcess", + // child: GenericProcess(processType: ProcessType.correctionProcess), + // ), - OnboardLandingPage.route: (context) => const TelemetryScreenWrapper( - screenName: "OnboardLandingPage", - child: OnboardLandingPage(), - ), + // OnboardLandingPage.route: (context) => const TelemetryScreenWrapper( + // screenName: "OnboardLandingPage", + // child: OnboardLandingPage(), + // ), - HomePage.route: (context) => const TelemetryScreenWrapper( - screenName: "HomePage", - child: HomePage(), - ), + // HomePage.route: (context) => const TelemetryScreenWrapper( + // screenName: "HomePage", + // child: HomePage(), + // ), // - // LoginPage.route: (context) => const LoginPage(), - // '/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(), + LoginPage.route: (context) => const LoginPage(), + '/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(), }; static Route? onUnknownRoute(RouteSettings settings) { From 0d2ce41e6d7ea1cd6431339aeed103996ebced1c Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Sat, 23 May 2026 15:44:48 +0530 Subject: [PATCH 06/11] fix: default telemetry to disabled until consent is loaded Signed-off-by: DELTA-45-G --- lib/telemetry/telemetry_manager.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/telemetry/telemetry_manager.dart b/lib/telemetry/telemetry_manager.dart index 96327fd09..26e8a902a 100644 --- a/lib/telemetry/telemetry_manager.dart +++ b/lib/telemetry/telemetry_manager.dart @@ -6,7 +6,7 @@ import 'telemetry_event.dart'; class TelemetryManager { static final FirebaseAnalytics _analytics = FirebaseAnalytics.instance; - static bool _isEnabled = true;// This should be loaded from user consent (e.g., SharedPreferences) in initialize() + static bool _isEnabled = false;// This should be loaded from user consent (e.g., SharedPreferences) in initialize() /// ✅ Initialize telemetry (call in main) static Future initialize() async { From e691f1ecb9b702f382fb3a2cb52e509487ce2d35 Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Sat, 23 May 2026 15:51:27 +0530 Subject: [PATCH 07/11] fix: avoid sending raw exception messages to telemetry Signed-off-by: DELTA-45-G --- lib/telemetry/telemetry_service.dart | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/telemetry/telemetry_service.dart b/lib/telemetry/telemetry_service.dart index af067b302..09aa42bab 100644 --- a/lib/telemetry/telemetry_service.dart +++ b/lib/telemetry/telemetry_service.dart @@ -51,9 +51,18 @@ class TelemetryService { trackEvent("screen_view", data: {"screen": screenName}); } - static void logError(Object error) { - trackEvent("error", data: {"message": error.toString()}); - } + // static void logError(Object error) { + // trackEvent("error", data: {"message": error.toString()}); + // } + static void logError(Object error, {String? context}) { + trackEvent( + "error", + data: { + "type": error.runtimeType.toString(), + if (context != null) "context": context, + }, + ); +} static void trackPerformance(String metric, int value) { trackEvent("performance", data: { From 61575a606f1debc62796872b4041f5e6d54b32a3 Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Sat, 23 May 2026 16:34:52 +0530 Subject: [PATCH 08/11] fix: use localized fallback text for machine details Signed-off-by: DELTA-45-G --- lib/ui/machine_keys.dart | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/ui/machine_keys.dart b/lib/ui/machine_keys.dart index bf3956cd2..19ee8846f 100644 --- a/lib/ui/machine_keys.dart +++ b/lib/ui/machine_keys.dart @@ -40,13 +40,13 @@ class _MachineKeysState extends State { @override void initState() { globalProvider = Provider.of(context, listen: false); - Map map = globalProvider.machineDetails; - if (map.isEmpty) { - // machineDetails = appLocalizations.not_initialized; - machineDetails = "Not initialized"; - } else { - machineDetails = jsonEncode(map).toString(); - } + // Map map = globalProvider.machineDetails; + // if (map.isEmpty) { + // // machineDetails = appLocalizations.not_initialized; + // machineDetails = "Not initialized"; + // } else { + // machineDetails = jsonEncode(map).toString(); + // } globalProvider.getAudit("REG-LOAD-002", "REG-MOD-101"); super.initState(); } @@ -55,6 +55,15 @@ class _MachineKeysState extends State { void didChangeDependencies() { super.didChangeDependencies(); appLocalizations = AppLocalizations.of(context)!; + globalProvider = Provider.of(context, listen: false); + + Map map = globalProvider.machineDetails; + + if (map.isEmpty) { + machineDetails = appLocalizations.not_initialized; + } else { + machineDetails = jsonEncode(map).toString(); + } } @override From dad6faa4039743980482970467289caa7afd2e07 Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Sat, 23 May 2026 16:43:31 +0530 Subject: [PATCH 09/11] fix: initialize telemetry consent before telemetry service Signed-off-by: DELTA-45-G --- lib/main.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/main.dart b/lib/main.dart index a8898615b..6c4e264f4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -44,8 +44,10 @@ void main() async { await FlutterConfig.loadEnvVariables(); await appLanguage.fetchLocale(); // Load telemetry config from backend or local config + // await TelemetryService.init(); + // await TelemetryManager.initialize();// Initialize telemetry manager (loads user consent and configures Firebase accordingly) + await TelemetryManager.initialize(); await TelemetryService.init(); - await TelemetryManager.initialize();// Initialize telemetry manager (loads user consent and configures Firebase accordingly) // runApp( const RestartWidget(child: RegistrationClientApp()), From be57fbd23ffc77ace62bc6b9676d950c83e153a6 Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Sat, 23 May 2026 16:50:53 +0530 Subject: [PATCH 10/11] fix: address auth service review comments Signed-off-by: DELTA-45-G --- lib/platform_android/auth_service_impl.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform_android/auth_service_impl.dart b/lib/platform_android/auth_service_impl.dart index 2b7dbcc2f..9f072497f 100644 --- a/lib/platform_android/auth_service_impl.dart +++ b/lib/platform_android/auth_service_impl.dart @@ -113,7 +113,7 @@ class AuthServiceImpl implements AuthService { @override Future getAutoLogoutPopupTimeout() async { - String refreshLoginTime = "0";; + String refreshLoginTime = "0"; try { refreshLoginTime = await AuthResponseApi().getAutoLogoutPopupTimeout(); } on PlatformException { From fd9d3a5061868d57506473cc9ba882585ffd0788 Mon Sep 17 00:00:00 2001 From: DELTA-45-G Date: Sat, 23 May 2026 16:58:08 +0530 Subject: [PATCH 11/11] fix: handle telemetry failures safely in network service Signed-off-by: DELTA-45-G --- .../network_service_impl.dart | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/platform_android/network_service_impl.dart b/lib/platform_android/network_service_impl.dart index f222e90b5..b2c656c63 100644 --- a/lib/platform_android/network_service_impl.dart +++ b/lib/platform_android/network_service_impl.dart @@ -26,14 +26,29 @@ class NetworkServiceImpl implements NetworkService { FlutterConfig.get('HEALTH_CHECK_PATH'))) .timeout(const Duration(seconds: 2)); return response.statusCode.toString(); - } catch (e) { - debugPrint("Network Connection failed $e"); - // log network failure event to telemetry with error details - TelemetryService.trackEvent("network_failure", data: { - "error": e.toString(), - "api": "health_check" - }); - TelemetryService.logError(e); + // } catch (e) { + // debugPrint("Network Connection failed $e"); + // // log network failure event to telemetry with error details + // TelemetryService.trackEvent("network_failure", data: { + // "error": e.toString(), + // "api": "health_check" + // }); + // TelemetryService.logError(e); + // + }catch (e) { + try { + TelemetryService.trackEvent( + "network_failure", + data: { + "error": e.toString(), + "api": "health_check", + }, + ); + + TelemetryService.logError(e); + } catch (_) { + debugPrint("Telemetry logging failed"); + } // } return "";