diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 226d4dd..2987d3b 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -26,8 +26,8 @@ android {
applicationId = "com.ejada.hmg"
// minSdk = 24
minSdk = 26
- targetSdk = 35
- compileSdk = 35
+ targetSdk = 36
+ compileSdk = 36
// targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
@@ -156,16 +156,16 @@ dependencies {
implementation("com.intuit.ssp:ssp-android:1.1.0")
implementation("com.intuit.sdp:sdp-android:1.1.0")
-// implementation("com.github.bumptech.glide:glide:4.16.0")
-// annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
+ implementation("com.github.bumptech.glide:glide:4.16.0")
+ annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.mapbox.maps:android:11.5.0")
// implementation("com.mapbox.maps:android:11.4.0")
// AARs
-// implementation(files("libs/PenNavUI.aar"))
-// implementation(files("libs/Penguin.aar"))
-// implementation(files("libs/PenguinRenderer.aar"))
+ implementation(files("libs/PenNavUI.aar"))
+ implementation(files("libs/Penguin.aar"))
+ implementation(files("libs/PenguinRenderer.aar"))
implementation("com.github.kittinunf.fuel:fuel:2.3.1")
implementation("com.github.kittinunf.fuel:fuel-android:2.3.1")
@@ -180,9 +180,11 @@ dependencies {
implementation("com.google.android.material:material:1.12.0")
implementation("pl.droidsonroids.gif:android-gif-drawable:1.2.25")
+ implementation("com.mapbox.mapboxsdk:mapbox-sdk-turf:7.3.1")
androidTestImplementation("androidx.test:core:1.6.1")
implementation("com.whatsapp.otp:whatsapp-otp-android-sdk:0.1.0")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
// implementation(project(":vitalSignEngine"))
+
}
\ No newline at end of file
diff --git a/android/app/libs/PenNavUI.aar b/android/app/libs/PenNavUI.aar
index d423bc1..7832df8 100644
Binary files a/android/app/libs/PenNavUI.aar and b/android/app/libs/PenNavUI.aar differ
diff --git a/android/app/libs/Penguin.aar b/android/app/libs/Penguin.aar
index 5c789c6..a769c7a 100644
Binary files a/android/app/libs/Penguin.aar and b/android/app/libs/Penguin.aar differ
diff --git a/android/app/libs/PenguinRenderer.aar b/android/app/libs/PenguinRenderer.aar
index b657ac6..2926e9a 100644
Binary files a/android/app/libs/PenguinRenderer.aar and b/android/app/libs/PenguinRenderer.aar differ
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 4f7ef74..8c1388b 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -49,7 +49,7 @@
-
+
@@ -58,6 +58,13 @@
+
+
+
+
+
+
+
,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+
+ val granted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
+ val intent = Intent("PERMISSION_RESULT_ACTION").apply {
+ putExtra("PERMISSION_GRANTED", granted)
+ }
+ sendBroadcast(intent)
+
+ // Log the request code and permission results
+ Log.d("PermissionsResult", "Request Code: $requestCode")
+ Log.d("PermissionsResult", "Permissions: ${permissions.joinToString()}")
+ Log.d("PermissionsResult", "Grant Results: ${grantResults.joinToString()}")
+
+ }
+
+ override fun onResume() {
+ super.onResume()
+ }
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PenguinInPlatformBridge.kt b/android/app/src/main/kotlin/com/ejada/hmg/PenguinInPlatformBridge.kt
new file mode 100644
index 0000000..4df25bc
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PenguinInPlatformBridge.kt
@@ -0,0 +1,61 @@
+package com.ejada.hmg.penguin
+
+import com.ejada.hmg.MainActivity
+import android.os.Build
+import android.util.Log
+import androidx.annotation.RequiresApi
+import com.ejada.hmg.penguin.PenguinView
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodCall
+import com.ejada.hmg.PermissionManager.HostNotificationPermissionManager
+import com.ejada.hmg.PermissionManager.HostBgLocationManager
+import com.ejada.hmg.PermissionManager.HostGpsStateManager
+import io.flutter.plugin.common.MethodChannel
+
+class PenguinInPlatformBridge(
+ private var flutterEngine: FlutterEngine,
+ private var mainActivity: MainActivity
+) {
+
+ private lateinit var channel: MethodChannel
+
+ companion object {
+ private const val CHANNEL = "launch_penguin_ui"
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ fun create() {
+// openTok = OpenTok(mainActivity, flutterEngine)
+ channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
+ channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
+ when (call.method) {
+ "launchPenguin" -> {
+ print("the platform channel is being called")
+
+ if (HostNotificationPermissionManager.isNotificationPermissionGranted(mainActivity))
+ else HostNotificationPermissionManager.requestNotificationPermission(mainActivity)
+ HostBgLocationManager.requestLocationBackgroundPermission(mainActivity)
+ HostGpsStateManager.requestLocationPermission(mainActivity)
+ val args = call.arguments as Map?
+ Log.d("TAG", "configureFlutterEngine: $args")
+ println("args")
+ args?.let {
+ PenguinView(
+ mainActivity,
+ 100,
+ args,
+ flutterEngine.dartExecutor.binaryMessenger,
+ activity = mainActivity,
+ channel
+ )
+ }
+ }
+
+ else -> {
+ result.notImplemented()
+ }
+ }
+ }
+ }
+
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/AppPreferences.java b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/AppPreferences.java
new file mode 100644
index 0000000..d012799
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/AppPreferences.java
@@ -0,0 +1,139 @@
+package com.ejada.hmg.PermissionManager;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+
+
+/**
+ * This preferences for app level
+ */
+
+public class AppPreferences {
+
+ public static final String PREF_NAME = "PenguinINUI_AppPreferences";
+ public static final int MODE = Context.MODE_PRIVATE;
+
+ public static final String campusIdKey = "campusId";
+
+ public static final String LANG = "Lang";
+
+ public static final String settingINFO = "SETTING-INFO";
+
+ public static final String userName = "userName";
+ public static final String passWord = "passWord";
+
+ private static HandlerThread handlerThread;
+ private static Handler handler;
+
+ static {
+ handlerThread = new HandlerThread("PreferencesHandlerThread");
+ handlerThread.start();
+ handler = new Handler(handlerThread.getLooper());
+ }
+
+
+
+ public static SharedPreferences getPreferences(final Context context) {
+ return context.getSharedPreferences(AppPreferences.PREF_NAME, AppPreferences.MODE);
+ }
+
+ public static SharedPreferences.Editor getEditor(final Context context) {
+ return getPreferences(context).edit();
+ }
+
+
+ public static void writeInt(final Context context, final String key, final int value) {
+ handler.post(() -> {
+ SharedPreferences.Editor editor = getEditor(context);
+ editor.putInt(key, value);
+ editor.apply();
+ });
+ }
+
+
+ public static int readInt(final Context context, final String key, final int defValue) {
+ Callable callable = () -> {
+ SharedPreferences preferences = getPreferences(context);
+ return preferences.getInt(key, -1);
+ };
+
+ Future future = new FutureTask<>(callable);
+ handler.post((Runnable) future);
+
+ try {
+ return future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace(); // Handle the exception appropriately
+ }
+
+ return -1; // Return the default value in case of an error
+ }
+
+ public static int getCampusId(final Context context) {
+ return readInt(context,campusIdKey,-1);
+ }
+
+
+
+ public static void writeString(final Context context, final String key, final String value) {
+ handler.post(() -> {
+ SharedPreferences.Editor editor = getEditor(context);
+ editor.putString(key, value);
+ editor.apply();
+ });
+ }
+
+
+ public static String readString(final Context context, final String key, final String defValue) {
+ Callable callable = () -> {
+ SharedPreferences preferences = getPreferences(context);
+ return preferences.getString(key, defValue);
+ };
+
+ Future future = new FutureTask<>(callable);
+ handler.post((Runnable) future);
+
+ try {
+ return future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace(); // Handle the exception appropriately
+ }
+
+ return defValue; // Return the default value in case of an error
+ }
+
+
+ public static void writeBoolean(final Context context, final String key, final boolean value) {
+ handler.post(() -> {
+ SharedPreferences.Editor editor = getEditor(context);
+ editor.putBoolean(key, value);
+ editor.apply();
+ });
+ }
+
+ public static boolean readBoolean(final Context context, final String key, final boolean defValue) {
+ Callable callable = () -> {
+ SharedPreferences preferences = getPreferences(context);
+ return preferences.getBoolean(key, defValue);
+ };
+
+ Future future = new FutureTask<>(callable);
+ handler.post((Runnable) future);
+
+ try {
+ return future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace(); // Handle the exception appropriately
+ }
+
+ return defValue; // Return the default value in case of an error
+ }
+
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostBgLocationManager.java b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostBgLocationManager.java
new file mode 100644
index 0000000..5bc332d
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostBgLocationManager.java
@@ -0,0 +1,136 @@
+package com.ejada.hmg.PermissionManager;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.provider.Settings;
+
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import com.peng.pennavmap.PlugAndPlaySDK;
+import com.peng.pennavmap.R;
+import com.peng.pennavmap.enums.InitializationErrorType;
+
+/**
+ * Manages background location permission requests and handling for the application.
+ */
+public class HostBgLocationManager {
+ /**
+ * Request code for background location permission
+ */
+ public static final int REQUEST_ACCESS_BACKGROUND_LOCATION_CODE = 301;
+
+ /**
+ * Request code for navigating to app settings
+ */
+ private static final int REQUEST_CODE_SETTINGS = 11234;
+
+ /**
+ * Alert dialog for denied permissions
+ */
+ private static AlertDialog deniedAlertDialog;
+
+ /**
+ * Checks if the background location permission has been granted.
+ *
+ * @param context the context of the application or activity
+ * @return true if the permission is granted, false otherwise
+ */
+
+ public static boolean isLocationBackgroundGranted(Context context) {
+ return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Requests the background location permission from the user.
+ *
+ * @param activity the activity from which the request is made
+ */
+ public static void requestLocationBackgroundPermission(Activity activity) {
+ // Check if the ACCESS_BACKGROUND_LOCATION permission is already granted
+ if (!isLocationBackgroundGranted(activity)) {
+ // Permission is not granted, so request it
+ ActivityCompat.requestPermissions(activity,
+ new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
+ REQUEST_ACCESS_BACKGROUND_LOCATION_CODE);
+ }
+ }
+
+ /**
+ * Displays a dialog prompting the user to grant the background location permission.
+ *
+ * @param activity the activity where the dialog is displayed
+ */
+ public static void showLocationBackgroundPermission(Activity activity) {
+ AlertDialog alertDialog = new AlertDialog.Builder(activity)
+ .setCancelable(false)
+ .setMessage(activity.getString(R.string.com_penguin_nav_ui_geofence_alert_msg))
+ .setPositiveButton(activity.getString(R.string.com_penguin_nav_ui_go_to_settings), (dialog, which) -> {
+ if (activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
+ HostBgLocationManager.requestLocationBackgroundPermission(activity);
+ } else {
+ openAppSettings(activity);
+ }
+ if (dialog != null) {
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(activity.getString(R.string.com_penguin_nav_ui_later), (dialog, which) -> {
+ dialog.cancel();
+ })
+ .create();
+
+ alertDialog.show();
+ }
+
+ /**
+ * Handles the scenario where permissions are denied by the user.
+ * Displays a dialog to guide the user to app settings or exit the activity.
+ *
+ * @param activity the activity where the dialog is displayed
+ */
+ public static synchronized void handlePermissionsDenied(Activity activity) {
+ if (deniedAlertDialog != null && deniedAlertDialog.isShowing()) {
+ deniedAlertDialog.dismiss();
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setCancelable(false)
+ .setMessage(activity.getString(R.string.com_penguin_nav_ui_permission_denied_dialog_msg))
+ .setNegativeButton(activity.getString(R.string.com_penguin_nav_ui_cancel), (dialogInterface, i) -> {
+ if (PlugAndPlaySDK.externalPenNavUIDelegate != null) {
+ PlugAndPlaySDK.externalPenNavUIDelegate.onPenNavInitializationError(
+ InitializationErrorType.permissions.getTypeKey(),
+ InitializationErrorType.permissions);
+ }
+ activity.finish();
+ })
+ .setPositiveButton(activity.getString(R.string.com_penguin_nav_ui_go_settings), (dialogInterface, i) -> {
+ dialogInterface.dismiss();
+ openAppSettings(activity);
+ });
+ deniedAlertDialog = builder.create();
+ deniedAlertDialog.show();
+ }
+
+ /**
+ * Opens the application's settings screen to allow the user to modify permissions.
+ *
+ * @param activity the activity from which the settings screen is launched
+ */
+ private static void openAppSettings(Activity activity) {
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
+ intent.setData(uri);
+
+ if (intent.resolveActivity(activity.getPackageManager()) != null) {
+ activity.startActivityForResult(intent, REQUEST_CODE_SETTINGS);
+ }
+ }
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostGpsStateManager.java b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostGpsStateManager.java
new file mode 100644
index 0000000..adde120
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostGpsStateManager.java
@@ -0,0 +1,68 @@
+package com.ejada.hmg.PermissionManager;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import com.peng.pennavmap.managers.permissions.managers.BgLocationManager;
+
+public class HostGpsStateManager {
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
+
+
+ public boolean checkGPSEnabled(Activity activity) {
+ LocationManager gpsStateManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
+ return gpsStateManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
+ }
+
+ public static boolean isGpsGranted(Activity activity) {
+ return BgLocationManager.isLocationBackgroundGranted(activity)
+ || ContextCompat.checkSelfPermission(
+ activity,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED
+ && ContextCompat.checkSelfPermission(
+ activity,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED;
+ }
+
+
+ /**
+ * Checks if the location permission is granted.
+ *
+ * @param activity the Activity context
+ * @return true if permission is granted, false otherwise
+ */
+ public static boolean isLocationPermissionGranted(Activity activity) {
+ return ContextCompat.checkSelfPermission(
+ activity,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED &&
+ ContextCompat.checkSelfPermission(
+ activity,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Requests the location permission.
+ *
+ * @param activity the Activity context
+ */
+ public static void requestLocationPermission(Activity activity) {
+ ActivityCompat.requestPermissions(
+ activity,
+ new String[]{
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ },
+ LOCATION_PERMISSION_REQUEST_CODE
+ );
+ }
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostNotificationPermissionManager.java b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostNotificationPermissionManager.java
new file mode 100644
index 0000000..5b9f19e
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostNotificationPermissionManager.java
@@ -0,0 +1,73 @@
+package com.ejada.hmg.PermissionManager;
+
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.ActivityCompat;
+import androidx.core.app.NotificationManagerCompat;
+
+public class HostNotificationPermissionManager {
+ private static final int REQUEST_NOTIFICATION_PERMISSION = 100;
+
+
+ /**
+ * Checks if the notification permission is granted.
+ *
+ * @return true if the notification permission is granted, false otherwise.
+ */
+ public static boolean isNotificationPermissionGranted(Activity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ try {
+ return ActivityCompat.checkSelfPermission(activity, android.Manifest.permission.POST_NOTIFICATIONS)
+ == PackageManager.PERMISSION_GRANTED;
+ } catch (Exception e) {
+ // Handle cases where the API is unavailable
+ e.printStackTrace();
+ return NotificationManagerCompat.from(activity).areNotificationsEnabled();
+ }
+ } else {
+ // Permissions were not required below Android 13 for notifications
+ return NotificationManagerCompat.from(activity).areNotificationsEnabled();
+ }
+ }
+
+ /**
+ * Requests the notification permission.
+ */
+ public static void requestNotificationPermission(Activity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (!isNotificationPermissionGranted(activity)) {
+ ActivityCompat.requestPermissions(activity,
+ new String[]{android.Manifest.permission.POST_NOTIFICATIONS},
+ REQUEST_NOTIFICATION_PERMISSION);
+ }
+ }
+ }
+
+ /**
+ * Handles the result of the permission request.
+ *
+ * @param requestCode The request code passed in requestPermissions().
+ * @param permissions The requested permissions.
+ * @param grantResults The grant results for the corresponding permissions.
+ */
+ public static boolean handlePermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (permissions.length > 0 &&
+ permissions[0].equals(android.Manifest.permission.POST_NOTIFICATIONS) &&
+ grantResults.length > 0 &&
+ grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // Permission granted
+ System.out.println("Notification permission granted.");
+ return true;
+ } else {
+ // Permission denied
+ System.out.println("Notification permission denied.");
+ return false;
+ }
+
+ }
+
+
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionHelper.kt b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionHelper.kt
new file mode 100644
index 0000000..9856a49
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionHelper.kt
@@ -0,0 +1,28 @@
+package com.ejada.hmg.PermissionManager
+
+import android.Manifest
+import android.os.Build
+
+object PermissionHelper {
+
+ fun getRequiredPermissions(): Array {
+ val permissions = mutableListOf(
+ Manifest.permission.INTERNET,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_NETWORK_STATE,
+ Manifest.permission.BLUETOOTH,
+ Manifest.permission.BLUETOOTH_ADMIN,
+// Manifest.permission.ACTIVITY_RECOGNITION
+ )
+
+ // For Android 12 (API level 31) and above, add specific permissions
+// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12 (API 31) and above
+ permissions.add(Manifest.permission.BLUETOOTH_SCAN)
+ permissions.add(Manifest.permission.BLUETOOTH_CONNECT)
+ permissions.add(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS)
+// }
+
+ return permissions.toTypedArray()
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionManager.kt b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionManager.kt
new file mode 100644
index 0000000..d8aea7b
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionManager.kt
@@ -0,0 +1,50 @@
+package com.ejada.hmg.PermissionManager
+
+import android.app.Activity
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+
+class PermissionManager(
+ private val context: Context,
+ val listener: PermissionListener,
+ private val requestCode: Int,
+ vararg permissions: String
+) {
+
+ private val permissionsArray = permissions
+
+ interface PermissionListener {
+ fun onPermissionGranted()
+ fun onPermissionDenied()
+ }
+
+ fun arePermissionsGranted(): Boolean {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ permissionsArray.all {
+ ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
+ }
+ } else {
+ true
+ }
+ }
+
+ fun requestPermissions(activity: Activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ ActivityCompat.requestPermissions(activity, permissionsArray, requestCode)
+ }
+ }
+
+ fun handlePermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
+ if (this.requestCode == requestCode) {
+ val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
+ if (allGranted) {
+ listener.onPermissionGranted()
+ } else {
+ listener.onPermissionDenied()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionResultReceiver.kt b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionResultReceiver.kt
new file mode 100644
index 0000000..c07d1de
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionResultReceiver.kt
@@ -0,0 +1,15 @@
+package com.ejada.hmg.PermissionManager
+
+// PermissionResultReceiver.kt
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+
+class PermissionResultReceiver(
+ private val callback: (Boolean) -> Unit
+) : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ val granted = intent?.getBooleanExtra("PERMISSION_GRANTED", false) ?: false
+ callback(granted)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinMethod.kt b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinMethod.kt
new file mode 100644
index 0000000..18463d2
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinMethod.kt
@@ -0,0 +1,13 @@
+package com.ejada.hmg.penguin
+
+enum class PenguinMethod {
+ // initializePenguin("initializePenguin"),
+ // configurePenguin("configurePenguin"),
+ // showPenguinUI("showPenguinUI"),
+ // onPenNavUIDismiss("onPenNavUIDismiss"),
+ // onReportIssue("onReportIssue"),
+ // onPenNavSuccess("onPenNavSuccess"),
+ onPenNavInitializationError // onLocationOffCampus("onLocationOffCampus"),
+ // navigateToPOI("navigateToPOI"),
+ // openSharedLocation("openSharedLocation");
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinNavigator.kt b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinNavigator.kt
new file mode 100644
index 0000000..b822d67
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinNavigator.kt
@@ -0,0 +1,97 @@
+package com.ejada.hmg.penguin
+
+import android.content.Context
+import com.google.gson.Gson
+import com.peng.pennavmap.PlugAndPlaySDK
+import com.peng.pennavmap.connections.ApiController
+import com.peng.pennavmap.interfaces.RefIdDelegate
+import com.peng.pennavmap.models.TokenModel
+import com.peng.pennavmap.models.postmodels.PostToken
+import com.peng.pennavmap.utils.AppSharedData
+import okhttp3.ResponseBody
+import retrofit2.Call
+import retrofit2.Callback
+import retrofit2.Response
+import android.util.Log
+
+
+class PenguinNavigator() {
+
+ fun navigateTo(mContext: Context, refID: String, delegate: RefIdDelegate,clientID : String,clientKey : String ) {
+ val postToken = PostToken(clientID, clientKey)
+ getToken(mContext, postToken, object : RefIdDelegate {
+ override fun onRefByIDSuccess(PoiId: String?) {
+ Log.e("navigateTo", "PoiId is+++++++ $PoiId")
+
+ PlugAndPlaySDK.navigateTo(mContext, refID, object : RefIdDelegate {
+ override fun onRefByIDSuccess(PoiId: String?) {
+ Log.e("navigateTo", "PoiId 2is+++++++ $PoiId")
+
+ delegate.onRefByIDSuccess(refID)
+
+ }
+
+ override fun onGetByRefIDError(error: String?) {
+ delegate.onRefByIDSuccess(error)
+ }
+
+ })
+
+
+ }
+
+ override fun onGetByRefIDError(error: String?) {
+ delegate.onRefByIDSuccess(error)
+ }
+
+ })
+
+ }
+
+ fun getToken(mContext: Context, postToken: PostToken?, apiTokenCallBack: RefIdDelegate) {
+ try {
+ // Create the API call
+ val purposesCall: Call = ApiController.getInstance(mContext)
+ .apiMethods
+ .getToken(postToken)
+
+ // Enqueue the call for asynchronous execution
+ purposesCall.enqueue(object : Callback {
+ override fun onResponse(
+ call: Call,
+ response: Response
+ ) {
+ if (response.isSuccessful() && response.body() != null) {
+ try {
+ response.body()?.use { responseBody ->
+ val responseBodyString: String = responseBody.string() // Use `string()` to get the actual response content
+ if (responseBodyString.isNotEmpty()) {
+ val tokenModel = Gson().fromJson(responseBodyString, TokenModel::class.java)
+ if (tokenModel != null && tokenModel.token != null) {
+ AppSharedData.apiToken = tokenModel.token
+ apiTokenCallBack.onRefByIDSuccess(tokenModel.token)
+ } else {
+ apiTokenCallBack.onGetByRefIDError("Failed to parse token model")
+ }
+ } else {
+ apiTokenCallBack.onGetByRefIDError("Response body is empty")
+ }
+ }
+ } catch (e: Exception) {
+ apiTokenCallBack.onGetByRefIDError("An error occurred: ${e.message}")
+ }
+ } else {
+ apiTokenCallBack.onGetByRefIDError("Unsuccessful response: " + response.code())
+ }
+ }
+
+ override fun onFailure(call: Call, t: Throwable) {
+ apiTokenCallBack.onGetByRefIDError(t.message)
+ }
+ })
+ } catch (error: Exception) {
+ apiTokenCallBack.onGetByRefIDError("Exception during API call: $error")
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinView.kt b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinView.kt
new file mode 100644
index 0000000..6c7306d
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinView.kt
@@ -0,0 +1,376 @@
+package com.ejada.hmg.penguin
+
+import android.app.Activity
+import android.content.Context
+import android.content.Context.RECEIVER_EXPORTED
+import android.content.IntentFilter
+import android.graphics.Color
+import android.os.Build
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import android.widget.RelativeLayout
+import android.widget.Toast
+import androidx.annotation.RequiresApi
+import com.ejada.hmg.PermissionManager.PermissionManager
+import com.ejada.hmg.PermissionManager.PermissionResultReceiver
+import com.ejada.hmg.MainActivity
+import com.ejada.hmg.PermissionManager.PermissionHelper
+import com.peng.pennavmap.PlugAndPlayConfiguration
+import com.peng.pennavmap.PlugAndPlaySDK
+import com.peng.pennavmap.enums.InitializationErrorType
+import com.peng.pennavmap.interfaces.PenNavUIDelegate
+import com.peng.pennavmap.utils.Languages
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.platform.PlatformView
+import com.ejada.hmg.penguin.PenguinNavigator
+import com.peng.pennavmap.interfaces.PIEventsDelegate
+import com.peng.pennavmap.interfaces.PILocationDelegate
+import com.peng.pennavmap.interfaces.RefIdDelegate
+import com.peng.pennavmap.models.LocationMessage
+import com.peng.pennavmap.models.PIReportIssue
+import java.util.ArrayList
+import penguin.com.pennav.renderer.PIRendererSettings
+
+/**
+ * Custom PlatformView for displaying Penguin UI components within a Flutter app.
+ * Implements `PlatformView` for rendering the view, `MethodChannel.MethodCallHandler` for handling method calls,
+ * and `PenNavUIDelegate` for handling SDK events.
+ */
+@RequiresApi(Build.VERSION_CODES.O)
+internal class PenguinView(
+ context: Context,
+ id: Int,
+ val creationParams: Map,
+ messenger: BinaryMessenger,
+ activity: MainActivity,
+ val channel: MethodChannel
+) : PlatformView, MethodChannel.MethodCallHandler, PenNavUIDelegate, PIEventsDelegate,
+ PILocationDelegate {
+ // The layout for displaying the Penguin UI
+ private val mapLayout: RelativeLayout = RelativeLayout(context)
+ private val _context: Context = context
+
+ private val permissionResultReceiver: PermissionResultReceiver
+ private val permissionIntentFilter = IntentFilter("PERMISSION_RESULT_ACTION")
+
+ private companion object {
+ const val PERMISSIONS_REQUEST_CODE = 1
+ }
+
+ private lateinit var permissionManager: PermissionManager
+
+ // Reference to the main activity
+ private var _activity: Activity = activity
+
+ private lateinit var mContext: Context
+
+ lateinit var navigator: PenguinNavigator
+
+ init {
+ // Set layout parameters for the mapLayout
+ mapLayout.layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
+ )
+
+ mContext = context
+
+
+ permissionResultReceiver = PermissionResultReceiver { granted ->
+ if (granted) {
+ onPermissionsGranted()
+ } else {
+ onPermissionsDenied()
+ }
+ }
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ mContext.registerReceiver(
+ permissionResultReceiver,
+ permissionIntentFilter,
+ RECEIVER_EXPORTED
+ )
+ } else {
+ mContext.registerReceiver(
+ permissionResultReceiver,
+ permissionIntentFilter,
+ )
+ }
+
+ // Set the background color of the layout
+ mapLayout.setBackgroundColor(Color.RED)
+
+ permissionManager = PermissionManager(
+ context = mContext,
+ listener = object : PermissionManager.PermissionListener {
+ override fun onPermissionGranted() {
+ // Handle permissions granted
+ onPermissionsGranted()
+ }
+
+ override fun onPermissionDenied() {
+ // Handle permissions denied
+ onPermissionsDenied()
+ }
+ },
+ requestCode = PERMISSIONS_REQUEST_CODE,
+ PermissionHelper.getRequiredPermissions().get(0)
+ )
+
+ if (!permissionManager.arePermissionsGranted()) {
+ permissionManager.requestPermissions(_activity)
+ } else {
+ // Permissions already granted
+ permissionManager.listener.onPermissionGranted()
+ }
+
+
+ }
+
+ private fun onPermissionsGranted() {
+ // Handle the actions when permissions are granted
+ Log.d("PermissionsResult", "onPermissionsGranted")
+ // Register the platform view factory for creating custom views
+
+ // Initialize the Penguin SDK
+ initPenguin()
+
+
+ }
+
+ private fun onPermissionsDenied() {
+ // Handle the actions when permissions are denied
+ Log.d("PermissionsResult", "onPermissionsDenied")
+
+ }
+
+ /**
+ * Returns the view associated with this PlatformView.
+ *
+ * @return The main view for this PlatformView.
+ */
+ override fun getView(): View {
+ return mapLayout
+ }
+
+ /**
+ * Cleans up resources associated with this PlatformView.
+ */
+ override fun dispose() {
+ // Cleanup code if needed
+ }
+
+ /**
+ * Handles method calls from Dart code.
+ *
+ * @param call The method call from Dart.
+ * @param result The result callback to send responses back to Dart.
+ */
+ override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
+ // Handle method calls from Dart code here
+ }
+
+ /**
+ * Initializes the Penguin SDK with custom configuration and delegates.
+ */
+ private fun initPenguin() {
+ navigator = PenguinNavigator()
+ // Configure the PlugAndPlaySDK
+ val language = when (creationParams["languageCode"] as String) {
+ "ar" -> Languages.ar
+ "en" -> Languages.en
+ else -> {
+ Languages.en
+ }
+ }
+
+
+// PlugAndPlaySDK.configuration = Builder()
+// .setClientData(MConstantsDemo.CLIENT_ID, MConstantsDemo.CLIENT_KEY)
+// .setLanguageID(selectedLanguage)
+// .setBaseUrl(MConstantsDemo.DATA_URL, MConstantsDemo.POSITION_URL)
+// .setServiceName(MConstantsDemo.DATA_SERVICE_NAME, MConstantsDemo.POSITION_SERVICE_NAME)
+// .setUserName(name)
+// .setSimulationModeEnabled(isSimulation)
+// .setCustomizeColor(if (MConstantsDemo.APP_COLOR != null) MConstantsDemo.APP_COLOR else "#2CA0AF")
+// .setEnableBackButton(MConstantsDemo.SHOW_BACK_BUTTON)
+// .setCampusId(MConstantsDemo.selectedCampusId)
+//
+// .setShowUILoader(true)
+// .build()
+
+ PIRendererSettings.styleUri = "mapbox://styles/rwaid/cm3h30b36007v01qz7ik8a0sk"
+
+ PlugAndPlaySDK.configuration = PlugAndPlayConfiguration.Builder()
+ .setBaseUrl(
+ creationParams["dataURL"] as String,
+ creationParams["positionURL"] as String
+ )
+ .setServiceName(
+ creationParams["dataServiceName"] as String,
+ creationParams["positionServiceName"] as String
+ )
+ .setClientData(
+ creationParams["clientID"] as String,
+ creationParams["clientKey"] as String
+ )
+ .setUserName(creationParams["username"] as String)
+// .setLanguageID(Languages.en)
+ .setLanguageID(language)
+ .setSimulationModeEnabled(creationParams["isSimulationModeEnabled"] as Boolean)
+ .setEnableBackButton(true)
+// .setDeepLinkData("deeplink")
+ .setCustomizeColor("#2CA0AF")
+ .setDeepLinkSchema("", "")
+ .setIsEnableReportIssue(true)
+ .setDeepLinkData("")
+ .setEnableSharedLocationCallBack(false)
+ .setShowUILoader(true)
+ .setCampusId(creationParams["projectID"] as Int)
+ .build()
+
+
+ Log.d(
+ "TAG",
+ "initPenguin: ${creationParams["projectID"]}"
+ )
+
+ Log.d(
+ "TAG",
+ "initPenguin: creation param are ${creationParams}"
+ )
+
+ // Set location delegate to handle location updates
+// PlugAndPlaySDK.setPiLocationDelegate {
+ // Example code to handle location updates
+ // Uncomment and modify as needed
+ // if (location.size() > 0)
+ // Toast.makeText(_context, "Location Info Latitude: ${location[0]}, Longitude: ${location[1]}", Toast.LENGTH_SHORT).show()
+// }
+
+ // Set events delegate for reporting issues
+// PlugAndPlaySDK.setPiEventsDelegate(new PIEventsDelegate() {
+// @Override
+// public void onReportIssue(PIReportIssue issue) {
+// Log.e("Issue Reported: ", issue.getReportType());
+// }
+// // Implement issue reporting logic here }
+// @Override
+// public void onSharedLocation(String link) {
+// // Implement Shared location logic here
+// }
+// })
+
+ // Start the Penguin SDK
+ PlugAndPlaySDK.setPiEventsDelegate(this)
+ PlugAndPlaySDK.setPiLocationDelegate(this)
+ PlugAndPlaySDK.start(mContext, this)
+ }
+
+
+ /**
+ * Navigates to the specified reference ID.
+ *
+ * @param refID The reference ID to navigate to.
+ */
+ fun navigateTo(refID: String) {
+ try {
+ if (refID.isBlank()) {
+ Log.e("navigateTo", "Invalid refID: The reference ID is blank.")
+ }
+// referenceId = refID
+ navigator.navigateTo(mContext, refID,object : RefIdDelegate {
+ override fun onRefByIDSuccess(PoiId: String?) {
+ Log.e("navigateTo", "PoiId is penguin view+++++++ $PoiId")
+
+// channelFlutter.invokeMethod(
+// PenguinMethod.navigateToPOI.name,
+// "navigateTo Success"
+// )
+ }
+
+ override fun onGetByRefIDError(error: String?) {
+ Log.e("navigateTo", "error is penguin view+++++++ $error")
+
+// channelFlutter.invokeMethod(
+// PenguinMethod.navigateToPOI.name,
+// "navigateTo Failed: Invalid refID"
+// )
+ }
+ } , creationParams["clientID"] as String, creationParams["clientKey"] as String )
+
+ } catch (e: Exception) {
+ Log.e("navigateTo", "Exception occurred during navigation: ${e.message}", e)
+// channelFlutter.invokeMethod(
+// PenguinMethod.navigateToPOI.name,
+// "Failed: Exception - ${e.message}"
+// )
+ }
+ }
+
+ /**
+ * Called when Penguin UI setup is successful.
+ *
+ * @param warningCode Optional warning code received from the SDK.
+ */
+ override fun onPenNavSuccess(warningCode: String?) {
+ val clinicId = creationParams["clinicID"] as String
+
+ if(clinicId.isEmpty()) return
+
+ navigateTo(clinicId)
+ }
+
+ /**
+ * Called when there is an initialization error with Penguin UI.
+ *
+ * @param description Description of the error.
+ * @param errorType Type of initialization error.
+ */
+ override fun onPenNavInitializationError(
+ description: String?,
+ errorType: InitializationErrorType?
+ ) {
+ val arguments: Map = mapOf(
+ "description" to description,
+ "type" to errorType?.name
+ )
+ Log.d(
+ "description",
+ "description : ${description}"
+ )
+
+ channel.invokeMethod(PenguinMethod.onPenNavInitializationError.name, arguments)
+ Toast.makeText(mContext, "Navigation Error: $description", Toast.LENGTH_SHORT).show()
+ }
+
+ /**
+ * Called when Penguin UI is dismissed.
+ */
+ override fun onPenNavUIDismiss() {
+ // Handle UI dismissal if needed
+ try {
+ mContext.unregisterReceiver(permissionResultReceiver)
+ dispose();
+ } catch (e: IllegalArgumentException) {
+ Log.e("PenguinView", "Receiver not registered: $e")
+ }
+ }
+
+ override fun onReportIssue(issue: PIReportIssue?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onSharedLocation(link: String?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onLocationOffCampus(location: ArrayList?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onLocationMessage(locationMessage: LocationMessage?) {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/android/app/src/main/res/values/mapbox_access_token.xml b/android/app/src/main/res/values/mapbox_access_token.xml
index f1daf69..65bc4b3 100644
--- a/android/app/src/main/res/values/mapbox_access_token.xml
+++ b/android/app/src/main/res/values/mapbox_access_token.xml
@@ -1,3 +1,3 @@
- sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 6c4ac3d..2d10333 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -19,5 +19,5 @@
Geofence requests happened too frequently.
- sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg
+ pk.eyJ1IjoicndhaWQiLCJhIjoiY2x5cGo4aHNjMGNsbTJyc2djempobGQxaSJ9.RCaC6WrUt4A4YnZNfxnONQ
diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html
index 866b270..9b679bc 100644
--- a/android/build/reports/problems/problems-report.html
+++ b/android/build/reports/problems/problems-report.html
@@ -650,7 +650,7 @@ code + .copy-button {
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index ab39a10..6d0842d 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -18,7 +18,8 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
- id("com.android.application") version "8.7.3" apply false
+// id("com.android.application") version "8.9.3" apply false
+ id("com.android.application") version "8.9.3" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}
diff --git a/assets/images/png/bmi_image_1.png b/assets/images/png/bmi_image_1.png
new file mode 100644
index 0000000..db3a613
Binary files /dev/null and b/assets/images/png/bmi_image_1.png differ
diff --git a/assets/images/png/home_health_care.png b/assets/images/png/home_health_care.png
new file mode 100644
index 0000000..21378c4
Binary files /dev/null and b/assets/images/png/home_health_care.png differ
diff --git a/assets/images/png/pharmacy_service.png b/assets/images/png/pharmacy_service.png
new file mode 100644
index 0000000..7093d41
Binary files /dev/null and b/assets/images/png/pharmacy_service.png differ
diff --git a/assets/images/png/smartwatches/Apple-Watch-6.png b/assets/images/png/smartwatches/Apple-Watch-6.png
new file mode 100644
index 0000000..1e67050
Binary files /dev/null and b/assets/images/png/smartwatches/Apple-Watch-6.png differ
diff --git a/assets/images/png/smartwatches/apple-watch-1.jpeg b/assets/images/png/smartwatches/apple-watch-1.jpeg
new file mode 100644
index 0000000..7262e7e
Binary files /dev/null and b/assets/images/png/smartwatches/apple-watch-1.jpeg differ
diff --git a/assets/images/png/smartwatches/apple-watch-2.jpg b/assets/images/png/smartwatches/apple-watch-2.jpg
new file mode 100644
index 0000000..f688f74
Binary files /dev/null and b/assets/images/png/smartwatches/apple-watch-2.jpg differ
diff --git a/assets/images/png/smartwatches/apple-watch-3.jpg b/assets/images/png/smartwatches/apple-watch-3.jpg
new file mode 100644
index 0000000..b68c1ce
Binary files /dev/null and b/assets/images/png/smartwatches/apple-watch-3.jpg differ
diff --git a/assets/images/png/smartwatches/apple-watch-4.jpg b/assets/images/png/smartwatches/apple-watch-4.jpg
new file mode 100644
index 0000000..2fc19b6
Binary files /dev/null and b/assets/images/png/smartwatches/apple-watch-4.jpg differ
diff --git a/assets/images/png/smartwatches/apple-watch-5.jpg b/assets/images/png/smartwatches/apple-watch-5.jpg
new file mode 100644
index 0000000..4c497ea
Binary files /dev/null and b/assets/images/png/smartwatches/apple-watch-5.jpg differ
diff --git a/assets/images/png/smartwatches/bloodoxygen_icon.svg b/assets/images/png/smartwatches/bloodoxygen_icon.svg
new file mode 100644
index 0000000..0971a30
--- /dev/null
+++ b/assets/images/png/smartwatches/bloodoxygen_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/png/smartwatches/calories_icon.svg b/assets/images/png/smartwatches/calories_icon.svg
new file mode 100644
index 0000000..660ce0d
--- /dev/null
+++ b/assets/images/png/smartwatches/calories_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/png/smartwatches/distance_icon.svg b/assets/images/png/smartwatches/distance_icon.svg
new file mode 100644
index 0000000..29dcf3d
--- /dev/null
+++ b/assets/images/png/smartwatches/distance_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/png/smartwatches/galaxy_fit_3.jpg b/assets/images/png/smartwatches/galaxy_fit_3.jpg
new file mode 100644
index 0000000..ff05834
Binary files /dev/null and b/assets/images/png/smartwatches/galaxy_fit_3.jpg differ
diff --git a/assets/images/png/smartwatches/galaxy_watch_7.webp b/assets/images/png/smartwatches/galaxy_watch_7.webp
new file mode 100644
index 0000000..09748b4
Binary files /dev/null and b/assets/images/png/smartwatches/galaxy_watch_7.webp differ
diff --git a/assets/images/png/smartwatches/galaxy_watch_7_classic.jpg b/assets/images/png/smartwatches/galaxy_watch_7_classic.jpg
new file mode 100644
index 0000000..f177dd4
Binary files /dev/null and b/assets/images/png/smartwatches/galaxy_watch_7_classic.jpg differ
diff --git a/assets/images/png/smartwatches/galaxy_watch_8.jpg b/assets/images/png/smartwatches/galaxy_watch_8.jpg
new file mode 100644
index 0000000..7fd4746
Binary files /dev/null and b/assets/images/png/smartwatches/galaxy_watch_8.jpg differ
diff --git a/assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg b/assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg
new file mode 100644
index 0000000..6e84096
Binary files /dev/null and b/assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg differ
diff --git a/assets/images/png/smartwatches/galaxy_watch_ultra.jpg b/assets/images/png/smartwatches/galaxy_watch_ultra.jpg
new file mode 100644
index 0000000..e401d73
Binary files /dev/null and b/assets/images/png/smartwatches/galaxy_watch_ultra.jpg differ
diff --git a/assets/images/png/smartwatches/heartrate_icon.svg b/assets/images/png/smartwatches/heartrate_icon.svg
new file mode 100644
index 0000000..dac05ef
--- /dev/null
+++ b/assets/images/png/smartwatches/heartrate_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/png/smartwatches/steps_icon.svg b/assets/images/png/smartwatches/steps_icon.svg
new file mode 100644
index 0000000..4af073a
--- /dev/null
+++ b/assets/images/png/smartwatches/steps_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/E_Referral.svg b/assets/images/svg/E_Referral.svg
new file mode 100644
index 0000000..fb6b859
--- /dev/null
+++ b/assets/images/svg/E_Referral.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/images/svg/activity.svg b/assets/images/svg/activity.svg
new file mode 100644
index 0000000..7e1c342
--- /dev/null
+++ b/assets/images/svg/activity.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/svg/add_icon_dark.svg b/assets/images/svg/add_icon_dark.svg
new file mode 100644
index 0000000..399df3c
--- /dev/null
+++ b/assets/images/svg/add_icon_dark.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/age_icon.svg b/assets/images/svg/age_icon.svg
new file mode 100644
index 0000000..8acfad3
--- /dev/null
+++ b/assets/images/svg/age_icon.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/images/svg/ancillary_orders_list_icon.svg b/assets/images/svg/ancillary_orders_list_icon.svg
new file mode 100644
index 0000000..f0497d8
--- /dev/null
+++ b/assets/images/svg/ancillary_orders_list_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/approximate_ovulation_accordion.svg b/assets/images/svg/approximate_ovulation_accordion.svg
new file mode 100644
index 0000000..a721d61
--- /dev/null
+++ b/assets/images/svg/approximate_ovulation_accordion.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/images/svg/ask_doctor_medical_file_icon.svg b/assets/images/svg/ask_doctor_medical_file_icon.svg
new file mode 100644
index 0000000..11facfb
--- /dev/null
+++ b/assets/images/svg/ask_doctor_medical_file_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/blood_pressure.svg b/assets/images/svg/blood_pressure.svg
new file mode 100644
index 0000000..67badbe
--- /dev/null
+++ b/assets/images/svg/blood_pressure.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/blood_pressure_icon.svg b/assets/images/svg/blood_pressure_icon.svg
new file mode 100644
index 0000000..0b027ad
--- /dev/null
+++ b/assets/images/svg/blood_pressure_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/blood_sugar_icon.svg b/assets/images/svg/blood_sugar_icon.svg
new file mode 100644
index 0000000..3c77019
--- /dev/null
+++ b/assets/images/svg/blood_sugar_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/blood_sugar_only_icon.svg b/assets/images/svg/blood_sugar_only_icon.svg
new file mode 100644
index 0000000..f81cee8
--- /dev/null
+++ b/assets/images/svg/blood_sugar_only_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/blood_type.svg b/assets/images/svg/blood_type.svg
new file mode 100644
index 0000000..5aded31
--- /dev/null
+++ b/assets/images/svg/blood_type.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/bloodcholestrol.svg b/assets/images/svg/bloodcholestrol.svg
new file mode 100644
index 0000000..8a77bb5
--- /dev/null
+++ b/assets/images/svg/bloodcholestrol.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/svg/bloodsugar.svg b/assets/images/svg/bloodsugar.svg
new file mode 100644
index 0000000..a97032c
--- /dev/null
+++ b/assets/images/svg/bloodsugar.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/bmi.svg b/assets/images/svg/bmi.svg
new file mode 100644
index 0000000..7ee99db
--- /dev/null
+++ b/assets/images/svg/bmi.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/bmi_2.svg b/assets/images/svg/bmi_2.svg
new file mode 100644
index 0000000..38468d7
--- /dev/null
+++ b/assets/images/svg/bmi_2.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/bmr.svg b/assets/images/svg/bmr.svg
new file mode 100644
index 0000000..6b797e4
--- /dev/null
+++ b/assets/images/svg/bmr.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/bulb.svg b/assets/images/svg/bulb.svg
new file mode 100644
index 0000000..94553a5
--- /dev/null
+++ b/assets/images/svg/bulb.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/calories.svg b/assets/images/svg/calories.svg
new file mode 100644
index 0000000..9f8d2b5
--- /dev/null
+++ b/assets/images/svg/calories.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/covid_19.svg b/assets/images/svg/covid_19.svg
new file mode 100644
index 0000000..f3aa128
--- /dev/null
+++ b/assets/images/svg/covid_19.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/images/svg/cup_add.svg b/assets/images/svg/cup_add.svg
new file mode 100644
index 0000000..ebe186a
--- /dev/null
+++ b/assets/images/svg/cup_add.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/cup_empty.svg b/assets/images/svg/cup_empty.svg
new file mode 100644
index 0000000..fae08fe
--- /dev/null
+++ b/assets/images/svg/cup_empty.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/cup_filled.svg b/assets/images/svg/cup_filled.svg
new file mode 100644
index 0000000..6a085bb
--- /dev/null
+++ b/assets/images/svg/cup_filled.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/daily_water_monitor.svg b/assets/images/svg/daily_water_monitor.svg
new file mode 100644
index 0000000..b5f057d
--- /dev/null
+++ b/assets/images/svg/daily_water_monitor.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/due_date_accordion.svg b/assets/images/svg/due_date_accordion.svg
new file mode 100644
index 0000000..828a1c9
--- /dev/null
+++ b/assets/images/svg/due_date_accordion.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/images/svg/dumbell_icon.svg b/assets/images/svg/dumbell_icon.svg
new file mode 100644
index 0000000..1d6db5f
--- /dev/null
+++ b/assets/images/svg/dumbell_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/fertile_window_accordion.svg b/assets/images/svg/fertile_window_accordion.svg
new file mode 100644
index 0000000..63f0173
--- /dev/null
+++ b/assets/images/svg/fertile_window_accordion.svg
@@ -0,0 +1,11 @@
+
diff --git a/assets/images/svg/file.svg b/assets/images/svg/file.svg
new file mode 100644
index 0000000..004145c
--- /dev/null
+++ b/assets/images/svg/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/images/svg/files.svg b/assets/images/svg/files.svg
new file mode 100644
index 0000000..dfc862e
--- /dev/null
+++ b/assets/images/svg/files.svg
@@ -0,0 +1,14 @@
+
diff --git a/assets/images/svg/gallery.svg b/assets/images/svg/gallery.svg
new file mode 100644
index 0000000..ec1c45e
--- /dev/null
+++ b/assets/images/svg/gallery.svg
@@ -0,0 +1,11 @@
+
diff --git a/assets/images/svg/genderInputIcon.svg b/assets/images/svg/genderInputIcon.svg
new file mode 100644
index 0000000..4482ae3
--- /dev/null
+++ b/assets/images/svg/genderInputIcon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/gender_icon.svg b/assets/images/svg/gender_icon.svg
new file mode 100644
index 0000000..4573125
--- /dev/null
+++ b/assets/images/svg/gender_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/general_health.svg b/assets/images/svg/general_health.svg
new file mode 100644
index 0000000..0102df1
--- /dev/null
+++ b/assets/images/svg/general_health.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/glass_icon.svg b/assets/images/svg/glass_icon.svg
new file mode 100644
index 0000000..1df8eec
--- /dev/null
+++ b/assets/images/svg/glass_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/graph_icon.svg b/assets/images/svg/graph_icon.svg
new file mode 100644
index 0000000..7bb6fbb
--- /dev/null
+++ b/assets/images/svg/graph_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/green_tick_icon.svg b/assets/images/svg/green_tick_icon.svg
new file mode 100644
index 0000000..e041191
--- /dev/null
+++ b/assets/images/svg/green_tick_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/health_calculators_services_icon.svg b/assets/images/svg/health_calculators_services_icon.svg
new file mode 100644
index 0000000..9f30d08
--- /dev/null
+++ b/assets/images/svg/health_calculators_services_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/health_converters_icon.svg b/assets/images/svg/health_converters_icon.svg
new file mode 100644
index 0000000..225ad01
--- /dev/null
+++ b/assets/images/svg/health_converters_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/heart_rate.svg b/assets/images/svg/heart_rate.svg
new file mode 100644
index 0000000..15c754f
--- /dev/null
+++ b/assets/images/svg/heart_rate.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/height.svg b/assets/images/svg/height.svg
new file mode 100644
index 0000000..f275d34
--- /dev/null
+++ b/assets/images/svg/height.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/height_2.svg b/assets/images/svg/height_2.svg
new file mode 100644
index 0000000..a1c361a
--- /dev/null
+++ b/assets/images/svg/height_2.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/height_icon.svg b/assets/images/svg/height_icon.svg
new file mode 100644
index 0000000..78cefdc
--- /dev/null
+++ b/assets/images/svg/height_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/ibw.svg b/assets/images/svg/ibw.svg
new file mode 100644
index 0000000..2f11ca4
--- /dev/null
+++ b/assets/images/svg/ibw.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/insurance_approval_icon.svg b/assets/images/svg/insurance_approval_icon.svg
new file mode 100644
index 0000000..b46a54a
--- /dev/null
+++ b/assets/images/svg/insurance_approval_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/internet_pairing_icon.svg b/assets/images/svg/internet_pairing_icon.svg
new file mode 100644
index 0000000..3e1ac63
--- /dev/null
+++ b/assets/images/svg/internet_pairing_icon.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/images/svg/invoices_list_icon.svg b/assets/images/svg/invoices_list_icon.svg
new file mode 100644
index 0000000..f123096
--- /dev/null
+++ b/assets/images/svg/invoices_list_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/list_icon.svg b/assets/images/svg/list_icon.svg
new file mode 100644
index 0000000..e68f20b
--- /dev/null
+++ b/assets/images/svg/list_icon.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/images/svg/low_indicator_icon.svg b/assets/images/svg/low_indicator_icon.svg
new file mode 100644
index 0000000..f2ca09f
--- /dev/null
+++ b/assets/images/svg/low_indicator_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/medical_reports_icon.svg b/assets/images/svg/medical_reports_icon.svg
new file mode 100644
index 0000000..862b813
--- /dev/null
+++ b/assets/images/svg/medical_reports_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/minimize_icon.svg b/assets/images/svg/minimize_icon.svg
new file mode 100644
index 0000000..b60a041
--- /dev/null
+++ b/assets/images/svg/minimize_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/monthly_reports_icon.svg b/assets/images/svg/monthly_reports_icon.svg
new file mode 100644
index 0000000..5e786e2
--- /dev/null
+++ b/assets/images/svg/monthly_reports_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/my_doctors_icon.svg b/assets/images/svg/my_doctors_icon.svg
new file mode 100644
index 0000000..c5fc541
--- /dev/null
+++ b/assets/images/svg/my_doctors_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/my_radiology_icon.svg b/assets/images/svg/my_radiology_icon.svg
new file mode 100644
index 0000000..7b5ebe4
--- /dev/null
+++ b/assets/images/svg/my_radiology_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/my_sick_leave_icon.svg b/assets/images/svg/my_sick_leave_icon.svg
new file mode 100644
index 0000000..f488cff
--- /dev/null
+++ b/assets/images/svg/my_sick_leave_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/next_period_accordion.svg b/assets/images/svg/next_period_accordion.svg
new file mode 100644
index 0000000..4ff65db
--- /dev/null
+++ b/assets/images/svg/next_period_accordion.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/images/svg/normal_status_green_icon.svg b/assets/images/svg/normal_status_green_icon.svg
new file mode 100644
index 0000000..b3f2619
--- /dev/null
+++ b/assets/images/svg/normal_status_green_icon.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/images/svg/notification_icon_grey.svg b/assets/images/svg/notification_icon_grey.svg
new file mode 100644
index 0000000..9e5e8d5
--- /dev/null
+++ b/assets/images/svg/notification_icon_grey.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/open_camera.svg b/assets/images/svg/open_camera.svg
new file mode 100644
index 0000000..171452a
--- /dev/null
+++ b/assets/images/svg/open_camera.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/images/svg/outer_bubbles.svg b/assets/images/svg/outer_bubbles.svg
new file mode 100644
index 0000000..cfe860d
--- /dev/null
+++ b/assets/images/svg/outer_bubbles.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/images/svg/phramacy_icon.svg b/assets/images/svg/phramacy_icon.svg
new file mode 100644
index 0000000..a9c5d1c
--- /dev/null
+++ b/assets/images/svg/phramacy_icon.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/svg/pregnancy_test_day_accordion.svg b/assets/images/svg/pregnancy_test_day_accordion.svg
new file mode 100644
index 0000000..5a29588
--- /dev/null
+++ b/assets/images/svg/pregnancy_test_day_accordion.svg
@@ -0,0 +1,20 @@
+
diff --git a/assets/images/svg/profile_icon.svg b/assets/images/svg/profile_icon.svg
new file mode 100644
index 0000000..20dfb2b
--- /dev/null
+++ b/assets/images/svg/profile_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/rate_1.svg b/assets/images/svg/rate_1.svg
new file mode 100644
index 0000000..8e1c2f2
--- /dev/null
+++ b/assets/images/svg/rate_1.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/rate_2.svg b/assets/images/svg/rate_2.svg
new file mode 100644
index 0000000..9500556
--- /dev/null
+++ b/assets/images/svg/rate_2.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/rate_3.svg b/assets/images/svg/rate_3.svg
new file mode 100644
index 0000000..7c9d0f5
--- /dev/null
+++ b/assets/images/svg/rate_3.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/rate_4.svg b/assets/images/svg/rate_4.svg
new file mode 100644
index 0000000..7f82267
--- /dev/null
+++ b/assets/images/svg/rate_4.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/rate_5.svg b/assets/images/svg/rate_5.svg
new file mode 100644
index 0000000..a7308ba
--- /dev/null
+++ b/assets/images/svg/rate_5.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/resp_rate.svg b/assets/images/svg/resp_rate.svg
new file mode 100644
index 0000000..7038793
--- /dev/null
+++ b/assets/images/svg/resp_rate.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/send_email_icon.svg b/assets/images/svg/send_email_icon.svg
new file mode 100644
index 0000000..eb8684a
--- /dev/null
+++ b/assets/images/svg/send_email_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/sick_leave_report_icon.svg b/assets/images/svg/sick_leave_report_icon.svg
new file mode 100644
index 0000000..521c063
--- /dev/null
+++ b/assets/images/svg/sick_leave_report_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/smartwatch_icon.svg b/assets/images/svg/smartwatch_icon.svg
new file mode 100644
index 0000000..162ab36
--- /dev/null
+++ b/assets/images/svg/smartwatch_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/switch.svg b/assets/images/svg/switch.svg
new file mode 100644
index 0000000..1db8753
--- /dev/null
+++ b/assets/images/svg/switch.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/symptom_bottom_icon.svg b/assets/images/svg/symptom_bottom_icon.svg
new file mode 100644
index 0000000..bc72971
--- /dev/null
+++ b/assets/images/svg/symptom_bottom_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/symptom_checker_icon.svg b/assets/images/svg/symptom_checker_icon.svg
new file mode 100644
index 0000000..e41c1dd
--- /dev/null
+++ b/assets/images/svg/symptom_checker_icon.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/svg/temperature.svg b/assets/images/svg/temperature.svg
new file mode 100644
index 0000000..14c7da4
--- /dev/null
+++ b/assets/images/svg/temperature.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/trade_down_red.svg b/assets/images/svg/trade_down_red.svg
new file mode 100644
index 0000000..7c77c8e
--- /dev/null
+++ b/assets/images/svg/trade_down_red.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/trade_down_yellow.svg b/assets/images/svg/trade_down_yellow.svg
new file mode 100644
index 0000000..93c6805
--- /dev/null
+++ b/assets/images/svg/trade_down_yellow.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/triglycerides.svg b/assets/images/svg/triglycerides.svg
new file mode 100644
index 0000000..0f8facd
--- /dev/null
+++ b/assets/images/svg/triglycerides.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/update_insurance_icon.svg b/assets/images/svg/update_insurance_icon.svg
new file mode 100644
index 0000000..684d672
--- /dev/null
+++ b/assets/images/svg/update_insurance_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/water_bottle.svg b/assets/images/svg/water_bottle.svg
new file mode 100644
index 0000000..4763d7e
--- /dev/null
+++ b/assets/images/svg/water_bottle.svg
@@ -0,0 +1,34 @@
+
diff --git a/assets/images/svg/weight.svg b/assets/images/svg/weight.svg
new file mode 100644
index 0000000..6c42c17
--- /dev/null
+++ b/assets/images/svg/weight.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/weight_2.svg b/assets/images/svg/weight_2.svg
new file mode 100644
index 0000000..c22441f
--- /dev/null
+++ b/assets/images/svg/weight_2.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/weight_icon.svg b/assets/images/svg/weight_icon.svg
new file mode 100644
index 0000000..f93c662
--- /dev/null
+++ b/assets/images/svg/weight_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/weight_scale_icon.svg b/assets/images/svg/weight_scale_icon.svg
new file mode 100644
index 0000000..c3329ff
--- /dev/null
+++ b/assets/images/svg/weight_scale_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/weight_tracker_icon.svg b/assets/images/svg/weight_tracker_icon.svg
new file mode 100644
index 0000000..5110575
--- /dev/null
+++ b/assets/images/svg/weight_tracker_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/women_health.svg b/assets/images/svg/women_health.svg
new file mode 100644
index 0000000..5eca669
--- /dev/null
+++ b/assets/images/svg/women_health.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/yellow_arrow_down_icon.svg b/assets/images/svg/yellow_arrow_down_icon.svg
new file mode 100644
index 0000000..f2ca09f
--- /dev/null
+++ b/assets/images/svg/yellow_arrow_down_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json
index 787a718..b8fc7eb 100644
--- a/assets/langs/ar-SA.json
+++ b/assets/langs/ar-SA.json
@@ -876,5 +876,6 @@
"endDate": "تاريخ الانتهاء",
"walkin": "زيارة بدون موعد",
"laserClinic": "عيادة الليزر",
- "continueString": "يكمل"
+ "continueString": "يكمل",
+ "covid_info": "تجري مستشفيات د. سليمان الحبيب فحص فيروس كورونا المستجد وتصدر شهادات السفر على مدار الساعة، طوال أيام الأسبوع، وبسرعة ودقة عالية. يمكن للراغبين في الاستفادة من هذه الخدمة زيارة أحد فروع مستشفيات د. سليمان الحبيب وإجراء فحص كورونا خلال بضع دقائق والحصول على النتائج خلال عدة ساعات خدمة فحص فيروس كورونا Covid 19 بتقنية PCR للكشف عن الفيروس وفقاً لأعلى المعايير العالمية وبأحدث أجهزة RT-PCR عالية الدقة (GeneXpert الأمريكي وغيره)، وهي طرق معتمدة من قبل هيئة الغذاء والدواء وكذلك من قبل المركز السعودي للوقاية من الأمراض المُعدية"
}
\ No newline at end of file
diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json
index 7b8c4b8..7839083 100644
--- a/assets/langs/en-US.json
+++ b/assets/langs/en-US.json
@@ -872,6 +872,6 @@
"searchClinic": "Search Clinic",
"walkin": "Walk In",
"continueString": "Continue",
- "laserClinic": "Laser Clinic"
-
+ "laserClinic": "Laser Clinic",
+ "covid_info" :"Dr. Sulaiman Al Habib hospitals are conducting a test for the emerging corona virus and issuing travel certificates 24/7 in a short time and with high accuracy. Those wishing to benefit from this service can visit one of Dr. Sulaiman Al Habib branches to conduct a corona test within few minutes, and obtain the result within several hours. Corona Virus Covid 19 testing service with PCR technology to detect the virus according to the highest international standards and with the latest high-precision RT-PCR devices (American GeneXpert and others), That is approved by the Food and Drug Authority as well as by the Saudi Center for Infectious Diseases Prevention."
}
\ No newline at end of file
diff --git a/devtools_options.yaml b/devtools_options.yaml
new file mode 100644
index 0000000..fa0b357
--- /dev/null
+++ b/devtools_options.yaml
@@ -0,0 +1,3 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
diff --git a/ios/Controllers/MainFlutterVC.swift b/ios/Controllers/MainFlutterVC.swift
new file mode 100644
index 0000000..4f91d05
--- /dev/null
+++ b/ios/Controllers/MainFlutterVC.swift
@@ -0,0 +1,118 @@
+//
+// MainFlutterVC.swift
+// Runner
+//
+// Created by ZiKambrani on 25/03/1442 AH.
+//
+
+import UIKit
+import Flutter
+import NetworkExtension
+import SystemConfiguration.CaptiveNetwork
+
+class MainFlutterVC: FlutterViewController {
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+// flutterMethodChannel?.setMethodCallHandler { (methodCall, result) in
+//
+// if methodCall.method == "connectHMGInternetWifi"{
+// self.connectHMGInternetWifi(methodCall:methodCall, result: result)
+//
+// }else if methodCall.method == "connectHMGGuestWifi"{
+// self.connectHMGGuestWifi(methodCall:methodCall, result: result)
+//
+// }else if methodCall.method == "isHMGNetworkAvailable"{
+// self.isHMGNetworkAvailable(methodCall:methodCall, result: result)
+//
+// }else if methodCall.method == "registerHmgGeofences"{
+// self.registerHmgGeofences(result: result)
+// }
+//
+// print("")
+// }
+//
+// FlutterText.with(key: "errorConnectingHmgNetwork") { (localized) in
+// print(localized)
+// }
+
+ }
+
+
+ // Connect HMG Wifi and Internet
+ func connectHMGInternetWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
+
+ guard let pateintId = (methodCall.arguments as? [Any])?.first as? String
+ else { return assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'") }
+
+
+ HMG_Internet.shared.connect(patientId: pateintId) { (status, message) in
+ result(status ? 1 : 0)
+ if status{
+ self.showMessage(title:"Congratulations", message:message)
+ }else{
+ self.showMessage(title:"Ooops,", message:message)
+ }
+ }
+ }
+
+ // Connect HMG-Guest for App Access
+ func connectHMGGuestWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
+ HMG_GUEST.shared.connect() { (status, message) in
+ result(status ? 1 : 0)
+ if status{
+ self.showMessage(title:"Congratulations", message:message)
+ }else{
+ self.showMessage(title:"Ooops,", message:message)
+ }
+ }
+ }
+
+ func isHMGNetworkAvailable(methodCall:FlutterMethodCall ,result: @escaping FlutterResult) -> Bool{
+ guard let ssid = methodCall.arguments as? String else {
+ assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'")
+ return false
+ }
+
+ let queue = DispatchQueue.init(label: "com.hmg.wifilist")
+ NEHotspotHelper.register(options: nil, queue: queue) { (command) in
+ print(command)
+
+ if(command.commandType == NEHotspotHelperCommandType.filterScanList) {
+ if let networkList = command.networkList{
+ for network in networkList{
+ print(network.ssid)
+ }
+ }
+ }
+ }
+ return false
+
+ }
+
+
+ // Message Dailog
+ func showMessage(title:String, message:String){
+ DispatchQueue.main.async {
+ let alert = UIAlertController(title: title, message: message, preferredStyle: .alert )
+ alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
+ self.present(alert, animated: true) {
+
+ }
+ }
+ }
+
+ // Register Geofence
+ func registerHmgGeofences(result: @escaping FlutterResult){
+ flutterMethodChannel?.invokeMethod("getGeofencePreferenceKey", arguments: nil){ geoFencesJsonString in
+ if let jsonString = geoFencesJsonString as? String{
+ let allZones = GeoZoneModel.list(from: jsonString)
+ HMG_Geofence().register(geoZones: allZones)
+
+ }else{
+ }
+ }
+ }
+
+}
diff --git a/ios/Helper/API.swift b/ios/Helper/API.swift
new file mode 100644
index 0000000..b487f03
--- /dev/null
+++ b/ios/Helper/API.swift
@@ -0,0 +1,22 @@
+//
+// API.swift
+// Runner
+//
+// Created by ZiKambrani on 04/04/1442 AH.
+//
+
+import UIKit
+
+fileprivate let DOMAIN = "https://uat.hmgwebservices.com"
+fileprivate let SERVICE = "Services/Patients.svc/REST"
+fileprivate let BASE_URL = "\(DOMAIN)/\(SERVICE)"
+
+struct API {
+ static let WIFI_CREDENTIALS = "\(BASE_URL)/Hmg_SMS_Get_By_ProjectID_And_PatientID"
+}
+
+
+//struct API {
+// static let WIFI_CREDENTIALS = FlutterConstants.WIFI_CREDENTIALS_URL
+// static let LOG_GEOFENCE = FlutterConstants.LOG_GEOFENCE_URL
+//}
diff --git a/ios/Helper/Extensions.swift b/ios/Helper/Extensions.swift
new file mode 100644
index 0000000..de67f9b
--- /dev/null
+++ b/ios/Helper/Extensions.swift
@@ -0,0 +1,150 @@
+//
+// Extensions.swift
+// Runner
+//
+// Created by ZiKambrani on 04/04/1442 AH.
+//
+
+import UIKit
+
+
+extension String{
+ func toUrl() -> URL?{
+ return URL(string: self)
+ }
+
+ func removeSpace() -> String?{
+ return self.replacingOccurrences(of: " ", with: "")
+ }
+}
+
+extension Date{
+ func toString(format:String) -> String{
+ let df = DateFormatter()
+ df.dateFormat = format
+ return df.string(from: self)
+ }
+}
+
+extension Dictionary{
+ func merge(dict:[String:Any?]) -> [String:Any?]{
+ var self_ = self as! [String:Any?]
+ dict.forEach { (kv) in
+ self_.updateValue(kv.value, forKey: kv.key)
+ }
+ return self_
+ }
+}
+
+extension Bundle {
+
+ func certificate(named name: String) -> SecCertificate {
+ let cerURL = self.url(forResource: name, withExtension: "cer")!
+ let cerData = try! Data(contentsOf: cerURL)
+ let cer = SecCertificateCreateWithData(nil, cerData as CFData)!
+ return cer
+ }
+
+ func identity(named name: String, password: String) -> SecIdentity {
+ let p12URL = self.url(forResource: name, withExtension: "p12")!
+ let p12Data = try! Data(contentsOf: p12URL)
+
+ var importedCF: CFArray? = nil
+ let options = [kSecImportExportPassphrase as String: password]
+ let err = SecPKCS12Import(p12Data as CFData, options as CFDictionary, &importedCF)
+ precondition(err == errSecSuccess)
+ let imported = importedCF! as NSArray as! [[String:AnyObject]]
+ precondition(imported.count == 1)
+
+ return (imported[0][kSecImportItemIdentity as String]!) as! SecIdentity
+ }
+
+
+}
+
+extension SecCertificate{
+ func trust() -> Bool?{
+ var optionalTrust: SecTrust?
+ let policy = SecPolicyCreateBasicX509()
+
+ let status = SecTrustCreateWithCertificates([self] as AnyObject,
+ policy,
+ &optionalTrust)
+ guard status == errSecSuccess else { return false}
+ let trust = optionalTrust!
+
+ let stat = optionalTrust?.evaluateAllowing(rootCertificates: [self])
+ return stat
+ }
+
+ func secTrustObject() -> SecTrust?{
+ var optionalTrust: SecTrust?
+ let policy = SecPolicyCreateBasicX509()
+
+ let status = SecTrustCreateWithCertificates([self] as AnyObject,
+ policy,
+ &optionalTrust)
+ return optionalTrust
+ }
+}
+
+
+extension SecTrust {
+
+ func evaluate() -> Bool {
+ var trustResult: SecTrustResultType = .invalid
+ let err = SecTrustEvaluate(self, &trustResult)
+ guard err == errSecSuccess else { return false }
+ return [.proceed, .unspecified].contains(trustResult)
+ }
+
+ func evaluateAllowing(rootCertificates: [SecCertificate]) -> Bool {
+
+ // Apply our custom root to the trust object.
+
+ var err = SecTrustSetAnchorCertificates(self, rootCertificates as CFArray)
+ guard err == errSecSuccess else { return false }
+
+ // Re-enable the system's built-in root certificates.
+
+ err = SecTrustSetAnchorCertificatesOnly(self, false)
+ guard err == errSecSuccess else { return false }
+
+ // Run a trust evaluation and only allow the connection if it succeeds.
+
+ return self.evaluate()
+ }
+}
+
+
+extension UIView{
+ func show(){
+ self.alpha = 0.0
+ self.isHidden = false
+ UIView.animate(withDuration: 0.25, animations: {
+ self.alpha = 1
+ }) { (complete) in
+
+ }
+ }
+
+ func hide(){
+ UIView.animate(withDuration: 0.25, animations: {
+ self.alpha = 0.0
+ }) { (complete) in
+ self.isHidden = true
+ }
+ }
+}
+
+
+extension UIViewController{
+ func showAlert(withTitle: String, message: String){
+ let alert = UIAlertController(title: withTitle, message: message, preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
+ present(alert, animated: true) {
+
+ }
+ }
+}
+
diff --git a/ios/Helper/FlutterConstants.swift b/ios/Helper/FlutterConstants.swift
new file mode 100644
index 0000000..f1b3f09
--- /dev/null
+++ b/ios/Helper/FlutterConstants.swift
@@ -0,0 +1,36 @@
+//
+// FlutterConstants.swift
+// Runner
+//
+// Created by ZiKambrani on 22/12/2020.
+//
+
+import UIKit
+
+class FlutterConstants{
+ static var LOG_GEOFENCE_URL:String?
+ static var WIFI_CREDENTIALS_URL:String?
+ static var DEFAULT_HTTP_PARAMS:[String:Any?]?
+
+ class func set(){
+
+ // (FiX) Take a start with FlutterMethodChannel (kikstart)
+ /* First call to flutter method is not returning the correct value (Always returning 'NSObject') then after it wroking fine and returning correct value*/
+ FlutterText.with(key: "test") { (test) in
+
+ flutterMethodChannel?.invokeMethod("getDefaultHttpParameters", arguments: nil){ (response) in
+ if let defaultHTTPParams = response as? [String:Any?]{
+ DEFAULT_HTTP_PARAMS = defaultHTTPParams
+ }
+
+ }
+
+ flutterMethodChannel?.invokeMethod("getLogGeofenceFullUrl", arguments:nil){ (response) in
+ if let url = response as? String{
+ LOG_GEOFENCE_URL = url
+ }
+ }
+
+ }
+ }
+}
diff --git a/ios/Helper/GeoZoneModel.swift b/ios/Helper/GeoZoneModel.swift
new file mode 100644
index 0000000..e703b64
--- /dev/null
+++ b/ios/Helper/GeoZoneModel.swift
@@ -0,0 +1,67 @@
+//
+// GeoZoneModel.swift
+// Runner
+//
+// Created by ZiKambrani on 13/12/2020.
+//
+
+import UIKit
+import CoreLocation
+
+class GeoZoneModel{
+ var geofenceId:Int = -1
+ var description:String = ""
+ var descriptionN:String?
+ var latitude:String?
+ var longitude:String?
+ var radius:Int?
+ var type:Int?
+ var projectID:Int?
+ var imageURL:String?
+ var isCity:String?
+
+ func identifier() -> String{
+ return "\(geofenceId)_hmg"
+ }
+
+ func message() -> String{
+ return description
+ }
+
+ func toRegion(locationManager:CLLocationManager) -> CLCircularRegion?{
+ if let rad = radius, let lat = latitude?.removeSpace(), let long = longitude?.removeSpace(),
+ let radius_d = Double("\(rad)"), let lat_d = Double(lat), let long_d = Double(long){
+
+ let coordinate = CLLocationCoordinate2D(latitude: lat_d, longitude: long_d)
+ let validatedRadius = min(radius_d, locationManager.maximumRegionMonitoringDistance)
+
+ let region = CLCircularRegion(center: coordinate, radius: validatedRadius, identifier: identifier())
+ region.notifyOnExit = true
+ region.notifyOnEntry = true
+ return region
+ }
+ return nil
+ }
+
+ class func from(json:[String:Any]) -> GeoZoneModel{
+ let model = GeoZoneModel()
+ model.geofenceId = json["GEOF_ID"] as? Int ?? 0
+ model.radius = json["Radius"] as? Int
+ model.projectID = json["ProjectID"] as? Int
+ model.type = json["Type"] as? Int
+ model.description = json["Description"] as? String ?? ""
+ model.descriptionN = json["DescriptionN"] as? String
+ model.latitude = json["Latitude"] as? String
+ model.longitude = json["Longitude"] as? String
+ model.imageURL = json["ImageURL"] as? String
+ model.isCity = json["IsCity"] as? String
+
+ return model
+ }
+
+ class func list(from jsonString:String) -> [GeoZoneModel]{
+ let value = dictionaryArray(from: jsonString)
+ let geoZones = value.map { GeoZoneModel.from(json: $0) }
+ return geoZones
+ }
+}
diff --git a/ios/Helper/GlobalHelper.swift b/ios/Helper/GlobalHelper.swift
new file mode 100644
index 0000000..3768780
--- /dev/null
+++ b/ios/Helper/GlobalHelper.swift
@@ -0,0 +1,119 @@
+//
+// GlobalHelper.swift
+// Runner
+//
+// Created by ZiKambrani on 29/03/1442 AH.
+//
+
+import UIKit
+
+func dictionaryArray(from:String) -> [[String:Any]]{
+ if let data = from.data(using: .utf8) {
+ do {
+ return try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] ?? []
+ } catch {
+ print(error.localizedDescription)
+ }
+ }
+ return []
+
+}
+
+func dictionary(from:String) -> [String:Any]?{
+ if let data = from.data(using: .utf8) {
+ do {
+ return try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
+ } catch {
+ print(error.localizedDescription)
+ }
+ }
+ return nil
+
+}
+
+let HmgLocalNotificationCategoryIdentifier = "hmg.local.notification"
+func showNotification(identifier:String? = nil, title:String?, subtitle:String?, message:String?, sound:UNNotificationSound = UNNotificationSound.default, categoryIdentifier:String = HmgLocalNotificationCategoryIdentifier){
+ DispatchQueue.main.async {
+ let notificationContent = UNMutableNotificationContent()
+ notificationContent.categoryIdentifier = categoryIdentifier
+
+ if identifier != nil { notificationContent.categoryIdentifier = identifier! }
+ if title != nil { notificationContent.title = title! }
+ if subtitle != nil { notificationContent.body = message! }
+ if message != nil { notificationContent.subtitle = subtitle! }
+
+ notificationContent.sound = UNNotificationSound.default
+ let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
+ let request = UNNotificationRequest(identifier: "\(Date().timeIntervalSinceNow)", content: notificationContent, trigger: trigger)
+
+
+ UNUserNotificationCenter.current().add(request) { error in
+ if let error = error {
+ print("Error: \(error)")
+ }
+ }
+ }
+}
+
+func appLanguageCode() -> Int{
+ let lang = UserDefaults.standard.string(forKey: "language") ?? "ar"
+ return lang == "ar" ? 2 : 1
+}
+
+func userProfile() -> [String:Any?]?{
+ var userProf = UserDefaults.standard.string(forKey: "flutter.imei-user-data")
+ if(userProf == nil){
+ userProf = UserDefaults.standard.string(forKey: "flutter.user-profile")
+ }
+ return dictionary(from: userProf ?? "{}")
+}
+
+fileprivate let defaultHTTPParams:[String : Any?] = [
+ "ZipCode" : "966",
+ "VersionID" : 5.8,
+ "Channel" : 3,
+ "LanguageID" : appLanguageCode(),
+ "IPAdress" : "10.20.10.20",
+ "generalid" : "Cs2020@2016$2958",
+ "PatientOutSA" : 0,
+ "SessionID" : nil,
+ "isDentalAllowedBackend" : false,
+ "DeviceTypeID" : 2
+]
+
+func httpPostRequest(urlString:String, jsonBody:[String:Any?], completion:((Bool,[String:Any]?)->Void)?){
+ var json: [String: Any?] = jsonBody
+ json = json.merge(dict: defaultHTTPParams)
+ let jsonData = try? JSONSerialization.data(withJSONObject: json)
+
+ // create post request
+ let url = URL(string: urlString)!
+ var request = URLRequest(url: url)
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+ request.addValue("*/*", forHTTPHeaderField: "Accept")
+ request.httpMethod = "POST"
+ request.httpBody = jsonData
+
+ let task = URLSession.shared.dataTask(with: request) { data, response, error in
+ guard let data = data, error == nil else {
+ print(error?.localizedDescription ?? "No data")
+ return
+ }
+
+ let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
+ if let responseJSON = responseJSON as? [String: Any], let status = responseJSON["MessageStatus"] as? Int{
+ print(responseJSON)
+ if status == 1{
+ completion?(true,responseJSON)
+ }else{
+ completion?(false,responseJSON)
+ }
+
+ }else{
+ completion?(false,nil)
+ }
+ }
+
+ task.resume()
+
+}
diff --git a/ios/Helper/HMGPenguinInPlatformBridge.swift b/ios/Helper/HMGPenguinInPlatformBridge.swift
new file mode 100644
index 0000000..c4a4424
--- /dev/null
+++ b/ios/Helper/HMGPenguinInPlatformBridge.swift
@@ -0,0 +1,94 @@
+import Foundation
+import FLAnimatedImage
+
+
+var flutterMethodChannelPenguinIn:FlutterMethodChannel? = nil
+fileprivate var mainViewController:MainFlutterVC!
+
+class HMGPenguinInPlatformBridge{
+
+ private let channelName = "launch_penguin_ui"
+ private static var shared_:HMGPenguinInPlatformBridge?
+
+ class func initialize(flutterViewController:MainFlutterVC){
+ shared_ = HMGPenguinInPlatformBridge()
+ mainViewController = flutterViewController
+ shared_?.openChannel()
+ }
+
+ func shared() -> HMGPenguinInPlatformBridge{
+ assert((HMGPenguinInPlatformBridge.shared_ != nil), "HMGPenguinInPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
+ return HMGPenguinInPlatformBridge.shared_!
+ }
+
+ private func openChannel(){
+ flutterMethodChannelPenguinIn = FlutterMethodChannel(name: channelName, binaryMessenger: mainViewController.binaryMessenger)
+
+ flutterMethodChannelPenguinIn?.setMethodCallHandler { (methodCall, result) in
+ print("Called function \(methodCall.method)")
+
+ if let arguments = methodCall.arguments as Any? {
+ if methodCall.method == "launchPenguin"{
+ print("====== launchPenguinView Launched =========")
+ self.launchPenguinView(arguments: arguments, result: result)
+ }
+ } else {
+ result(FlutterError(code: "INVALID_ARGUMENT", message: "Storyboard name is required", details: nil))
+ }
+ }
+ }
+
+ private func launchPenguinView(arguments: Any, result: @escaping FlutterResult) {
+
+ let penguinView = PenguinView(
+ frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height),
+ viewIdentifier: 0,
+ arguments: arguments,
+ binaryMessenger: mainViewController.binaryMessenger
+ )
+
+ let penguinUIView = penguinView.view()
+ penguinUIView.frame = mainViewController.view.bounds
+ penguinUIView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+
+ mainViewController.view.addSubview(penguinUIView)
+
+ guard let args = arguments as? [String: Any],
+ let loaderImageData = args["loaderImage"] as? FlutterStandardTypedData else {
+ print("loaderImage data not found in arguments")
+ result(FlutterError(code: "ARGUMENT_ERROR", message: "Missing loaderImage data", details: nil))
+ return
+ }
+
+ let loadingOverlay = UIView(frame: UIScreen.main.bounds)
+ loadingOverlay.backgroundColor = UIColor.black.withAlphaComponent(0.5) // Semi-transparent overlay
+ loadingOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+
+ // Display the GIF using FLAnimatedImage
+ let animatedImage = FLAnimatedImage(animatedGIFData: loaderImageData.data)
+ let gifImageView = FLAnimatedImageView()
+ gifImageView.animatedImage = animatedImage
+ gifImageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
+ gifImageView.center = loadingOverlay.center
+ gifImageView.contentMode = .scaleAspectFit
+ loadingOverlay.addSubview(gifImageView)
+
+
+ if let window = UIApplication.shared.windows.first {
+ window.addSubview(loadingOverlay)
+
+ } else {
+ print("Error: Main window not found")
+ }
+
+ penguinView.onSuccess = {
+ // Hide and remove the loader
+ DispatchQueue.main.async {
+ loadingOverlay.removeFromSuperview()
+
+ }
+ }
+
+ result(nil)
+ }
+}
diff --git a/ios/Helper/HMGPlatformBridge.swift b/ios/Helper/HMGPlatformBridge.swift
new file mode 100644
index 0000000..fd9fb40
--- /dev/null
+++ b/ios/Helper/HMGPlatformBridge.swift
@@ -0,0 +1,140 @@
+//
+// HMGPlatformBridge.swift
+// Runner
+//
+// Created by ZiKambrani on 14/12/2020.
+//
+
+import UIKit
+import NetworkExtension
+import SystemConfiguration.CaptiveNetwork
+
+var flutterMethodChannel:FlutterMethodChannel? = nil
+fileprivate var mainViewController:MainFlutterVC!
+
+class HMGPlatformBridge{
+ private let channelName = "HMG-Platform-Bridge"
+ private static var shared_:HMGPlatformBridge?
+
+ class func initialize(flutterViewController:MainFlutterVC){
+ shared_ = HMGPlatformBridge()
+ mainViewController = flutterViewController
+ shared_?.openChannel()
+ }
+
+ func shared() -> HMGPlatformBridge{
+ assert((HMGPlatformBridge.shared_ != nil), "HMGPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
+ return HMGPlatformBridge.shared_!
+ }
+
+ private func openChannel(){
+ flutterMethodChannel = FlutterMethodChannel(name: channelName, binaryMessenger: mainViewController.binaryMessenger)
+ flutterMethodChannel?.setMethodCallHandler { (methodCall, result) in
+ print("Called function \(methodCall.method)")
+ if methodCall.method == "connectHMGInternetWifi"{
+ self.connectHMGInternetWifi(methodCall:methodCall, result: result)
+
+ }else if methodCall.method == "connectHMGGuestWifi"{
+ self.connectHMGGuestWifi(methodCall:methodCall, result: result)
+
+ }else if methodCall.method == "isHMGNetworkAvailable"{
+ self.isHMGNetworkAvailable(methodCall:methodCall, result: result)
+
+ }else if methodCall.method == "registerHmgGeofences"{
+ self.registerHmgGeofences(result: result)
+
+ }else if methodCall.method == "unRegisterHmgGeofences"{
+ self.unRegisterHmgGeofences(result: result)
+ }
+
+ print("")
+ }
+ Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
+ FlutterConstants.set()
+ }
+ }
+
+
+
+ // Connect HMG Wifi and Internet
+ func connectHMGInternetWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
+
+ guard let pateintId = (methodCall.arguments as? [Any])?.first as? String
+ else { return assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'") }
+
+
+ HMG_Internet.shared.connect(patientId: pateintId) { (status, message) in
+ result(status ? 1 : 0)
+ if status{
+ self.showMessage(title:"Congratulations", message:message)
+ }else{
+ self.showMessage(title:"Ooops,", message:message)
+ }
+ }
+ }
+
+ // Connect HMG-Guest for App Access
+ func connectHMGGuestWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
+ HMG_GUEST.shared.connect() { (status, message) in
+ result(status ? 1 : 0)
+ if status{
+ self.showMessage(title:"Congratulations", message:message)
+ }else{
+ self.showMessage(title:"Ooops,", message:message)
+ }
+ }
+ }
+
+ func isHMGNetworkAvailable(methodCall:FlutterMethodCall ,result: @escaping FlutterResult) -> Bool{
+ guard let ssid = methodCall.arguments as? String else {
+ assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'")
+ return false
+ }
+
+ let queue = DispatchQueue.init(label: "com.hmg.wifilist")
+ NEHotspotHelper.register(options: nil, queue: queue) { (command) in
+ print(command)
+
+ if(command.commandType == NEHotspotHelperCommandType.filterScanList) {
+ if let networkList = command.networkList{
+ for network in networkList{
+ print(network.ssid)
+ }
+ }
+ }
+ }
+ return false
+
+ }
+
+
+ // Message Dailog
+ func showMessage(title:String, message:String){
+ DispatchQueue.main.async {
+ let alert = UIAlertController(title: title, message: message, preferredStyle: .alert )
+ alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
+ mainViewController.present(alert, animated: true) {
+
+ }
+ }
+ }
+
+ // Register Geofence
+ func registerHmgGeofences(result: @escaping FlutterResult){
+ flutterMethodChannel?.invokeMethod("getGeoZones", arguments: nil){ geoFencesJsonString in
+ if let jsonString = geoFencesJsonString as? String{
+ let allZones = GeoZoneModel.list(from: jsonString)
+ HMG_Geofence.shared().register(geoZones: allZones)
+ result(true)
+ }else{
+ }
+ }
+ }
+
+ // Register Geofence
+ func unRegisterHmgGeofences(result: @escaping FlutterResult){
+ HMG_Geofence.shared().unRegisterAll()
+ result(true)
+ }
+
+}
diff --git a/ios/Helper/HMG_Geofence.swift b/ios/Helper/HMG_Geofence.swift
new file mode 100644
index 0000000..47454d3
--- /dev/null
+++ b/ios/Helper/HMG_Geofence.swift
@@ -0,0 +1,183 @@
+//
+// HMG_Geofence.swift
+// Runner
+//
+// Created by ZiKambrani on 13/12/2020.
+//
+
+import UIKit
+import CoreLocation
+
+fileprivate var df = DateFormatter()
+fileprivate var transition = ""
+
+enum Transition:Int {
+ case entry = 1
+ case exit = 2
+ func name() -> String{
+ return self.rawValue == 1 ? "Enter" : "Exit"
+ }
+}
+
+class HMG_Geofence:NSObject{
+
+ var geoZones:[GeoZoneModel]?
+ var locationManager:CLLocationManager!{
+ didSet{
+ // https://developer.apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati
+
+ locationManager.allowsBackgroundLocationUpdates = true
+ locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
+ locationManager.activityType = .other
+ locationManager.delegate = self
+ locationManager.requestAlwaysAuthorization()
+ // locationManager.distanceFilter = 500
+ // locationManager.startMonitoringSignificantLocationChanges()
+ }
+ }
+
+ private static var shared_:HMG_Geofence?
+ class func shared() -> HMG_Geofence{
+ if HMG_Geofence.shared_ == nil{
+ HMG_Geofence.initGeofencing()
+ }
+ return shared_!
+ }
+
+ class func initGeofencing(){
+ shared_ = HMG_Geofence()
+ shared_?.locationManager = CLLocationManager()
+ }
+
+ func register(geoZones:[GeoZoneModel]){
+
+ self.geoZones = geoZones
+
+ let monitoredRegions_ = monitoredRegions()
+ self.geoZones?.forEach({ (zone) in
+ if let region = zone.toRegion(locationManager: locationManager){
+ if let already = monitoredRegions_.first(where: {$0.identifier == zone.identifier()}){
+ debugPrint("Already monitering region: \(already)")
+ }else{
+ startMonitoring(region: region)
+ }
+ }else{
+ debugPrint("Invalid region: \(zone.latitude ?? "invalid_latitude"),\(zone.longitude ?? "invalid_longitude"),r\(zone.radius ?? 0) | \(zone.identifier())")
+ }
+ })
+ }
+
+ func monitoredRegions() -> Set{
+ return locationManager.monitoredRegions
+ }
+
+ func unRegisterAll(){
+ for region in locationManager.monitoredRegions {
+ locationManager.stopMonitoring(for: region)
+ }
+ }
+
+}
+
+// CLLocationManager Delegates
+extension HMG_Geofence : CLLocationManagerDelegate{
+
+ func startMonitoring(region: CLCircularRegion) {
+ if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
+ return
+ }
+
+ if CLLocationManager.authorizationStatus() != .authorizedAlways {
+ let message = """
+ Your geotification is saved but will only be activated once you grant
+ HMG permission to access the device location.
+ """
+ debugPrint(message)
+ }
+
+ locationManager.startMonitoring(for: region)
+ locationManager.requestState(for: region)
+ debugPrint("Starts monitering region: \(region)")
+ }
+
+ func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
+ debugPrint("didEnterRegion: \(region)")
+ if region is CLCircularRegion {
+ handleEvent(for: region,transition: .entry, location: manager.location)
+ }
+ }
+
+ func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
+ debugPrint("didExitRegion: \(region)")
+ if region is CLCircularRegion {
+ handleEvent(for: region,transition: .exit, location: manager.location)
+ }
+ }
+
+ func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
+ debugPrint("didDetermineState: \(state)")
+ }
+
+ func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
+ debugPrint("didUpdateLocations: \(locations)")
+ }
+
+
+}
+
+// Helpers
+extension HMG_Geofence{
+
+ func handleEvent(for region: CLRegion!, transition:Transition, location:CLLocation?) {
+ if let userProfile = userProfile(){
+ notifyUser(forRegion: region, transition: transition, location: locationManager.location, userProfile: userProfile)
+ notifyServer(forRegion: region, transition: transition, location: locationManager.location, userProfile: userProfile)
+ }
+ }
+
+ func geoZone(by id: String) -> GeoZoneModel? {
+ var zone:GeoZoneModel? = nil
+ if let zones_ = geoZones{
+ zone = zones_.first(where: { $0.identifier() == id})
+ }else{
+ // let jsonArray = UserDefaults.standard.string(forKey: "hmg-geo-fences")
+ }
+ return zone
+ }
+
+
+ func notifyUser(forRegion:CLRegion, transition:Transition, location:CLLocation?, userProfile:[String:Any?]){
+ if let patientId = userProfile["PatientID"] as? Int{
+
+ }
+ }
+
+ func notifyServer(forRegion:CLRegion, transition:Transition, location:CLLocation?, userProfile:[String:Any?]){
+ if let patientId = userProfile["PatientID"] as? Int{
+
+ if let idString = forRegion.identifier.split(separator: "_").first, let idInt = Int(idString){
+ let body:[String:Any] = [
+ "PointsID":idInt,
+ "GeoType":transition.rawValue,
+ "PatientID":patientId
+ ]
+
+ var logs = UserDefaults.init(suiteName: "GeoFenceLog")?.dictionary(forKey: "GEOFENCE_LOGS") ?? [:]
+ var geo = (logs[forRegion.identifier] as? [String]) ?? []
+
+ let url = "https://hmgwebservices.com/Services/Patients.svc/REST/GeoF_InsertPatientFileInfo"
+ httpPostRequest(urlString: url, jsonBody: body){ (status,json) in
+ let status_ = status ? "Notified successfully:" : "Failed to notify:"
+ showNotification(title: transition.name(), subtitle: forRegion.identifier, message: status_)
+
+
+ geo.append("\(status_) \(transition.name()) at \(Date().toString(format: "dd/MMM/yyy hh:mm:ss"))")
+ logs.updateValue( geo, forKey: forRegion.identifier)
+
+ UserDefaults.init(suiteName: "GeoFenceLog")?.set(logs, forKey: "GEOFENCE_LOGS")
+ }
+ }
+ }
+ }
+}
+
diff --git a/ios/Helper/LocalizedFromFlutter.swift b/ios/Helper/LocalizedFromFlutter.swift
new file mode 100644
index 0000000..8853064
--- /dev/null
+++ b/ios/Helper/LocalizedFromFlutter.swift
@@ -0,0 +1,22 @@
+//
+// LocalizedFromFlutter.swift
+// Runner
+//
+// Created by ZiKambrani on 10/04/1442 AH.
+//
+
+import UIKit
+
+class FlutterText{
+
+ class func with(key:String,completion: @escaping (String)->Void){
+ flutterMethodChannel?.invokeMethod("localizedValue", arguments: key, result: { (result) in
+ if let localized = result as? String{
+ completion(localized)
+ }else{
+ completion(key)
+ }
+ })
+ }
+
+}
diff --git a/ios/Helper/OpenTokPlatformBridge.swift b/ios/Helper/OpenTokPlatformBridge.swift
new file mode 100644
index 0000000..4da39dc
--- /dev/null
+++ b/ios/Helper/OpenTokPlatformBridge.swift
@@ -0,0 +1,61 @@
+//
+// HMGPlatformBridge.swift
+// Runner
+//
+// Created by ZiKambrani on 14/12/2020.
+//
+
+import UIKit
+import NetworkExtension
+import SystemConfiguration.CaptiveNetwork
+
+
+fileprivate var openTok:OpenTok?
+
+class OpenTokPlatformBridge : NSObject{
+ private var methodChannel:FlutterMethodChannel? = nil
+ private var mainViewController:MainFlutterVC!
+ private static var shared_:OpenTokPlatformBridge?
+
+ class func initialize(flutterViewController:MainFlutterVC, registrar:FlutterPluginRegistrar?){
+ shared_ = OpenTokPlatformBridge()
+ shared_?.mainViewController = flutterViewController
+
+ shared_?.openChannel()
+ openTok = OpenTok(mainViewController: flutterViewController, registrar: registrar)
+ }
+
+ func shared() -> OpenTokPlatformBridge{
+ assert((OpenTokPlatformBridge.shared_ != nil), "OpenTokPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
+ return OpenTokPlatformBridge.shared_!
+ }
+
+ private func openChannel(){
+ methodChannel = FlutterMethodChannel(name: "OpenTok-Platform-Bridge", binaryMessenger: mainViewController.binaryMessenger)
+ methodChannel?.setMethodCallHandler { (call, result) in
+ print("Called function \(call.method)")
+
+ switch(call.method) {
+ case "initSession":
+ openTok?.initSession(call: call, result: result)
+
+ case "swapCamera":
+ openTok?.swapCamera(call: call, result: result)
+
+ case "toggleAudio":
+ openTok?.toggleAudio(call: call, result: result)
+
+ case "toggleVideo":
+ openTok?.toggleVideo(call: call, result: result)
+
+ case "hangupCall":
+ openTok?.hangupCall(call: call, result: result)
+
+ default:
+ result(FlutterMethodNotImplemented)
+ }
+
+ print("")
+ }
+ }
+}
diff --git a/ios/Penguin/PenguinModel.swift b/ios/Penguin/PenguinModel.swift
new file mode 100644
index 0000000..e41979d
--- /dev/null
+++ b/ios/Penguin/PenguinModel.swift
@@ -0,0 +1,76 @@
+//
+// PenguinModel.swift
+// Runner
+//
+// Created by Amir on 06/08/2024.
+//
+
+import Foundation
+
+// Define the model class
+struct PenguinModel {
+ let baseURL: String
+ let dataURL: String
+ let dataServiceName: String
+ let positionURL: String
+ let clientKey: String
+ let storyboardName: String
+ let mapBoxKey: String
+ let clientID: String
+ let positionServiceName: String
+ let username: String
+ let isSimulationModeEnabled: Bool
+ let isShowUserName: Bool
+ let isUpdateUserLocationSmoothly: Bool
+ let isEnableReportIssue: Bool
+ let languageCode: String
+ let clinicID: String
+ let patientID: String
+ let projectID: String
+
+ // Initialize the model from a dictionary
+ init?(from dictionary: [String: Any]) {
+ guard
+ let baseURL = dictionary["baseURL"] as? String,
+ let dataURL = dictionary["dataURL"] as? String,
+ let dataServiceName = dictionary["dataServiceName"] as? String,
+ let positionURL = dictionary["positionURL"] as? String,
+ let clientKey = dictionary["clientKey"] as? String,
+ let storyboardName = dictionary["storyboardName"] as? String,
+ let mapBoxKey = dictionary["mapBoxKey"] as? String,
+ let clientID = dictionary["clientID"] as? String,
+ let positionServiceName = dictionary["positionServiceName"] as? String,
+ let username = dictionary["username"] as? String,
+ let isSimulationModeEnabled = dictionary["isSimulationModeEnabled"] as? Bool,
+ let isShowUserName = dictionary["isShowUserName"] as? Bool,
+ let isUpdateUserLocationSmoothly = dictionary["isUpdateUserLocationSmoothly"] as? Bool,
+ let isEnableReportIssue = dictionary["isEnableReportIssue"] as? Bool,
+ let languageCode = dictionary["languageCode"] as? String,
+ let clinicID = dictionary["clinicID"] as? String,
+ let patientID = dictionary["patientID"] as? String,
+ let projectID = dictionary["projectID"] as? String
+ else {
+ print("Initialization failed due to missing or invalid keys.")
+ return nil
+ }
+
+ self.baseURL = baseURL
+ self.dataURL = dataURL
+ self.dataServiceName = dataServiceName
+ self.positionURL = positionURL
+ self.clientKey = clientKey
+ self.storyboardName = storyboardName
+ self.mapBoxKey = mapBoxKey
+ self.clientID = clientID
+ self.positionServiceName = positionServiceName
+ self.username = username
+ self.isSimulationModeEnabled = isSimulationModeEnabled
+ self.isShowUserName = isShowUserName
+ self.isUpdateUserLocationSmoothly = isUpdateUserLocationSmoothly
+ self.isEnableReportIssue = isEnableReportIssue
+ self.languageCode = languageCode
+ self.clinicID = clinicID
+ self.patientID = patientID
+ self.projectID = projectID
+ }
+}
diff --git a/ios/Penguin/PenguinNavigator.swift b/ios/Penguin/PenguinNavigator.swift
new file mode 100644
index 0000000..e7ce55b
--- /dev/null
+++ b/ios/Penguin/PenguinNavigator.swift
@@ -0,0 +1,57 @@
+import PenNavUI
+import UIKit
+
+class PenguinNavigator {
+ private var config: PenguinModel
+
+ init(config: PenguinModel) {
+ self.config = config
+ }
+
+ private func logError(_ message: String) {
+ // Centralized logging function
+ print("PenguinSDKNavigator Error: \(message)")
+ }
+
+ func navigateToPOI( referenceId:String,completion: @escaping (Bool, String?) -> Void) {
+ PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey) { [weak self] token, error in
+
+ if let error = error {
+ let errorMessage = "Token error while getting the for Navigate to method"
+ completion(false, "Failed to get token: \(errorMessage)")
+
+ print("Failed to get token: \(errorMessage)")
+ return
+ }
+
+ guard let token = token else {
+ completion(false, "Token is nil")
+ print("Token is nil")
+ return
+ }
+ print("Token Generated")
+ print(token);
+
+
+ }
+ }
+
+ private func handleNavigation(referenceId: String, token: String, completion: @escaping (Bool, String?) -> Void) {
+ DispatchQueue.main.async {
+ PenNavUIManager.shared.setToken(token: token)
+
+ PenNavUIManager.shared.navigate(to: referenceId) { [weak self] _, navError in
+ guard let self = self else { return }
+
+ if let navError = navError {
+ self.logError("Navigation error: Reference ID invalid")
+ completion(false, "Navigation error: \(navError.localizedDescription)")
+ return
+ }
+
+ // Navigation successful
+ completion(true, nil)
+ }
+ }
+ }
+}
diff --git a/ios/Penguin/PenguinPlugin.swift b/ios/Penguin/PenguinPlugin.swift
new file mode 100644
index 0000000..029bec3
--- /dev/null
+++ b/ios/Penguin/PenguinPlugin.swift
@@ -0,0 +1,31 @@
+//
+// BlueGpsPlugin.swift
+// Runner
+//
+// Created by Penguin .
+//
+
+//import Foundation
+//import Flutter
+//
+///**
+// * A Flutter plugin for integrating Penguin SDK functionality.
+// * This class registers a view factory with the Flutter engine to create native views.
+// */
+//class PenguinPlugin: NSObject, FlutterPlugin {
+//
+// /**
+// * Registers the plugin with the Flutter engine.
+// *
+// * @param registrar The [FlutterPluginRegistrar] used to register the plugin.
+// * This method is called when the plugin is initialized, and it sets up the communication
+// * between Flutter and native code.
+// */
+// public static func register(with registrar: FlutterPluginRegistrar) {
+// // Create an instance of PenguinViewFactory with the binary messenger from the registrar
+// let factory = PenguinViewFactory(messenger: registrar.messenger())
+//
+// // Register the view factory with a unique ID for use in Flutter code
+// registrar.register(factory, withId: "penguin_native")
+// }
+//}
diff --git a/ios/Penguin/PenguinView.swift b/ios/Penguin/PenguinView.swift
new file mode 100644
index 0000000..b5161eb
--- /dev/null
+++ b/ios/Penguin/PenguinView.swift
@@ -0,0 +1,445 @@
+//
+
+// BlueGpsView.swift
+
+// Runner
+
+//
+
+// Created by Penguin.
+
+//
+
+
+
+import Foundation
+import UIKit
+import Flutter
+import PenNavUI
+
+import Foundation
+import Flutter
+import UIKit
+
+
+
+/**
+
+ * A custom Flutter platform view for displaying Penguin UI components.
+
+ * This class integrates with the Penguin navigation SDK and handles UI events.
+
+ */
+
+class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitializationDelegate
+
+{
+ // The main view displayed within the platform view
+
+ private var _view: UIView
+
+ private var model: PenguinModel?
+
+ private var methodChannel: FlutterMethodChannel
+
+ var onSuccess: (() -> Void)?
+
+
+
+
+
+
+
+ /**
+
+ * Initializes the PenguinView with the provided parameters.
+
+ *
+
+ * @param frame The frame of the view, specifying its size and position.
+
+ * @param viewId A unique identifier for this view instance.
+
+ * @param args Optional arguments provided for creating the view.
+
+ * @param messenger The [FlutterBinaryMessenger] used for communication with Dart.
+
+ */
+
+ init(
+
+ frame: CGRect,
+
+ viewIdentifier viewId: Int64,
+
+ arguments args: Any?,
+
+ binaryMessenger messenger: FlutterBinaryMessenger?
+
+ ) {
+
+ _view = UIView()
+
+ methodChannel = FlutterMethodChannel(name: "launch_penguin_ui", binaryMessenger: messenger!)
+
+
+
+ super.init()
+
+
+
+ // Get the screen's width and height to set the view's frame
+
+ let screenWidth = UIScreen.main.bounds.width
+
+ let screenHeight = UIScreen.main.bounds.height
+
+
+
+ // Uncomment to set the background color of the view
+
+ // _view.backgroundColor = UIColor.red
+
+
+
+ // Set the frame of the view to cover the entire screen
+
+ _view.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
+
+ print("========Inside Penguin View ========")
+
+ print(args)
+
+ guard let arguments = args as? [String: Any] else {
+
+ print("Error: Arguments are not in the expected format.")
+
+ return
+
+ }
+
+ print("===== i got tha Args=======")
+
+
+
+ // Initialize the model from the arguments
+
+ if let penguinModel = PenguinModel(from: arguments) {
+
+ self.model = penguinModel
+
+ initPenguin(args: penguinModel)
+
+ } else {
+
+ print("Error: Failed to initialize PenguinModel from arguments ")
+
+ }
+
+ // Initialize the Penguin SDK with required configurations
+
+ // initPenguin( arguments: args)
+
+ }
+
+
+
+ /**
+
+ * Initializes the Penguin SDK with custom configuration settings.
+
+ */
+
+ func initPenguin(args: PenguinModel) {
+
+// Set the initialization delegate to handle SDK initialization events
+
+ PenNavUIManager.shared.initializationDelegate = self
+
+ // Configure the Penguin SDK with necessary parameters
+
+ PenNavUIManager.shared
+
+ .setClientKey(args.clientKey)
+
+ .setClientID(args.clientID)
+
+ .setUsername(args.username)
+
+ .setSimulationModeEnabled(isEnable: args.isSimulationModeEnabled)
+
+ .setBaseURL(dataURL: args.dataURL, positionURL: args.positionURL)
+
+ .setServiceName(dataServiceName: args.dataServiceName, positionServiceName: args.positionServiceName)
+
+ .setIsShowUserName(args.isShowUserName)
+
+ .setIsUpdateUserLocationSmoothly(args.isUpdateUserLocationSmoothly)
+
+ .setEnableReportIssue(enable: args.isEnableReportIssue)
+
+ .setLanguage(args.languageCode)
+
+ .setBackButtonVisibility(true)
+
+ .build()
+
+ }
+
+
+
+
+
+ /**
+
+ * Returns the main view associated with this platform view.
+
+ *
+
+ * @return The UIView instance that represents this platform view.
+
+ */
+
+ func view() -> UIView {
+
+ return _view
+
+ }
+
+
+
+ // MARK: - PIEventsDelegate Methods
+
+
+
+
+
+
+
+
+
+ /**
+
+ * Called when the Penguin UI is dismissed.
+
+ */
+
+ func onPenNavUIDismiss() {
+
+ // Handle UI dismissal if needed
+
+ print("====== onPenNavUIDismiss =========")
+
+
+
+
+
+ self.view().removeFromSuperview()
+
+ }
+
+
+
+ /**
+
+ * Called when a report issue is generated.
+
+ *
+
+ * @param issue The type of issue reported.
+
+ */
+
+ func onReportIssue(_ issue: PenNavUI.IssueType) {
+
+ // Handle report issue events if needed
+
+ print("====== onReportIssueError =========")
+
+ methodChannel.invokeMethod("onReportIssue", arguments: ["issueType": issue])
+
+
+
+ }
+
+
+
+ /**
+
+ * Called when the Penguin UI setup is successful.
+
+ */
+
+ func onPenNavSuccess() {
+
+ print("====== onPenNavSuccess =========")
+
+ onSuccess?()
+
+ methodChannel.invokeMethod("onPenNavSuccess", arguments: nil)
+
+ // Obtain the FlutterViewController instance
+
+ let controller: FlutterViewController = UIApplication.shared.windows.first?.rootViewController as! FlutterViewController
+
+
+
+ print("====== after controller onPenNavSuccess =========")
+
+
+
+ // Set the events delegate to handle SDK events
+
+ PenNavUIManager.shared.eventsDelegate = self
+
+
+
+ print("====== after eventsDelegate onPenNavSuccess =========")
+
+
+
+ // Present the Penguin UI on top of the Flutter view controller
+
+ PenNavUIManager.shared.present(root: controller, view: _view)
+
+
+
+
+
+ print("====== after present onPenNavSuccess =========")
+
+ print(model?.clinicID)
+
+ print("====== after present onPenNavSuccess =========")
+
+
+
+ guard let config = self.model else {
+
+ print("Error: Config Model is nil")
+
+ return
+
+ }
+
+
+
+ guard let clinicID = self.model?.clinicID,
+
+ let clientID = self.model?.clientID, !clientID.isEmpty else {
+
+ print("Error: Config Client ID is nil or empty")
+
+ return
+
+ }
+
+
+
+ let navigator = PenguinNavigator(config: config)
+
+
+
+ PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey) { [weak self] token, error in
+
+ if let error = error {
+
+ let errorMessage = "Token error while getting the for Navigate to method"
+
+ print("Failed to get token: \(errorMessage)")
+
+ return
+
+ }
+
+
+
+ guard let token = token else {
+
+ print("Token is nil")
+
+ return
+
+ }
+
+ print("Token Generated")
+
+ print(token);
+
+
+
+ self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in
+
+ if success {
+
+ print("Navigation successful")
+
+ } else {
+
+ print("Navigation failed: \(errorMessage ?? "Unknown error")")
+
+ }
+
+
+
+ }
+
+
+
+ print("====== after Token onPenNavSuccess =========")
+
+ }
+
+
+
+ }
+
+
+
+
+
+
+
+ private func handleNavigation(clinicID: String, token: String, completion: @escaping (Bool, String?) -> Void) {
+
+ DispatchQueue.main.async {
+
+ PenNavUIManager.shared.setToken(token: token)
+
+ PenNavUIManager.shared.navigate(to: clinicID)
+
+ completion(true,nil)
+
+ }
+
+ }
+
+
+
+
+
+
+
+
+
+ /**
+
+ * Called when there is an initialization error with the Penguin UI.
+
+ *
+
+ * @param errorType The type of initialization error.
+
+ * @param errorDescription A description of the error.
+
+ */
+
+ func onPenNavInitializationError(errorType: PenNavUI.PenNavUIError, errorDescription: String) {
+
+ // Handle initialization errors if needed
+
+ print("onPenNavInitializationErrorType: \(errorType.rawValue)")
+
+ print("onPenNavInitializationError: \(errorDescription)")
+ }
+}
diff --git a/ios/Penguin/PenguinViewFactory.swift b/ios/Penguin/PenguinViewFactory.swift
new file mode 100644
index 0000000..a88bb5d
--- /dev/null
+++ b/ios/Penguin/PenguinViewFactory.swift
@@ -0,0 +1,59 @@
+//
+// BlueGpsViewFactory.swift
+// Runner
+//
+// Created by Penguin .
+//
+
+import Foundation
+import Flutter
+
+/**
+ * A factory class for creating instances of [PenguinView].
+ * This class implements `FlutterPlatformViewFactory` to create and manage native views.
+ */
+class PenguinViewFactory: NSObject, FlutterPlatformViewFactory {
+
+ // The binary messenger used for communication with the Flutter engine
+ private var messenger: FlutterBinaryMessenger
+
+ /**
+ * Initializes the PenguinViewFactory with the given messenger.
+ *
+ * @param messenger The [FlutterBinaryMessenger] used to communicate with Dart code.
+ */
+ init(messenger: FlutterBinaryMessenger) {
+ self.messenger = messenger
+ super.init()
+ }
+
+ /**
+ * Creates a new instance of [PenguinView].
+ *
+ * @param frame The frame of the view, specifying its size and position.
+ * @param viewId A unique identifier for this view instance.
+ * @param args Optional arguments provided for creating the view.
+ * @return An instance of [PenguinView] configured with the provided parameters.
+ */
+ func create(
+ withFrame frame: CGRect,
+ viewIdentifier viewId: Int64,
+ arguments args: Any?
+ ) -> FlutterPlatformView {
+ return PenguinView(
+ frame: frame,
+ viewIdentifier: viewId,
+ arguments: args,
+ binaryMessenger: messenger)
+ }
+
+ /**
+ * Returns the codec used for encoding and decoding method channel arguments.
+ * This method is required when `arguments` in `create` is not `nil`.
+ *
+ * @return A [FlutterMessageCodec] instance used for serialization.
+ */
+ public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
+ return FlutterStandardMessageCodec.sharedInstance()
+ }
+}
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 2eab03a..7a41ae2 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -11,11 +11,23 @@
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
478CFA942E638C8E0064F3D7 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */; };
+ 61243B492EC5FA3700D46FA0 /* PenguinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B422EC5FA3700D46FA0 /* PenguinModel.swift */; };
+ 61243B4C2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B3D2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift */; };
+ 61243B502EC5FA3700D46FA0 /* PenguinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B452EC5FA3700D46FA0 /* PenguinView.swift */; };
+ 61243B542EC5FA3700D46FA0 /* PenguinPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B442EC5FA3700D46FA0 /* PenguinPlugin.swift */; };
+ 61243B562EC5FA3700D46FA0 /* PenguinNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B432EC5FA3700D46FA0 /* PenguinNavigator.swift */; };
+ 61243B572EC5FA3700D46FA0 /* PenguinViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B462EC5FA3700D46FA0 /* PenguinViewFactory.swift */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 766D8CB62EC60BE600D05E07 /* Penguin.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB32EC60BE600D05E07 /* Penguin.xcframework */; };
+ 766D8CB72EC60BE600D05E07 /* Penguin.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB32EC60BE600D05E07 /* Penguin.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 766D8CB82EC60BE600D05E07 /* PenNavUI.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */; };
+ 766D8CB92EC60BE600D05E07 /* PenNavUI.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 766D8CBA2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */; };
+ 766D8CBB2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
- B976FB9C47411C32B24D5E01 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACE60DF9393168FD748550B3 /* Pods_Runner.framework */; };
+ DE1EF78253E64BE86845D2CC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -35,6 +47,9 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
+ 766D8CB72EC60BE600D05E07 /* Penguin.xcframework in Embed Frameworks */,
+ 766D8CBB2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Embed Frameworks */,
+ 766D8CB92EC60BE600D05E07 /* PenNavUI.xcframework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@@ -49,9 +64,18 @@
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
478CFA952E6E20A60064F3D7 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
+ 61243B3D2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HMGPenguinInPlatformBridge.swift; sourceTree = ""; };
+ 61243B422EC5FA3700D46FA0 /* PenguinModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinModel.swift; sourceTree = ""; };
+ 61243B432EC5FA3700D46FA0 /* PenguinNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinNavigator.swift; sourceTree = ""; };
+ 61243B442EC5FA3700D46FA0 /* PenguinPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinPlugin.swift; sourceTree = ""; };
+ 61243B452EC5FA3700D46FA0 /* PenguinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinView.swift; sourceTree = ""; };
+ 61243B462EC5FA3700D46FA0 /* PenguinViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinViewFactory.swift; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7595037DD52211B91157B0F3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 766D8CB32EC60BE600D05E07 /* Penguin.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Penguin.xcframework; path = Frameworks/Penguin.xcframework; sourceTree = ""; };
+ 766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PenNavUI.xcframework; path = Frameworks/PenNavUI.xcframework; sourceTree = ""; };
+ 766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PenguinINRenderer.xcframework; path = Frameworks/PenguinINRenderer.xcframework; sourceTree = ""; };
769C9BF82E6F106D009F68A9 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
8E12CEEB8E334EE22D5259D7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
@@ -62,7 +86,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- ACE60DF9393168FD748550B3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D6BB17A036DF7FCE75271203 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
@@ -71,7 +95,10 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- B976FB9C47411C32B24D5E01 /* Pods_Runner.framework in Frameworks */,
+ 766D8CB62EC60BE600D05E07 /* Penguin.xcframework in Frameworks */,
+ 766D8CBA2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Frameworks */,
+ 766D8CB82EC60BE600D05E07 /* PenNavUI.xcframework in Frameworks */,
+ DE1EF78253E64BE86845D2CC /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -86,6 +113,37 @@
path = RunnerTests;
sourceTree = "";
};
+ 61243B412EC5FA3700D46FA0 /* Helper */ = {
+ isa = PBXGroup;
+ children = (
+ 61243B3D2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift */,
+ );
+ path = Helper;
+ sourceTree = "";
+ };
+ 61243B472EC5FA3700D46FA0 /* Penguin */ = {
+ isa = PBXGroup;
+ children = (
+ 61243B422EC5FA3700D46FA0 /* PenguinModel.swift */,
+ 61243B432EC5FA3700D46FA0 /* PenguinNavigator.swift */,
+ 61243B442EC5FA3700D46FA0 /* PenguinPlugin.swift */,
+ 61243B452EC5FA3700D46FA0 /* PenguinView.swift */,
+ 61243B462EC5FA3700D46FA0 /* PenguinViewFactory.swift */,
+ );
+ path = Penguin;
+ sourceTree = "";
+ };
+ 766D8CB22EC60BE600D05E07 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 766D8CB32EC60BE600D05E07 /* Penguin.xcframework */,
+ 766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */,
+ 766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */,
+ D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
79DD2093A1D9674C94359FC8 /* Pods */ = {
isa = PBXGroup;
children = (
@@ -115,7 +173,7 @@
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
79DD2093A1D9674C94359FC8 /* Pods */,
- A07D637C76A0ABB38659D189 /* Frameworks */,
+ 766D8CB22EC60BE600D05E07 /* Frameworks */,
);
sourceTree = "";
};
@@ -131,6 +189,8 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
+ 61243B412EC5FA3700D46FA0 /* Helper */,
+ 61243B472EC5FA3700D46FA0 /* Penguin */,
769C9BF82E6F106D009F68A9 /* RunnerDebug.entitlements */,
478CFA952E6E20A60064F3D7 /* Runner.entitlements */,
478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */,
@@ -146,14 +206,6 @@
path = Runner;
sourceTree = "";
};
- A07D637C76A0ABB38659D189 /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- ACE60DF9393168FD748550B3 /* Pods_Runner.framework */,
- );
- name = Frameworks;
- sourceTree = "";
- };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -362,6 +414,12 @@
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 61243B492EC5FA3700D46FA0 /* PenguinModel.swift in Sources */,
+ 61243B4C2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift in Sources */,
+ 61243B502EC5FA3700D46FA0 /* PenguinView.swift in Sources */,
+ 61243B542EC5FA3700D46FA0 /* PenguinPlugin.swift in Sources */,
+ 61243B562EC5FA3700D46FA0 /* PenguinNavigator.swift in Sources */,
+ 61243B572EC5FA3700D46FA0 /* PenguinViewFactory.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index 6a5d34f..64d7428 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -1,7 +1,7 @@
import Flutter
import UIKit
-//import FirebaseCore
-//import FirebaseMessaging
+import FirebaseCore
+import FirebaseMessaging
import GoogleMaps
@main
@objc class AppDelegate: FlutterAppDelegate {
@@ -10,11 +10,18 @@ import GoogleMaps
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng")
-// FirebaseApp.configure()
+ FirebaseApp.configure()
+ initializePlatformChannels()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
-
+ func initializePlatformChannels(){
+ if let mainViewController = window?.rootViewController as? FlutterViewController{ // platform initialization suppose to be in foreground
+
+ HMGPenguinInPlatformBridge.initialize(flutterViewController: mainViewController)
+
+ }
+ }
override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken:Data){
// Messaging.messaging().apnsToken = deviceToken
super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
diff --git a/ios/Runner/Controllers/MainFlutterVC.swift b/ios/Runner/Controllers/MainFlutterVC.swift
new file mode 100644
index 0000000..4f91d05
--- /dev/null
+++ b/ios/Runner/Controllers/MainFlutterVC.swift
@@ -0,0 +1,118 @@
+//
+// MainFlutterVC.swift
+// Runner
+//
+// Created by ZiKambrani on 25/03/1442 AH.
+//
+
+import UIKit
+import Flutter
+import NetworkExtension
+import SystemConfiguration.CaptiveNetwork
+
+class MainFlutterVC: FlutterViewController {
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+// flutterMethodChannel?.setMethodCallHandler { (methodCall, result) in
+//
+// if methodCall.method == "connectHMGInternetWifi"{
+// self.connectHMGInternetWifi(methodCall:methodCall, result: result)
+//
+// }else if methodCall.method == "connectHMGGuestWifi"{
+// self.connectHMGGuestWifi(methodCall:methodCall, result: result)
+//
+// }else if methodCall.method == "isHMGNetworkAvailable"{
+// self.isHMGNetworkAvailable(methodCall:methodCall, result: result)
+//
+// }else if methodCall.method == "registerHmgGeofences"{
+// self.registerHmgGeofences(result: result)
+// }
+//
+// print("")
+// }
+//
+// FlutterText.with(key: "errorConnectingHmgNetwork") { (localized) in
+// print(localized)
+// }
+
+ }
+
+
+ // Connect HMG Wifi and Internet
+ func connectHMGInternetWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
+
+ guard let pateintId = (methodCall.arguments as? [Any])?.first as? String
+ else { return assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'") }
+
+
+ HMG_Internet.shared.connect(patientId: pateintId) { (status, message) in
+ result(status ? 1 : 0)
+ if status{
+ self.showMessage(title:"Congratulations", message:message)
+ }else{
+ self.showMessage(title:"Ooops,", message:message)
+ }
+ }
+ }
+
+ // Connect HMG-Guest for App Access
+ func connectHMGGuestWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
+ HMG_GUEST.shared.connect() { (status, message) in
+ result(status ? 1 : 0)
+ if status{
+ self.showMessage(title:"Congratulations", message:message)
+ }else{
+ self.showMessage(title:"Ooops,", message:message)
+ }
+ }
+ }
+
+ func isHMGNetworkAvailable(methodCall:FlutterMethodCall ,result: @escaping FlutterResult) -> Bool{
+ guard let ssid = methodCall.arguments as? String else {
+ assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'")
+ return false
+ }
+
+ let queue = DispatchQueue.init(label: "com.hmg.wifilist")
+ NEHotspotHelper.register(options: nil, queue: queue) { (command) in
+ print(command)
+
+ if(command.commandType == NEHotspotHelperCommandType.filterScanList) {
+ if let networkList = command.networkList{
+ for network in networkList{
+ print(network.ssid)
+ }
+ }
+ }
+ }
+ return false
+
+ }
+
+
+ // Message Dailog
+ func showMessage(title:String, message:String){
+ DispatchQueue.main.async {
+ let alert = UIAlertController(title: title, message: message, preferredStyle: .alert )
+ alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
+ self.present(alert, animated: true) {
+
+ }
+ }
+ }
+
+ // Register Geofence
+ func registerHmgGeofences(result: @escaping FlutterResult){
+ flutterMethodChannel?.invokeMethod("getGeofencePreferenceKey", arguments: nil){ geoFencesJsonString in
+ if let jsonString = geoFencesJsonString as? String{
+ let allZones = GeoZoneModel.list(from: jsonString)
+ HMG_Geofence().register(geoZones: allZones)
+
+ }else{
+ }
+ }
+ }
+
+}
diff --git a/ios/Runner/Helper/API.swift b/ios/Runner/Helper/API.swift
new file mode 100644
index 0000000..b487f03
--- /dev/null
+++ b/ios/Runner/Helper/API.swift
@@ -0,0 +1,22 @@
+//
+// API.swift
+// Runner
+//
+// Created by ZiKambrani on 04/04/1442 AH.
+//
+
+import UIKit
+
+fileprivate let DOMAIN = "https://uat.hmgwebservices.com"
+fileprivate let SERVICE = "Services/Patients.svc/REST"
+fileprivate let BASE_URL = "\(DOMAIN)/\(SERVICE)"
+
+struct API {
+ static let WIFI_CREDENTIALS = "\(BASE_URL)/Hmg_SMS_Get_By_ProjectID_And_PatientID"
+}
+
+
+//struct API {
+// static let WIFI_CREDENTIALS = FlutterConstants.WIFI_CREDENTIALS_URL
+// static let LOG_GEOFENCE = FlutterConstants.LOG_GEOFENCE_URL
+//}
diff --git a/ios/Runner/Helper/Extensions.swift b/ios/Runner/Helper/Extensions.swift
new file mode 100644
index 0000000..de67f9b
--- /dev/null
+++ b/ios/Runner/Helper/Extensions.swift
@@ -0,0 +1,150 @@
+//
+// Extensions.swift
+// Runner
+//
+// Created by ZiKambrani on 04/04/1442 AH.
+//
+
+import UIKit
+
+
+extension String{
+ func toUrl() -> URL?{
+ return URL(string: self)
+ }
+
+ func removeSpace() -> String?{
+ return self.replacingOccurrences(of: " ", with: "")
+ }
+}
+
+extension Date{
+ func toString(format:String) -> String{
+ let df = DateFormatter()
+ df.dateFormat = format
+ return df.string(from: self)
+ }
+}
+
+extension Dictionary{
+ func merge(dict:[String:Any?]) -> [String:Any?]{
+ var self_ = self as! [String:Any?]
+ dict.forEach { (kv) in
+ self_.updateValue(kv.value, forKey: kv.key)
+ }
+ return self_
+ }
+}
+
+extension Bundle {
+
+ func certificate(named name: String) -> SecCertificate {
+ let cerURL = self.url(forResource: name, withExtension: "cer")!
+ let cerData = try! Data(contentsOf: cerURL)
+ let cer = SecCertificateCreateWithData(nil, cerData as CFData)!
+ return cer
+ }
+
+ func identity(named name: String, password: String) -> SecIdentity {
+ let p12URL = self.url(forResource: name, withExtension: "p12")!
+ let p12Data = try! Data(contentsOf: p12URL)
+
+ var importedCF: CFArray? = nil
+ let options = [kSecImportExportPassphrase as String: password]
+ let err = SecPKCS12Import(p12Data as CFData, options as CFDictionary, &importedCF)
+ precondition(err == errSecSuccess)
+ let imported = importedCF! as NSArray as! [[String:AnyObject]]
+ precondition(imported.count == 1)
+
+ return (imported[0][kSecImportItemIdentity as String]!) as! SecIdentity
+ }
+
+
+}
+
+extension SecCertificate{
+ func trust() -> Bool?{
+ var optionalTrust: SecTrust?
+ let policy = SecPolicyCreateBasicX509()
+
+ let status = SecTrustCreateWithCertificates([self] as AnyObject,
+ policy,
+ &optionalTrust)
+ guard status == errSecSuccess else { return false}
+ let trust = optionalTrust!
+
+ let stat = optionalTrust?.evaluateAllowing(rootCertificates: [self])
+ return stat
+ }
+
+ func secTrustObject() -> SecTrust?{
+ var optionalTrust: SecTrust?
+ let policy = SecPolicyCreateBasicX509()
+
+ let status = SecTrustCreateWithCertificates([self] as AnyObject,
+ policy,
+ &optionalTrust)
+ return optionalTrust
+ }
+}
+
+
+extension SecTrust {
+
+ func evaluate() -> Bool {
+ var trustResult: SecTrustResultType = .invalid
+ let err = SecTrustEvaluate(self, &trustResult)
+ guard err == errSecSuccess else { return false }
+ return [.proceed, .unspecified].contains(trustResult)
+ }
+
+ func evaluateAllowing(rootCertificates: [SecCertificate]) -> Bool {
+
+ // Apply our custom root to the trust object.
+
+ var err = SecTrustSetAnchorCertificates(self, rootCertificates as CFArray)
+ guard err == errSecSuccess else { return false }
+
+ // Re-enable the system's built-in root certificates.
+
+ err = SecTrustSetAnchorCertificatesOnly(self, false)
+ guard err == errSecSuccess else { return false }
+
+ // Run a trust evaluation and only allow the connection if it succeeds.
+
+ return self.evaluate()
+ }
+}
+
+
+extension UIView{
+ func show(){
+ self.alpha = 0.0
+ self.isHidden = false
+ UIView.animate(withDuration: 0.25, animations: {
+ self.alpha = 1
+ }) { (complete) in
+
+ }
+ }
+
+ func hide(){
+ UIView.animate(withDuration: 0.25, animations: {
+ self.alpha = 0.0
+ }) { (complete) in
+ self.isHidden = true
+ }
+ }
+}
+
+
+extension UIViewController{
+ func showAlert(withTitle: String, message: String){
+ let alert = UIAlertController(title: withTitle, message: message, preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
+ present(alert, animated: true) {
+
+ }
+ }
+}
+
diff --git a/ios/Runner/Helper/FlutterConstants.swift b/ios/Runner/Helper/FlutterConstants.swift
new file mode 100644
index 0000000..f1b3f09
--- /dev/null
+++ b/ios/Runner/Helper/FlutterConstants.swift
@@ -0,0 +1,36 @@
+//
+// FlutterConstants.swift
+// Runner
+//
+// Created by ZiKambrani on 22/12/2020.
+//
+
+import UIKit
+
+class FlutterConstants{
+ static var LOG_GEOFENCE_URL:String?
+ static var WIFI_CREDENTIALS_URL:String?
+ static var DEFAULT_HTTP_PARAMS:[String:Any?]?
+
+ class func set(){
+
+ // (FiX) Take a start with FlutterMethodChannel (kikstart)
+ /* First call to flutter method is not returning the correct value (Always returning 'NSObject') then after it wroking fine and returning correct value*/
+ FlutterText.with(key: "test") { (test) in
+
+ flutterMethodChannel?.invokeMethod("getDefaultHttpParameters", arguments: nil){ (response) in
+ if let defaultHTTPParams = response as? [String:Any?]{
+ DEFAULT_HTTP_PARAMS = defaultHTTPParams
+ }
+
+ }
+
+ flutterMethodChannel?.invokeMethod("getLogGeofenceFullUrl", arguments:nil){ (response) in
+ if let url = response as? String{
+ LOG_GEOFENCE_URL = url
+ }
+ }
+
+ }
+ }
+}
diff --git a/ios/Runner/Helper/GeoZoneModel.swift b/ios/Runner/Helper/GeoZoneModel.swift
new file mode 100644
index 0000000..e703b64
--- /dev/null
+++ b/ios/Runner/Helper/GeoZoneModel.swift
@@ -0,0 +1,67 @@
+//
+// GeoZoneModel.swift
+// Runner
+//
+// Created by ZiKambrani on 13/12/2020.
+//
+
+import UIKit
+import CoreLocation
+
+class GeoZoneModel{
+ var geofenceId:Int = -1
+ var description:String = ""
+ var descriptionN:String?
+ var latitude:String?
+ var longitude:String?
+ var radius:Int?
+ var type:Int?
+ var projectID:Int?
+ var imageURL:String?
+ var isCity:String?
+
+ func identifier() -> String{
+ return "\(geofenceId)_hmg"
+ }
+
+ func message() -> String{
+ return description
+ }
+
+ func toRegion(locationManager:CLLocationManager) -> CLCircularRegion?{
+ if let rad = radius, let lat = latitude?.removeSpace(), let long = longitude?.removeSpace(),
+ let radius_d = Double("\(rad)"), let lat_d = Double(lat), let long_d = Double(long){
+
+ let coordinate = CLLocationCoordinate2D(latitude: lat_d, longitude: long_d)
+ let validatedRadius = min(radius_d, locationManager.maximumRegionMonitoringDistance)
+
+ let region = CLCircularRegion(center: coordinate, radius: validatedRadius, identifier: identifier())
+ region.notifyOnExit = true
+ region.notifyOnEntry = true
+ return region
+ }
+ return nil
+ }
+
+ class func from(json:[String:Any]) -> GeoZoneModel{
+ let model = GeoZoneModel()
+ model.geofenceId = json["GEOF_ID"] as? Int ?? 0
+ model.radius = json["Radius"] as? Int
+ model.projectID = json["ProjectID"] as? Int
+ model.type = json["Type"] as? Int
+ model.description = json["Description"] as? String ?? ""
+ model.descriptionN = json["DescriptionN"] as? String
+ model.latitude = json["Latitude"] as? String
+ model.longitude = json["Longitude"] as? String
+ model.imageURL = json["ImageURL"] as? String
+ model.isCity = json["IsCity"] as? String
+
+ return model
+ }
+
+ class func list(from jsonString:String) -> [GeoZoneModel]{
+ let value = dictionaryArray(from: jsonString)
+ let geoZones = value.map { GeoZoneModel.from(json: $0) }
+ return geoZones
+ }
+}
diff --git a/ios/Runner/Helper/GlobalHelper.swift b/ios/Runner/Helper/GlobalHelper.swift
new file mode 100644
index 0000000..3768780
--- /dev/null
+++ b/ios/Runner/Helper/GlobalHelper.swift
@@ -0,0 +1,119 @@
+//
+// GlobalHelper.swift
+// Runner
+//
+// Created by ZiKambrani on 29/03/1442 AH.
+//
+
+import UIKit
+
+func dictionaryArray(from:String) -> [[String:Any]]{
+ if let data = from.data(using: .utf8) {
+ do {
+ return try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] ?? []
+ } catch {
+ print(error.localizedDescription)
+ }
+ }
+ return []
+
+}
+
+func dictionary(from:String) -> [String:Any]?{
+ if let data = from.data(using: .utf8) {
+ do {
+ return try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
+ } catch {
+ print(error.localizedDescription)
+ }
+ }
+ return nil
+
+}
+
+let HmgLocalNotificationCategoryIdentifier = "hmg.local.notification"
+func showNotification(identifier:String? = nil, title:String?, subtitle:String?, message:String?, sound:UNNotificationSound = UNNotificationSound.default, categoryIdentifier:String = HmgLocalNotificationCategoryIdentifier){
+ DispatchQueue.main.async {
+ let notificationContent = UNMutableNotificationContent()
+ notificationContent.categoryIdentifier = categoryIdentifier
+
+ if identifier != nil { notificationContent.categoryIdentifier = identifier! }
+ if title != nil { notificationContent.title = title! }
+ if subtitle != nil { notificationContent.body = message! }
+ if message != nil { notificationContent.subtitle = subtitle! }
+
+ notificationContent.sound = UNNotificationSound.default
+ let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
+ let request = UNNotificationRequest(identifier: "\(Date().timeIntervalSinceNow)", content: notificationContent, trigger: trigger)
+
+
+ UNUserNotificationCenter.current().add(request) { error in
+ if let error = error {
+ print("Error: \(error)")
+ }
+ }
+ }
+}
+
+func appLanguageCode() -> Int{
+ let lang = UserDefaults.standard.string(forKey: "language") ?? "ar"
+ return lang == "ar" ? 2 : 1
+}
+
+func userProfile() -> [String:Any?]?{
+ var userProf = UserDefaults.standard.string(forKey: "flutter.imei-user-data")
+ if(userProf == nil){
+ userProf = UserDefaults.standard.string(forKey: "flutter.user-profile")
+ }
+ return dictionary(from: userProf ?? "{}")
+}
+
+fileprivate let defaultHTTPParams:[String : Any?] = [
+ "ZipCode" : "966",
+ "VersionID" : 5.8,
+ "Channel" : 3,
+ "LanguageID" : appLanguageCode(),
+ "IPAdress" : "10.20.10.20",
+ "generalid" : "Cs2020@2016$2958",
+ "PatientOutSA" : 0,
+ "SessionID" : nil,
+ "isDentalAllowedBackend" : false,
+ "DeviceTypeID" : 2
+]
+
+func httpPostRequest(urlString:String, jsonBody:[String:Any?], completion:((Bool,[String:Any]?)->Void)?){
+ var json: [String: Any?] = jsonBody
+ json = json.merge(dict: defaultHTTPParams)
+ let jsonData = try? JSONSerialization.data(withJSONObject: json)
+
+ // create post request
+ let url = URL(string: urlString)!
+ var request = URLRequest(url: url)
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+ request.addValue("*/*", forHTTPHeaderField: "Accept")
+ request.httpMethod = "POST"
+ request.httpBody = jsonData
+
+ let task = URLSession.shared.dataTask(with: request) { data, response, error in
+ guard let data = data, error == nil else {
+ print(error?.localizedDescription ?? "No data")
+ return
+ }
+
+ let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
+ if let responseJSON = responseJSON as? [String: Any], let status = responseJSON["MessageStatus"] as? Int{
+ print(responseJSON)
+ if status == 1{
+ completion?(true,responseJSON)
+ }else{
+ completion?(false,responseJSON)
+ }
+
+ }else{
+ completion?(false,nil)
+ }
+ }
+
+ task.resume()
+
+}
diff --git a/ios/Runner/Helper/HMGPenguinInPlatformBridge.swift b/ios/Runner/Helper/HMGPenguinInPlatformBridge.swift
new file mode 100644
index 0000000..db02e8f
--- /dev/null
+++ b/ios/Runner/Helper/HMGPenguinInPlatformBridge.swift
@@ -0,0 +1,94 @@
+import Foundation
+import FLAnimatedImage
+
+
+var flutterMethodChannelPenguinIn:FlutterMethodChannel? = nil
+fileprivate var mainViewController:FlutterViewController!
+
+class HMGPenguinInPlatformBridge{
+
+ private let channelName = "launch_penguin_ui"
+ private static var shared_:HMGPenguinInPlatformBridge?
+
+ class func initialize(flutterViewController:FlutterViewController){
+ shared_ = HMGPenguinInPlatformBridge()
+ mainViewController = flutterViewController
+ shared_?.openChannel()
+ }
+
+ func shared() -> HMGPenguinInPlatformBridge{
+ assert((HMGPenguinInPlatformBridge.shared_ != nil), "HMGPenguinInPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
+ return HMGPenguinInPlatformBridge.shared_!
+ }
+
+ private func openChannel(){
+ flutterMethodChannelPenguinIn = FlutterMethodChannel(name: channelName, binaryMessenger: mainViewController.binaryMessenger)
+
+ flutterMethodChannelPenguinIn?.setMethodCallHandler { (methodCall, result) in
+ print("Called function \(methodCall.method)")
+
+ if let arguments = methodCall.arguments as Any? {
+ if methodCall.method == "launchPenguin"{
+ print("====== launchPenguinView Launched =========")
+ self.launchPenguinView(arguments: arguments, result: result)
+ }
+ } else {
+ result(FlutterError(code: "INVALID_ARGUMENT", message: "Storyboard name is required", details: nil))
+ }
+ }
+ }
+
+ private func launchPenguinView(arguments: Any, result: @escaping FlutterResult) {
+
+ let penguinView = PenguinView(
+ frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height),
+ viewIdentifier: 0,
+ arguments: arguments,
+ binaryMessenger: mainViewController.binaryMessenger
+ )
+
+ let penguinUIView = penguinView.view()
+ penguinUIView.frame = mainViewController.view.bounds
+ penguinUIView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+
+ mainViewController.view.addSubview(penguinUIView)
+
+ let args = arguments as? [String: Any]
+// let loaderImageData = args["loaderImage"] as? FlutterStandardTypedData else {
+// print("loaderImage data not found in arguments")
+// result(FlutterError(code: "ARGUMENT_ERROR", message: "Missing loaderImage data", details: nil))
+// return
+// }
+
+// let loadingOverlay = UIView(frame: UIScreen.main.bounds)
+// loadingOverlay.backgroundColor = UIColor.black.withAlphaComponent(0.5) // Semi-transparent overlay
+// loadingOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+
+ // Display the GIF using FLAnimatedImage
+// let animatedImage = FLAnimatedImage(animatedGIFData: loaderImageData.data)
+// let gifImageView = FLAnimatedImageView()
+// gifImageView.animatedImage = animatedImage
+// gifImageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
+// gifImageView.center = loadingOverlay.center
+// gifImageView.contentMode = .scaleAspectFit
+// loadingOverlay.addSubview(gifImageView)
+
+
+// if let window = UIApplication.shared.windows.first {
+// window.addSubview(loadingOverlay)
+//
+// } else {
+// print("Error: Main window not found")
+// }
+
+ penguinView.onSuccess = {
+ // Hide and remove the loader
+// DispatchQueue.main.async {
+// loadingOverlay.removeFromSuperview()
+//
+// }
+ }
+
+ result(nil)
+ }
+}
diff --git a/ios/Runner/Helper/HMGPlatformBridge.swift b/ios/Runner/Helper/HMGPlatformBridge.swift
new file mode 100644
index 0000000..fd9fb40
--- /dev/null
+++ b/ios/Runner/Helper/HMGPlatformBridge.swift
@@ -0,0 +1,140 @@
+//
+// HMGPlatformBridge.swift
+// Runner
+//
+// Created by ZiKambrani on 14/12/2020.
+//
+
+import UIKit
+import NetworkExtension
+import SystemConfiguration.CaptiveNetwork
+
+var flutterMethodChannel:FlutterMethodChannel? = nil
+fileprivate var mainViewController:MainFlutterVC!
+
+class HMGPlatformBridge{
+ private let channelName = "HMG-Platform-Bridge"
+ private static var shared_:HMGPlatformBridge?
+
+ class func initialize(flutterViewController:MainFlutterVC){
+ shared_ = HMGPlatformBridge()
+ mainViewController = flutterViewController
+ shared_?.openChannel()
+ }
+
+ func shared() -> HMGPlatformBridge{
+ assert((HMGPlatformBridge.shared_ != nil), "HMGPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
+ return HMGPlatformBridge.shared_!
+ }
+
+ private func openChannel(){
+ flutterMethodChannel = FlutterMethodChannel(name: channelName, binaryMessenger: mainViewController.binaryMessenger)
+ flutterMethodChannel?.setMethodCallHandler { (methodCall, result) in
+ print("Called function \(methodCall.method)")
+ if methodCall.method == "connectHMGInternetWifi"{
+ self.connectHMGInternetWifi(methodCall:methodCall, result: result)
+
+ }else if methodCall.method == "connectHMGGuestWifi"{
+ self.connectHMGGuestWifi(methodCall:methodCall, result: result)
+
+ }else if methodCall.method == "isHMGNetworkAvailable"{
+ self.isHMGNetworkAvailable(methodCall:methodCall, result: result)
+
+ }else if methodCall.method == "registerHmgGeofences"{
+ self.registerHmgGeofences(result: result)
+
+ }else if methodCall.method == "unRegisterHmgGeofences"{
+ self.unRegisterHmgGeofences(result: result)
+ }
+
+ print("")
+ }
+ Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
+ FlutterConstants.set()
+ }
+ }
+
+
+
+ // Connect HMG Wifi and Internet
+ func connectHMGInternetWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
+
+ guard let pateintId = (methodCall.arguments as? [Any])?.first as? String
+ else { return assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'") }
+
+
+ HMG_Internet.shared.connect(patientId: pateintId) { (status, message) in
+ result(status ? 1 : 0)
+ if status{
+ self.showMessage(title:"Congratulations", message:message)
+ }else{
+ self.showMessage(title:"Ooops,", message:message)
+ }
+ }
+ }
+
+ // Connect HMG-Guest for App Access
+ func connectHMGGuestWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
+ HMG_GUEST.shared.connect() { (status, message) in
+ result(status ? 1 : 0)
+ if status{
+ self.showMessage(title:"Congratulations", message:message)
+ }else{
+ self.showMessage(title:"Ooops,", message:message)
+ }
+ }
+ }
+
+ func isHMGNetworkAvailable(methodCall:FlutterMethodCall ,result: @escaping FlutterResult) -> Bool{
+ guard let ssid = methodCall.arguments as? String else {
+ assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'")
+ return false
+ }
+
+ let queue = DispatchQueue.init(label: "com.hmg.wifilist")
+ NEHotspotHelper.register(options: nil, queue: queue) { (command) in
+ print(command)
+
+ if(command.commandType == NEHotspotHelperCommandType.filterScanList) {
+ if let networkList = command.networkList{
+ for network in networkList{
+ print(network.ssid)
+ }
+ }
+ }
+ }
+ return false
+
+ }
+
+
+ // Message Dailog
+ func showMessage(title:String, message:String){
+ DispatchQueue.main.async {
+ let alert = UIAlertController(title: title, message: message, preferredStyle: .alert )
+ alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
+ mainViewController.present(alert, animated: true) {
+
+ }
+ }
+ }
+
+ // Register Geofence
+ func registerHmgGeofences(result: @escaping FlutterResult){
+ flutterMethodChannel?.invokeMethod("getGeoZones", arguments: nil){ geoFencesJsonString in
+ if let jsonString = geoFencesJsonString as? String{
+ let allZones = GeoZoneModel.list(from: jsonString)
+ HMG_Geofence.shared().register(geoZones: allZones)
+ result(true)
+ }else{
+ }
+ }
+ }
+
+ // Register Geofence
+ func unRegisterHmgGeofences(result: @escaping FlutterResult){
+ HMG_Geofence.shared().unRegisterAll()
+ result(true)
+ }
+
+}
diff --git a/ios/Runner/Helper/HMG_Geofence.swift b/ios/Runner/Helper/HMG_Geofence.swift
new file mode 100644
index 0000000..47454d3
--- /dev/null
+++ b/ios/Runner/Helper/HMG_Geofence.swift
@@ -0,0 +1,183 @@
+//
+// HMG_Geofence.swift
+// Runner
+//
+// Created by ZiKambrani on 13/12/2020.
+//
+
+import UIKit
+import CoreLocation
+
+fileprivate var df = DateFormatter()
+fileprivate var transition = ""
+
+enum Transition:Int {
+ case entry = 1
+ case exit = 2
+ func name() -> String{
+ return self.rawValue == 1 ? "Enter" : "Exit"
+ }
+}
+
+class HMG_Geofence:NSObject{
+
+ var geoZones:[GeoZoneModel]?
+ var locationManager:CLLocationManager!{
+ didSet{
+ // https://developer.apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati
+
+ locationManager.allowsBackgroundLocationUpdates = true
+ locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
+ locationManager.activityType = .other
+ locationManager.delegate = self
+ locationManager.requestAlwaysAuthorization()
+ // locationManager.distanceFilter = 500
+ // locationManager.startMonitoringSignificantLocationChanges()
+ }
+ }
+
+ private static var shared_:HMG_Geofence?
+ class func shared() -> HMG_Geofence{
+ if HMG_Geofence.shared_ == nil{
+ HMG_Geofence.initGeofencing()
+ }
+ return shared_!
+ }
+
+ class func initGeofencing(){
+ shared_ = HMG_Geofence()
+ shared_?.locationManager = CLLocationManager()
+ }
+
+ func register(geoZones:[GeoZoneModel]){
+
+ self.geoZones = geoZones
+
+ let monitoredRegions_ = monitoredRegions()
+ self.geoZones?.forEach({ (zone) in
+ if let region = zone.toRegion(locationManager: locationManager){
+ if let already = monitoredRegions_.first(where: {$0.identifier == zone.identifier()}){
+ debugPrint("Already monitering region: \(already)")
+ }else{
+ startMonitoring(region: region)
+ }
+ }else{
+ debugPrint("Invalid region: \(zone.latitude ?? "invalid_latitude"),\(zone.longitude ?? "invalid_longitude"),r\(zone.radius ?? 0) | \(zone.identifier())")
+ }
+ })
+ }
+
+ func monitoredRegions() -> Set{
+ return locationManager.monitoredRegions
+ }
+
+ func unRegisterAll(){
+ for region in locationManager.monitoredRegions {
+ locationManager.stopMonitoring(for: region)
+ }
+ }
+
+}
+
+// CLLocationManager Delegates
+extension HMG_Geofence : CLLocationManagerDelegate{
+
+ func startMonitoring(region: CLCircularRegion) {
+ if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
+ return
+ }
+
+ if CLLocationManager.authorizationStatus() != .authorizedAlways {
+ let message = """
+ Your geotification is saved but will only be activated once you grant
+ HMG permission to access the device location.
+ """
+ debugPrint(message)
+ }
+
+ locationManager.startMonitoring(for: region)
+ locationManager.requestState(for: region)
+ debugPrint("Starts monitering region: \(region)")
+ }
+
+ func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
+ debugPrint("didEnterRegion: \(region)")
+ if region is CLCircularRegion {
+ handleEvent(for: region,transition: .entry, location: manager.location)
+ }
+ }
+
+ func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
+ debugPrint("didExitRegion: \(region)")
+ if region is CLCircularRegion {
+ handleEvent(for: region,transition: .exit, location: manager.location)
+ }
+ }
+
+ func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
+ debugPrint("didDetermineState: \(state)")
+ }
+
+ func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
+ debugPrint("didUpdateLocations: \(locations)")
+ }
+
+
+}
+
+// Helpers
+extension HMG_Geofence{
+
+ func handleEvent(for region: CLRegion!, transition:Transition, location:CLLocation?) {
+ if let userProfile = userProfile(){
+ notifyUser(forRegion: region, transition: transition, location: locationManager.location, userProfile: userProfile)
+ notifyServer(forRegion: region, transition: transition, location: locationManager.location, userProfile: userProfile)
+ }
+ }
+
+ func geoZone(by id: String) -> GeoZoneModel? {
+ var zone:GeoZoneModel? = nil
+ if let zones_ = geoZones{
+ zone = zones_.first(where: { $0.identifier() == id})
+ }else{
+ // let jsonArray = UserDefaults.standard.string(forKey: "hmg-geo-fences")
+ }
+ return zone
+ }
+
+
+ func notifyUser(forRegion:CLRegion, transition:Transition, location:CLLocation?, userProfile:[String:Any?]){
+ if let patientId = userProfile["PatientID"] as? Int{
+
+ }
+ }
+
+ func notifyServer(forRegion:CLRegion, transition:Transition, location:CLLocation?, userProfile:[String:Any?]){
+ if let patientId = userProfile["PatientID"] as? Int{
+
+ if let idString = forRegion.identifier.split(separator: "_").first, let idInt = Int(idString){
+ let body:[String:Any] = [
+ "PointsID":idInt,
+ "GeoType":transition.rawValue,
+ "PatientID":patientId
+ ]
+
+ var logs = UserDefaults.init(suiteName: "GeoFenceLog")?.dictionary(forKey: "GEOFENCE_LOGS") ?? [:]
+ var geo = (logs[forRegion.identifier] as? [String]) ?? []
+
+ let url = "https://hmgwebservices.com/Services/Patients.svc/REST/GeoF_InsertPatientFileInfo"
+ httpPostRequest(urlString: url, jsonBody: body){ (status,json) in
+ let status_ = status ? "Notified successfully:" : "Failed to notify:"
+ showNotification(title: transition.name(), subtitle: forRegion.identifier, message: status_)
+
+
+ geo.append("\(status_) \(transition.name()) at \(Date().toString(format: "dd/MMM/yyy hh:mm:ss"))")
+ logs.updateValue( geo, forKey: forRegion.identifier)
+
+ UserDefaults.init(suiteName: "GeoFenceLog")?.set(logs, forKey: "GEOFENCE_LOGS")
+ }
+ }
+ }
+ }
+}
+
diff --git a/ios/Runner/Helper/LocalizedFromFlutter.swift b/ios/Runner/Helper/LocalizedFromFlutter.swift
new file mode 100644
index 0000000..8853064
--- /dev/null
+++ b/ios/Runner/Helper/LocalizedFromFlutter.swift
@@ -0,0 +1,22 @@
+//
+// LocalizedFromFlutter.swift
+// Runner
+//
+// Created by ZiKambrani on 10/04/1442 AH.
+//
+
+import UIKit
+
+class FlutterText{
+
+ class func with(key:String,completion: @escaping (String)->Void){
+ flutterMethodChannel?.invokeMethod("localizedValue", arguments: key, result: { (result) in
+ if let localized = result as? String{
+ completion(localized)
+ }else{
+ completion(key)
+ }
+ })
+ }
+
+}
diff --git a/ios/Runner/Helper/OpenTokPlatformBridge.swift b/ios/Runner/Helper/OpenTokPlatformBridge.swift
new file mode 100644
index 0000000..4da39dc
--- /dev/null
+++ b/ios/Runner/Helper/OpenTokPlatformBridge.swift
@@ -0,0 +1,61 @@
+//
+// HMGPlatformBridge.swift
+// Runner
+//
+// Created by ZiKambrani on 14/12/2020.
+//
+
+import UIKit
+import NetworkExtension
+import SystemConfiguration.CaptiveNetwork
+
+
+fileprivate var openTok:OpenTok?
+
+class OpenTokPlatformBridge : NSObject{
+ private var methodChannel:FlutterMethodChannel? = nil
+ private var mainViewController:MainFlutterVC!
+ private static var shared_:OpenTokPlatformBridge?
+
+ class func initialize(flutterViewController:MainFlutterVC, registrar:FlutterPluginRegistrar?){
+ shared_ = OpenTokPlatformBridge()
+ shared_?.mainViewController = flutterViewController
+
+ shared_?.openChannel()
+ openTok = OpenTok(mainViewController: flutterViewController, registrar: registrar)
+ }
+
+ func shared() -> OpenTokPlatformBridge{
+ assert((OpenTokPlatformBridge.shared_ != nil), "OpenTokPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
+ return OpenTokPlatformBridge.shared_!
+ }
+
+ private func openChannel(){
+ methodChannel = FlutterMethodChannel(name: "OpenTok-Platform-Bridge", binaryMessenger: mainViewController.binaryMessenger)
+ methodChannel?.setMethodCallHandler { (call, result) in
+ print("Called function \(call.method)")
+
+ switch(call.method) {
+ case "initSession":
+ openTok?.initSession(call: call, result: result)
+
+ case "swapCamera":
+ openTok?.swapCamera(call: call, result: result)
+
+ case "toggleAudio":
+ openTok?.toggleAudio(call: call, result: result)
+
+ case "toggleVideo":
+ openTok?.toggleVideo(call: call, result: result)
+
+ case "hangupCall":
+ openTok?.hangupCall(call: call, result: result)
+
+ default:
+ result(FlutterMethodNotImplemented)
+ }
+
+ print("")
+ }
+ }
+}
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 8f2ef94..ab9828e 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -71,6 +71,8 @@
This app requires contacts access to show incoming virtual consultation request.
NSFaceIDUsageDescription
This app requires Face ID to allow biometric authentication for app login.
+ NSHealthClinicalHealthRecordsShareUsageDescription
+ This App need access to HealthKit to read heart rate & other data from your smart watch.
NSHealthShareUsageDescription
This App need access to HealthKit to read heart rate & other data from your smart watch.
NSHealthUpdateUsageDescription
diff --git a/ios/Runner/Penguin/PenguinModel.swift b/ios/Runner/Penguin/PenguinModel.swift
new file mode 100644
index 0000000..7b6ab2d
--- /dev/null
+++ b/ios/Runner/Penguin/PenguinModel.swift
@@ -0,0 +1,77 @@
+//
+// PenguinModel.swift
+// Runner
+//
+// Created by Amir on 06/08/2024.
+//
+
+import Foundation
+
+// Define the model class
+struct PenguinModel {
+ let baseURL: String
+ let dataURL: String
+ let dataServiceName: String
+ let positionURL: String
+ let clientKey: String
+ let storyboardName: String
+ let mapBoxKey: String
+ let clientID: String
+ let positionServiceName: String
+ let username: String
+ let isSimulationModeEnabled: Bool
+ let isShowUserName: Bool
+ let isUpdateUserLocationSmoothly: Bool
+ let isEnableReportIssue: Bool
+ let languageCode: String
+ let clinicID: String
+ let patientID: String
+ let projectID: Int
+
+ // Initialize the model from a dictionary
+ init?(from dictionary: [String: Any]) {
+
+ guard
+ let baseURL = dictionary["baseURL"] as? String,
+ let dataURL = dictionary["dataURL"] as? String,
+ let dataServiceName = dictionary["dataServiceName"] as? String,
+ let positionURL = dictionary["positionURL"] as? String,
+ let clientKey = dictionary["clientKey"] as? String,
+ let storyboardName = dictionary["storyboardName"] as? String,
+ let mapBoxKey = dictionary["mapBoxKey"] as? String,
+ let clientID = dictionary["clientID"] as? String,
+ let positionServiceName = dictionary["positionServiceName"] as? String,
+ let username = dictionary["username"] as? String,
+ let isSimulationModeEnabled = dictionary["isSimulationModeEnabled"] as? Bool,
+ let isShowUserName = dictionary["isShowUserName"] as? Bool,
+ let isUpdateUserLocationSmoothly = dictionary["isUpdateUserLocationSmoothly"] as? Bool,
+ let isEnableReportIssue = dictionary["isEnableReportIssue"] as? Bool,
+ let languageCode = dictionary["languageCode"] as? String,
+ let clinicID = dictionary["clinicID"] as? String,
+ let patientID = dictionary["patientID"] as? String,
+ let projectID = dictionary["projectID"] as? Int
+ else {
+ print("Initialization failed due to missing or invalid keys.")
+ return nil
+ }
+
+ self.baseURL = baseURL
+ self.dataURL = dataURL
+ self.dataServiceName = dataServiceName
+ self.positionURL = positionURL
+ self.clientKey = clientKey
+ self.storyboardName = storyboardName
+ self.mapBoxKey = mapBoxKey
+ self.clientID = clientID
+ self.positionServiceName = positionServiceName
+ self.username = username
+ self.isSimulationModeEnabled = isSimulationModeEnabled
+ self.isShowUserName = isShowUserName
+ self.isUpdateUserLocationSmoothly = isUpdateUserLocationSmoothly
+ self.isEnableReportIssue = isEnableReportIssue
+ self.languageCode = languageCode
+ self.clinicID = clinicID
+ self.patientID = patientID
+ self.projectID = projectID
+ }
+}
diff --git a/ios/Runner/Penguin/PenguinNavigator.swift b/ios/Runner/Penguin/PenguinNavigator.swift
new file mode 100644
index 0000000..31cf626
--- /dev/null
+++ b/ios/Runner/Penguin/PenguinNavigator.swift
@@ -0,0 +1,57 @@
+import PenNavUI
+import UIKit
+
+class PenguinNavigator {
+ private var config: PenguinModel
+
+ init(config: PenguinModel) {
+ self.config = config
+ }
+
+ private func logError(_ message: String) {
+ // Centralized logging function
+ print("PenguinSDKNavigator Error: \(message)")
+ }
+
+ func navigateToPOI( referenceId:String,completion: @escaping (Bool, String?) -> Void) {
+ PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey, showProgress: true) { [weak self] token, error in
+
+ if let error = error {
+ let errorMessage = "Token error while getting the for Navigate to method"
+ completion(false, "Failed to get token: \(errorMessage)")
+
+ print("Failed to get token: \(errorMessage)")
+ return
+ }
+
+ guard let token = token else {
+ completion(false, "Token is nil")
+ print("Token is nil")
+ return
+ }
+ print("Token Generated")
+ print(token);
+
+
+ }
+ }
+
+ private func handleNavigation(referenceId: String, token: String, completion: @escaping (Bool, String?) -> Void) {
+ DispatchQueue.main.async {
+ PenNavUIManager.shared.setToken(token: token)
+
+ PenNavUIManager.shared.navigate(to: referenceId) { [weak self] _, navError in
+ guard let self = self else { return }
+
+ if let navError = navError {
+ self.logError("Navigation error: Reference ID invalid")
+ completion(false, "Navigation error: \(navError.localizedDescription)")
+ return
+ }
+
+ // Navigation successful
+ completion(true, nil)
+ }
+ }
+ }
+}
diff --git a/ios/Runner/Penguin/PenguinPlugin.swift b/ios/Runner/Penguin/PenguinPlugin.swift
new file mode 100644
index 0000000..029bec3
--- /dev/null
+++ b/ios/Runner/Penguin/PenguinPlugin.swift
@@ -0,0 +1,31 @@
+//
+// BlueGpsPlugin.swift
+// Runner
+//
+// Created by Penguin .
+//
+
+//import Foundation
+//import Flutter
+//
+///**
+// * A Flutter plugin for integrating Penguin SDK functionality.
+// * This class registers a view factory with the Flutter engine to create native views.
+// */
+//class PenguinPlugin: NSObject, FlutterPlugin {
+//
+// /**
+// * Registers the plugin with the Flutter engine.
+// *
+// * @param registrar The [FlutterPluginRegistrar] used to register the plugin.
+// * This method is called when the plugin is initialized, and it sets up the communication
+// * between Flutter and native code.
+// */
+// public static func register(with registrar: FlutterPluginRegistrar) {
+// // Create an instance of PenguinViewFactory with the binary messenger from the registrar
+// let factory = PenguinViewFactory(messenger: registrar.messenger())
+//
+// // Register the view factory with a unique ID for use in Flutter code
+// registrar.register(factory, withId: "penguin_native")
+// }
+//}
diff --git a/ios/Runner/Penguin/PenguinView.swift b/ios/Runner/Penguin/PenguinView.swift
new file mode 100644
index 0000000..d5303e2
--- /dev/null
+++ b/ios/Runner/Penguin/PenguinView.swift
@@ -0,0 +1,462 @@
+//
+
+// BlueGpsView.swift
+
+// Runner
+
+//
+
+// Created by Penguin.
+
+//
+
+
+
+import Foundation
+import UIKit
+import Flutter
+import PenNavUI
+import PenguinINRenderer
+
+import Foundation
+import Flutter
+import UIKit
+
+
+
+/**
+
+ * A custom Flutter platform view for displaying Penguin UI components.
+
+ * This class integrates with the Penguin navigation SDK and handles UI events.
+
+ */
+
+class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitializationDelegate
+
+{
+ // The main view displayed within the platform view
+
+ private var _view: UIView
+
+ private var model: PenguinModel?
+
+ private var methodChannel: FlutterMethodChannel
+
+ var onSuccess: (() -> Void)?
+
+
+
+
+
+
+
+ /**
+
+ * Initializes the PenguinView with the provided parameters.
+
+ *
+
+ * @param frame The frame of the view, specifying its size and position.
+
+ * @param viewId A unique identifier for this view instance.
+
+ * @param args Optional arguments provided for creating the view.
+
+ * @param messenger The [FlutterBinaryMessenger] used for communication with Dart.
+
+ */
+
+ init(
+
+ frame: CGRect,
+
+ viewIdentifier viewId: Int64,
+
+ arguments args: Any?,
+
+ binaryMessenger messenger: FlutterBinaryMessenger?
+
+ ) {
+
+ _view = UIView()
+
+ methodChannel = FlutterMethodChannel(name: "launch_penguin_ui", binaryMessenger: messenger!)
+
+
+
+ super.init()
+
+
+
+ // Get the screen's width and height to set the view's frame
+
+ let screenWidth = UIScreen.main.bounds.width
+
+ let screenHeight = UIScreen.main.bounds.height
+
+
+
+ // Uncomment to set the background color of the view
+
+ // _view.backgroundColor = UIColor.red
+
+
+
+ // Set the frame of the view to cover the entire screen
+
+ _view.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
+
+ print("========Inside Penguin View ========")
+
+ print(args)
+
+ guard let arguments = args as? [String: Any] else {
+
+ print("Error: Arguments are not in the expected format.")
+
+ return
+
+ }
+
+ print("===== i got tha Args=======")
+
+
+
+ // Initialize the model from the arguments
+
+ if let penguinModel = PenguinModel(from: arguments) {
+
+ self.model = penguinModel
+
+ initPenguin(args: penguinModel)
+
+ } else {
+
+ print("Error: Failed to initialize PenguinModel from arguments ")
+
+ }
+
+ // Initialize the Penguin SDK with required configurations
+
+ // initPenguin( arguments: args)
+
+ }
+
+
+
+ /**
+
+ * Initializes the Penguin SDK with custom configuration settings.
+
+ */
+
+ func initPenguin(args: PenguinModel) {
+
+// Set the initialization delegate to handle SDK initialization events
+
+ PenNavUIManager.shared.initializationDelegate = self
+
+ // Configure the Penguin SDK with necessary parameters
+
+ PIRendererSettings.styleUri = "mapbox://styles/rwaid/cm3h30b36007v01qz7ik8a0sk"
+
+ PenNavUIManager.shared
+
+ .setClientKey(args.clientKey)
+
+ .setClientID(args.clientID)
+
+ .setUsername(args.username)
+
+ .setSimulationModeEnabled(isEnable: args.isSimulationModeEnabled)
+
+ .setBaseURL(dataURL: args.dataURL, positionURL: args.positionURL)
+
+ .setServiceName(dataServiceName: args.dataServiceName, positionServiceName: args.positionServiceName)
+
+ .setIsShowUserName(args.isShowUserName)
+
+ .setIsUpdateUserLocationSmoothly(args.isUpdateUserLocationSmoothly)
+
+ .setEnableReportIssue(enable: args.isEnableReportIssue)
+
+ .setLanguage(args.languageCode)
+
+ .setBackButtonVisibility(visible: true)
+
+ .setCampusID(args.projectID)
+
+ .build()
+
+ }
+
+
+
+
+
+ /**
+
+ * Returns the main view associated with this platform view.
+
+ *
+
+ * @return The UIView instance that represents this platform view.
+
+ */
+
+ func view() -> UIView {
+
+ return _view
+
+ }
+
+
+
+ // MARK: - PIEventsDelegate Methods
+
+
+
+
+
+
+
+
+
+ /**
+
+ * Called when the Penguin UI is dismissed.
+
+ */
+
+ func onPenNavUIDismiss() {
+
+ // Handle UI dismissal if needed
+
+ print("====== onPenNavUIDismiss =========")
+
+ self.view().removeFromSuperview()
+
+ }
+
+
+
+ /**
+
+ * Called when a report issue is generated.
+
+ *
+
+ * @param issue The type of issue reported.
+
+ */
+
+ func onReportIssue(_ issue: PenNavUI.IssueType) {
+
+ // Handle report issue events if needed
+
+ print("====== onReportIssueError =========")
+
+ methodChannel.invokeMethod("onReportIssue", arguments: ["issueType": issue])
+
+
+
+ }
+
+
+
+ /**
+
+ * Called when the Penguin UI setup is successful.
+
+ */
+
+// func onPenNavInitializationSuccess() {
+// isInitilized = true
+// if let referenceId = referenceId {
+// navigator?.navigateToPOI(referenceId: referenceId){ [self] success, errorMessage in
+//
+// channel?.invokeMethod(PenguinMethod.navigateToPOI.rawValue, arguments: errorMessage)
+//
+// }
+// }
+//
+// channel?.invokeMethod(PenguinMethod.onPenNavSuccess.rawValue, arguments: nil)
+// }
+
+ func onPenNavInitializationSuccess() {
+
+ print("====== onPenNavSuccess =========")
+
+ onSuccess?()
+
+ methodChannel.invokeMethod("onPenNavSuccess", arguments: nil)
+
+ // Obtain the FlutterViewController instance
+
+ let controller: FlutterViewController = UIApplication.shared.windows.first?.rootViewController as! FlutterViewController
+
+
+
+ print("====== after controller onPenNavSuccess =========")
+
+ _view = UIView(frame: UIScreen.main.bounds)
+ _view.backgroundColor = .clear
+
+ controller.view.addSubview(_view)
+
+ // Set the events delegate to handle SDK events
+
+ PenNavUIManager.shared.eventsDelegate = self
+
+
+
+ print("====== after eventsDelegate onPenNavSuccess =========")
+
+
+
+ // Present the Penguin UI on top of the Flutter view controller
+
+ PenNavUIManager.shared.present(root: controller, view: _view)
+
+
+
+
+
+ print("====== after present onPenNavSuccess =========")
+
+ print(model?.clinicID)
+
+ print("====== after present onPenNavSuccess =========")
+
+
+
+ guard let config = self.model else {
+
+ print("Error: Config Model is nil")
+
+ return
+
+ }
+
+
+
+ guard let clinicID = self.model?.clinicID,
+
+ let clientID = self.model?.clientID, !clientID.isEmpty else {
+
+ print("Error: Config Client ID is nil or empty")
+
+ return
+
+ }
+
+
+
+ let navigator = PenguinNavigator(config: config)
+
+
+
+ PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey, showProgress: false) { [weak self] token, error in
+
+ if let error = error {
+
+ let errorMessage = "Token error while getting the for Navigate to method"
+
+ print("Failed to get token: \(errorMessage)")
+
+ return
+
+ }
+
+
+
+ guard let token = token else {
+
+ print("Token is nil")
+
+ return
+
+ }
+
+ print("Token Generated")
+
+ print(token);
+
+
+
+ self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in
+
+ if success {
+
+ print("Navigation successful")
+
+ } else {
+
+ print("Navigation failed: \(errorMessage ?? "Unknown error")")
+
+ }
+
+
+
+ }
+
+
+
+ print("====== after Token onPenNavSuccess =========")
+
+ }
+
+
+
+ }
+
+
+
+
+
+
+
+ private func handleNavigation(clinicID: String, token: String, completion: @escaping (Bool, String?) -> Void) {
+
+ DispatchQueue.main.async {
+
+ PenNavUIManager.shared.setToken(token: token)
+
+ PenNavUIManager.shared.navigate(to: clinicID)
+
+ completion(true,nil)
+
+ }
+
+ }
+
+
+
+
+
+
+
+
+
+ /**
+
+ * Called when there is an initialization error with the Penguin UI.
+
+ *
+
+ * @param errorType The type of initialization error.
+
+ * @param errorDescription A description of the error.
+
+ */
+
+ func onPenNavInitializationError(errorType: PenNavUI.PenNavUIError, errorDescription: String) {
+
+ // Handle initialization errors if needed
+
+ print("onPenNavInitializationErrorType: \(errorType.rawValue)")
+
+ print("onPenNavInitializationError: \(errorDescription)")
+ }
+}
diff --git a/ios/Runner/Penguin/PenguinViewFactory.swift b/ios/Runner/Penguin/PenguinViewFactory.swift
new file mode 100644
index 0000000..a88bb5d
--- /dev/null
+++ b/ios/Runner/Penguin/PenguinViewFactory.swift
@@ -0,0 +1,59 @@
+//
+// BlueGpsViewFactory.swift
+// Runner
+//
+// Created by Penguin .
+//
+
+import Foundation
+import Flutter
+
+/**
+ * A factory class for creating instances of [PenguinView].
+ * This class implements `FlutterPlatformViewFactory` to create and manage native views.
+ */
+class PenguinViewFactory: NSObject, FlutterPlatformViewFactory {
+
+ // The binary messenger used for communication with the Flutter engine
+ private var messenger: FlutterBinaryMessenger
+
+ /**
+ * Initializes the PenguinViewFactory with the given messenger.
+ *
+ * @param messenger The [FlutterBinaryMessenger] used to communicate with Dart code.
+ */
+ init(messenger: FlutterBinaryMessenger) {
+ self.messenger = messenger
+ super.init()
+ }
+
+ /**
+ * Creates a new instance of [PenguinView].
+ *
+ * @param frame The frame of the view, specifying its size and position.
+ * @param viewId A unique identifier for this view instance.
+ * @param args Optional arguments provided for creating the view.
+ * @return An instance of [PenguinView] configured with the provided parameters.
+ */
+ func create(
+ withFrame frame: CGRect,
+ viewIdentifier viewId: Int64,
+ arguments args: Any?
+ ) -> FlutterPlatformView {
+ return PenguinView(
+ frame: frame,
+ viewIdentifier: viewId,
+ arguments: args,
+ binaryMessenger: messenger)
+ }
+
+ /**
+ * Returns the codec used for encoding and decoding method channel arguments.
+ * This method is required when `arguments` in `create` is not `nil`.
+ *
+ * @return A [FlutterMessageCodec] instance used for serialization.
+ */
+ public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
+ return FlutterStandardMessageCodec.sharedInstance()
+ }
+}
diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements
index 319178a..2c37e77 100644
--- a/ios/Runner/Runner.entitlements
+++ b/ios/Runner/Runner.entitlements
@@ -4,6 +4,14 @@
aps-environment
development
+ com.apple.developer.healthkit
+
+ com.apple.developer.healthkit.access
+
+ health-records
+
+ com.apple.developer.healthkit.background-delivery
+
com.apple.developer.in-app-payments
merchant.com.hmgwebservices
diff --git a/ios/Runner/RunnerDebug.entitlements b/ios/Runner/RunnerDebug.entitlements
new file mode 100644
index 0000000..319178a
--- /dev/null
+++ b/ios/Runner/RunnerDebug.entitlements
@@ -0,0 +1,17 @@
+
+
+
+
+ aps-environment
+ development
+ com.apple.developer.in-app-payments
+
+ merchant.com.hmgwebservices
+ merchant.com.hmgwebservices.uat
+
+ com.apple.developer.nfc.readersession.formats
+
+ TAG
+
+
+
diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart
index 888f704..039787b 100644
--- a/lib/core/api/api_client.dart
+++ b/lib/core/api/api_client.dart
@@ -19,7 +19,7 @@ abstract class ApiClient {
Future post(
String endPoint, {
- required Map body,
+ required dynamic body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny,
@@ -27,6 +27,8 @@ abstract class ApiClient {
bool isRCService,
bool isPaymentServices,
bool bypassConnectionCheck,
+ Map apiHeaders,
+ bool isBodyPlainText,
});
Future get(
@@ -89,7 +91,7 @@ class ApiClientImp implements ApiClient {
@override
post(
String endPoint, {
- required Map body,
+ required dynamic body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = false,
@@ -97,6 +99,8 @@ class ApiClientImp implements ApiClient {
bool isRCService = false,
bool isPaymentServices = false,
bool bypassConnectionCheck = true,
+ Map? apiHeaders,
+ bool isBodyPlainText = false,
}) async {
String url;
if (isExternal) {
@@ -110,80 +114,84 @@ class ApiClientImp implements ApiClient {
}
// try {
var user = _appState.getAuthenticatedUser();
- Map headers = {'Content-Type': 'application/json', 'Accept': 'application/json'};
- if (!isExternal) {
- String? token = _appState.appAuthToken;
+ Map headers = apiHeaders ?? {'Content-Type': 'application/json', 'Accept': 'application/json'};
- if (body.containsKey('SetupID')) {
- body['SetupID'] = body.containsKey('SetupID') ? body['SetupID'] ?? body[''] : SETUP_ID;
- } else {}
+ // When isBodyPlainText is true, skip all body manipulation and use body as-is
+ if (!isBodyPlainText) {
+ if (!isExternal) {
+ String? token = _appState.appAuthToken;
- if (body.containsKey('isDentalAllowedBackend')) {
- body['isDentalAllowedBackend'] =
- body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
- }
+ if (body.containsKey('SetupID')) {
+ body['SetupID'] = body.containsKey('SetupID') ? body['SetupID'] ?? body[''] : SETUP_ID;
+ } else {}
- if (!body.containsKey('IsPublicRequest')) {
- // if (!body.containsKey('PatientType')) {
- if (user != null && user.patientType != null) {
- body['PatientType'] = user.patientType;
- } else {
- body['PatientType'] = PATIENT_TYPE.toString();
+ if (body.containsKey('isDentalAllowedBackend')) {
+ body['isDentalAllowedBackend'] =
+ body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
}
- if (user != null && user.patientType != null) {
- body['PatientTypeID'] = user.patientType;
- } else {
- body['PatientType'] = PATIENT_TYPE_ID.toString();
- }
+ if (!body.containsKey('IsPublicRequest')) {
+ // if (!body.containsKey('PatientType')) {
+ if (user != null && user.patientType != null) {
+ body['PatientType'] = user.patientType;
+ } else {
+ body['PatientType'] = PATIENT_TYPE.toString();
+ }
+
+ if (user != null && user.patientType != null) {
+ body['PatientTypeID'] = user.patientType;
+ } else {
+ body['PatientType'] = PATIENT_TYPE_ID.toString();
+ }
- if (user != null) {
- body['TokenID'] = body['TokenID'] ?? token;
+ if (user != null) {
+ body['TokenID'] = body['TokenID'] ?? token;
- body['PatientID'] = body['PatientID'] ?? user.patientId;
+ body['PatientID'] = body['PatientID'] ?? user.patientId;
- body['PatientOutSA'] = body.containsKey('PatientOutSA') ? body['PatientOutSA'] ?? user.outSa : user.outSa;
- body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
+ body['PatientOutSA'] = body.containsKey('PatientOutSA') ? body['PatientOutSA'] ?? user.outSa : user.outSa;
+ body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
+ }
+ // else {
+ // body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
+ //
+ // }
}
- // else {
- // body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
- //
- // }
}
- }
- // request.versionID = VERSION_ID;
- // request.channel = CHANNEL;
- // request.iPAdress = IP_ADDRESS;
- // request.generalid = GENERAL_ID;
- // request.languageID = (languageID == 'ar' ? 1 : 2);
- // request.patientOutSA = (request.zipCode == '966' || request.zipCode == '+966') ? 0 : 1;
-
- // body['VersionID'] = ApiConsts.appVersionID.toString();
- if (!isExternal) {
- body['VersionID'] = ApiConsts.appVersionID.toString();
- body['Channel'] = ApiConsts.appChannelId.toString();
- body['IPAdress'] = ApiConsts.appIpAddress;
- body['generalid'] = ApiConsts.appGeneralId;
-
- body['LanguageID'] = _appState.getLanguageID().toString();
- body['Latitude'] = _appState.userLat.toString();
- body['Longitude'] = _appState.userLong.toString();
- body['DeviceTypeID'] = _appState.deviceTypeID;
- if (_appState.appAuthToken.isNotEmpty) {
- body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken;
+ // request.versionID = VERSION_ID;
+ // request.channel = CHANNEL;
+ // request.iPAdress = IP_ADDRESS;
+ // request.generalid = GENERAL_ID;
+ // request.languageID = (languageID == 'ar' ? 1 : 2);
+ // request.patientOutSA = (request.zipCode == '966' || request.zipCode == '+966') ? 0 : 1;
+
+ // body['VersionID'] = ApiConsts.appVersionID.toString();
+ if (!isExternal) {
+ body['VersionID'] = ApiConsts.appVersionID.toString();
+ body['Channel'] = ApiConsts.appChannelId.toString();
+ body['IPAdress'] = ApiConsts.appIpAddress;
+ body['generalid'] = ApiConsts.appGeneralId;
+
+ body['LanguageID'] = _appState.getLanguageID().toString();
+ body['Latitude'] = _appState.userLat.toString();
+ body['Longitude'] = _appState.userLong.toString();
+ body['DeviceTypeID'] = _appState.deviceTypeID;
+ if (_appState.appAuthToken.isNotEmpty) {
+ body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken;
+ }
+
+ // body['TokenID'] = "@dm!n";
+ // body['PatientID'] = 1018977;
+ // body['PatientTypeID'] = 1;
+ //
+ // body['PatientOutSA'] = 0;
+ // body['SessionID'] = "45786230487560q";
}
- // body['TokenID'] = "@dm!n";
- // body['PatientID'] = 1018977;
- // body['PatientTypeID'] = 1;
- //
- // body['PatientOutSA'] = 0;
- // body['SessionID'] = "45786230487560q";
+ body.removeWhere((key, value) => value == null);
}
- body.removeWhere((key, value) => value == null);
-
final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck);
if (!networkStatus) {
@@ -196,12 +204,13 @@ class ApiClientImp implements ApiClient {
return;
}
- final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers);
+ // Handle body encoding based on isBodyPlainText flag
+ final dynamic requestBody = isBodyPlainText ? body : json.encode(body);
+ final response = await http.post(Uri.parse(url.trim()), body: requestBody, headers: headers);
final int statusCode = response.statusCode;
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
- // log("response.body: ${response.body}");
- // log("response.body: ${response.body}");
+ log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) {
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index 5499787..07f9ee5 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -14,8 +14,8 @@ var PACKAGES_ORDERS = '/api/orders';
var PACKAGES_ORDER_HISTORY = '/api/orders/items';
var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara';
// var BASE_URL = 'http://10.50.100.198:2018/';
-var BASE_URL = 'https://uat.hmgwebservices.com/';
-// var BASE_URL = 'https://hmgwebservices.com/';
+// var BASE_URL = 'https://uat.hmgwebservices.com/';
+var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'http://10.201.204.103/';
// var BASE_URL = 'https://orash.cloudsolutions.com.sa/';
// var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/';
@@ -334,6 +334,8 @@ var GET_PATIENT_SHARE_FOR_WALKIN_APPOINTMENT = 'Services/Doctors.svc/REST/GetChe
var CAN_PAY_FOR_FOR_WALKIN_APPOINTMENT = 'Services/Doctors.svc/REST/CanPayForWalkinAppointment';
+var GET_APPOINTMENT_NEAREST_GATE = 'Services/OUTPs.svc/REST/getGateByProjectIDandClinicID';
+
//URL to get medicine and pharmacies list
var CHANNEL = 3;
var GENERAL_ID = 'Cs2020@2016\$2958';
@@ -396,19 +398,6 @@ var GET_COVID_DRIVETHRU_PROCEDURES_LIST = 'Services/Doctors.svc/REST/COVID19_Get
var GET_PATIENT_LAST_RECORD = 'Services/Patients.svc/REST/Med_GetPatientLastRecord';
var INSERT_PATIENT_HEALTH_DATA = 'Services/Patients.svc/REST/Med_InsertTransactions';
-///My Trackers
-var GET_DIABETIC_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetDiabeticResultAverage';
-var GET_DIABTEC_RESULT = 'Services/Patients.svc/REST/Patient_GetDiabtecResults';
-var ADD_DIABTEC_RESULT = 'Services/Patients.svc/REST/Patient_AddDiabtecResult';
-
-var GET_BLOOD_PRESSURE_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetBloodPressureResultAverage';
-var GET_BLOOD_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_GetBloodPressureResult';
-var ADD_BLOOD_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_AddBloodPressureResult';
-
-var GET_WEIGHT_PRESSURE_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResultAverage';
-var GET_WEIGHT_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResult';
-var ADD_WEIGHT_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_AddWeightMeasurementResult';
-
var ADD_ACTIVE_PRESCRIPTIONS_REPORT_BY_PATIENT_ID = 'Services/Patients.svc/Rest/GetActivePrescriptionReportByPatientID';
var GET_CALL_INFO_HOURS_RESULT = 'Services/Doctors.svc/REST/GetCallInfoHoursResult';
@@ -418,7 +407,6 @@ var GET_QUESTION_TYPES = 'Services/OUTPs.svc/REST/getQuestionsTypes';
var UPDATE_DIABETIC_RESULT = 'Services/Patients.svc/REST/Patient_UpdateDiabeticResult';
-var SEND_AVERAGE_BLOOD_SUGAR_REPORT = 'Services/Notifications.svc/REST/SendAverageBloodSugarReport';
var DEACTIVATE_DIABETIC_STATUS = 'services/Patients.svc/REST/Patient_DeactivateDiabeticStatus';
var DEACTIVATE_BLOOD_PRESSURES_STATUS = 'services/Patients.svc/REST/Patient_DeactivateBloodPressureStatus';
@@ -437,14 +425,6 @@ var RATE_DOCTOR_RESPONSE = 'Services/OUTPs.svc/REST/insertAppointmentQuestionRat
var GET_PATIENT_ALLERGIES = 'Services/Patients.svc/REST/GetPatientAllergies';
-// H2O
-var H2O_GET_USER_PROGRESS = "Services/H2ORemainder.svc/REST/H2O_GetUserProgress";
-var H2O_INSERT_USER_ACTIVITY = "Services/H2ORemainder.svc/REST/H2O_InsertUserActivity";
-var H2O_GET_USER_DETAIL = "Services/H2ORemainder.svc/REST/H2O_GetUserDetails_New";
-var H2O_UPDATE_USER_DETAIL = "Services/H2ORemainder.svc/REST/H2O_UpdateUserDetails_New";
-var H2O_UNDO_USER_ACTIVITY = "Services/H2ORemainder.svc/REST/H2o_UndoUserActivity";
-//E_Referral Services
-
// Encillary Orders
var GET_ANCILLARY_ORDERS = 'Services/Doctors.svc/REST/GetOnlineAncillaryOrderList';
@@ -454,8 +434,6 @@ var GET_ANCILLARY_ORDERS_DETAILS = 'Services/Doctors.svc/REST/GetOnlineAncillary
//Pharmacy wishlist
// var GET_WISHLIST = "http://swd-pharapp-01:7200/api/shopping_cart_items/";
-var GET_DOCTOR_LIST_BY_TIME = "Services/Doctors.svc/REST/SearchDoctorsByTime";
-
// pharmacy
var PHARMACY_AUTORZIE_CUSTOMER = "AutorizeCustomer";
var PHARMACY_VERIFY_CUSTOMER = "VerifyCustomer";
@@ -553,7 +531,6 @@ var GET_FINAL_PRODUCTS =
'products?fields=id,reviews,discount_ids,name,namen,localized_names,display_order,short_description,full_description,full_descriptionn,sku,order_minimum_quantity,order_maximum_quantity,price,old_price,images,is_rx,rx_message,rx_messagen,discount_name,discount_namen,approved_rating_sum,approved_total_reviews,allow_back_in_stock_subscriptions,stock_quantity,stock_availability,stock_availabilityn,discount_percentage&CategoryId=';
var GET_CLINIC_CATEGORY = 'Services/Doctors.svc/REST/DP_GetClinicCategory';
var GET_DISEASE_BY_CLINIC_ID = 'Services/Doctors.svc/REST/DP_GetDiseasesByClinicID';
-var SEARCH_DOCTOR_BY_TIME = 'Services/Doctors.svc/REST/SearchDoctorsByTime';
var TIMER_MIN = 10;
@@ -673,25 +650,6 @@ var addPayFortApplePayResponse = "Services/PayFort_Serv.svc/REST/AddResponse";
// Auth Provider Consts
-const String INSERT_DEVICE_IMEI = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI';
-const String SELECT_DEVICE_IMEI = 'Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI';
-const String CHECK_PATIENT_AUTH = 'Services/Authentication.svc/REST/CheckPatientAuthentication';
-const GET_MOBILE_INFO = 'Services/Authentication.svc/REST/GetMobileLoginInfo';
-
-const FORGOT_PASSWORD = 'Services/Authentication.svc/REST/CheckActivationCodeForSendFileNo';
-const CHECK_PATIENT_FOR_REGISTRATION = "Services/Authentication.svc/REST/CheckPatientForRegisteration";
-
-const CHECK_USER_STATUS = "Services/NHIC.svc/REST/GetPatientInfo";
-const REGISTER_USER = 'Services/Authentication.svc/REST/PatientRegistration';
-const LOGGED_IN_USER_URL = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo';
-
-const FORGOT_PATIENT_ID = 'Services/Authentication.svc/REST/SendPatientIDSMSByMobileNumber';
-const DASHBOARD = 'Services/Patients.svc/REST/PatientDashboard';
-const PROFILE_SETTING = 'Services/Patients.svc/REST/GetPateintInfoForUpdate';
-const SAVE_SETTING = 'Services/Patients.svc/REST/UpdatePateintInfo';
-
-const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_InsertUpdate';
-
var ER_CREATE_ADVANCE_PAYMENT = "services/Doctors.svc/REST/ER_CreateAdvancePaymentForClinic";
//family Files
@@ -723,6 +681,8 @@ class ApiConsts {
static String GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments";
static String GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid=';
+ static String QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
+
// static String GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
// var payFortEnvironment = FortEnvironment.test;
@@ -739,6 +699,7 @@ class ApiConsts {
GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid=';
rcBaseUrl = 'https://rc.hmg.com/';
+ QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
break;
case AppEnvironmentTypeEnum.dev:
baseUrl = "https://uat.hmgwebservices.com/";
@@ -749,6 +710,7 @@ class ApiConsts {
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
rcBaseUrl = 'https://rc.hmg.com/uat/';
+ QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
break;
case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/";
@@ -759,6 +721,7 @@ class ApiConsts {
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
rcBaseUrl = 'https://rc.hmg.com/uat/';
+ QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
break;
case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/";
@@ -769,6 +732,7 @@ class ApiConsts {
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
rcBaseUrl = 'https://rc.hmg.com/';
+ QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
break;
case AppEnvironmentTypeEnum.qa:
baseUrl = "https://uat.hmgwebservices.com/";
@@ -779,6 +743,7 @@ class ApiConsts {
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
rcBaseUrl = 'https://rc.hmg.com/uat/';
+ QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
break;
case AppEnvironmentTypeEnum.staging:
baseUrl = "https://uat.hmgwebservices.com/";
@@ -789,6 +754,7 @@ class ApiConsts {
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
rcBaseUrl = 'https://rc.hmg.com/uat/';
+ QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
break;
}
}
@@ -845,15 +811,15 @@ class ApiConsts {
static final String updateHHCOrder = 'api/hhc/update';
static final String addHHCOrder = 'api/HHC/add';
- // SYMPTOMS CHECKER
+ // SYMPTOMS CHECKER API
+ static final String symptomsUserLogin = '$symptomsCheckerApi/user_login';
static final String getBodySymptomsByName = '$symptomsCheckerApi/GetBodySymptomsByName';
static final String getRiskFactors = '$symptomsCheckerApi/GetRiskFactors';
- static final String getGeneralSuggestion = '$symptomsCheckerApi/GetGeneralSggestion';
- static final String diagnosis = '$symptomsCheckerApi/diagnosis';
+ static final String getSuggestions = '$symptomsCheckerApi/GetSuggestion';
+ static final String diagnosis = '$symptomsCheckerApi/GetDiagnosis';
static final String explain = '$symptomsCheckerApi/explain';
//E-REFERRAL SERVICES
-
static final getAllRelationshipTypes = "Services/Patients.svc/REST/GetAllRelationshipTypes";
static final sendActivationCodeForEReferral = 'Services/Authentication.svc/REST/SendActivationCodeForEReferral';
static final checkActivationCodeForEReferral = 'Services/Authentication.svc/REST/CheckActivationCodeForEReferral';
@@ -861,6 +827,45 @@ class ApiConsts {
static final createEReferral = "Services/Patients.svc/REST/CreateEReferral";
static final getEReferrals = "Services/Patients.svc/REST/GetEReferrals";
+ //WATER CONSUMPTION
+ static String h2oGetUserProgress = "Services/H2ORemainder.svc/REST/H2O_GetUserProgress";
+ static String h2oInsertUserActivity = "Services/H2ORemainder.svc/REST/H2O_InsertUserActivity";
+ static String h2oInsertUserDetailsNew = "Services/H2ORemainder.svc/REST/H2O_InsertUserDetails_New";
+ static String h2oGetUserDetail = "Services/H2ORemainder.svc/REST/H2O_GetUserDetails_New";
+ static String h2oUpdateUserDetail = "Services/H2ORemainder.svc/REST/H2O_UpdateUserDetails_New";
+ static String h2oUndoUserActivity = "Services/H2ORemainder.svc/REST/H2o_UndoUserActivity";
+
+ // HEALTH TRACKERS
+ // Blood Sugar (Diabetic)
+ static String getDiabeticResultAverage = 'Services/Patients.svc/REST/Patient_GetDiabeticResultAverage';
+ static String getDiabeticResult = 'Services/Patients.svc/REST/Patient_GetDiabtecResults';
+ static String addDiabeticResult = 'Services/Patients.svc/REST/Patient_AddDiabtecResult';
+ static String updateDiabeticResult = 'Services/Patients.svc/REST/Patient_UpdateDiabtecResult';
+ static String deactivateDiabeticStatus = 'Services/Patients.svc/REST/Patient_DeactivateDiabeticStatus';
+ static String sendAverageBloodSugarReport = 'Services/Notifications.svc/REST/SendAverageBloodSugarReport';
+
+ // Blood Pressure
+ static String getBloodPressureResultAverage = 'Services/Patients.svc/REST/Patient_GetBloodPressureResultAverage';
+ static String getBloodPressureResult = 'Services/Patients.svc/REST/Patient_GetBloodPressureResult';
+ static String addBloodPressureResult = 'Services/Patients.svc/REST/Patient_AddBloodPressureResult';
+ static String updateBloodPressureResult = 'Services/Patients.svc/REST/Patient_UpdateBloodPressureResult';
+ static String deactivateBloodPressureStatus = 'Services/Patients.svc/REST/Patient_DeactivateBloodPressureStatus';
+ static String sendAverageBloodPressureReport = 'Services/Notifications.svc/REST/SendAverageBloodPressureReport';
+
+ // Weight Measurement
+ static String getWeightMeasurementResultAverage = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResultAverage';
+ static String getWeightMeasurementResult = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResult';
+ static String addWeightMeasurementResult = 'Services/Patients.svc/REST/Patient_AddWeightMeasurementResult';
+ static String updateWeightMeasurementResult = 'Services/Patients.svc/REST/Patient_UpdateWeightMeasurementResult';
+ static String deactivateWeightMeasurementStatus = 'Services/Patients.svc/REST/Patient_DeactivateWeightMeasurementStatus';
+ static String sendAverageBodyWeightReport = 'Services/Notifications.svc/REST/SendAverageBodyWeightReport';
+
+ //Blood Donation
+ static String bloodGroupUpdate = "Services/PatientVarification.svc/REST/BloodDonation_RegisterBloodType";
+ static String userAgreementForBloodGroupUpdate = "Services/PatientVarification.svc/REST/AddUserAgreementForBloodDonation";
+ static String getProjectsHaveBDClinics = "Services/OUTPs.svc/REST/BD_getProjectsHaveBDClinics";
+ static String getClinicsBDFreeSlots = "Services/OUTPs.svc/REST/BD_GetFreeSlots";
+
// ************ static values for Api ****************
static final double appVersionID = 50.3;
static final int appChannelId = 3;
@@ -872,3 +877,34 @@ class ApiConsts {
class ApiKeyConstants {
static final String googleMapsApiKey = 'AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng';
}
+
+//flutter: URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_InsertUserActivity
+// flutter: {"IdentificationNo":"2530976584","MobileNumber":"504278212","QuantityIntake":200,"VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
+// flutter: response.body:
+// flutter: {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":true,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":[{"QuantityConsumed":200.00,"PercentageConsumed":9.41,"PercentageLeft":90.59,"QuantityLimit":2125.00}],"UserProgressForWeekData":null,"UserProgressHistoryData":[{"Quantity":200.000,"CreatedDate":"\/Date(1766911222217+0300)\/"}],"VerificationCode":null,"isSMSSent":false}
+
+// URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2o_UndoUserActivity
+// flutter: {"Progress":1,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
+// flutter: response.body:
+// flutter: {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":[{"QuantityConsumed":0.00,"PercentageConsumed":0.00,"PercentageLeft":100.00,"QuantityLimit":2125.00}],"UserProgressForWeekData":null,"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
+
+// Progress":2 means weekly data
+
+// flutter: URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_GetUserProgress
+// flutter: {"Progress":2,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
+// flutter: response.body:
+// [log] {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":null,"UserProgressForWeekData":[{"DayNumber":1,"DayDate":null,"DayName":"Sunday","PercentageConsumed":0},{"DayNumber":7,"DayDate":null,"DayName":"Saturday","PercentageConsumed":0},{"DayNumber":6,"DayDate":null,"DayName":"Friday","PercentageConsumed":0},{"DayNumber":5,"DayDate":null,"DayName":"Thursday","PercentageConsumed":0},{"DayNumber":4,"DayDate":null,"DayName":"Wednesday","PercentageConsumed":0},{"DayNumber":3,"DayDate":null,"DayName":"Tuesday","PercentageConsumed":0},{"DayNumber":2,"DayDate":null,"DayName":"Monday","PercentageConsumed":0}],"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
+
+// Progress":1 means daily data
+
+//URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_GetUserProgress
+// flutter: {"Progress":1,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
+// flutter: response.body:
+// flutter: {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":[{"QuantityConsumed":0.00,"PercentageConsumed":0.00,"PercentageLeft":100.00,"QuantityLimit":2125.00}],"UserProgressForWeekData":null,"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
+
+// Progress":1 means monthly data
+
+// flutter: URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_GetUserProgress
+// flutter: {"Progress":3,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
+// flutter: response.body:
+// [log] {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":[{"MonthNumber":1,"MonthName":"January","PercentageConsumed":0},{"MonthNumber":2,"MonthName":"February","PercentageConsumed":0},{"MonthNumber":3,"MonthName":"March","PercentageConsumed":0},{"MonthNumber":4,"MonthName":"April","PercentageConsumed":0},{"MonthNumber":5,"MonthName":"May","PercentageConsumed":0},{"MonthNumber":6,"MonthName":"June","PercentageConsumed":0},{"MonthNumber":7,"MonthName":"July","PercentageConsumed":0},{"MonthNumber":8,"MonthName":"August","PercentageConsumed":0},{"MonthNumber":9,"MonthName":"September","PercentageConsumed":0},{"MonthNumber":10,"MonthName":"October","PercentageConsumed":0},{"MonthNumber":11,"MonthName":"November","PercentageConsumed":0},{"MonthNumber":12,"MonthName":"December","PercentageConsumed":0}],"UserProgressForTodayData":null,"UserProgressForWeekData":null,"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index 3ee41f2..c470bc2 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -188,6 +188,42 @@ class AppAssets {
static const String latest_news_icon = '$svgBasePath/latest_news_icon.svg';
static const String hmg_contact_icon = '$svgBasePath/hmg_contact_icon.svg';
static const String services_medical_file_icon = '$svgBasePath/services_medical_file_icon.svg';
+ static const String blood_sugar_icon = '$svgBasePath/blood_sugar_icon.svg';
+ static const String weight_tracker_icon = '$svgBasePath/weight_tracker_icon.svg';
+ static const String ask_doctor_medical_file_icon = '$svgBasePath/ask_doctor_medical_file_icon.svg';
+ static const String internet_pairing_icon = '$svgBasePath/internet_pairing_icon.svg';
+ static const String my_doctors_icon = '$svgBasePath/my_doctors_icon.svg';
+ static const String my_sick_leave_icon = '$svgBasePath/my_sick_leave_icon.svg';
+ static const String my_radiology_icon = '$svgBasePath/my_radiology_icon.svg';
+ static const String monthly_reports_icon = '$svgBasePath/monthly_reports_icon.svg';
+ static const String medical_reports_icon = '$svgBasePath/medical_reports_icon.svg';
+ static const String sick_leave_report_icon = '$svgBasePath/sick_leave_report_icon.svg';
+ static const String update_insurance_icon = '$svgBasePath/update_insurance_icon.svg';
+ static const String insurance_approval_icon = '$svgBasePath/insurance_approval_icon.svg';
+ static const String invoices_list_icon = '$svgBasePath/invoices_list_icon.svg';
+ static const String ancillary_orders_list_icon = '$svgBasePath/ancillary_orders_list_icon.svg';
+ static const String daily_water_monitor_icon = '$svgBasePath/daily_water_monitor.svg';
+ static const String health_calculators_services_icon = '$svgBasePath/health_calculators_services_icon.svg';
+ static const String health_converters_icon = '$svgBasePath/health_converters_icon.svg';
+ static const String smartwatch_icon = '$svgBasePath/smartwatch_icon.svg';
+ static const String bmi = '$svgBasePath/bmi.svg';
+ static const String bmr = '$svgBasePath/bmr.svg';
+ static const String calories = '$svgBasePath/calories.svg';
+ static const String ibw = '$svgBasePath/ibw.svg';
+ static const String general_health = '$svgBasePath/general_health.svg';
+ static const String women_health = '$svgBasePath/women_health.svg';
+
+ static const String height = '$svgBasePath/height.svg';
+ static const String weight = '$svgBasePath/weight.svg';
+ static const String activity = '$svgBasePath/activity.svg';
+ static const String age = '$svgBasePath/age_icon.svg';
+ static const String gender = '$svgBasePath/gender_icon.svg';
+ static const String genderInputIcon = '$svgBasePath/genderInputIcon.svg';
+ static const String bloodType = '$svgBasePath/blood_type.svg';
+
+ static const String trade_down_yellow = '$svgBasePath/trade_down_yellow.svg';
+ static const String trade_down_red = '$svgBasePath/trade_down_red.svg';
+ static const String pharmacy_icon = '$svgBasePath/phramacy_icon.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';
@@ -212,10 +248,67 @@ class AppAssets {
static const String rotateIcon = '$svgBasePath/rotate_icon.svg';
static const String refreshIcon = '$svgBasePath/refresh.svg';
static const String homeBorderedIcon = '$svgBasePath/home_bordered.svg';
+ static const String symptomCheckerIcon = '$svgBasePath/symptom_checker_icon.svg';
+ static const String symptomCheckerBottomIcon = '$svgBasePath/symptom_bottom_icon.svg';
+
+ // Water Monitor
+ static const String waterBottle = '$svgBasePath/water_bottle.svg';
+ static const String cupAdd = '$svgBasePath/cup_add.svg';
+ static const String cupFilled = '$svgBasePath/cup_filled.svg';
+ static const String waterBottleOuterBubbles = '$svgBasePath/outer_bubbles.svg';
+ static const String cupEmpty = '$svgBasePath/cup_empty.svg';
+ static const String dumbellIcon = '$svgBasePath/dumbell_icon.svg';
+ static const String weightScaleIcon = '$svgBasePath/weight_scale_icon.svg';
+ static const String heightIcon = '$svgBasePath/height_icon.svg';
+ static const String profileIcon = '$svgBasePath/profile_icon.svg';
+ static const String notificationIconGrey = '$svgBasePath/notification_icon_grey.svg';
+ static const String minimizeIcon = '$svgBasePath/minimize_icon.svg';
+ static const String addIconDark = '$svgBasePath/add_icon_dark.svg';
+ static const String glassIcon = '$svgBasePath/glass_icon.svg';
+ static const String graphIcon = '$svgBasePath/graph_icon.svg';
+ static const String listIcon = '$svgBasePath/list_icon.svg';
+ static const String yellowArrowDownIcon = '$svgBasePath/yellow_arrow_down_icon.svg';
+ static const String greenTickIcon = '$svgBasePath/green_tick_icon.svg';
+
+ static const String bloodSugar = '$svgBasePath/bloodsugar.svg';
+ static const String bloodCholestrol = '$svgBasePath/bloodcholestrol.svg';
+ static const String triglycerides = '$svgBasePath/triglycerides.svg';
+ static const String bulb = '$svgBasePath/bulb.svg';
+ static const String switchBtn = '$svgBasePath/switch.svg';
+
+ //Health Trackers
+ static const String bloodPressureIcon = '$svgBasePath/blood_pressure_icon.svg';
+ static const String bloodSugarOnlyIcon = '$svgBasePath/blood_sugar_only_icon.svg';
+ static const String weightIcon = '$svgBasePath/weight_icon.svg';
+ static const String normalStatusGreenIcon = '$svgBasePath/normal_status_green_icon.svg';
+ static const String sendEmailIcon = '$svgBasePath/send_email_icon.svg';
+ static const String lowIndicatorIcon = '$svgBasePath/low_indicator_icon.svg';
+
+ // Health Calculators
+ static const String ovulationAccordion = '$svgBasePath/approximate_ovulation_accordion.svg';
+ static const String nextPeriodAccordion = '$svgBasePath/next_period_accordion.svg';
+ static const String fertileAccordion = '$svgBasePath/fertile_window_accordion.svg';
+ static const String pregnancyDayAccordion = '$svgBasePath/pregnancy_test_day_accordion.svg';
+ static const String pregnancyDueDateAccordion = '$svgBasePath/due_date_accordion.svg';
+
+ static const String covid19icon = '$svgBasePath/covid_19.svg';
+
+ //vital sign
+ static const String heartRate = '$svgBasePath/heart_rate.svg';
+ static const String respRate = '$svgBasePath/resp_rate.svg';
+ static const String weightVital = '$svgBasePath/weight_2.svg';
+ static const String bmiVital = '$svgBasePath/bmi_2.svg';
+ static const String heightVital = '$svgBasePath/height_2.svg';
+ static const String bloodPressure = '$svgBasePath/blood_pressure.svg';
+ static const String temperature = '$svgBasePath/temperature.svg';
// PNGS //
static const String hmgLogo = '$pngBasePath/hmg_logo.png';
static const String liveCareService = '$pngBasePath/livecare_service.png';
+
+ static const String homeHealthCareService = '$pngBasePath/home_health_care.png';
+ static const String pharmacyService = '$pngBasePath/pharmacy_service.png';
+
static const String maleImg = '$pngBasePath/male_img.png';
static const String femaleImg = '$pngBasePath/female_img.png';
static const String babyGirlImg = '$pngBasePath/baby_girl_img.png';
@@ -234,6 +327,7 @@ class AppAssets {
static const String fullBodyFront = '$pngBasePath/full_body_front.png';
static const String fullBodyBack = '$pngBasePath/full_body_back.png';
+ static const String bmiFullBody = '$pngBasePath/bmi_image_1.png';
}
class AppAnimations {
diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart
index c8659a7..e1d6e05 100644
--- a/lib/core/app_state.dart
+++ b/lib/core/app_state.dart
@@ -4,10 +4,8 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:gms_check/gms_check.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/HMCProjectListModel.dart';
-import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/VidaPlusProjectListModel.dart';
-import 'package:hmg_patient_app_new/features/authentication/models/request_models/send_activation_request_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_user_staus_nhic_response_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
@@ -45,7 +43,7 @@ class AppState {
bool isChildLoggedIn = false;
bool isGMSAvailable = true;
bool isAndroid = true;
-
+ bool isRatedVisible =false;
void setAuthenticatedUser(AuthenticatedUser? authenticatedUser, {bool isFamily = false}) {
if (isFamily) {
_authenticatedChildUser = authenticatedUser;
@@ -172,4 +170,8 @@ class AppState {
userLong = 0.0;
userLong = 0.0;
}
+
+ setRatedVisible(bool value) {
+ isRatedVisible = value;
+ }
}
diff --git a/lib/core/cache_consts.dart b/lib/core/cache_consts.dart
index bcbb185..c1e06aa 100644
--- a/lib/core/cache_consts.dart
+++ b/lib/core/cache_consts.dart
@@ -63,6 +63,7 @@ class CacheConst {
static const String pharmacyAutorzieToken = 'PHARMACY_AUTORZIE_TOKEN';
static const String h2oUnit = 'H2O_UNIT';
static const String h2oReminder = 'H2O_REMINDER';
+ static const String waterReminderEnabled = 'WATER_REMINDER_ENABLED';
static const String livecareClinicData = 'LIVECARE_CLINIC_DATA';
static const String doctorScheduleDateSel = 'DOCTOR_SCHEDULE_DATE_SEL';
static const String appointmentHistoryMedical = 'APPOINTMENT_HISTORY_MEDICAL';
@@ -74,6 +75,7 @@ class CacheConst {
static const String patientOccupationList = 'patient-occupation-list';
static const String hasEnabledQuickLogin = 'has-enabled-quick-login';
static const String quickLoginEnabled = 'quick-login-enabled';
+ static const String isMonthlyReportEnabled = 'is-monthly-report-enabled';
static const String zoomRoomID = 'zoom-room-id';
static String isAppOpenedFromCall = "is_app_opened_from_call";
diff --git a/lib/core/common_models/data_points.dart b/lib/core/common_models/data_points.dart
index 3f5065c..f156ecb 100644
--- a/lib/core/common_models/data_points.dart
+++ b/lib/core/common_models/data_points.dart
@@ -1,26 +1,26 @@
-
-
///class used to provide value for the [DynamicResultChart] to plot the values
class DataPoint {
///values that is displayed on the graph and dot is plotted on this
final double value;
+
///label shown on the bottom of the graph
String label;
String referenceValue;
String actualValue;
- String? unitOfMeasurement ;
+ String? unitOfMeasurement;
+
DateTime time;
String displayTime;
- DataPoint(
- {required this.value,
- required this.label,
- required this.referenceValue,
- required this.actualValue,
- required this.time,
- required this.displayTime,
- this.unitOfMeasurement
- });
+ DataPoint({
+ required this.value,
+ required this.label,
+ required this.actualValue,
+ required this.time,
+ required this.displayTime,
+ this.unitOfMeasurement,
+ this.referenceValue = '',
+ });
@override
String toString() {
diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart
index 4c17de6..582b795 100644
--- a/lib/core/dependencies.dart
+++ b/lib/core/dependencies.dart
@@ -1,4 +1,5 @@
import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
@@ -17,6 +18,7 @@ import 'package:hmg_patient_app_new/features/emergency_services/emergency_servic
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart';
@@ -29,9 +31,14 @@ import 'package:hmg_patient_app_new/features/location/location_repo.dart';
import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
+import 'package:hmg_patient_app_new/features/monthly_report/monthly_report_repo.dart';
+import 'package:hmg_patient_app_new/features/monthly_report/monthly_report_view_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/appointment_rating_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_repo.dart';
+import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart';
@@ -39,10 +46,14 @@ import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_mo
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
+import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart';
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_repo.dart';
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_repo.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
+import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_repo.dart';
+import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart';
+import 'package:hmg_patient_app_new/presentation/health_trackers/health_trackers_view_model.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
@@ -51,11 +62,14 @@ import 'package:hmg_patient_app_new/services/firebase_service.dart';
import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
+import 'package:hmg_patient_app_new/services/notification_service.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:local_auth/local_auth.dart';
import 'package:logger/web.dart';
import 'package:shared_preferences/shared_preferences.dart';
+import '../presentation/health_calculators_and_converts/health_calculator_view_model.dart';
+
GetIt getIt = GetIt.instance;
class AppDependencies {
@@ -97,6 +111,13 @@ class AppDependencies {
final sharedPreferences = await SharedPreferences.getInstance();
getIt.registerLazySingleton(() => CacheServiceImp(sharedPreferences: sharedPreferences, loggerService: getIt()));
+
+ final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
+ getIt.registerLazySingleton(() => NotificationServiceImp(
+ flutterLocalNotificationsPlugin: flutterLocalNotificationsPlugin,
+ loggerService: getIt(),
+ ));
+
getIt.registerLazySingleton(() => ApiClientImp(appState: getIt()));
getIt.registerLazySingleton(
() => LocalAuthService(loggerService: getIt(), localAuth: getIt()),
@@ -122,6 +143,10 @@ class AppDependencies {
getIt.registerLazySingleton(() => HmgServicesRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => SymptomsCheckerRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => BloodDonationRepoImp(loggerService: getIt(), apiClient: getIt()));
+ getIt.registerLazySingleton(() => WaterMonitorRepoImp(loggerService: getIt(), apiClient: getIt()));
+ getIt.registerLazySingleton(() => MyInvoicesRepoImp(loggerService: getIt(), apiClient: getIt()));
+ getIt.registerLazySingleton(() => HealthTrackersRepoImp(loggerService: getIt(), apiClient: getIt()));
+ getIt.registerLazySingleton(() => MonthlyReportRepoImp(loggerService: getIt(), apiClient: getIt()));
// ViewModels
// Global/shared VMs → LazySingleton
@@ -132,26 +157,20 @@ class AppDependencies {
() => RadiologyViewModel(radiologyRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
);
- getIt.registerLazySingleton(
- () => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
+ getIt.registerLazySingleton(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton(() => InsuranceViewModel(insuranceRepo: getIt(), errorHandlerService: getIt()));
- getIt.registerLazySingleton(
- () => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
+ getIt.registerLazySingleton(() => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
+
+ getIt.registerLazySingleton(() => AppointmentRatingViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
getIt.registerLazySingleton(
- () => PayfortViewModel(
- payfortRepo: getIt(),
- errorHandlerService: getIt(),
- ),
+ () => PayfortViewModel(payfortRepo: getIt(), errorHandlerService: getIt()),
);
getIt.registerLazySingleton(
- () => HabibWalletViewModel(
- habibWalletRepo: getIt(),
- errorHandlerService: getIt(),
- ),
+ () => HabibWalletViewModel(habibWalletRepo: getIt(), errorHandlerService: getIt()),
);
getIt.registerLazySingleton(
@@ -163,12 +182,7 @@ class AppDependencies {
getIt.registerLazySingleton(
() => BookAppointmentsViewModel(
- bookAppointmentsRepo: getIt(),
- errorHandlerService: getIt(),
- navigationService: getIt(),
- myAppointmentsViewModel: getIt(),
- locationUtils: getIt(),
- dialogService: getIt()),
+ bookAppointmentsRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), myAppointmentsViewModel: getIt(), locationUtils: getIt(), dialogService: getIt()),
);
getIt.registerLazySingleton(
@@ -182,14 +196,9 @@ class AppDependencies {
getIt.registerLazySingleton(
() => AuthenticationViewModel(
- authenticationRepo: getIt(),
- cacheService: getIt(),
- navigationService: getIt(),
- dialogService: getIt(),
- appState: getIt(),
- errorHandlerService: getIt(),
- localAuthService: getIt()),
+ authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()),
);
+
getIt.registerLazySingleton(() => ProfileSettingsViewModel());
getIt.registerLazySingleton(() => DateRangeSelectorRangeViewModel());
@@ -202,13 +211,14 @@ class AppDependencies {
getIt.registerLazySingleton(
() => EmergencyServicesViewModel(
- locationUtils: getIt(),
- navServices: getIt(),
- emergencyServicesRepo: getIt(),
- appState: getIt(),
- errorHandlerService: getIt(),
- appointmentRepo: getIt(),
- dialogService: getIt()),
+ locationUtils: getIt(),
+ navServices: getIt(),
+ emergencyServicesRepo: getIt(),
+ appState: getIt(),
+ errorHandlerService: getIt(),
+ appointmentRepo: getIt(),
+ dialogService: getIt(),
+ ),
);
getIt.registerLazySingleton(
@@ -219,26 +229,50 @@ class AppDependencies {
() => ContactUsViewModel(contactUsRepo: getIt(), appState: getIt(), errorHandlerService: getIt()),
);
- getIt.registerLazySingleton(
- () => TodoSectionViewModel(todoSectionRepo: getIt(), errorHandlerService: getIt()),
+ getIt.registerLazySingleton(() => HealthCalcualtorViewModel());
+
+ getIt.registerLazySingleton(() => TodoSectionViewModel(todoSectionRepo: getIt(), errorHandlerService: getIt()));
+
+ getIt.registerLazySingleton(
+ () => SymptomsCheckerViewModel(
+ errorHandlerService: getIt(),
+ symptomsCheckerRepo: getIt(),
+ appState: getIt(),
+ ),
);
- getIt.registerLazySingleton(() => SymptomsCheckerViewModel(errorHandlerService: getIt(), symptomsCheckerRepo: getIt()));
getIt.registerLazySingleton(
- () => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
+ () => HmgServicesViewModel(
+ bookAppointmentsRepo: getIt(),
+ hmgServicesRepo: getIt(),
+ errorHandlerService: getIt(),
+ navigationService: getIt(),
+ ),
);
getIt.registerLazySingleton(
- () => BloodDonationViewModel(bloodDonationRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt()),
+ () => BloodDonationViewModel(
+ bloodDonationRepo: getIt(),
+ errorHandlerService: getIt(),
+ navigationService: getIt(),
+ dialogService: getIt(),
+ appState: getIt(),
+ navServices: getIt(),
+ ),
);
- // Screen-specific VMs → Factory
- // getIt.registerFactory(
- // () => BookAppointmentsViewModel(
- // bookAppointmentsRepo: getIt(),
- // dialogService: getIt(),
- // errorHandlerService: getIt(),
- // ),
- // );
+ getIt.registerLazySingleton(() => HealthProvider());
+
+ getIt.registerLazySingleton(() => WaterMonitorViewModel(waterMonitorRepo: getIt(), errorHandlerService: getIt()));
+
+ getIt.registerLazySingleton(() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
+
+ getIt.registerLazySingleton(() => MonthlyReportViewModel(errorHandlerService: getIt(), monthlyReportRepo: getIt()));
+ getIt.registerLazySingleton(() => MyInvoicesViewModel(
+ myInvoicesRepo: getIt(),
+ errorHandlerService: getIt(),
+ navServices: getIt(),
+ ));
+ getIt.registerLazySingleton(() => HealthTrackersViewModel(healthTrackersRepo: getIt(), errorHandlerService: getIt()));
}
}
diff --git a/lib/core/enums.dart b/lib/core/enums.dart
index 6754e8c..6dc3bf6 100644
--- a/lib/core/enums.dart
+++ b/lib/core/enums.dart
@@ -16,7 +16,7 @@ enum CountryEnum { saudiArabia, unitedArabEmirates }
enum CalenderEnum { gregorian, hijri }
-enum SelectionTypeEnum { dropdown, calendar, search }
+enum SelectionTypeEnum { dropdown, calendar, search, time }
enum GenderTypeEnum { male, female }
@@ -34,6 +34,70 @@ enum FamilyFileEnum { active, inactive, blocked, deleted, pending, rejected }
enum BodyView { front, back }
+enum HealthCalConEnum { calculator, converter }
+
+enum HealthCalculatorEnum { general, women }
+
+enum HealthCalculatorsTypeEnum {
+ bmi,
+ calories,
+ bmr,
+ idealBodyWeight,
+ bodyFat,
+ crabsProteinFat,
+ ovulation,
+ deliveryDueDate,
+ bloodSugar,
+ bloodCholesterol,
+ triglycerides
+}
+
+extension HealthCalculatorExtenshion on HealthCalculatorsTypeEnum {
+ String get displayName {
+ AppState appState = getIt.get();
+ bool isArabic = appState.getLanguageID() == 1 ? true : false;
+ switch (this) {
+ case HealthCalculatorsTypeEnum.bmi:
+ return isArabic ? "حاسبة مؤشر كتلة الجسم" : "BMI Calculator";
+ case HealthCalculatorsTypeEnum.calories:
+ return isArabic ? "حاسبة السعرات الحرارية" : "Calories Calculator";
+ case HealthCalculatorsTypeEnum.bmr:
+ return isArabic ? "حاسبة معدل الأيض الأساسي" : "BMR Calculator";
+ case HealthCalculatorsTypeEnum.idealBodyWeight:
+ return isArabic ? "الوزن المثالي للجسم" : "Ideal Body Weight Calculator";
+ case HealthCalculatorsTypeEnum.bodyFat:
+ return isArabic ? "حاسبة الدهون في الجسم" : "Body Fat Calculator";
+ case HealthCalculatorsTypeEnum.crabsProteinFat:
+ return isArabic ? "حاسبة البروتين والدهون في سرطان البحر" : "Crabs Protein & Fat Calculator";
+ case HealthCalculatorsTypeEnum.ovulation:
+ return isArabic ? "فترة الإباضة" : "Ovulation Period";
+ case HealthCalculatorsTypeEnum.deliveryDueDate:
+ return isArabic ? "تاريخ استحقاق التسليم" : "Delivery Due Date";
+ case HealthCalculatorsTypeEnum.bloodSugar:
+ return isArabic ? "سكر الدم" : "Blood Sugar";
+ case HealthCalculatorsTypeEnum.bloodCholesterol:
+ return isArabic ? "كوليسترول الدم" : "Blood Cholesterol";
+ case HealthCalculatorsTypeEnum.triglycerides:
+ return isArabic ? "الدهون الثلاثية في الدم" : "Triglycerides Fat Blood";
+ }
+ }
+
+ static LoginTypeEnum? fromValue(int value) {
+ switch (value) {
+ case 1:
+ return LoginTypeEnum.sms;
+ case 2:
+ return LoginTypeEnum.fingerprint;
+ case 3:
+ return LoginTypeEnum.face;
+ case 4:
+ return LoginTypeEnum.whatsapp;
+ default:
+ return null;
+ }
+ }
+}
+
extension CalenderExtension on CalenderEnum {
int get toInt {
switch (this) {
@@ -245,3 +309,5 @@ extension ServiceTypeEnumExt on ServiceTypeEnum {
// SymptomsChecker
enum PossibleConditionsSeverityEnum { seekMedicalAdvice, monitorOnly, emergency }
+
+enum HealthTrackerTypeEnum { bloodSugar, bloodPressure, weightTracker }
diff --git a/lib/core/exceptions/api_exception.dart b/lib/core/exceptions/api_exception.dart
index eb11b71..eb0258f 100644
--- a/lib/core/exceptions/api_exception.dart
+++ b/lib/core/exceptions/api_exception.dart
@@ -1,7 +1,5 @@
import 'dart:convert';
-import 'package:equatable/equatable.dart';
-import 'package:hmg_patient_app_new/core/api/api_client.dart';
class APIException implements Exception {
static const String BAD_REQUEST = 'api_common_bad_request';
diff --git a/lib/core/location_util.dart b/lib/core/location_util.dart
index 487b228..9dcdbb5 100644
--- a/lib/core/location_util.dart
+++ b/lib/core/location_util.dart
@@ -12,8 +12,9 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
-import 'package:huawei_location/huawei_location.dart' as HmsLocation show FusedLocationProviderClient, Location, LocationSettingsRequest, LocationRequest;
-import 'package:location/location.dart' show Location, PermissionStatus, LocationData;
+import 'package:huawei_location/huawei_location.dart' as HmsLocation
+ show FusedLocationProviderClient, Location, LocationSettingsRequest, LocationRequest;
+import 'package:location/location.dart' show Location;
import 'package:permission_handler/permission_handler.dart' show Permission, PermissionListActions, PermissionStatusGetters, openAppSettings;
class LocationUtils {
@@ -59,37 +60,22 @@ class LocationUtils {
// }
void getLocation(
- {Function(LatLng)? onSuccess,
- VoidCallback? onFailure,
- bool isShowConfirmDialog = false,
- VoidCallback? onLocationDeniedForever}) async {
+ {Function(LatLng)? onSuccess, VoidCallback? onFailure, bool isShowConfirmDialog = false, VoidCallback? onLocationDeniedForever}) async {
this.isShowConfirmDialog = isShowConfirmDialog;
if (Platform.isIOS) {
- getCurrentLocation(
- onFailure: onFailure,
- onSuccess: onSuccess,
- onLocationDeniedForever: onLocationDeniedForever);
+ getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess, onLocationDeniedForever: onLocationDeniedForever);
return;
}
if (await isGMSDevice ?? true) {
- getCurrentLocation(
- onFailure: onFailure,
- onSuccess: onSuccess,
- onLocationDeniedForever: onLocationDeniedForever);
+ getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess, onLocationDeniedForever: onLocationDeniedForever);
return;
}
- getHMSLocation(
- onFailure: onFailure,
- onSuccess: onSuccess,
- onLocationDeniedForever: onLocationDeniedForever);
+ getHMSLocation(onFailure: onFailure, onSuccess: onSuccess, onLocationDeniedForever: onLocationDeniedForever);
}
- void getCurrentLocation(
- {Function(LatLng)? onSuccess,
- VoidCallback? onFailure,
- VoidCallback? onLocationDeniedForever}) async {
+ void getCurrentLocation({Function(LatLng)? onSuccess, VoidCallback? onFailure, VoidCallback? onLocationDeniedForever}) async {
var location = Location();
bool isLocationEnabled = await location.serviceEnabled();
@@ -113,14 +99,12 @@ class LocationUtils {
}
} else if (permissionGranted == LocationPermission.deniedForever) {
appState.resetLocation();
- if(onLocationDeniedForever == null && isShowConfirmDialog){
+ if (onLocationDeniedForever == null && isShowConfirmDialog) {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
- loadingText:
- "Please grant location permission from app settings to see better results"
- .needTranslation,
+ loadingText: "Please grant location permission from app settings to see better results".needTranslation,
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
@@ -253,10 +237,7 @@ class LocationUtils {
appState.userLong = locationData.longitude;
}
- void getHMSLocation(
- {VoidCallback? onFailure,
- Function(LatLng p1)? onSuccess,
- VoidCallback? onLocationDeniedForever}) async {
+ void getHMSLocation({VoidCallback? onFailure, Function(LatLng p1)? onSuccess, VoidCallback? onLocationDeniedForever}) async {
try {
var location = Location();
HmsLocation.FusedLocationProviderClient locationService = HmsLocation.FusedLocationProviderClient()..initFusedLocationService();
@@ -279,14 +260,12 @@ class LocationUtils {
permissionGranted = await Geolocator.requestPermission();
if (permissionGranted == LocationPermission.deniedForever) {
appState.resetLocation();
- if(onLocationDeniedForever == null && isShowConfirmDialog){
+ if (onLocationDeniedForever == null && isShowConfirmDialog) {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
- loadingText:
- "Please grant location permission from app settings to see better results"
- .needTranslation,
+ loadingText: "Please grant location permission from app settings to see better results".needTranslation,
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
@@ -311,7 +290,7 @@ class LocationUtils {
HmsLocation.Location data = await locationService.getLastLocation();
if (data.latitude == null || data.longitude == null) {
- appState.resetLocation();
+ appState.resetLocation();
HmsLocation.LocationRequest request = HmsLocation.LocationRequest()
..priority = HmsLocation.LocationRequest.PRIORITY_HIGH_ACCURACY
..interval = 1000 // 1 second
diff --git a/lib/core/post_params_model.dart b/lib/core/post_params_model.dart
index cf52306..e13eb5c 100644
--- a/lib/core/post_params_model.dart
+++ b/lib/core/post_params_model.dart
@@ -14,19 +14,20 @@ class PostParamsModel {
String? sessionID;
String? setupID;
- PostParamsModel(
- {this.versionID,
- this.channel,
- this.languageID,
- this.logInTokenID,
- this.tokenID,
- this.language,
- this.ipAddress,
- this.generalId,
- this.latitude,
- this.longitude,
- this.deviceTypeID,
- this.sessionID});
+ PostParamsModel({
+ this.versionID,
+ this.channel,
+ this.languageID,
+ this.logInTokenID,
+ this.tokenID,
+ this.language,
+ this.ipAddress,
+ this.generalId,
+ this.latitude,
+ this.longitude,
+ this.deviceTypeID,
+ this.sessionID,
+ });
PostParamsModel.fromJson(Map json) {
versionID = json['VersionID'];
diff --git a/lib/core/utils/date_util.dart b/lib/core/utils/date_util.dart
index a42a44d..746d2a7 100644
--- a/lib/core/utils/date_util.dart
+++ b/lib/core/utils/date_util.dart
@@ -6,8 +6,6 @@ class DateUtil {
/// convert String To Date function
/// [date] String we want to convert
static DateTime convertStringToDate(String? date) {
-
-
if (date == null) return DateTime.now();
if (date.isEmpty) return DateTime.now();
@@ -522,6 +520,64 @@ class DateUtil {
}
return "";
}
+
+ /// Get short month name from full month name
+ /// [monthName] Full month name like "January"
+ /// Returns short form like "Jan"
+ static String getShortMonthName(String monthName) {
+ switch (monthName.toLowerCase()) {
+ case 'january':
+ return 'Jan';
+ case 'february':
+ return 'Feb';
+ case 'march':
+ return 'Mar';
+ case 'april':
+ return 'Apr';
+ case 'may':
+ return 'May';
+ case 'june':
+ return 'Jun';
+ case 'july':
+ return 'Jul';
+ case 'august':
+ return 'Aug';
+ case 'september':
+ return 'Sep';
+ case 'october':
+ return 'Oct';
+ case 'november':
+ return 'Nov';
+ case 'december':
+ return 'Dec';
+ default:
+ return monthName; // Return as-is if not recognized
+ }
+ }
+
+ /// Get short weekday name from full weekday name
+ /// [weekDayName] Full weekday name like "Monday"
+ /// Returns short form like "Mon"
+ static String getShortWeekDayName(String weekDayName) {
+ switch (weekDayName.toLowerCase().trim()) {
+ case 'monday':
+ return 'Mon';
+ case 'tuesday':
+ return 'Tue';
+ case 'wednesday':
+ return 'Wed';
+ case 'thursday':
+ return 'Thu';
+ case 'friday':
+ return 'Fri';
+ case 'saturday':
+ return 'Sat';
+ case 'sunday':
+ return 'Sun';
+ default:
+ return weekDayName; // Return as-is if not recognized
+ }
+ }
}
extension OnlyDate on DateTime {
diff --git a/lib/core/utils/doctor_response_mapper.dart b/lib/core/utils/doctor_response_mapper.dart
index 994e9a1..05ed2fd 100644
--- a/lib/core/utils/doctor_response_mapper.dart
+++ b/lib/core/utils/doctor_response_mapper.dart
@@ -1,7 +1,5 @@
import 'dart:math';
-import 'package:hmg_patient_app_new/core/cache_consts.dart' show CacheConst;
-import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart' show RegionList, PatientDoctorAppointmentList, DoctorList, PatientDoctorAppointmentListByRegion;
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel;
diff --git a/lib/core/utils/local_notifications.dart b/lib/core/utils/local_notifications.dart
deleted file mode 100644
index aba01f8..0000000
--- a/lib/core/utils/local_notifications.dart
+++ /dev/null
@@ -1,191 +0,0 @@
-import 'dart:math';
-import 'dart:typed_data';
-
-import 'package:flutter_local_notifications/flutter_local_notifications.dart';
-
-final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
-
-class LocalNotification {
- Function(String payload)? _onNotificationClick;
- static LocalNotification? _instance;
-
- static LocalNotification? getInstance() {
- return _instance;
- }
-
- static init({required Function(String payload) onNotificationClick}) {
- if (_instance == null) {
- _instance = LocalNotification();
- _instance?._onNotificationClick = onNotificationClick;
- _instance?._initialize();
- } else {
- // assert(false,(){
- // //TODO fix it
- // "LocalNotification Already Initialized";
- // });
- }
- }
-
- _initialize() async {
- try {
- var initializationSettingsAndroid = new AndroidInitializationSettings('app_icon');
- var initializationSettingsIOS = DarwinInitializationSettings();
- var initializationSettings = InitializationSettings(android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
- await flutterLocalNotificationsPlugin.initialize(
- initializationSettings,
- onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) {
- switch (notificationResponse.notificationResponseType) {
- case NotificationResponseType.selectedNotification:
- // selectNotificationStream.add(notificationResponse.payload);
- break;
- case NotificationResponseType.selectedNotificationAction:
- // if (notificationResponse.actionId == navigationActionId) {
- // selectNotificationStream.add(notificationResponse.payload);
- // }
- break;
- }
- },
- // onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
- );
- } catch (ex) {
- print(ex.toString());
- }
- // flutterLocalNotificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse notificationResponse)
- // {
- // switch (notificationResponse.notificationResponseType) {
- // case NotificationResponseType.selectedNotification:
- // // selectNotificationStream.add(notificationResponse.payload);
- // break;
- // case NotificationResponseType.selectedNotificationAction:
- // // if (notificationResponse.actionId == navigationActionId) {
- // // selectNotificationStream.add(notificationResponse.payload);
- // }
- // // break;
- // },}
- //
- // ,
- //
- // );
- }
-
- // void notificationTapBackground(NotificationResponse notificationResponse) {
- // // ignore: avoid_print
- // print('notification(${notificationResponse.id}) action tapped: '
- // '${notificationResponse.actionId} with'
- // ' payload: ${notificationResponse.payload}');
- // if (notificationResponse.input?.isNotEmpty ?? false) {
- // // ignore: avoid_print
- // print('notification action tapped with input: ${notificationResponse.input}');
- // }
- // }
-
- var _random = new Random();
-
- _randomNumber({int from = 100000}) {
- return _random.nextInt(from);
- }
-
- _vibrationPattern() {
- var vibrationPattern = Int64List(4);
- vibrationPattern[0] = 0;
- vibrationPattern[1] = 1000;
- vibrationPattern[2] = 5000;
- vibrationPattern[3] = 2000;
-
- return vibrationPattern;
- }
-
- Future? showNow({required String title, required String subtitle, required String payload}) {
- Future.delayed(Duration(seconds: 1)).then((result) async {
- var androidPlatformChannelSpecifics = AndroidNotificationDetails(
- 'com.hmg.local_notification',
- 'HMG',
- channelDescription: 'HMG',
- importance: Importance.max,
- priority: Priority.high,
- ticker: 'ticker',
- vibrationPattern: _vibrationPattern(),
- ongoing: true,
- autoCancel: false,
- usesChronometer: true,
- when: DateTime.now().millisecondsSinceEpoch - 120 * 1000,
- );
- var iOSPlatformChannelSpecifics = DarwinNotificationDetails();
- var platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics);
- await flutterLocalNotificationsPlugin.show(25613, title, subtitle, platformChannelSpecifics, payload: payload).catchError((err) {
- print(err);
- });
- });
- }
-
- Future scheduleNotification({required DateTime scheduledNotificationDateTime, required String title, required String description}) async {
- ///vibrationPattern
- var vibrationPattern = Int64List(4);
- vibrationPattern[0] = 0;
- vibrationPattern[1] = 1000;
- vibrationPattern[2] = 5000;
- vibrationPattern[3] = 2000;
-
- // var androidPlatformChannelSpecifics = AndroidNotificationDetails('active-prescriptions', 'ActivePrescriptions',
- // channelDescription: 'ActivePrescriptionsDescription',
- // // icon: 'secondary_icon',
- // sound: RawResourceAndroidNotificationSound('slow_spring_board'),
- //
- // ///change it to be as ionic
- // // largeIcon: DrawableResourceAndroidBitmap('sample_large_icon'),///change it to be as ionic
- // vibrationPattern: vibrationPattern,
- // enableLights: true,
- // color: const Color.fromARGB(255, 255, 0, 0),
- // ledColor: const Color.fromARGB(255, 255, 0, 0),
- // ledOnMs: 1000,
- // ledOffMs: 500);
- // var iOSPlatformChannelSpecifics = DarwinNotificationDetails(sound: 'slow_spring_board.aiff');
-
- // /change it to be as ionic
- // var platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics);
- // await flutterLocalNotificationsPlugin.schedule(0, title, description, scheduledNotificationDateTime, platformChannelSpecifics);
- }
-
- ///Repeat notification every day at approximately 10:00:00 am
- Future showDailyAtTime() async {
- // var time = Time(10, 0, 0);
- // var androidPlatformChannelSpecifics = AndroidNotificationDetails('repeatDailyAtTime channel id', 'repeatDailyAtTime channel name', channelDescription: 'repeatDailyAtTime description');
- // var iOSPlatformChannelSpecifics = DarwinNotificationDetails();
- // var platformChannelSpecifics = NotificationDetails(
- // androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
- // await flutterLocalNotificationsPlugin.showDailyAtTime(
- // 0,
- // 'show daily title',
- // 'Daily notification shown at approximately ${_toTwoDigitString(time.hour)}:${_toTwoDigitString(time.minute)}:${_toTwoDigitString(time.second)}',
- // time,
- // platformChannelSpecifics);
- }
-
- ///Repeat notification weekly on Monday at approximately 10:00:00 am
- Future showWeeklyAtDayAndTime() async {
- // var time = Time(10, 0, 0);
- // var androidPlatformChannelSpecifics = AndroidNotificationDetails('show weekly channel id', 'show weekly channel name', channelDescription: 'show weekly description');
- // var iOSPlatformChannelSpecifics = DarwinNotificationDetails();
- // var platformChannelSpecifics = NotificationDetails(
- // androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
- // await flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime(
- // 0,
- // 'show weekly title',
- // 'Weekly notification shown on Monday at approximately ${_toTwoDigitString(time.hour)}:${_toTwoDigitString(time.minute)}:${_toTwoDigitString(time.second)}',
- // Day.Monday,
- // time,
- // platformChannelSpecifics);
- }
-
- String _toTwoDigitString(int value) {
- return value.toString().padLeft(2, '0');
- }
-
- Future cancelNotification() async {
- await flutterLocalNotificationsPlugin.cancel(0);
- }
-
- Future cancelAllNotifications() async {
- await flutterLocalNotificationsPlugin.cancelAll();
- }
-}
diff --git a/lib/core/utils/penguin_method_channel.dart b/lib/core/utils/penguin_method_channel.dart
new file mode 100644
index 0000000..1f19037
--- /dev/null
+++ b/lib/core/utils/penguin_method_channel.dart
@@ -0,0 +1,105 @@
+import 'package:flutter/services.dart';
+
+class PenguinMethodChannel {
+ static const MethodChannel _channel = MethodChannel('launch_penguin_ui');
+
+ Future loadGif() async {
+ return await rootBundle.load("assets/images/progress-loading-red-crop-1.gif").then((data) => data.buffer.asUint8List());
+ }
+
+ Future launch(String storyboardName, String languageCode, String username, {NavigationClinicDetails? details}) async {
+ // Uint8List image = await loadGif();
+ try {
+ await _channel.invokeMethod('launchPenguin', {
+ "storyboardName": storyboardName,
+ "baseURL": "https://penguinuat.hmg.com",
+ // "dataURL": "https://hmg.nav.penguinin.com",
+ // "positionURL": "https://hmg.nav.penguinin.com",
+ // "dataURL": "https://hmg-v33.local.penguinin.com",
+ // "positionURL": "https://hmg-v33.local.penguinin.com",
+ "dataURL": "https://penguinuat.hmg.com",
+ "positionURL": "https://penguinuat.hmg.com",
+ "dataServiceName": "api",
+ "positionServiceName": "pe",
+ "clientID": "HMG",
+ "clientKey": "UGVuZ3VpbklOX1Blbk5hdl9QSUY=",
+ "username": details?.patientId ?? "Haroon",
+ // "username": "Haroon",
+ "isSimulationModeEnabled": false,
+ "isShowUserName": false,
+ "isUpdateUserLocationSmoothly": true,
+ "isEnableReportIssue": true,
+ "languageCode": languageCode,
+ "mapBoxKey": "pk.eyJ1IjoicndhaWQiLCJhIjoiY2x5cGo4aHNjMGNsbTJyc2djempobGQxaSJ9.RCaC6WrUt4A4YnZNfxnONQ",
+ "clinicID": details?.clinicId ?? "",
+ // "clinicID": "108", // 46 ,49, 133
+ "patientID": details?.patientId ?? "",
+ "projectID": int.parse(details?.projectId ?? "-1"),
+ // "loaderImage": image,
+ });
+ } on PlatformException catch (e) {
+ print("Failed to launch PenguinIn: '${e.message}'.");
+ }
+ }
+
+ void setMethodCallHandler(){
+ _channel.setMethodCallHandler((MethodCall call) async {
+ try {
+
+ print(call.method);
+
+ switch (call.method) {
+
+ case PenguinMethodNames.onPenNavInitializationError:
+ _handleInitializationError(call.arguments); // Handle onPenNavInitializationError errors.
+ break;
+ case PenguinMethodNames.onPenNavUIDismiss:
+ //todo handle pen dismissable
+ // _handlePenNavUIDismiss(); // Handle UI dismissal event.
+ break;
+ case PenguinMethodNames.onReportIssue:
+ // Handle the report issue event.
+ _handleInitializationError(call.arguments);
+ break;
+ default:
+ _handleUnknownMethod(call.method); // Handle unknown method calls.
+ }
+ } catch (e) {
+ print("Error handling method call '${call.method}': $e");
+ // Optionally, log this error to an external service
+ }
+ });
+ }
+ static void _handleUnknownMethod(String method) {
+ print("Unknown method: $method");
+ // Optionally, handle this unknown method case, such as reporting or ignoring it
+ }
+
+
+ static void _handleInitializationError(Map error) {
+ final type = error['type'] as String?;
+ final description = error['description'] as String?;
+ print("Initialization Error: ${type ?? 'Unknown Type'}, ${description ?? 'No Description'}");
+
+ }
+
+}
+// Define constants for method names
+class PenguinMethodNames {
+ static const String showPenguinUI = 'showPenguinUI';
+ static const String openSharedLocation = 'openSharedLocation';
+
+ // ---- Handler Method
+ static const String onPenNavSuccess = 'onPenNavSuccess'; // Tested Android,iOS
+ static const String onPenNavInitializationError = 'onPenNavInitializationError'; // Tested Android,iOS
+ static const String onPenNavUIDismiss = 'onPenNavUIDismiss'; //Tested Android,iOS
+ static const String onReportIssue = 'onReportIssue'; // Tested Android,iOS
+ static const String onLocationOffCampus = 'onLocationOffCampus'; // Tested iOS,Android
+ static const String navigateToPOI = 'navigateToPOI'; // Tested Android,iOS
+}
+
+class NavigationClinicDetails {
+ String? clinicId;
+ String? patientId;
+ String? projectId;
+}
diff --git a/lib/core/utils/push_notification_handler.dart b/lib/core/utils/push_notification_handler.dart
index ee05335..88e8cc8 100644
--- a/lib/core/utils/push_notification_handler.dart
+++ b/lib/core/utils/push_notification_handler.dart
@@ -15,18 +15,11 @@ import 'package:flutter_callkit_incoming/entities/notification_params.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_ios_voip_kit_karmm/call_state_type.dart';
import 'package:flutter_ios_voip_kit_karmm/flutter_ios_voip_kit.dart';
-// import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
-
-import 'package:flutter_local_notifications/flutter_local_notifications.dart';
-import 'package:get_it/get_it.dart';
-import 'package:hmg_patient_app_new/core/utils/local_notifications.dart';
+import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
-import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:uuid/uuid.dart';
-import '../cache_consts.dart';
-
// |--> Push Notification Background
@pragma('vm:entry-point')
Future backgroundMessageHandler(dynamic message) async {
@@ -38,7 +31,7 @@ Future backgroundMessageHandler(dynamic message) async {
// showCallkitIncoming(message);
_incomingCall(message.data);
return;
- } else {}
+ }
}
callPage(String sessionID, String token) async {}
@@ -325,7 +318,7 @@ class PushNotificationHandler {
if (fcmToken != null) onToken(fcmToken);
// }
} catch (ex) {
- print("Notification Exception: " + ex.toString());
+ print("Notification Exception: $ex");
}
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
}
@@ -333,7 +326,7 @@ class PushNotificationHandler {
if (Platform.isIOS) {
final permission = await FirebaseMessaging.instance.requestPermission();
await FirebaseMessaging.instance.getAPNSToken().then((value) async {
- log("APNS token: " + value.toString());
+ log("APNS token: $value");
await Utils.saveStringFromPrefs(CacheConst.apnsToken, value.toString());
});
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
@@ -380,14 +373,14 @@ class PushNotificationHandler {
});
FirebaseMessaging.instance.getToken().then((String? token) {
- print("Push Notification getToken: " + token!);
+ print("Push Notification getToken: ${token!}");
onToken(token!);
}).catchError((err) {
print(err);
});
FirebaseMessaging.instance.onTokenRefresh.listen((fcm_token) {
- print("Push Notification onTokenRefresh: " + fcm_token);
+ print("Push Notification onTokenRefresh: $fcm_token");
onToken(fcm_token);
});
@@ -403,7 +396,7 @@ class PushNotificationHandler {
}
newMessage(RemoteMessage remoteMessage) async {
- print("Remote Message: " + remoteMessage.data.toString());
+ print("Remote Message: ${remoteMessage.data}");
if (remoteMessage.data.isEmpty) {
return;
}
@@ -429,7 +422,7 @@ class PushNotificationHandler {
}
onToken(String token) async {
- print("Push Notification Token: " + token);
+ print("Push Notification Token: $token");
await Utils.saveStringFromPrefs(CacheConst.pushToken, token);
}
@@ -443,9 +436,7 @@ class PushNotificationHandler {
Future requestPermissions() async {
try {
if (Platform.isIOS) {
- await flutterLocalNotificationsPlugin
- .resolvePlatformSpecificImplementation()
- ?.requestPermissions(alert: true, badge: true, sound: true);
+ await FirebaseMessaging.instance.requestPermission(alert: true, badge: true, sound: true);
} else if (Platform.isAndroid) {
Map statuses = await [
Permission.notification,
diff --git a/lib/core/utils/size_config.dart b/lib/core/utils/size_config.dart
index 9f9d835..0f8766a 100644
--- a/lib/core/utils/size_config.dart
+++ b/lib/core/utils/size_config.dart
@@ -1,6 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
-import 'package:hmg_patient_app_new/core/cache_consts.dart';
class SizeConfig {
static double _blockWidth = 0;
diff --git a/lib/core/utils/size_utils.dart b/lib/core/utils/size_utils.dart
index fdd0d30..02b8195 100644
--- a/lib/core/utils/size_utils.dart
+++ b/lib/core/utils/size_utils.dart
@@ -1,4 +1,5 @@
import 'dart:developer';
+import 'dart:math' as math;
import 'package:flutter/material.dart'; // These are the Viewport values of your Figma Design.
@@ -6,6 +7,14 @@ import 'package:flutter/material.dart'; // These are the Viewport values of your
const num figmaDesignWidth = 375; // iPhone X / 12 base width
const num figmaDesignHeight = 812; // iPhone X / 12 base height
+extension ConstrainedResponsive on num {
+ /// Width with max cap for tablets
+ double get wCapped => isTablet ? math.min(w, this * 1.3) : w;
+
+ /// Height with max cap for tablets
+ double get hCapped => isTablet ? math.min(h, this * 1.3) : h;
+}
+
extension ResponsiveExtension on num {
double get _screenWidth => SizeUtils.width;
@@ -27,7 +36,7 @@ extension ResponsiveExtension on num {
double clamp;
if (SizeUtils.deviceType == DeviceType.tablet || _isFoldable) {
// More conservative scaling for tablets and foldables
- clamp = (aspectRatio > 1.5 || aspectRatio < 0.67) ? 1.4 : 1.1;
+ clamp = (aspectRatio > 1.5 || aspectRatio < 0.67) ? 1.6 : 1.4;
} else {
// Original logic for phones
clamp = (aspectRatio > 1.3 || aspectRatio < 0.77) ? 1.6 : 1.2;
@@ -68,7 +77,7 @@ extension ResponsiveExtension on num {
double get r {
double baseScale = (this * _screenWidth) / figmaDesignWidth;
- if (_isFoldable) {
+ if (_isFoldable || isTablet) {
// Use the same logic as enhanced width for foldables
double scale = _screenWidth / figmaDesignWidth;
scale = scale.clamp(0.8, 1.4);
diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart
index 857c0c2..f5ffd36 100644
--- a/lib/core/utils/utils.dart
+++ b/lib/core/utils/utils.dart
@@ -39,6 +39,50 @@ class Utils {
static bool get isLoading => _isLoadingVisible;
+ static var navigationProjectsList = [
+ {
+ "Desciption": "Sahafa Hospital",
+ "DesciptionN": "مستشفى الصحافة",
+ "ID": 1,
+ "LegalName": "Sahafa Hospital",
+ "LegalNameN": "مستشفى الصحافة",
+ "Name": "Sahafa Hospital",
+ "NameN": "مستشفى الصحافة",
+ "PhoneNumber": "+966115222222",
+ "SetupID": "013311",
+ "DistanceInKilometers": 0,
+ "HasVida3": false,
+ "IsActive": true,
+ "IsHmg": true,
+ "IsVidaPlus": false,
+ "Latitude": "24.8113774",
+ "Longitude": "46.6239813",
+ "MainProjectID": 130,
+ "ProjectOutSA": false,
+ "UsingInDoctorApp": false
+ },{
+ "Desciption": "Jeddah Hospital",
+ "DesciptionN": "مستشفى الصحافة",
+ "ID": 3,
+ "LegalName": "Jeddah Hospital",
+ "LegalNameN": "مستشفى الصحافة",
+ "Name": "Jeddah Hospital",
+ "NameN": "مستشفى الصحافة",
+ "PhoneNumber": "+966115222222",
+ "SetupID": "013311",
+ "DistanceInKilometers": 0,
+ "HasVida3": false,
+ "IsActive": true,
+ "IsHmg": true,
+ "IsVidaPlus": false,
+ "Latitude": "24.8113774",
+ "Longitude": "46.6239813",
+ "MainProjectID": 130,
+ "ProjectOutSA": false,
+ "UsingInDoctorApp": false
+ }
+ ];
+
static void showToast(String message, {bool longDuration = true}) {
Fluttertoast.showToast(
msg: message,
@@ -218,16 +262,6 @@ class Utils {
return await prefs.remove(key);
}
- static void showLoading({bool isNeedBinding = true}) {
- if (isNeedBinding) {
- WidgetsBinding.instance.addPostFrameCallback((_) {
- showLoadingDialog();
- });
- } else {
- showLoadingDialog();
- }
- }
-
static void showLoadingDialog() {
_isLoadingVisible = true;
showDialog(
@@ -244,18 +278,6 @@ class Utils {
);
}
- static void hideLoading() {
- try {
- if (_isLoadingVisible) {
- _isLoadingVisible = false;
- Navigator.of(navigationService.navigatorKey.currentContext!).pop();
- }
- _isLoadingVisible = false;
- } catch (e) {
- log("errr: ${e.toString()}");
- }
- }
-
static List uniqueBy(List list, K Function(T) keySelector) {
final seenKeys = {};
return list.where((item) => seenKeys.add(keySelector(item))).toList();
@@ -326,7 +348,7 @@ class Utils {
children: [
SizedBox(height: isSmallWidget ? 0.h : 48.h),
Lottie.asset(AppAnimations.noData,
- repeat: false, reverse: false, frameRate: FrameRate(60), width: width.h, height: height.h, fit: BoxFit.fill),
+ repeat: false, reverse: false, frameRate: FrameRate(60), width: width.w, height: height.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
(noDataText ?? LocaleKeys.noDataAvailable.tr())
.toText14(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true)
@@ -351,10 +373,10 @@ class Utils {
).center;
}
- static Widget getSuccessWidget({String? loadingText}) {
+ static Widget getSuccessWidget({String? loadingText, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
+ crossAxisAlignment: crossAxisAlignment,
children: [
Lottie.asset(AppAnimations.checkmark, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
@@ -722,7 +744,16 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset(AppAssets.mada, width: 25.h, height: 25.h),
- Image.asset(AppAssets.tamaraEng, width: 25.h, height: 25.h),
+ Image.asset(
+ AppAssets.tamaraEng,
+ width: 25.h,
+ height: 25.h,
+ fit: BoxFit.contain,
+ errorBuilder: (context, error, stackTrace) {
+ debugPrint('Failed to load Tamara PNG in payment methods: $error');
+ return Utils.buildSvgWithAssets(icon: AppAssets.tamara, width: 25.h, height: 25.h, fit: BoxFit.contain);
+ },
+ ),
Image.asset(AppAssets.visa, width: 25.h, height: 25.h),
Image.asset(AppAssets.mastercard, width: 25.h, height: 25.h),
Image.asset(AppAssets.applePay, width: 25.h, height: 25.h),
@@ -859,6 +890,17 @@ class Utils {
isHMC: hospital.isHMC);
}
+
+ static HospitalsModel? convertToHospitalsModel(PatientDoctorAppointmentList? item) {
+ if (item == null) return null;
+ return HospitalsModel(
+ name: item.filterName,
+ nameN: item.filterName,
+ distanceInKilometers: item.distanceInKMs,
+ isHMC: item.isHMC,
+ );
+ }
+
static bool havePrivilege(int id) {
bool isHavePrivilege = false;
try {
@@ -876,7 +918,6 @@ class Utils {
launchUrl(uri, mode: LaunchMode.inAppBrowserView);
}
-
static Color getCardBorderColor(int currentQueueStatus) {
switch (currentQueueStatus) {
case 0:
@@ -913,16 +954,23 @@ class Utils {
return AppColors.primaryRedColor;
}
- static String getCardButtonText(int currentQueueStatus) {
+ static String getCardButtonText(int currentQueueStatus, String roomNumber) {
switch (currentQueueStatus) {
case 0:
return "Please wait! you will be called for vital signs".needTranslation;
case 1:
- return "Please visit Room S5 for vital signs".needTranslation;
+ return "Please visit Room $roomNumber for vital signs".needTranslation;
case 2:
- return "Please visit Room S5 to the Doctor".needTranslation;
+ return "Please visit Room $roomNumber to the Doctor".needTranslation;
}
return "";
}
+ static bool isDateToday(DateTime dateToCheck) {
+ final DateTime now = DateTime.now();
+ final DateTime today = DateTime(now.year, now.month, now.day);
+ final DateTime checkDate = DateTime(dateToCheck.year, dateToCheck.month, dateToCheck.day);
+
+ return checkDate == today;
+ }
}
diff --git a/lib/extensions/int_extensions.dart b/lib/extensions/int_extensions.dart
index 80b3171..75460c5 100644
--- a/lib/extensions/int_extensions.dart
+++ b/lib/extensions/int_extensions.dart
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
-import 'package:hmg_patient_app_new/theme/colors.dart';
extension IntExtensions on int {
Widget get height => SizedBox(height: toDouble());
diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart
index 2039fb8..309dde1 100644
--- a/lib/extensions/string_extensions.dart
+++ b/lib/extensions/string_extensions.dart
@@ -23,14 +23,15 @@ extension CapExtension on String {
extension EmailValidator on String {
Widget get toWidget => Text(this);
- Widget toText8({Color? color, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text(
+ Widget toText8({Color? color, FontWeight? fontWeight, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) =>
+ Text(
this,
maxLines: maxlines,
overflow: textOverflow,
style: TextStyle(
fontSize: 8.f,
fontStyle: fontStyle ?? FontStyle.normal,
- fontWeight: isBold ? FontWeight.bold : FontWeight.normal,
+ fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,
letterSpacing: 0,
),
@@ -41,7 +42,7 @@ extension EmailValidator on String {
FontWeight? weight,
bool isBold = false,
bool isUnderLine = false,
- bool isCenter = false,
+ bool isCenter = false,
int? maxlines,
FontStyle? fontStyle,
TextOverflow? textOverflow,
@@ -191,7 +192,8 @@ extension EmailValidator on String {
letterSpacing: letterSpacing,
height: height,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
- decoration: isUnderLine ? TextDecoration.underline : null),
+ decoration: isUnderLine ? TextDecoration.underline : null,
+ decorationColor: color ?? AppColors.blackColor),
);
Widget toText15(
@@ -214,39 +216,38 @@ extension EmailValidator on String {
decoration: isUnderLine ? TextDecoration.underline : null),
);
- Widget toText16({
- Color? color,
- bool isUnderLine = false,
- bool isBold = false,
- bool isCenter = false,
- int? maxlines,
- double? height,
- TextAlign? textAlign,
- FontWeight? weight,
- TextOverflow? textOverflow,
- double? letterSpacing = -0.4,
- Color decorationColor =AppColors.errorColor
- }) =>
+ Widget toText16(
+ {Color? color,
+ bool isUnderLine = false,
+ bool isBold = false,
+ bool isCenter = false,
+ int? maxlines,
+ double? height,
+ TextAlign? textAlign,
+ FontWeight? weight,
+ TextOverflow? textOverflow,
+ double? letterSpacing = -0.4,
+ Color decorationColor = AppColors.errorColor}) =>
Text(
this,
maxLines: maxlines,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
- color: color ?? AppColors.blackColor,
- fontSize: 16.f,
- letterSpacing: letterSpacing,
- height: height,
- overflow: textOverflow,
- fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
- decoration: isUnderLine ? TextDecoration.underline : null,
- decorationColor: decorationColor
- ),
+ color: color ?? AppColors.blackColor,
+ fontSize: 16.f,
+ letterSpacing: letterSpacing,
+ height: height,
+ overflow: textOverflow,
+ fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
+ decoration: isUnderLine ? TextDecoration.underline : null,
+ decorationColor: decorationColor),
);
Widget toText17({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
- style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 17.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ style: TextStyle(
+ color: color ?? AppColors.blackColor, fontSize: 17.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toText18({Color? color, FontWeight? weight, bool isBold = false, bool isCenter = false, int? maxlines, TextOverflow? textOverflow}) => Text(
@@ -255,39 +256,62 @@ extension EmailValidator on String {
this,
overflow: textOverflow,
style: TextStyle(
- fontSize: 18.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4),
+ fontSize: 18.f,
+ fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
+ color: color ?? AppColors.blackColor,
+ letterSpacing: -0.4),
);
Widget toText19({Color? color, bool isBold = false}) => Text(
this,
- style: TextStyle(fontSize: 19.f, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
+ style: TextStyle(
+ fontSize: 19.f, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
);
- Widget toText20({Color? color, FontWeight? weight, bool isBold = false, }) => Text(
+ Widget toText20({
+ Color? color,
+ FontWeight? weight,
+ bool isBold = false,
+ }) =>
+ Text(
this,
style: TextStyle(
- fontSize: 20.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4),
+ fontSize: 20.f,
+ fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
+ color: color ?? AppColors.blackColor,
+ letterSpacing: -0.4),
);
Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text(
this,
maxLines: maxlines,
style: TextStyle(
- color: color ?? AppColors.blackColor, fontSize: 21.f, letterSpacing: -1, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
+ color: color ?? AppColors.blackColor,
+ fontSize: 21.f,
+ letterSpacing: -1,
+ fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
);
Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
- height: 1, color: color ?? AppColors.blackColor, fontSize: 22.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ height: 1,
+ color: color ?? AppColors.blackColor,
+ fontSize: 22.f,
+ letterSpacing: -1,
+ fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight, double? letterSpacing}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
- height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: letterSpacing??-1, fontWeight: isBold ? FontWeight.bold : fontWeight??FontWeight.normal),
+ height: 23 / 24,
+ color: color ?? AppColors.blackColor,
+ fontSize: 24.f,
+ letterSpacing: letterSpacing ?? -1,
+ fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.normal),
);
Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing}) => Text(
@@ -312,17 +336,25 @@ extension EmailValidator on String {
fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
- Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text(
+ Widget toText32({FontWeight? weight, Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
- height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ height: 32 / 32,
+ color: color ?? AppColors.blackColor,
+ fontSize: 32.f,
+ letterSpacing: -1,
+ fontWeight: isBold ? FontWeight.bold : weight ?? FontWeight.normal),
);
Widget toText44({Color? color, bool isBold = false}) => Text(
this,
style: TextStyle(
- height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ height: 32 / 32,
+ color: color ?? AppColors.blackColor,
+ fontSize: 44.f,
+ letterSpacing: -1,
+ fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) {
diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart
index c9796e8..9b09c43 100644
--- a/lib/features/authentication/authentication_repo.dart
+++ b/lib/features/authentication/authentication_repo.dart
@@ -5,10 +5,8 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
-import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
-import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_activation_code_register_request_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
@@ -262,10 +260,10 @@ class AuthenticationRepoImp implements AuthenticationRepo {
newRequest.forRegisteration = newRequest.isRegister ?? false;
newRequest.isRegister = false;
//silent login case removed token and login token
- // if(newRequest.logInTokenID.isEmpty && newRequest.isSilentLogin == true) {
- // newRequest.logInTokenID = null;
- // newRequest.deviceToken = null;
- // }
+ if(newRequest.logInTokenID.isEmpty && newRequest.isSilentLogin == true && (newRequest.loginType==1 || newRequest.loginType==4)) {
+ newRequest.logInTokenID = null;
+ newRequest.deviceToken = null;
+ }
}
diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart
index fa16423..b393772 100644
--- a/lib/features/authentication/authentication_view_model.dart
+++ b/lib/features/authentication/authentication_view_model.dart
@@ -27,12 +27,12 @@ import 'package:hmg_patient_app_new/features/authentication/models/resp_models/a
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_activation_code_resp_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_user_staus_nhic_response_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
-import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/authentication/saved_login_screen.dart';
+import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
@@ -40,6 +40,7 @@ import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
+import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart';
import 'models/request_models/get_user_mobile_device_data.dart';
@@ -566,7 +567,7 @@ class AuthenticationViewModel extends ChangeNotifier {
if (!_appState.getIsChildLoggedIn) {
await medicalVm.getFamilyFiles(status: 0);
await medicalVm.getAllPendingRecordsByResponseId();
- _navigationService.popUntilNamed(AppRoutes.landingScreen);
+ _navigationService.replaceAllRoutesAndNavigateToLanding();
}
} else {
if (activation.list != null && activation.list!.isNotEmpty) {
@@ -586,6 +587,7 @@ class AuthenticationViewModel extends ChangeNotifier {
activation.list!.first.bloodGroup = activation.patientBlodType;
_appState.setAuthenticatedUser(activation.list!.first);
_appState.setPrivilegeModelList(activation.list!.first.listPrivilege!);
+ _appState.setUserBloodGroup = activation.patientBlodType ?? "N/A";
}
// _appState.setUserBloodGroup = (activation.patientBlodType ?? "");
_appState.setAppAuthToken = activation.authenticationTokenId;
@@ -675,7 +677,12 @@ class AuthenticationViewModel extends ChangeNotifier {
}
Future navigateToHomeScreen() async {
- _navigationService.pushAndReplace(AppRoutes.landingScreen);
+ Navigator.pushAndRemoveUntil(
+ _navigationService.navigatorKey.currentContext!,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
}
Future navigateToOTPScreen(
diff --git a/lib/features/blood_donation/blood_donation_repo.dart b/lib/features/blood_donation/blood_donation_repo.dart
index 84997b2..5643635 100644
--- a/lib/features/blood_donation/blood_donation_repo.dart
+++ b/lib/features/blood_donation/blood_donation_repo.dart
@@ -3,14 +3,26 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_hospitals_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_response_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/models/cities_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class BloodDonationRepo {
Future>>> getAllCities();
+ Future>>> getProjectList();
+
+ Future>>> getBloodDonationProjectsList();
+
Future>> getPatientBloodGroupDetails();
+
+ Future>> updateBloodGroup({required Map request});
+
+ Future>> getFreeBloodDonationSlots({required Map request});
+
+ Future>> addUserAgreementForBloodDonation({required Map request});
}
class BloodDonationRepoImp implements BloodDonationRepo {
@@ -66,6 +78,7 @@ class BloodDonationRepoImp implements BloodDonationRepo {
await apiClient.post(
GET_BLOOD_REQUEST,
body: mapDevice,
+ isAllowAny: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
@@ -92,4 +105,186 @@ class BloodDonationRepoImp implements BloodDonationRepo {
return Left(UnknownFailure(e.toString()));
}
}
-}
\ No newline at end of file
+
+ @override
+ Future>>> getProjectList() async {
+ Map request = {};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_PROJECT_LIST,
+ body: request,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final list = response['ListProject'];
+
+ final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: appointmentsList,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>>> getBloodDonationProjectsList() async {
+ Map request = {};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ ApiConsts.getProjectsHaveBDClinics,
+ body: request,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final listData = (response['BD_getProjectsHaveBDClinics'] as List);
+ final list = listData.map((item) => BdGetProjectsHaveBdClinic.fromJson(item as Map)).toList();
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: list,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>>> updateBloodGroup({required Map request}) async {
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ ApiConsts.bloodGroupUpdate,
+ body: request,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ // final list = response['ListProject'];
+
+ // final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: response,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>>> getFreeBloodDonationSlots({required Map request}) async {
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ ApiConsts.getClinicsBDFreeSlots,
+ body: request,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ // final list = response['ListProject'];
+
+ // final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: response,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>>> addUserAgreementForBloodDonation({required Map request}) async {
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ ApiConsts.userAgreementForBloodGroupUpdate,
+ body: request,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ // final list = response['ListProject'];
+
+ // final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: response,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+}
diff --git a/lib/features/blood_donation/blood_donation_view_model.dart b/lib/features/blood_donation/blood_donation_view_model.dart
index f345359..8325cf7 100644
--- a/lib/features/blood_donation/blood_donation_view_model.dart
+++ b/lib/features/blood_donation/blood_donation_view_model.dart
@@ -1,14 +1,25 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
+import 'package:hmg_patient_app_new/core/utils/doctor_response_mapper.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_repo.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_hospitals_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_list_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_response_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/models/cities_model.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
+import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
class BloodDonationViewModel extends ChangeNotifier {
final DialogService dialogService;
@@ -16,8 +27,21 @@ class BloodDonationViewModel extends ChangeNotifier {
ErrorHandlerService errorHandlerService;
final NavigationService navigationService;
final AppState appState;
+ bool isTermsAccepted = false;
+ BdGetProjectsHaveBdClinic? selectedHospital;
+ CitiesModel? selectedCity;
+ BloodGroupListModel? selectedBloodGroup;
+ int _selectedHospitalIndex = 0;
+ int _selectedBloodTypeIndex = 0;
+ GenderTypeEnum? selectedGender;
+ String? selectedBloodType;
+ final NavigationService navServices;
+
+ List hospitalList = [];
List citiesList = [];
+ List_BloodGroupDetailsModel patientBloodGroupDetailsModel = List_BloodGroupDetailsModel();
+
List bloodGroupList = [
BloodGroupListModel("O+", 0),
BloodGroupListModel("O-", 1),
@@ -29,35 +53,34 @@ class BloodDonationViewModel extends ChangeNotifier {
BloodGroupListModel("B-", 7),
];
- List genderList = [
- BloodGroupListModel(LocaleKeys.malE.tr(), 1),
- BloodGroupListModel(LocaleKeys.female.tr(), 2),
- ];
-
- late CitiesModel selectedCity;
- late BloodGroupListModel selectedBloodGroup;
- int _selectedHospitalIndex = 0;
- int _selectedBloodTypeIndex = 0;
- String selectedBloodType = '';
-
- List_BloodGroupDetailsModel patientBloodGroupDetailsModel = List_BloodGroupDetailsModel();
-
- BloodDonationViewModel({required this.bloodDonationRepo, required this.errorHandlerService, required this.navigationService, required this.dialogService, required this.appState});
+ BloodDonationViewModel({
+ required this.bloodDonationRepo,
+ required this.errorHandlerService,
+ required this.navigationService,
+ required this.dialogService,
+ required this.appState,
+ required this.navServices,
+ });
setSelectedCity(CitiesModel city) {
selectedCity = city;
notifyListeners();
}
+ void onGenderChange(String? status) {
+ selectedGender = GenderTypeExtension.fromType(status)!;
+ notifyListeners();
+ }
+
setSelectedBloodGroup(BloodGroupListModel bloodGroup) {
selectedBloodGroup = bloodGroup;
- selectedBloodType = selectedBloodGroup.name;
+ selectedBloodType = selectedBloodGroup!.name;
notifyListeners();
}
Future getRegionSelectedClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
citiesList.clear();
- selectedCity = CitiesModel();
+ selectedCity = null;
notifyListeners();
final result = await bloodDonationRepo.getAllCities();
@@ -70,6 +93,7 @@ class BloodDonationViewModel extends ChangeNotifier {
onError!(apiResponse.errorMessage ?? 'An unexpected error occurred');
} else if (apiResponse.messageStatus == 1) {
citiesList = apiResponse.data!;
+ citiesList.sort((a, b) => a.description!.compareTo(b.description!));
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@@ -99,7 +123,7 @@ class BloodDonationViewModel extends ChangeNotifier {
citiesModel.descriptionN = citiesList[_selectedHospitalIndex].descriptionN;
selectedCity = citiesModel;
selectedBloodType = patientBloodGroupDetailsModel.bloodGroup!;
- _selectedBloodTypeIndex = getBloodIndex(selectedBloodType);
+ _selectedBloodTypeIndex = getBloodIndex(selectedBloodType ?? '');
notifyListeners();
if (onSuccess != null) {
@@ -112,11 +136,11 @@ class BloodDonationViewModel extends ChangeNotifier {
int getSelectedCityID() {
int cityID = 1;
- citiesList.forEach((element) {
+ for (var element in citiesList) {
if (element.description == patientBloodGroupDetailsModel.city) {
cityID = element.iD!;
}
- });
+ }
return cityID;
}
@@ -143,4 +167,120 @@ class BloodDonationViewModel extends ChangeNotifier {
return 0;
}
}
+
+ void onTermAccepted() {
+ isTermsAccepted = !isTermsAccepted;
+ notifyListeners();
+ }
+
+ bool isUserAuthanticated() {
+ print("the app state is ${appState.isAuthenticated}");
+ if (!appState.isAuthenticated) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ Future fetchHospitalsList() async {
+ // hospitalList.clear();
+ notifyListeners();
+ final result = await bloodDonationRepo.getBloodDonationProjectsList();
+
+ result.fold(
+ (failure) async => await errorHandlerService.handleError(failure: failure),
+ (apiResponse) async {
+ if (apiResponse.messageStatus == 2) {
+ } else if (apiResponse.messageStatus == 1) {
+ hospitalList = apiResponse.data!;
+ hospitalList.sort((a, b) => a.projectName!.compareTo(b.projectName!));
+ notifyListeners();
+ }
+ },
+ );
+ }
+
+ Future getFreeBloodDonationSlots({required Map request}) async {
+ final result = await bloodDonationRepo.getFreeBloodDonationSlots(request: request);
+
+ result.fold(
+ (failure) async => await errorHandlerService.handleError(failure: failure),
+ (apiResponse) async {
+ if (apiResponse.messageStatus == 2) {
+ } else if (apiResponse.messageStatus == 1) {
+ // TODO: Handle free slots data
+ print(apiResponse.data['BD_FreeSlots']);
+ notifyListeners();
+ }
+ },
+ );
+ }
+
+ bool isLocationEnabled() {
+ return appState.userLong != 0.0 && appState.userLong != 0.0;
+ }
+
+ setSelectedHospital(BdGetProjectsHaveBdClinic hospital) {
+ selectedHospital = hospital;
+ notifyListeners();
+ }
+
+ Future validateSelections() async {
+ if (selectedCity == null) {
+ await dialogService.showErrorBottomSheet(
+ message: "Please choose city",
+ );
+ return false;
+ }
+
+ if (selectedBloodGroup == null) {
+ await dialogService.showErrorBottomSheet(
+ message: "Please choose Gender",
+ );
+ return false;
+ }
+
+ if (selectedBloodType == null) {
+ await dialogService.showErrorBottomSheet(
+ message: "Please choose Blood Group",
+ );
+ return false;
+ }
+
+ if (!isTermsAccepted) {
+ await dialogService.showErrorBottomSheet(
+ message: "Please accept Terms and Conditions to continue",
+ );
+ return false;
+ }
+ return true;
+ }
+
+ Future updateBloodGroup() async {
+ LoaderBottomSheet.showLoader();
+ // body['City'] = detailsModel.city;
+ // body['cityCode'] = detailsModel.cityCode;
+ // body['Gender'] = detailsModel.gender;
+ // body['BloodGroup'] = detailsModel.bloodGroup;
+ // body['CellNumber'] = user.mobileNumber;
+ // body['LanguageID'] = languageID;
+ // body['NationalID'] = user.nationalityID;
+ // body['ZipCode'] = user.zipCode ?? "+966";
+ // body['isDentalAllowedBackend'] = false;
+ Map payload = {
+ "City": selectedCity?.description,
+ "cityCode": selectedCity?.iD,
+ "Gender": selectedGender?.value,
+ "isDentalAllowedBackend": false
+ // "Gender": selectedGender?.value,
+ };
+ await bloodDonationRepo.updateBloodGroup(request: payload);
+ await addUserAgreementForBloodDonation();
+ LoaderBottomSheet.hideLoader();
+ }
+
+ Future addUserAgreementForBloodDonation() async {
+ Map payload = {"IsAgreed": true};
+ await bloodDonationRepo.addUserAgreementForBloodDonation(request: payload);
+ }
}
diff --git a/lib/features/blood_donation/models/blood_group_hospitals_model.dart b/lib/features/blood_donation/models/blood_group_hospitals_model.dart
new file mode 100644
index 0000000..10b4e67
--- /dev/null
+++ b/lib/features/blood_donation/models/blood_group_hospitals_model.dart
@@ -0,0 +1,81 @@
+import 'dart:convert';
+
+class BdProjectsHaveBdClinicsModel {
+ List? bdGetProjectsHaveBdClinics;
+
+ BdProjectsHaveBdClinicsModel({
+ this.bdGetProjectsHaveBdClinics,
+ });
+
+ factory BdProjectsHaveBdClinicsModel.fromRawJson(String str) => BdProjectsHaveBdClinicsModel.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory BdProjectsHaveBdClinicsModel.fromJson(Map json) => BdProjectsHaveBdClinicsModel(
+ bdGetProjectsHaveBdClinics: json["BD_getProjectsHaveBDClinics"] == null ? [] : List.from(json["BD_getProjectsHaveBDClinics"]!.map((x) => BdGetProjectsHaveBdClinic.fromJson(x))),
+ );
+
+ Map toJson() => {
+ "BD_getProjectsHaveBDClinics": bdGetProjectsHaveBdClinics == null ? [] : List.from(bdGetProjectsHaveBdClinics!.map((x) => x.toJson())),
+ };
+}
+
+class BdGetProjectsHaveBdClinic {
+ int? rowId;
+ int? id;
+ int? projectId;
+ int? numberOfRooms;
+ bool? isActive;
+ int? createdBy;
+ String? createdOn;
+ dynamic editedBy;
+ dynamic editedOn;
+ String? projectName;
+ dynamic projectNameN;
+
+ BdGetProjectsHaveBdClinic({
+ this.rowId,
+ this.id,
+ this.projectId,
+ this.numberOfRooms,
+ this.isActive,
+ this.createdBy,
+ this.createdOn,
+ this.editedBy,
+ this.editedOn,
+ this.projectName,
+ this.projectNameN,
+ });
+
+ factory BdGetProjectsHaveBdClinic.fromRawJson(String str) => BdGetProjectsHaveBdClinic.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory BdGetProjectsHaveBdClinic.fromJson(Map json) => BdGetProjectsHaveBdClinic(
+ rowId: json["RowID"],
+ id: json["ID"],
+ projectId: json["ProjectID"],
+ numberOfRooms: json["NumberOfRooms"],
+ isActive: json["IsActive"],
+ createdBy: json["CreatedBy"],
+ createdOn: json["CreatedOn"],
+ editedBy: json["EditedBy"],
+ editedOn: json["EditedON"],
+ projectName: json["ProjectName"],
+ projectNameN: json["ProjectNameN"],
+ );
+
+ Map toJson() => {
+ "RowID": rowId,
+ "ID": id,
+ "ProjectID": projectId,
+ "NumberOfRooms": numberOfRooms,
+ "IsActive": isActive,
+ "CreatedBy": createdBy,
+ "CreatedOn": createdOn,
+ "EditedBy": editedBy,
+ "EditedON": editedOn,
+ "ProjectName": projectName,
+ "ProjectNameN": projectNameN,
+ };
+}
diff --git a/lib/features/blood_donation/widgets/hospital_selection.dart b/lib/features/blood_donation/widgets/hospital_selection.dart
new file mode 100644
index 0000000..288ac34
--- /dev/null
+++ b/lib/features/blood_donation/widgets/hospital_selection.dart
@@ -0,0 +1,86 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_hospitals_model.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
+import 'package:provider/provider.dart';
+
+class HospitalBottomSheetBodySelection extends StatelessWidget {
+ final Function(BdGetProjectsHaveBdClinic userSelection) onUserHospitalSelection;
+
+ const HospitalBottomSheetBodySelection({super.key, required this.onUserHospitalSelection(BdGetProjectsHaveBdClinic userSelection)});
+
+ @override
+ Widget build(BuildContext context) {
+ final bloodDonationVm = Provider.of(context, listen: false);
+ AppState appState = getIt.get();
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Please select the hospital you want to make an appointment.".needTranslation,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ color: AppColors.greyTextColor,
+ ),
+ ),
+ SizedBox(height: 16.h),
+ SizedBox(
+ height: MediaQuery.sizeOf(context).height * .4,
+ child: ListView.separated(
+ itemBuilder: (_, index) {
+ return DecoratedBox(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ spacing: 8.h,
+ children: [
+ hospitalName(bloodDonationVm.hospitalList[index]).onPress(() {
+ onUserHospitalSelection(bloodDonationVm.hospitalList[index]);
+ Navigator.of(context).pop();
+ })
+ ],
+ ),
+ ),
+ Transform.flip(
+ flipX: appState.isArabic(),
+ child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.blackColor, width: 40.h, height: 40.h, fit: BoxFit.contain),
+ ),
+ ],
+ ).paddingSymmetrical(16.h, 16.h),
+ ).onPress(() {
+ bloodDonationVm.setSelectedHospital(bloodDonationVm.hospitalList[index]);
+ Navigator.of(context).pop();
+ });
+ },
+ separatorBuilder: (_, __) => SizedBox(height: 16.h),
+ itemCount: bloodDonationVm.hospitalList.length),
+ )
+ ],
+ );
+ }
+
+ Widget hospitalName(dynamic hospital) => Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.hmg).paddingOnly(right: 10),
+ Expanded(
+ child: Text(hospital.projectName ?? "", style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16, color: AppColors.blackColor)),
+ )
+ ],
+ );
+}
diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart
index 3683d57..cfd473e 100644
--- a/lib/features/book_appointments/book_appointments_repo.dart
+++ b/lib/features/book_appointments/book_appointments_repo.dart
@@ -1,4 +1,3 @@
-import 'dart:io';
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
@@ -6,6 +5,7 @@ import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
@@ -26,9 +26,12 @@ abstract class BookAppointmentsRepo {
Future>>> getDoctorsList(int clinicID, int projectID, bool isNearest, int doctorId, String doctorName, {isContinueDentalPlan = false});
+ Future>>> getDoctorsListByHealthCal(int calculationID);
+
Future>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});
- Future>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError});
+ Future>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,
+ {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError});
Future>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
@@ -83,8 +86,7 @@ abstract class BookAppointmentsRepo {
Future>>> getDentalChiefComplaintDoctorsList(int projectID, int chiefComplaintID,
{Function(dynamic)? onSuccess, Function(String)? onError});
- Future>>> getLaserClinics(int laserCategoryID, int projectID, int languageID,
- {Function(dynamic)? onSuccess, Function(String)? onError});
+ Future>>> getLaserClinics(int laserCategoryID, int projectID, int languageID, {Function(dynamic)? onSuccess, Function(String)? onError});
Future>> checkScannedNFCAndQRCode(String nfcCode, int projectId, {Function(dynamic)? onSuccess, Function(String)? onError});
@@ -101,6 +103,8 @@ abstract class BookAppointmentsRepo {
required int userAge,
Function(dynamic)? onSuccess,
Function(String)? onError});
+
+ Future>> getAppointmentNearestGate({required int projectID, required int clinicID});
}
class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@@ -206,6 +210,45 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
}
}
+ @override
+ Future>>> getDoctorsListByHealthCal(int calculationID, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ Map mapDevice = {"CalculationID": calculationID};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_DOCTOR_LIST_CALCULATION,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ onError!(error);
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final list = response['List_CalculationTable'];
+
+ final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: doctorsList,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
@override
Future>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map mapDevice = {
@@ -824,7 +867,8 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
}
@override
- Future>>> getLaserClinics(int laserCategoryID, int projectID, int languageID, {Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async {
+ Future>>> getLaserClinics(int laserCategoryID, int projectID, int languageID,
+ {Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async {
Map mapDevice = {
"LaserCategoryID": laserCategoryID,
"ProjectID": projectID,
@@ -1005,4 +1049,40 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
+
+ @override
+ Future>> getAppointmentNearestGate({required int projectID, required int clinicID}) async {
+ Map mapRequest = {"ProjectID": projectID, "ClinicID": clinicID};
+
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_APPOINTMENT_NEAREST_GATE,
+ body: mapRequest,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final nearestGateResponse = AppointmentNearestGateResponseModel.fromJson(response['getGateByProjectIDandClinicIDList'][0]);
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: nearestGateResponse,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
}
diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart
index d96cb4f..380d2db 100644
--- a/lib/features/book_appointments/book_appointments_view_model.dart
+++ b/lib/features/book_appointments/book_appointments_view_model.dart
@@ -14,6 +14,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/LaserCategoryType.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
@@ -44,11 +45,18 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isDoctorsListLoading = false;
bool isDoctorProfileLoading = false;
bool isDoctorSearchByNameStarted = false;
+ bool isAppointmentNearestGateLoading = false;
bool isLiveCareSchedule = false;
+ bool isGetDocForHealthCal = false;
+ bool showSortFilterButtons = false;
+ int? calculationID = 0;
+ bool isSortByClinic = true;
int initialSlotDuration = 0;
+ bool isNearestAppointmentSelected = false;
+
LocationUtils locationUtils;
List clinicsList = [];
@@ -61,6 +69,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
List doctorsList = [];
List filteredDoctorList = [];
+ // Grouped doctors lists
+ List> doctorsListByClinic = [];
+ List> doctorsListByHospital = [];
+ List> doctorsListGrouped = [];
+
List liveCareDoctorsList = [];
List patientDentalPlanEstimationList = [];
@@ -122,6 +135,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
PatientAppointmentShareResponseModel? patientWalkInAppointmentShareResponseModel;
+ AppointmentNearestGateResponseModel? appointmentNearestGateResponseModel;
+
///variables for laser clinic
List femaleLaserCategory = [
LaserCategoryType(1, 'bodyString'),
@@ -143,6 +158,31 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isBodyPartsLoading = false;
int duration = 0;
+
+ setIsSortByClinic(bool value) {
+ isSortByClinic = value;
+ doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
+ notifyListeners();
+ }
+
+ // Group doctors by clinic and hospital
+ void _groupDoctorsList() {
+ final clinicMap = >{};
+ final hospitalMap = >{};
+
+ for (var doctor in doctorsList) {
+ final clinicKey = (doctor.clinicName ?? 'Unknown').trim();
+ clinicMap.putIfAbsent(clinicKey, () => []).add(doctor);
+
+ final hospitalKey = (doctor.projectName ?? 'Unknown').trim();
+ hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor);
+ }
+
+ doctorsListByClinic = clinicMap.values.toList();
+ doctorsListByHospital = hospitalMap.values.toList();
+ doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
+ }
+
BookAppointmentsViewModel(
{required this.bookAppointmentsRepo,
required this.errorHandlerService,
@@ -161,8 +201,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
void filterClinics(String? query) {
if (query!.isEmpty) {
_filteredClinicsList = List.from(clinicsList);
+ showSortFilterButtons = false;
} else {
_filteredClinicsList = clinicsList.where((clinic) => clinic.clinicDescription?.toLowerCase().contains(query!.toLowerCase()) ?? false).toList();
+ showSortFilterButtons = query.length >= 3;
}
notifyListeners();
}
@@ -187,6 +229,18 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
+ setIsNearestAppointmentSelected(bool isNearestAppointmentSelected) {
+ this.isNearestAppointmentSelected = isNearestAppointmentSelected;
+
+ if (isNearestAppointmentSelected) {
+ doctorsList.sort((a, b) => DateUtil.convertStringToDate(a.nearestFreeSlot!).compareTo(DateUtil.convertStringToDate(b.nearestFreeSlot!)));
+ } else {
+ doctorsList.sort((a, b) => b.decimalDoctorRate!.compareTo(a.decimalDoctorRate!));
+ }
+
+ notifyListeners();
+ }
+
setIsWaitingAppointmentSelected(bool isWaitingAppointmentSelected) {
this.isWaitingAppointmentSelected = isWaitingAppointmentSelected;
notifyListeners();
@@ -280,6 +334,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
void onTabChanged(int index) {
+ calculationID = null;
+ isGetDocForHealthCal = false;
selectedTabIndex = index;
notifyListeners();
}
@@ -319,8 +375,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future getLiveCareScheduleClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
liveCareClinicsList.clear();
- final result =
- await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);
+ final result = await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
@@ -342,9 +397,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
- final result = await bookAppointmentsRepo.getLiveCareDoctorsList(
- selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!,
- onError: onError);
+ final result =
+ await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError);
result.fold(
(failure) async {
@@ -367,11 +421,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
//TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
- Future getDoctorsList(
- {int projectID = 0, bool isNearest = true, int doctorId = 0,
- String doctorName = "",
- Function(dynamic)? onSuccess,
- Function(String)? onError}) async {
+ Future getDoctorsList({int projectID = 0, bool isNearest = true, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
final result =
@@ -391,9 +441,43 @@ class BookAppointmentsViewModel extends ChangeNotifier {
doctorsList = apiResponse.data!;
filteredDoctorList = doctorsList;
isDoctorsListLoading = false;
+ doctorsList.sort((a, b) => b.decimalDoctorRate!.compareTo(a.decimalDoctorRate!));
initializeFilteredList();
clearSearchFilters();
getFiltersFromDoctorList();
+ _groupDoctorsList();
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
+ //TODO: GetDockets & Calculations For Health Calculator
+ Future getDoctorsListByHealthCal({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ doctorsList.clear();
+ final result = await bookAppointmentsRepo.getDoctorsListByHealthCal(calculationID!);
+ result.fold(
+ (failure) async {
+ isDoctorsListLoading = false;
+ if (onError != null) onError("No doctors found for the search criteria".needTranslation);
+
+ notifyListeners();
+ },
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ doctorsList = apiResponse.data!;
+ setIsSortByClinic(true);
+ filteredDoctorList = doctorsList;
+ isDoctorsListLoading = false;
+ initializeFilteredList();
+ clearSearchFilters();
+ getFiltersFromDoctorList();
+ _groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@@ -404,13 +488,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
Future getMappedDoctors(
- {int projectID = 0,
- bool isNearest = false,
- int doctorId = 0,
- String doctorName = "",
- isContinueDentalPlan = false,
- Function(dynamic)? onSuccess,
- Function(String)? onError}) async {
+ {int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
filteredHospitalList = null;
hospitalList = null;
isRegionListLoading = true;
@@ -446,8 +524,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
Future getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async {
- final result = await bookAppointmentsRepo
- .getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError);
+ final result = await bookAppointmentsRepo.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError);
result.fold(
(failure) async {},
@@ -506,8 +583,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// :
date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
slotsList.add(FreeSlot(date, ['slot']));
- docFreeSlots.add(TimeSlot(
- isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
+ docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
});
notifyListeners();
@@ -526,8 +602,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
Map _eventsParsed;
- final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots(selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0,
- selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare,
+ final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots(
+ selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare,
onError: onError);
result.fold(
@@ -551,8 +627,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// :
date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
slotsList.add(FreeSlot(date, ['slot']));
- docFreeSlots.add(TimeSlot(
- isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
+ docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
});
notifyListeners();
@@ -564,10 +639,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
);
}
- Future cancelAppointment(
- {required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
- Function(dynamic)? onSuccess,
- Function(String)? onError}) async {
+ Future cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);
result.fold(
@@ -651,15 +723,13 @@ class BookAppointmentsViewModel extends ChangeNotifier {
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
- LoadingUtils.showFullScreenLoader(
- barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
+ LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
- LoadingUtils.showFullScreenLoader(
- barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
+ LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
@@ -749,15 +819,13 @@ class BookAppointmentsViewModel extends ChangeNotifier {
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
- LoadingUtils.showFullScreenLoader(
- barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
+ LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
- LoadingUtils.showFullScreenLoader(
- barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
+ LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
@@ -831,9 +899,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} else {
filteredHospitalList = RegionList();
- var list = isHMG
- ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList
- : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
+ var list = isHMG ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
if (list != null && list.isEmpty) {
notifyListeners();
@@ -916,8 +982,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
- void setSelections(List? selectedFacilityForFilters, List? selectedRegionForFilters, String? selectedClinicForFilters,
- PatientDoctorAppointmentList? selectedHospitalForFilters, bool applyFilters) {
+ void setSelections(
+ List? selectedFacilityForFilters, List? selectedRegionForFilters, String? selectedClinicForFilters, PatientDoctorAppointmentList? selectedHospitalForFilters, bool applyFilters) {
this.selectedFacilityForFilters = selectedFacilityForFilters;
this.selectedClinicForFilters = selectedClinicForFilters;
this.selectedHospitalForFilters = selectedHospitalForFilters;
@@ -985,15 +1051,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
List getDoctorListAsPerSelection() {
if (!applyFilters) return doctorsList;
- if ((selectedRegionForFilters?.isEmpty == true) &&
- (selectedFacilityForFilters?.isEmpty == true) &&
- selectedClinicForFilters == null &&
- selectedHospitalForFilters == null) {
+ if ((selectedRegionForFilters?.isEmpty == true) && (selectedFacilityForFilters?.isEmpty == true) && selectedClinicForFilters == null && selectedHospitalForFilters == null) {
return doctorsList;
}
var list = doctorsList.where((element) {
- var isInSelectedRegion =
- (selectedRegionForFilters?.isEmpty == true) ? true : selectedRegionForFilters?.any((region) => region == element.getRegionName(isArabic()));
+ var isInSelectedRegion = (selectedRegionForFilters?.isEmpty == true) ? true : selectedRegionForFilters?.any((region) => region == element.getRegionName(isArabic()));
var shouldApplyFacilityFilter = (selectedFacilityForFilters?.isEmpty == true) ? false : true;
var isHMC = (selectedFacilityForFilters?.isEmpty == true) ? true : selectedFacilityForFilters?.any((item) => item.contains("hmc"));
var isInSelectedClinic = (selectedClinicForFilters == null) ? true : selectedClinicForFilters == element.clinicName;
@@ -1044,8 +1106,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
dentalChiefComplaintsList.clear();
notifyListeners();
int patientID = _appState.isAuthenticated ? _appState.getAuthenticatedUser()!.patientId ?? -1 : -1;
- final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(
- patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17);
+ final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
@@ -1084,6 +1145,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// initializeFilteredList();
// clearSearchFilters();
// getFiltersFromDoctorList();
+ _groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@@ -1288,4 +1350,32 @@ class BookAppointmentsViewModel extends ChangeNotifier {
},
);
}
+
+ Future getAppointmentNearestGate({required int projectID, required int clinicID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ isAppointmentNearestGateLoading = true;
+ notifyListeners();
+
+ final result = await bookAppointmentsRepo.getAppointmentNearestGate(projectID: projectID, clinicID: clinicID);
+
+ result.fold(
+ (failure) async {
+ if (onError != null) {
+ onError(failure.message);
+ }
+ },
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ onError!(apiResponse.errorMessage!);
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ appointmentNearestGateResponseModel = apiResponse.data!;
+ isAppointmentNearestGateLoading = false;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
}
diff --git a/lib/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart b/lib/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart
new file mode 100644
index 0000000..bdaa4e2
--- /dev/null
+++ b/lib/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart
@@ -0,0 +1,64 @@
+class AppointmentNearestGateResponseModel {
+ String? clinicDescription;
+ String? clinicDescriptionN;
+ int? clinicID;
+ String? clinicLocation;
+ String? clinicLocationN;
+ int? gender;
+ int? iD;
+ String? nearestGateNumber;
+ String? nearestGateNumberN;
+ int? projectID;
+ String? projectName;
+ String? projectNameN;
+ int? rowID;
+
+ AppointmentNearestGateResponseModel(
+ {this.clinicDescription,
+ this.clinicDescriptionN,
+ this.clinicID,
+ this.clinicLocation,
+ this.clinicLocationN,
+ this.gender,
+ this.iD,
+ this.nearestGateNumber,
+ this.nearestGateNumberN,
+ this.projectID,
+ this.projectName,
+ this.projectNameN,
+ this.rowID});
+
+ AppointmentNearestGateResponseModel.fromJson(Map json) {
+ clinicDescription = json['ClinicDescription'];
+ clinicDescriptionN = json['ClinicDescriptionN'];
+ clinicID = json['ClinicID'];
+ clinicLocation = json['ClinicLocation'];
+ clinicLocationN = json['ClinicLocationN'];
+ gender = json['Gender'];
+ iD = json['ID'];
+ nearestGateNumber = json['NearestGateNumber'];
+ nearestGateNumberN = json['NearestGateNumberN'];
+ projectID = json['ProjectID'];
+ projectName = json['ProjectName'];
+ projectNameN = json['ProjectNameN'];
+ rowID = json['RowID'];
+ }
+
+ Map toJson() {
+ final Map data = Map();
+ data['ClinicDescription'] = clinicDescription;
+ data['ClinicDescriptionN'] = clinicDescriptionN;
+ data['ClinicID'] = clinicID;
+ data['ClinicLocation'] = clinicLocation;
+ data['ClinicLocationN'] = clinicLocationN;
+ data['Gender'] = gender;
+ data['ID'] = iD;
+ data['NearestGateNumber'] = nearestGateNumber;
+ data['NearestGateNumberN'] = nearestGateNumberN;
+ data['ProjectID'] = projectID;
+ data['ProjectName'] = projectName;
+ data['ProjectNameN'] = projectNameN;
+ data['RowID'] = rowID;
+ return data;
+ }
+}
diff --git a/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart b/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart
index 5df6ed8..75ed122 100644
--- a/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart
+++ b/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart
@@ -65,75 +65,77 @@ class DoctorsListResponseModel {
String? regionID;
String? projectBottomName;
String? projectTopName;
+ int? calcID;
-
- DoctorsListResponseModel(
- {this.clinicID,
- this.clinicName,
- this.clinicNameN,
- this.doctorTitle,
- this.iD,
- this.name,
- this.projectID,
- this.projectName,
- this.actualDoctorRate,
- this.clinicRoomNo,
- this.date,
- this.dayName,
- this.decimalDoctorRate,
- this.doctorAvailability,
- this.doctorID,
- this.doctorImageURL,
- this.doctorMobileNumber,
- this.doctorProfile,
- this.doctorProfileInfo,
- this.doctorRate,
- this.doctorStarsRate,
- this.employmentType,
- this.gender,
- this.genderDescription,
- this.hISRegionId,
- this.isActive,
- this.isAllowWaitList,
- this.isAppointmentAllowed,
- this.isDoctorAllowVedioCall,
- this.isDoctorDummy,
- this.isDoctorHasPrePostImages,
- this.isHMC,
- this.isHmg,
- this.isLiveCare,
- this.latitude,
- this.longitude,
- this.nationalityFlagURL,
- this.nationalityID,
- this.nationalityName,
- this.nearestFreeSlot,
- this.noOfFreeSlotsAvailable,
- this.noOfPatientsRate,
- this.originalClinicID,
- this.personRate,
- this.projectDistanceInKiloMeters,
- this.projectNameBottom,
- this.projectNameTop,
- this.qR,
- this.qRString,
- this.rateNumber,
- this.regionName,
- this.regionNameN,
- this.serviceID,
- this.setupID,
- this.speciality,
- this.specialityN,
- this.transactionType,
- this.virtualEmploymentType,
- this.workingHours,
- this.vida3Id,
- this.region,
- this.regionArabic,
- this.regionEnglish,
- this.regionID,
- this.projectBottomName,
- this.projectTopName,});
+ DoctorsListResponseModel({
+ this.clinicID,
+ this.clinicName,
+ this.clinicNameN,
+ this.doctorTitle,
+ this.iD,
+ this.name,
+ this.projectID,
+ this.projectName,
+ this.actualDoctorRate,
+ this.clinicRoomNo,
+ this.date,
+ this.dayName,
+ this.decimalDoctorRate,
+ this.doctorAvailability,
+ this.doctorID,
+ this.doctorImageURL,
+ this.doctorMobileNumber,
+ this.doctorProfile,
+ this.doctorProfileInfo,
+ this.doctorRate,
+ this.doctorStarsRate,
+ this.employmentType,
+ this.gender,
+ this.genderDescription,
+ this.hISRegionId,
+ this.isActive,
+ this.isAllowWaitList,
+ this.isAppointmentAllowed,
+ this.isDoctorAllowVedioCall,
+ this.isDoctorDummy,
+ this.isDoctorHasPrePostImages,
+ this.isHMC,
+ this.isHmg,
+ this.isLiveCare,
+ this.latitude,
+ this.longitude,
+ this.nationalityFlagURL,
+ this.nationalityID,
+ this.nationalityName,
+ this.nearestFreeSlot,
+ this.noOfFreeSlotsAvailable,
+ this.noOfPatientsRate,
+ this.originalClinicID,
+ this.personRate,
+ this.projectDistanceInKiloMeters,
+ this.projectNameBottom,
+ this.projectNameTop,
+ this.qR,
+ this.qRString,
+ this.rateNumber,
+ this.regionName,
+ this.regionNameN,
+ this.serviceID,
+ this.setupID,
+ this.speciality,
+ this.specialityN,
+ this.transactionType,
+ this.virtualEmploymentType,
+ this.workingHours,
+ this.vida3Id,
+ this.region,
+ this.regionArabic,
+ this.regionEnglish,
+ this.regionID,
+ this.projectBottomName,
+ this.projectTopName,
+ this.calcID,
+ });
DoctorsListResponseModel.fromJson(Map json) {
clinicID = json['ClinicID'];
@@ -141,7 +143,7 @@ class DoctorsListResponseModel {
clinicNameN = json['ClinicNameN'];
doctorTitle = json['DoctorTitle'];
iD = json['ID'];
- name = json['Name'];
+ name = json['Name'] ?? json["DoctorName"];
projectID = json['ProjectID'];
projectName = json['ProjectName'];
actualDoctorRate = json['ActualDoctorRate'];
@@ -174,7 +176,7 @@ class DoctorsListResponseModel {
longitude = json['Longitude'];
nationalityFlagURL = json['NationalityFlagURL'];
nationalityID = json['NationalityID'];
- nationalityName = json['NationalityName'];
+ nationalityName = json['NationalityName'] ?? json["Nationality"];
nearestFreeSlot = json['NearestFreeSlot'];
noOfFreeSlotsAvailable = json['NoOfFreeSlotsAvailable'];
noOfPatientsRate = json['NoOfPatientsRate'];
@@ -200,6 +202,7 @@ class DoctorsListResponseModel {
regionEnglish = json['RegionName'];
projectBottomName = json['ProjectNameBottom'];
projectTopName = json['ProjectNameTop'];
+ calcID = json["CalcID"];
}
Map toJson() {
@@ -264,6 +267,7 @@ class DoctorsListResponseModel {
data['VirtualEmploymentType'] = this.virtualEmploymentType;
data['WorkingHours'] = this.workingHours;
data['vida3Id'] = this.vida3Id;
+ data['CalcID'] = this.calcID;
return data;
}
@@ -273,7 +277,8 @@ class DoctorsListResponseModel {
}
return regionEnglish;
}
- String getProjectCompleteName(){
+
+ String getProjectCompleteName() {
return "${this.projectTopName} ${this.projectBottomName}";
}
diff --git a/lib/features/health_trackers/health_trackers_repo.dart b/lib/features/health_trackers/health_trackers_repo.dart
new file mode 100644
index 0000000..2a64fc4
--- /dev/null
+++ b/lib/features/health_trackers/health_trackers_repo.dart
@@ -0,0 +1,873 @@
+import 'package:dartz/dartz.dart';
+import 'package:hmg_patient_app_new/core/api/api_client.dart';
+import 'package:hmg_patient_app_new/core/api_consts.dart';
+import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
+import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
+import 'package:hmg_patient_app_new/services/logger_service.dart';
+
+/// Progress types to request different ranges from the progress API.
+enum ProgressType { today, week, month }
+
+abstract class HealthTrackersRepo {
+ // ==================== BLOOD SUGAR (DIABETIC) ====================
+ /// Get blood sugar result averages (week, month, year).
+ Future>> getDiabeticResultAverage();
+
+ /// Get blood sugar results (week, month, year).
+ Future>> getDiabeticResults();
+
+ /// Add new blood sugar result.
+ Future>> addDiabeticResult({
+ required String bloodSugarDateChart,
+ required String bloodSugarResult,
+ required String diabeticUnit,
+ required int measuredTime,
+ });
+
+ /// Update existing blood sugar result.
+ Future>> updateDiabeticResult({
+ required DateTime month,
+ required DateTime hour,
+ required String bloodSugarResult,
+ required String diabeticUnit,
+ required int measuredTime,
+ required int lineItemNo,
+ });
+
+ /// Deactivate blood sugar record.
+ Future>> deactivateDiabeticStatus({
+ required int lineItemNo,
+ });
+
+ /// Send blood sugar report by email.
+ Future>> sendBloodSugarReportByEmail({
+ required String email,
+ });
+
+ // ==================== BLOOD PRESSURE ====================
+ /// Get blood pressure result averages (week, month, year).
+ Future>> getBloodPressureResultAverage();
+
+ /// Get blood pressure results (week, month, year).
+ Future>> getBloodPressureResults();
+
+ /// Add new blood pressure result.
+ Future