Merge branch 'master' into dev_aamir

pull/210/head
aamir-csol 5 days ago
commit aa17688de6

File diff suppressed because it is too large Load Diff

@ -7,6 +7,7 @@ plugins {
id("com.google.gms.google-services") version "4.4.1" // Add the version here
id("dev.flutter.flutter-gradle-plugin")
id("com.huawei.agconnect")
id("kotlin-parcelize")
// id("com.mapbox.gradle.application")
// id("com.mapbox.gradle.plugins.ndk")
}
@ -33,7 +34,8 @@ android {
defaultConfig {
applicationId = "com.cloudsolutions.HMGPatientApp"
// minSdk = 24
minSdk = 26
// minSdk = 26
minSdk = 29
targetSdk = 35
compileSdk = 36
// targetSdk = flutter.targetSdkVersion
@ -191,6 +193,9 @@ dependencies {
implementation(files("libs/PenNavUI.aar"))
implementation(files("libs/Penguin.aar"))
implementation(files("libs/PenguinRenderer.aar"))
api(files("libs/samsung-health-data-api.aar"))
implementation("com.huawei.hms:health:6.11.0.300")
implementation("com.huawei.hms:hmscoreinstaller:6.6.0.300")
implementation("com.github.kittinunf.fuel:fuel:2.3.1")
implementation("com.github.kittinunf.fuel:fuel-android:2.3.1")

@ -92,7 +92,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" tools:node="remove"/>
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />
@ -124,6 +124,7 @@
</queries>
<application
android:foregroundServiceType="mediaPlayback|connectedDevice|dataSync"
android:name=".Application"
android:allowBackup="false"

@ -0,0 +1,27 @@
//package com.cloud.diplomaticquarterapp
package com.cloudsolutions.HMGPatientApp
import io.flutter.app.FlutterApplication
class Application : FlutterApplication() {
override fun onCreate() {
super.onCreate()
}
}
//import io.flutter.app.FlutterApplication
//import io.flutter.plugin.common.PluginRegistry
//import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
//import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
//
//class Application : FlutterApplication(), PluginRegistrantCallback {
// override fun onCreate() {
// super.onCreate()
// FlutterFirebaseMessagingService.setPluginRegistrant(this)
// }
//
// override fun registerWith(registry: PluginRegistry?) {
// FirebaseCloudMessagingPluginRegistrant.registerWith(registry)
// }
//}

@ -0,0 +1,78 @@
package com.cloudsolutions.HMGPatientApp
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import android.view.WindowManager
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi
import com.cloudsolutions.HMGPatientApp.watch.samsung_watch.SamsungWatch
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity: FlutterFragmentActivity() {
@RequiresApi(Build.VERSION_CODES.O)
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
// Create Flutter Platform Bridge
this.window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON)
PenguinInPlatformBridge(flutterEngine, this).create()
SamsungWatch(flutterEngine, this)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
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()
}
// override fun onActivityResult(requestCode: Int, resultCode: Int, @Nullable data: Intent?) {
// super.onActivityResult(requestCode, resultCode, data)
//
// // Process only the response result of the authorization process.
// if (requestCode == 1002) {
// // Obtain the authorization response result from the intent.
// val result: HealthKitAuthResult? = huaweiWatch?.mSettingController?.parseHealthKitAuthResultFromIntent(data)
// if (result == null) {
// Log.w(huaweiWatch?.TAG, "authorization fail")
// return
// }
//
// if (result.isSuccess) {
// Log.i(huaweiWatch?.TAG, "authorization success")
// if (result.getAuthAccount() != null && result.authAccount.authorizedScopes != null) {
// val authorizedScopes: MutableSet<Scope?> = result.authAccount.authorizedScopes
// if(authorizedScopes.isNotEmpty()) {
// huaweiWatch?.getHealthAppAuthorization()
// }
// }
// } else {
// Log.w("MainActivty", "authorization fail, errorCode:" + result.getErrorCode())
// }
// }
// }
}

@ -0,0 +1,60 @@
package com.cloudsolutions.HMGPatientApp
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.cloudsolutions.HMGPatientApp.penguin.PenguinView
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import com.cloudsolutions.HMGPatientApp.PermissionManager.HostNotificationPermissionManager
import com.cloudsolutions.HMGPatientApp.PermissionManager.HostBgLocationManager
import com.cloudsolutions.HMGPatientApp.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<String, Any>?
Log.d("TAG", "configureFlutterEngine: $args")
println("args")
args?.let {
PenguinView(
mainActivity,
100,
args,
flutterEngine.dartExecutor.binaryMessenger,
activity = mainActivity,
channel
)
}
}
else -> {
result.notImplemented()
}
}
}
}
}

@ -0,0 +1,139 @@
package com.cloudsolutions.HMGPatientApp.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<Integer> callable = () -> {
SharedPreferences preferences = getPreferences(context);
return preferences.getInt(key, -1);
};
Future<Integer> 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<String> callable = () -> {
SharedPreferences preferences = getPreferences(context);
return preferences.getString(key, defValue);
};
Future<String> 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<Boolean> callable = () -> {
SharedPreferences preferences = getPreferences(context);
return preferences.getBoolean(key, defValue);
};
Future<Boolean> 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
}
}

@ -0,0 +1,136 @@
package com.cloudsolutions.HMGPatientApp.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);
}
}
}

@ -0,0 +1,68 @@
package com.cloudsolutions.HMGPatientApp.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
);
}
}

@ -0,0 +1,73 @@
package com.cloudsolutions.HMGPatientApp.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;
}
}
}

@ -0,0 +1,27 @@
package com.cloudsolutions.HMGPatientApp.PermissionManager
import android.Manifest
object PermissionHelper {
fun getRequiredPermissions(): Array<String> {
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()
}
}

@ -0,0 +1,50 @@
package com.cloudsolutions.HMGPatientApp.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<out String>, grantResults: IntArray) {
if (this.requestCode == requestCode) {
val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
if (allGranted) {
listener.onPermissionGranted()
} else {
listener.onPermissionDenied()
}
}
}
}

@ -0,0 +1,15 @@
package com.cloudsolutions.HMGPatientApp.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)
}
}

@ -0,0 +1,13 @@
package com.cloudsolutions.HMGPatientApp.penguin
enum class PenguinMethod {
// initializePenguin("initializePenguin"),
// configurePenguin("configurePenguin"),
// showPenguinUI("showPenguinUI"),
// onPenNavUIDismiss("onPenNavUIDismiss"),
// onReportIssue("onReportIssue"),
// onPenNavSuccess("onPenNavSuccess"),
onPenNavInitializationError // onLocationOffCampus("onLocationOffCampus"),
// navigateToPOI("navigateToPOI"),
// openSharedLocation("openSharedLocation");
}

@ -0,0 +1,97 @@
package com.cloudsolutions.HMGPatientApp.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+++++++ $refID")
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<ResponseBody> = ApiController.getInstance(mContext)
.apiMethods
.getToken(postToken)
// Enqueue the call for asynchronous execution
purposesCall.enqueue(object : Callback<ResponseBody?> {
override fun onResponse(
call: Call<ResponseBody?>,
response: Response<ResponseBody?>
) {
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<ResponseBody?>, t: Throwable) {
apiTokenCallBack.onGetByRefIDError(t.message)
}
})
} catch (error: Exception) {
apiTokenCallBack.onGetByRefIDError("Exception during API call: $error")
}
}
}

@ -0,0 +1,376 @@
package com.cloudsolutions.HMGPatientApp.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.cloudsolutions.HMGPatientApp.PermissionManager.PermissionManager
import com.cloudsolutions.HMGPatientApp.PermissionManager.PermissionResultReceiver
import com.cloudsolutions.HMGPatientApp.MainActivity
import com.cloudsolutions.HMGPatientApp.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.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<String, Any>,
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)
// navigateTo("3-1")
}
/**
* 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<String, Any?> = 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<Double>?) {
TODO("Not yet implemented")
}
override fun onLocationMessage(locationMessage: LocationMessage?) {
TODO("Not yet implemented")
}
}

@ -0,0 +1,402 @@
package com.cloudsolutions.HMGPatientApp.watch.samsung_watch
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.cloudsolutions.HMGPatientApp.MainActivity
import com.cloudsolutions.HMGPatientApp.watch.samsung_watch.model.Vitals
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import com.samsung.android.sdk.health.data.HealthDataService
import com.samsung.android.sdk.health.data.HealthDataStore
import com.samsung.android.sdk.health.data.data.AggregatedData
import com.samsung.android.sdk.health.data.data.HealthDataPoint
import com.samsung.android.sdk.health.data.permission.AccessType
import com.samsung.android.sdk.health.data.permission.Permission
import com.samsung.android.sdk.health.data.request.DataType
import com.samsung.android.sdk.health.data.request.DataTypes
import com.samsung.android.sdk.health.data.request.LocalTimeFilter
import com.samsung.android.sdk.health.data.request.LocalTimeGroup
import com.samsung.android.sdk.health.data.request.LocalTimeGroupUnit
import com.samsung.android.sdk.health.data.request.Ordering
import com.samsung.android.sdk.health.data.response.DataResponse
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import java.time.LocalDateTime
import java.time.LocalTime
class SamsungWatch(
private var flutterEngine: FlutterEngine,
private var mainActivity: MainActivity
) {
private lateinit var channel: MethodChannel
private lateinit var dataStore: HealthDataStore
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val TAG = "SamsungWatch"
private lateinit var vitals: MutableMap<String, List<Vitals>>
companion object {
private const val CHANNEL = "samsung_watch"
}
init{
create()
}
@RequiresApi(Build.VERSION_CODES.O)
fun create() {
Log.d(TAG, "create: is called")
// openTok = OpenTok(mainActivity, flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
when (call.method) {
"init" -> {
Log.d(TAG, "onMethodCall: init called")
dataStore = HealthDataService.getStore(mainActivity)
vitals = mutableMapOf()
result.success("initialized")
}
"getPermission"->{
if(!this::dataStore.isInitialized)
result.error("DataStoreNotInitialized", "Please call init before requesting permissions", null)
val permSet = setOf(
Permission.of(DataTypes.HEART_RATE, AccessType.READ),
Permission.of(DataTypes.STEPS, AccessType.READ),
Permission.of(DataTypes.BLOOD_OXYGEN, AccessType.READ),
Permission.of(DataTypes.ACTIVITY_SUMMARY, AccessType.READ),
Permission.of(DataTypes.SLEEP, AccessType.READ),
Permission.of(DataTypes.BODY_TEMPERATURE, AccessType.READ),
Permission.of(DataTypes.EXERCISE, AccessType.READ),
// Permission.of(DataTypes.SKIN_TEMPERATURE, AccessType.READ),
// Permission.of(DataTypes.NUTRITION, AccessType.READ),
)
scope.launch {
try {
var granted = dataStore.getGrantedPermissions(permSet)
if (granted.containsAll(permSet)) {
result.success("Permission Granted")
return@launch
}
granted = dataStore.requestPermissions(permSet, mainActivity)
if (granted.containsAll(permSet)) {
result.success("Permission Granted") // adapt result as needed
return@launch
}
result.error("PermissionError", "Permission Not Granted", null) // adapt result as needed
} catch (e: Exception) {
Log.e(TAG, "create: getPermission failed", e)
result.error("PermissionError", e.message, null)
}
}
}
"getHeartRate"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val readRequest = DataTypes.HEART_RATE.readDataRequestBuilder
.setLocalTimeFilter(localTimeFilter)
.setOrdering(Ordering.DESC)
.build()
scope.launch {
val heartRateList = dataStore.readData(readRequest).dataList
processHeartVital(heartRateList)
Log.d("TAG"," the data is ${vitals}")
print("the data is ${vitals}")
result.success("Data is obtained")
}
}
"getSleepData" -> {
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val readRequest = DataTypes.SLEEP.readDataRequestBuilder
.setLocalTimeFilter(localTimeFilter)
.setOrdering(Ordering.ASC)
.build()
scope.launch {
val sleepData = dataStore.readData(readRequest).dataList
processSleepVital(sleepData)
print("the data is $vitals")
Log.d(TAG, "the data is $vitals")
result.success("Data is obtained")
}
}
"steps"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
val aggregateRequest = DataType.StepsType.TOTAL.requestBuilder
.setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
.setOrdering(Ordering.ASC)
.build()
scope.launch {
val steps = dataStore.aggregateData(aggregateRequest)
processStepsCount(steps)
print("the data is $vitals")
Log.d(TAG, "the data is $vitals")
result.success("Data is obtained")
}
}
"activitySummary"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
val readRequest = DataType.ActivitySummaryType.TOTAL_ACTIVE_CALORIES_BURNED
.requestBuilder
.setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
.setOrdering(Ordering.DESC)
.build()
scope.launch {
val activityResult = dataStore.aggregateData(readRequest).dataList
processActivity(activityResult)
Log.d("TAG"," the data is ${vitals}")
print("the data is ${vitals}")
result.success("Data is obtained")
}
// val readRequest = DataTypes.EXERCISE.readDataRequestBuilder
// .setLocalTimeFilter(localTimeFilter)
// .build()
//
// scope.launch{
// try {
// val readResult = dataStore.readData(readRequest)
// val dataPoints = readResult.dataList
//
// processActivity(dataPoints)
//
//
// } catch (e: Exception) {
// e.printStackTrace()
// }
// result.success("Data is obtained")
// }
}
"bloodOxygen"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val readRequest = DataTypes.BLOOD_OXYGEN.readDataRequestBuilder
.setLocalTimeFilter(localTimeFilter)
.setOrdering(Ordering.DESC)
.build()
scope.launch {
val bloodOxygenList = dataStore.readData(readRequest).dataList
processBloodOxygen(bloodOxygenList)
Log.d("TAG"," the data is ${vitals}")
print("the data is ${vitals["bloodOxygen"]}")
result.success("Data is obtained")
}
}
"bodyTemperature"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val readRequest = DataTypes.BODY_TEMPERATURE.readDataRequestBuilder
.setLocalTimeFilter(localTimeFilter)
.setOrdering(Ordering.DESC)
.build()
scope.launch {
val bodyTemperatureList = dataStore.readData(readRequest).dataList
processBodyTemperature(bodyTemperatureList)
Log.d("TAG"," the data is ${vitals}")
print("the data is ${vitals["bodyTemperature"]}")
result.success("Data is obtained")
}
}
"distance"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
val readRequest = DataType.ActivitySummaryType.TOTAL_DISTANCE.requestBuilder
.setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
.setOrdering(Ordering.DESC)
.build()
scope.launch {
val activityResult = dataStore.aggregateData(readRequest).dataList
processDistance(activityResult)
Log.d("TAG"," the data is ${vitals}")
print("the data is ${vitals}")
result.success("Data is obtained")
}
}
"retrieveData"->{
if(vitals.isEmpty()){
result.error("NoDataFound", "No Data was obtained", null)
return@setMethodCallHandler
}
result.success("""
{
"heartRate": ${vitals["heartRate"]},
"steps": ${vitals["steps"]},
"sleep": ${vitals["sleep"]},
"activity": ${vitals["activity"]},
"bloodOxygen": ${vitals["bloodOxygen"]},
"bodyTemperature": ${vitals["bodyTemperature"]},
"distance": ${vitals["distance"]}
}
""".trimIndent())
}
"closeCoroutineScope"->{
destroy()
result.success("Coroutine Scope Cancelled")
}
else -> {
result.notImplemented()
}
}
}
}
private fun CoroutineScope.processDistance(activityResult: List<AggregatedData<Float>>) {
vitals["distance"] = mutableListOf()
activityResult.forEach { stepData ->
val vitalData = Vitals().apply {
value = stepData.value.toString()
timeStamp = stepData.startTime.toString()
}
(vitals["distance"] as MutableList).add(vitalData)
}
}
private fun CoroutineScope.processBodyTemperature( bodyTemperatureList :List<HealthDataPoint>) {
vitals["bodyTemperature"] = mutableListOf()
bodyTemperatureList.forEach { stepData ->
val vitalData = Vitals().apply {
value = stepData.getValue(DataType.BodyTemperatureType.BODY_TEMPERATURE).toString()
timeStamp = stepData.endTime.toString()
}
(vitals["bodyTemperature"] as MutableList).add(vitalData)
}
}
private fun CoroutineScope.processBloodOxygen( bloodOxygenList :List<HealthDataPoint>) {
vitals["bloodOxygen"] = mutableListOf()
bloodOxygenList.forEach { stepData ->
val vitalData = Vitals().apply {
value = stepData.getValue(DataType.BloodOxygenType.OXYGEN_SATURATION).toString()
timeStamp = stepData.endTime.toString()
}
(vitals["bloodOxygen"] as MutableList).add(vitalData)
}
}
// private fun CoroutineScope.processActivity(activityResult: List<AggregatedData<Float>>) {
//
// vitals["activity"] = mutableListOf()
// activityResult.forEach { stepData ->
// val vitalData = Vitals().apply {
//
// value = stepData.value.toString()
// timeStamp = stepData.startTime.toString()
// }
// (vitals["activity"] as MutableList).add(vitalData)
// }
// }
private fun CoroutineScope.processActivity(activityResult: List<AggregatedData<Float>>) {
vitals["activity"] = mutableListOf()
activityResult.forEach { stepData ->
val vitalData = Vitals().apply {
value = stepData.value.toString()
timeStamp = stepData.startTime.toString()
}
(vitals["activity"] as MutableList).add(vitalData)
}
// dataPoints.forEach { dataPoint ->
// val sessions = dataPoint.getValue(DataType.ExerciseType.SESSIONS)
//
// sessions?.forEach { session ->
//
// val exerciseSessionCalories = session.calories
// val vitalData = Vitals().apply {
// value = exerciseSessionCalories.toString()
// timeStamp = session.startTime.toString()
// }
// (vitals["activity"] as MutableList).add(vitalData)
// }
// }
}
private fun CoroutineScope.processStepsCount(result: DataResponse<AggregatedData<Long>>) {
val stepCount = ArrayList<AggregatedData<Long>>()
var totalSteps: Long = 0
vitals["steps"] = mutableListOf()
result.dataList.forEach { stepData ->
val vitalData = Vitals().apply {
value = (stepData.value as Long).toString()
timeStamp = stepData.startTime.toString()
}
(vitals["steps"] as MutableList).add(vitalData)
}
}
private fun CoroutineScope.processSleepVital(sleepData: List<HealthDataPoint>) {
vitals["sleep"] = mutableListOf()
sleepData.forEach {
(vitals["sleep"] as MutableList).add(
Vitals().apply {
timeStamp = it.startTime.toString()
value = (it.getValue(DataType.SleepType.DURATION)?.toMillis().toString())
}
)
}
}
private suspend fun CoroutineScope.processHeartVital(
heartRateList: List<HealthDataPoint>,
) {
vitals["heartRate"] = mutableListOf()
heartRateList.forEach {
(vitals["heartRate"] as MutableList).add(processHeartRateData(it))
}
}
private fun processHeartRateData(heartRateData: HealthDataPoint) =
Vitals().apply {
heartRateData.getValue(DataType.HeartRateType.MAX_HEART_RATE)?.let {
value = it.toString()
}
timeStamp = heartRateData.startTime.toString()
}
fun destroy() {
scope.cancel()
}
}

@ -0,0 +1,13 @@
package com.cloudsolutions.HMGPatientApp.watch.samsung_watch.model
data class Vitals(
var value : String = "",
var timeStamp :String = ""
){
override fun toString(): String {
return """{
"value": "$value",
"timeStamp": "$timeStamp"}
""".trimIndent()
}
}

@ -0,0 +1,274 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.cloudsolutions.HMGPatientApp">
<!--
io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here.
-->
<uses-permission
android:name="android.permission.ACTIVITY_RECOGNITION"
tools:node="remove" />
<uses-permission
android:name="android.permission.READ_PHONE_STATE"
tools:node="remove" /> <!-- <uses-permission android:name="android.permission.BLUETOOTH" tools:node="remove"/> -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" tools:node="remove"/> -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove"/> -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_SCAN" tools:node="remove"/> -->
<uses-permission
android:name="android.permission.BROADCAST_STICKY"
tools:node="remove" />
<uses-permission
android:name="com.google.android.gms.permission.AD_ID"
tools:node="remove" /> <!-- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> -->
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"
tools:node="remove" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" tools:node="remove" />
<!-- Added by open_filex -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" tools:node="remove" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" tools:node="remove" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" tools:node="remove" />
<uses-permission
android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
tools:node="remove" /> <!-- <uses-permission android:name="android.permission.INTERNET" /> -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-feature
android:name="android.hardware.sensor.stepcounter"
android:required="false"
tools:node="replace" />
<uses-feature
android:name="android.hardware.sensor.stepdetector"
android:required="false"
tools:node="replace" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-feature
android:name="android.hardware.location.network"
android:required="false" />
<uses-feature
android:name="android.hardware.location.gps"
android:required="false" />
<uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA" /> <!-- <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" /> -->
<!-- Wifi Permissions -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> -->
<!-- Detect Reboot Permission -->
<!-- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> -->
<queries>
<intent>
<action android:name="android.speech.RecognitionService" />
</intent>
<package android:name="com.whatsapp" />
<package android:name="com.whatsapp.w4b" />
</queries>
<application
android:foregroundServiceType="mediaPlayback|connectedDevice|dataSync"
android:name=".Application"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher_local"
android:label="Dr. Alhabib Beta"
android:screenOrientation="sensorPortrait"
android:showOnLockScreen="true"
android:usesCleartextTraffic="true"
tools:replace="android:label">
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="102857389"/>
<!-- <activity-->
<!-- android:name="com.cloud.hmg_patient_app.whatsapp.WhatsAppCodeActivity"-->
<!-- android:exported="true"-->
<!-- android:enabled="true"-->
<!-- android:launchMode="standard"-->
<!-- >-->
<!-- <intent-filter>-->
<!-- <action android:name="com.whatsapp.otp.OTP_RETRIEVED" />-->
<!-- </intent-filter>-->
<!-- </activity>-->
<meta-data
android:name="push_kit_auto_init_enabled"
android:value="true" />
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:enabled="true"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:showOnLockScreen="true"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize"
tools:node="merge">
<!--
Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI.
-->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<!--
Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame.
-->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity> <!-- <receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver" android:exported="true"> -->
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.BOOT_COMPLETED"/> -->
<!-- <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/> -->
<!-- </intent-filter> -->
<!-- </receiver> -->
<!-- Geofencing -->
<!-- <service-->
<!-- android:name=".geofence.intent_receivers.GeofenceTransitionsJobIntentService"-->
<!-- android:exported="true"-->
<!-- android:permission="android.permission.BIND_JOB_SERVICE" />-->
<!-- <receiver-->
<!-- android:name=".geofence.intent_receivers.GeofenceBroadcastReceiver"-->
<!-- android:enabled="true"-->
<!-- android:exported="false" />-->
<!-- <receiver-->
<!-- android:name=".geofence.intent_receivers.GeofencingRebootBroadcastReceiver"-->
<!-- android:enabled="true"-->
<!-- android:exported="false">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.BOOT_COMPLETED" />-->
<!-- <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />-->
<!-- </intent-filter>-->
<!-- </receiver>-->
<!-- <receiver-->
<!-- android:name=".geofence.intent_receivers.LocationProviderChangeReceiver"-->
<!-- android:exported="false">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.location.PROVIDERS_CHANGED" />-->
<!-- </intent-filter>-->
<!-- </receiver>-->
<!-- <service-->
<!-- android:name=".geofence.intent_receivers.ReregisterGeofenceJobService"-->
<!-- android:exported="true"-->
<!-- android:permission="android.permission.BIND_JOB_SERVICE" /> &lt;!&ndash; Geofencing &ndash;&gt;-->
<!--
Huawei Push Notifications
Set push kit auto enable to true (for obtaining the token on initialize)
-->
<!-- <meta-data -->
<!-- android:name="push_kit_auto_init_enabled" -->
<!-- android:value="true" /> -->
<!-- These receivers are for sending scheduled local notifications -->
<!-- <receiver-->
<!-- android:name="com.huawei.hms.flutter.push.receiver.local.HmsLocalNotificationBootEventReceiver"-->
<!-- android:exported="false">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.BOOT_COMPLETED" />-->
<!-- </intent-filter>-->
<!-- </receiver>-->
<!-- <receiver-->
<!-- android:name="com.huawei.hms.flutter.push.receiver.local.HmsLocalNotificationScheduledPublisher"-->
<!-- android:enabled="true"-->
<!-- android:exported="false" />-->
<!-- <receiver-->
<!-- android:name="com.huawei.hms.flutter.push.receiver.BackgroundMessageBroadcastReceiver"-->
<!-- android:enabled="true"-->
<!-- android:exported="true">-->
<!-- <intent-filter>-->
<!-- <action android:name="com.huawei.hms.flutter.push.receiver.BACKGROUND_REMOTE_MESSAGE" />-->
<!-- </intent-filter>-->
<!-- </receiver> &lt;!&ndash; Huawei Push Notifications &ndash;&gt;-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng" />
<!--
Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java
-->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>

@ -0,0 +1,84 @@
package com.ejada.hmg
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import android.view.WindowManager
import androidx.annotation.NonNull
import androidx.annotation.Nullable
import androidx.annotation.RequiresApi
import com.ejada.hmg.penguin.PenguinInPlatformBridge
import com.ejada.hmg.watch.huawei.HuaweiWatch
import com.ejada.hmg.watch.huawei.samsung_watch.SamsungWatch
import com.huawei.hms.hihealth.result.HealthKitAuthResult
import com.huawei.hms.support.api.entity.auth.Scope
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterFragmentActivity() {
private var huaweiWatch : HuaweiWatch? = null
@RequiresApi(Build.VERSION_CODES.O)
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
// Create Flutter Platform Bridge
this.window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON)
PenguinInPlatformBridge(flutterEngine, this).create()
SamsungWatch(flutterEngine, this)
huaweiWatch = HuaweiWatch(flutterEngine, this)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
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()
}
// override fun onActivityResult(requestCode: Int, resultCode: Int, @Nullable data: Intent?) {
// super.onActivityResult(requestCode, resultCode, data)
//
// // Process only the response result of the authorization process.
// if (requestCode == 1002) {
// // Obtain the authorization response result from the intent.
// val result: HealthKitAuthResult? = huaweiWatch?.mSettingController?.parseHealthKitAuthResultFromIntent(data)
// if (result == null) {
// Log.w(huaweiWatch?.TAG, "authorization fail")
// return
// }
//
// if (result.isSuccess) {
// Log.i(huaweiWatch?.TAG, "authorization success")
// if (result.getAuthAccount() != null && result.authAccount.authorizedScopes != null) {
// val authorizedScopes: MutableSet<Scope?> = result.authAccount.authorizedScopes
// if(authorizedScopes.isNotEmpty()) {
// huaweiWatch?.getHealthAppAuthorization()
// }
// }
// } else {
// Log.w("MainActivty", "authorization fail, errorCode:" + result.getErrorCode())
// }
// }
// }
}

@ -0,0 +1,402 @@
package com.ejada.hmg.watch.huawei.samsung_watch
import com.ejada.hmg.MainActivity
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import com.ejada.hmg.watch.huawei.samsung_watch.model.Vitals
import com.samsung.android.sdk.health.data.HealthDataService
import com.samsung.android.sdk.health.data.HealthDataStore
import com.samsung.android.sdk.health.data.data.AggregatedData
import com.samsung.android.sdk.health.data.data.HealthDataPoint
import com.samsung.android.sdk.health.data.permission.AccessType
import com.samsung.android.sdk.health.data.permission.Permission
import com.samsung.android.sdk.health.data.request.DataType
import com.samsung.android.sdk.health.data.request.DataTypes
import com.samsung.android.sdk.health.data.request.LocalTimeFilter
import com.samsung.android.sdk.health.data.request.LocalTimeGroup
import com.samsung.android.sdk.health.data.request.LocalTimeGroupUnit
import com.samsung.android.sdk.health.data.request.Ordering
import com.samsung.android.sdk.health.data.response.DataResponse
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import java.time.LocalDateTime
import java.time.LocalTime
class SamsungWatch(
private var flutterEngine: FlutterEngine,
private var mainActivity: MainActivity
) {
private lateinit var channel: MethodChannel
private lateinit var dataStore: HealthDataStore
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val TAG = "SamsungWatch"
private lateinit var vitals: MutableMap<String, List<Vitals>>
companion object {
private const val CHANNEL = "samsung_watch"
}
init{
create()
}
@RequiresApi(Build.VERSION_CODES.O)
fun create() {
Log.d(TAG, "create: is called")
// openTok = OpenTok(mainActivity, flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
when (call.method) {
"init" -> {
Log.d(TAG, "onMethodCall: init called")
dataStore = HealthDataService.getStore(mainActivity)
vitals = mutableMapOf()
result.success("initialized")
}
"getPermission"->{
if(!this::dataStore.isInitialized)
result.error("DataStoreNotInitialized", "Please call init before requesting permissions", null)
val permSet = setOf(
Permission.of(DataTypes.HEART_RATE, AccessType.READ),
Permission.of(DataTypes.STEPS, AccessType.READ),
Permission.of(DataTypes.BLOOD_OXYGEN, AccessType.READ),
Permission.of(DataTypes.ACTIVITY_SUMMARY, AccessType.READ),
Permission.of(DataTypes.SLEEP, AccessType.READ),
Permission.of(DataTypes.BODY_TEMPERATURE, AccessType.READ),
Permission.of(DataTypes.EXERCISE, AccessType.READ),
// Permission.of(DataTypes.SKIN_TEMPERATURE, AccessType.READ),
// Permission.of(DataTypes.NUTRITION, AccessType.READ),
)
scope.launch {
try {
var granted = dataStore.getGrantedPermissions(permSet)
if (granted.containsAll(permSet)) {
result.success("Permission Granted")
return@launch
}
granted = dataStore.requestPermissions(permSet, mainActivity)
if (granted.containsAll(permSet)) {
result.success("Permission Granted") // adapt result as needed
return@launch
}
result.error("PermissionError", "Permission Not Granted", null) // adapt result as needed
} catch (e: Exception) {
Log.e(TAG, "create: getPermission failed", e)
result.error("PermissionError", e.message, null)
}
}
}
"getHeartRate"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val readRequest = DataTypes.HEART_RATE.readDataRequestBuilder
.setLocalTimeFilter(localTimeFilter)
.setOrdering(Ordering.DESC)
.build()
scope.launch {
val heartRateList = dataStore.readData(readRequest).dataList
processHeartVital(heartRateList)
Log.d("TAG"," the data is ${vitals}")
print("the data is ${vitals}")
result.success("Data is obtained")
}
}
"getSleepData" -> {
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val readRequest = DataTypes.SLEEP.readDataRequestBuilder
.setLocalTimeFilter(localTimeFilter)
.setOrdering(Ordering.ASC)
.build()
scope.launch {
val sleepData = dataStore.readData(readRequest).dataList
processSleepVital(sleepData)
print("the data is $vitals")
Log.d(TAG, "the data is $vitals")
result.success("Data is obtained")
}
}
"steps"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
val aggregateRequest = DataType.StepsType.TOTAL.requestBuilder
.setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
.setOrdering(Ordering.ASC)
.build()
scope.launch {
val steps = dataStore.aggregateData(aggregateRequest)
processStepsCount(steps)
print("the data is $vitals")
Log.d(TAG, "the data is $vitals")
result.success("Data is obtained")
}
}
"activitySummary"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
val readRequest = DataType.ActivitySummaryType.TOTAL_ACTIVE_CALORIES_BURNED
.requestBuilder
.setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
.setOrdering(Ordering.DESC)
.build()
scope.launch {
val activityResult = dataStore.aggregateData(readRequest).dataList
processActivity(activityResult)
Log.d("TAG"," the data is ${vitals}")
print("the data is ${vitals}")
result.success("Data is obtained")
}
// val readRequest = DataTypes.EXERCISE.readDataRequestBuilder
// .setLocalTimeFilter(localTimeFilter)
// .build()
//
// scope.launch{
// try {
// val readResult = dataStore.readData(readRequest)
// val dataPoints = readResult.dataList
//
// processActivity(dataPoints)
//
//
// } catch (e: Exception) {
// e.printStackTrace()
// }
// result.success("Data is obtained")
// }
}
"bloodOxygen"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val readRequest = DataTypes.BLOOD_OXYGEN.readDataRequestBuilder
.setLocalTimeFilter(localTimeFilter)
.setOrdering(Ordering.DESC)
.build()
scope.launch {
val bloodOxygenList = dataStore.readData(readRequest).dataList
processBloodOxygen(bloodOxygenList)
Log.d("TAG"," the data is ${vitals}")
print("the data is ${vitals["bloodOxygen"]}")
result.success("Data is obtained")
}
}
"bodyTemperature"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val readRequest = DataTypes.BODY_TEMPERATURE.readDataRequestBuilder
.setLocalTimeFilter(localTimeFilter)
.setOrdering(Ordering.DESC)
.build()
scope.launch {
val bodyTemperatureList = dataStore.readData(readRequest).dataList
processBodyTemperature(bodyTemperatureList)
Log.d("TAG"," the data is ${vitals}")
print("the data is ${vitals["bodyTemperature"]}")
result.success("Data is obtained")
}
}
"distance"->{
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
val readRequest = DataType.ActivitySummaryType.TOTAL_DISTANCE.requestBuilder
.setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
.setOrdering(Ordering.DESC)
.build()
scope.launch {
val activityResult = dataStore.aggregateData(readRequest).dataList
processDistance(activityResult)
Log.d("TAG"," the data is ${vitals}")
print("the data is ${vitals}")
result.success("Data is obtained")
}
}
"retrieveData"->{
if(vitals.isEmpty()){
result.error("NoDataFound", "No Data was obtained", null)
return@setMethodCallHandler
}
result.success("""
{
"heartRate": ${vitals["heartRate"]},
"steps": ${vitals["steps"]},
"sleep": ${vitals["sleep"]},
"activity": ${vitals["activity"]},
"bloodOxygen": ${vitals["bloodOxygen"]},
"bodyTemperature": ${vitals["bodyTemperature"]},
"distance": ${vitals["distance"]}
}
""".trimIndent())
}
"closeCoroutineScope"->{
destroy()
result.success("Coroutine Scope Cancelled")
}
else -> {
result.notImplemented()
}
}
}
}
private fun CoroutineScope.processDistance(activityResult: List<AggregatedData<Float>>) {
vitals["distance"] = mutableListOf()
activityResult.forEach { stepData ->
val vitalData = Vitals().apply {
value = stepData.value.toString()
timeStamp = stepData.startTime.toString()
}
(vitals["distance"] as MutableList).add(vitalData)
}
}
private fun CoroutineScope.processBodyTemperature( bodyTemperatureList :List<HealthDataPoint>) {
vitals["bodyTemperature"] = mutableListOf()
bodyTemperatureList.forEach { stepData ->
val vitalData = Vitals().apply {
value = stepData.getValue(DataType.BodyTemperatureType.BODY_TEMPERATURE).toString()
timeStamp = stepData.endTime.toString()
}
(vitals["bodyTemperature"] as MutableList).add(vitalData)
}
}
private fun CoroutineScope.processBloodOxygen( bloodOxygenList :List<HealthDataPoint>) {
vitals["bloodOxygen"] = mutableListOf()
bloodOxygenList.forEach { stepData ->
val vitalData = Vitals().apply {
value = stepData.getValue(DataType.BloodOxygenType.OXYGEN_SATURATION).toString()
timeStamp = stepData.endTime.toString()
}
(vitals["bloodOxygen"] as MutableList).add(vitalData)
}
}
// private fun CoroutineScope.processActivity(activityResult: List<AggregatedData<Float>>) {
//
// vitals["activity"] = mutableListOf()
// activityResult.forEach { stepData ->
// val vitalData = Vitals().apply {
//
// value = stepData.value.toString()
// timeStamp = stepData.startTime.toString()
// }
// (vitals["activity"] as MutableList).add(vitalData)
// }
// }
private fun CoroutineScope.processActivity(activityResult: List<AggregatedData<Float>>) {
vitals["activity"] = mutableListOf()
activityResult.forEach { stepData ->
val vitalData = Vitals().apply {
value = stepData.value.toString()
timeStamp = stepData.startTime.toString()
}
(vitals["activity"] as MutableList).add(vitalData)
}
// dataPoints.forEach { dataPoint ->
// val sessions = dataPoint.getValue(DataType.ExerciseType.SESSIONS)
//
// sessions?.forEach { session ->
//
// val exerciseSessionCalories = session.calories
// val vitalData = Vitals().apply {
// value = exerciseSessionCalories.toString()
// timeStamp = session.startTime.toString()
// }
// (vitals["activity"] as MutableList).add(vitalData)
// }
// }
}
private fun CoroutineScope.processStepsCount(result: DataResponse<AggregatedData<Long>>) {
val stepCount = ArrayList<AggregatedData<Long>>()
var totalSteps: Long = 0
vitals["steps"] = mutableListOf()
result.dataList.forEach { stepData ->
val vitalData = Vitals().apply {
value = (stepData.value as Long).toString()
timeStamp = stepData.startTime.toString()
}
(vitals["steps"] as MutableList).add(vitalData)
}
}
private fun CoroutineScope.processSleepVital(sleepData: List<HealthDataPoint>) {
vitals["sleep"] = mutableListOf()
sleepData.forEach {
(vitals["sleep"] as MutableList).add(
Vitals().apply {
timeStamp = it.startTime.toString()
value = (it.getValue(DataType.SleepType.DURATION)?.toMillis().toString())
}
)
}
}
private suspend fun CoroutineScope.processHeartVital(
heartRateList: List<HealthDataPoint>,
) {
vitals["heartRate"] = mutableListOf()
heartRateList.forEach {
(vitals["heartRate"] as MutableList).add(processHeartRateData(it))
}
}
private fun processHeartRateData(heartRateData: HealthDataPoint) =
Vitals().apply {
heartRateData.getValue(DataType.HeartRateType.MAX_HEART_RATE)?.let {
value = it.toString()
}
timeStamp = heartRateData.startTime.toString()
}
fun destroy() {
scope.cancel()
}
}

@ -0,0 +1,13 @@
package com.ejada.hmg.watch.huawei.samsung_watch.model
data class Vitals(
var value : String = "",
var timeStamp :String = ""
){
override fun toString(): String {
return """{
"value": "$value",
"timeStamp": "$timeStamp"}
""".trimIndent()
}
}

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.cloud.diplomaticquarterapp.whatsapp.WhatsAppCodeActivity">
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal">
<FrameLayout
android:id="@+id/publisher_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF9800" />
</LinearLayout>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal">
<FrameLayout
android:id="@+id/subscriber_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3F51B5" />
<TextView
android:text="Remote"
android:textColor="#FFFFFF"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/*,@raw/slow_spring_board" />

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

@ -0,0 +1,3 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- <string name="mapbox_access_token" translatable="false" tools:ignore="UnusedResources">sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg</string>-->
</resources>

@ -0,0 +1,23 @@
<resources>
<string name="app_name">HMG Patient App</string>
<string name="geofence_unknown_error">
Unknown error: the Geofence service is not available now.
</string>
<string name="geofence_not_available">
Geofence service is not available now. Go to Settings>Location>Mode and choose High accuracy.
</string>
<string name="geofence_too_many_geofences">
Your app has registered too many geofences.
</string>
<string name="geofence_too_many_pending_intents">
You have provided too many PendingIntents to the addGeofences() call.
</string>
<string name="GEOFENCE_INSUFFICIENT_LOCATION_PERMISSION">
App do not have permission to access location service.
</string>
<string name="GEOFENCE_REQUEST_TOO_FREQUENT">
Geofence requests happened too frequently.
</string>
<string name="mapbox_access_token" translatable="false">pk.eyJ1IjoicndhaWQiLCJhIjoiY2x5cGo4aHNjMGNsbTJyc2djempobGQxaSJ9.RCaC6WrUt4A4YnZNfxnONQ</string>
</resources>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@android:color/white</item>
</style>
</resources>

@ -1,3 +1,10 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
#org.gradle.jvmargs=-xmx4608m
android.enableR8=true
android.enableJetifier=true
android.useDeprecatedNdk=true
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true
android.useAndroidX=true
android.enableImpeller=false

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

@ -0,0 +1,6 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#0B85F7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.2743 13.4324L23.9575 13.9486C24.351 14.2459 24.7161 14.5217 24.975 14.784C25.2585 15.0713 25.5387 15.4636 25.5387 16.0028C25.5387 16.542 25.2585 16.9343 24.975 17.2216C24.7161 17.4839 24.351 17.7597 23.9575 18.057L21.3817 20.0034L23.9575 21.9498C24.351 22.2471 24.7161 22.5229 24.975 22.7852C25.2585 23.0726 25.5387 23.4648 25.5387 24.004C25.5387 24.5432 25.2585 24.9355 24.975 25.2228C24.7161 25.4851 24.351 25.7609 23.9575 26.0582L23.2743 26.5745C22.7022 27.0068 22.1995 27.3868 21.7763 27.6186C21.3462 27.8541 20.7396 28.082 20.1123 27.766C19.4877 27.4513 19.3065 26.8305 19.2358 26.345C19.166 25.8658 19.1661 25.2334 19.1662 24.5121L19.1662 21.6777L15.2078 24.6689C14.8406 24.9464 14.318 24.8736 14.0405 24.5064C13.763 24.1393 13.8358 23.6166 14.203 23.3392L18.6173 20.0034L14.203 16.6677C13.8358 16.3902 13.763 15.8676 14.0405 15.5004C14.318 15.1332 14.8406 15.0605 15.2078 15.338L19.1662 18.3292L19.1662 15.4947C19.1661 14.7735 19.166 14.141 19.2358 13.6619C19.3065 13.1764 19.4877 12.5555 20.1123 12.2409C20.7396 11.9248 21.3462 12.1528 21.7763 12.3883C22.1995 12.62 22.7022 13 23.2743 13.4324ZM20.8328 22.0125C20.8328 21.7542 20.8939 21.7238 21.1 21.8795L22.9088 23.2464C22.9773 23.2981 23.0475 23.3494 23.1183 23.401C23.3302 23.5556 23.5466 23.7136 23.7318 23.8966C23.8298 23.9934 23.8298 24.0146 23.7318 24.1114C23.5466 24.2945 23.3302 24.4524 23.1183 24.607C23.0475 24.6587 22.9773 24.7099 22.9088 24.7616L22.3206 25.2061C22.2186 25.2832 22.1131 25.3659 22.005 25.4505C21.7062 25.6846 21.3876 25.9343 21.0677 26.125C20.885 26.2339 20.8327 26.1958 20.8327 25.9859L20.8328 22.0125ZM21.1 18.1273C20.8939 18.2831 20.8328 18.2527 20.8328 17.9943L20.8327 14.0683C20.8327 13.8237 20.8908 13.7927 21.0925 13.9315C21.4296 14.1634 22.0109 14.5667 22.3206 14.8007L22.9088 15.2452C22.9772 15.2969 23.0475 15.3482 23.1182 15.3998C23.3301 15.5544 23.5466 15.7123 23.7318 15.8954C23.8298 15.9922 23.8298 16.0134 23.7318 16.1102C23.5466 16.2933 23.3302 16.4512 23.1183 16.6058C23.0475 16.6574 22.9773 16.7087 22.9088 16.7604L21.1 18.1273Z" fill="white"/>
<path d="M24.9999 20.0026C24.9999 19.5424 25.3713 19.1693 25.8295 19.1693H25.837C26.2952 19.1693 26.6666 19.5424 26.6666 20.0026C26.6666 20.4628 26.2952 20.8359 25.837 20.8359H25.8295C25.3713 20.8359 24.9999 20.4628 24.9999 20.0026Z" fill="white"/>
<path d="M14.1629 19.1693C13.7047 19.1693 13.3333 19.5424 13.3333 20.0026C13.3333 20.4628 13.7047 20.8359 14.1629 20.8359H14.1703C14.6285 20.8359 14.9999 20.4628 14.9999 20.0026C14.9999 19.5424 14.6285 19.1693 14.1703 19.1693H14.1629Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.59383 1.11189C8.74419 0.954165 8.97212 0.897474 9.17885 0.966384C11.5824 1.76758 13.7863 3.79724 14.9435 6.12945C16.1907 8.48889 16.237 11.1216 15.2416 13.2249C14.2387 15.344 12.193 16.8897 9.36327 17.0615C9.35192 17.0622 9.34056 17.0625 9.32919 17.0625C2.03061 17.0625 -0.541949 8.37522 5.21547 4.37907C5.38744 4.25971 5.61147 4.24572 5.79696 4.34276C5.98245 4.43981 6.0987 4.63183 6.0987 4.84117C6.0987 5.56151 6.1749 6.07698 6.2867 6.42883C6.39975 6.78462 6.53247 6.92354 6.60852 6.9729C6.66642 7.01048 6.74359 7.03168 6.88312 6.98726C7.03735 6.93815 7.24136 6.81277 7.46854 6.5839C7.92091 6.12814 8.35983 5.3671 8.58869 4.47155C8.8162 3.58128 8.82489 2.60277 8.47644 1.70319C8.39773 1.49999 8.44347 1.26961 8.59383 1.11189Z" fill="#18C273"/>
</svg>

After

Width:  |  Height:  |  Size: 861 B

@ -0,0 +1,4 @@
<svg width="78" height="78" viewBox="0 0 78 78" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M76.4788 48.5219C81.7373 27.8239 69.2209 6.7813 48.5227 1.52193C27.8244 -3.73744 6.78231 8.77805 1.5238 29.4761C-3.7347 50.1741 8.78169 71.2167 29.4799 76.4761C50.1782 81.7354 71.2203 69.2199 76.4788 48.5219ZM22.1364 34.7137C24.5028 25.3996 33.9717 19.7676 43.2859 22.1343C52.6001 24.501 58.2325 33.9702 55.8662 43.2843C53.4999 52.5984 44.0309 58.2304 34.7167 55.8637C25.4025 53.497 19.7701 44.0278 22.1364 34.7137Z" fill="#8F9AA3" fill-opacity="0.15"/>
<path d="M39.4762 75.6628C39.4905 76.7673 40.3984 77.6564 41.5006 77.585C50.1568 77.0243 58.3957 73.566 64.8713 67.7366C71.8436 61.4602 76.2964 52.8631 77.4 43.5472C78.5036 34.2312 76.1828 24.8318 70.87 17.1C65.9356 9.91907 58.7328 4.63155 50.4473 2.0639C49.3922 1.73695 48.3017 2.38934 48.0297 3.4599L43.7775 20.1953C43.5055 21.2659 44.1586 22.3434 45.1909 22.7364C48.481 23.9889 51.3301 26.2169 53.3415 29.1441C55.7323 32.6234 56.7766 36.8531 56.28 41.0453C55.7834 45.2375 53.7796 49.1062 50.6421 51.9306C48.0024 54.3068 44.7117 55.8075 41.22 56.2566C40.1244 56.3975 39.2376 57.2926 39.2519 58.3971L39.4762 75.6628Z" fill="#18C273"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,6 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.75 2.8125C6.75 1.56986 7.75736 0.5625 9 0.5625C10.2426 0.5625 11.25 1.56986 11.25 2.8125C11.25 3.61712 10.8277 4.32309 10.1926 4.72081C12.3938 5.03292 13.7317 7.48108 12.8512 9.64368C12.6596 10.1143 12.2102 10.4375 11.6939 10.4375H11.3249L10.704 13.0516C10.5142 13.8505 9.82252 14.4375 9 14.4375C8.17749 14.4375 7.48577 13.8505 7.29604 13.0516L6.67515 10.4375H6.30607C5.78977 10.4375 5.34042 10.1143 5.14881 9.64368C4.26828 7.48109 5.60621 5.03292 7.80744 4.72081C7.17235 4.32308 6.75 3.61711 6.75 2.8125Z" fill="#FFA800"/>
<path d="M5.25 11.4375H4.5C4.08579 11.4375 3.75 11.7733 3.75 12.1875C3.75 12.6017 4.08579 12.9375 4.5 12.9375H5.25C5.66421 12.9375 6 12.6017 6 12.1875C6 11.7733 5.66421 11.4375 5.25 11.4375Z" fill="#FFA800"/>
<path d="M12.75 11.4375C12.3358 11.4375 12 11.7733 12 12.1875C12 12.6017 12.3358 12.9375 12.75 12.9375H13.5C13.9142 12.9375 14.25 12.6017 14.25 12.1875C14.25 11.7733 13.9142 11.4375 13.5 11.4375H12.75Z" fill="#FFA800"/>
<path d="M9.75 15.9375C9.75 15.5233 9.41421 15.1875 9 15.1875C8.58579 15.1875 8.25 15.5233 8.25 15.9375V16.6875C8.25 17.1017 8.58579 17.4375 9 17.4375C9.41421 17.4375 9.75 17.1017 9.75 16.6875V15.9375Z" fill="#FFA800"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,3 @@
<svg width="76" height="76" viewBox="0 0 76 76" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M72.5 41.5C70.567 41.5 69 39.933 69 38C69 36.067 70.567 34.5 72.5 34.5C74.433 34.5 76 36.067 76 38C76 39.933 74.433 41.5 72.5 41.5Z" fill="#FFA800"/>
</svg>

After

Width:  |  Height:  |  Size: 302 B

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.6534 14.6421C11.8109 14.48 11.8561 14.2393 11.7682 14.0311C11.6802 13.8228 11.4761 13.6875 11.25 13.6875L9.75003 13.6875L9.75003 4.3125L11.25 4.3125C11.4761 4.3125 11.6802 4.17717 11.7682 3.96895C11.8561 3.76072 11.8109 3.52004 11.6534 3.35794C11.5684 3.27054 11.4331 3.10126 11.2428 2.85898C11.0716 2.64081 10.8299 2.333 10.6246 2.09069C10.4042 1.83063 10.1609 1.56453 9.91996 1.35933C9.79927 1.25653 9.666 1.15757 9.52489 1.08206C9.38825 1.00894 9.20686 0.9375 9.00002 0.9375C8.79317 0.9375 8.61178 1.00894 8.47514 1.08206C8.33403 1.15757 8.20076 1.25653 8.08007 1.35933C7.83914 1.56453 7.59578 1.83063 7.37543 2.09069C7.1701 2.333 6.92848 2.64081 6.75722 2.85898C6.56692 3.10126 6.43162 3.27054 6.34666 3.35794C6.1891 3.52004 6.14389 3.76072 6.23188 3.96895C6.31986 4.17718 6.52396 4.3125 6.75002 4.3125H8.25003L8.25003 13.6875H6.75001C6.52396 13.6875 6.31986 13.8228 6.23187 14.0311C6.14389 14.2393 6.1891 14.48 6.34666 14.6421C6.43162 14.7295 6.56692 14.8987 6.75722 15.141C6.92847 15.3592 7.1701 15.667 7.37543 15.9093C7.59578 16.1694 7.83914 16.4355 8.08007 16.6407C8.20076 16.7435 8.33403 16.8424 8.47514 16.9179C8.61178 16.9911 8.79317 17.0625 9.00001 17.0625C9.20686 17.0625 9.38825 16.9911 9.52489 16.9179C9.666 16.8424 9.79927 16.7435 9.91996 16.6407C10.1609 16.4355 10.4042 16.1694 10.6246 15.9093C10.8299 15.667 11.0716 15.3592 11.2428 15.141C11.4331 14.8987 11.5684 14.7295 11.6534 14.6421Z" fill="#FFA800"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.937503 12.9126L0.937501 12.9141V15.5625C0.937501 15.9767 1.27329 16.3125 1.6875 16.3125C2.10172 16.3125 2.4375 15.9767 2.4375 15.5625V13.6875H15.5625V15.5625C15.5625 15.9767 15.8983 16.3125 16.3125 16.3125C16.7267 16.3125 17.0625 15.9767 17.0625 15.5625V13.1263V13.125V11.961C17.0625 11.2871 17.0625 10.7252 17.0026 10.2791C16.9393 9.8083 16.8 9.3832 16.4584 9.04159C16.2015 8.78471 15.8974 8.64225 15.5622 8.56047V5.52518L15.5623 5.43571C15.5635 5.03078 15.5647 4.61131 15.3546 4.21689C15.1452 3.82363 14.8276 3.60814 14.5257 3.40327L14.4676 3.36382C12.9184 2.30718 11.0321 1.6875 9 1.6875C6.96785 1.6875 5.08161 2.30718 3.53236 3.36382L3.47433 3.40327C3.17241 3.60814 2.85484 3.82363 2.64539 4.21689C2.43531 4.61131 2.43651 5.03079 2.43767 5.43571L2.43784 5.52518V8.56048C2.10258 8.64226 1.79848 8.78471 1.5416 9.04159C1.19999 9.3832 1.06074 9.8083 0.997437 10.2791C0.937463 10.7252 0.93748 11.2871 0.937501 11.961L0.937503 12.9126ZM13.539 8.4375C13.737 8.43749 13.9253 8.43749 14.1039 8.439V5.52518C14.1039 5.26409 14.1031 5.1195 14.0919 5.01006C14.0827 4.91955 14.0697 4.89418 14.0624 4.88041C14.0486 4.85453 14.0344 4.83345 13.9859 4.79172C13.9194 4.73446 13.8252 4.66899 13.6357 4.53977C12.3235 3.64477 10.726 3.11932 9 3.11932C7.27396 3.11932 5.67655 3.64477 4.36429 4.53977C4.17483 4.66899 4.08064 4.73446 4.01409 4.79172C3.9656 4.83345 3.95139 4.85453 3.93761 4.88041C3.93028 4.89418 3.91726 4.91955 3.90806 5.01006C3.89692 5.1195 3.8961 5.26409 3.8961 5.52518V8.439C4.07463 8.43749 4.26291 8.43749 4.46086 8.4375H5.4375V7.96336C5.4375 7.76396 5.44903 7.4943 5.60078 7.25058C5.75766 6.99861 6.00084 6.87279 6.19956 6.788C6.98337 6.45359 7.94807 6.1875 9 6.1875C10.0519 6.1875 11.0166 6.45359 11.8004 6.788C11.9992 6.87279 12.2423 6.99861 12.3992 7.25058C12.551 7.4943 12.5625 7.76396 12.5625 7.96336V8.4375H13.539Z" fill="#2563EB"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@ -0,0 +1,5 @@
<svg width="76" height="76" viewBox="0 0 76 76" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M38 76C58.9868 76 76 58.9868 76 38C76 17.0132 58.9868 0 38 0C17.0132 0 0 17.0132 0 38C0 58.9868 17.0132 76 38 76Z" fill="#EEF0F1"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M51.1733 57.7622C62.0865 57.7622 70.9333 48.9153 70.9333 38.0022C70.9333 27.089 62.0865 18.2422 51.1733 18.2422C40.2602 18.2422 31.4133 27.089 31.4133 38.0022C31.4133 48.9153 40.2602 57.7622 51.1733 57.7622Z" fill="#0B85F7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M58.5195 45.3418C62.577 45.3418 65.8662 42.0526 65.8662 37.9951C65.8662 33.9377 62.577 30.6484 58.5195 30.6484C54.4621 30.6484 51.1729 33.9377 51.1729 37.9951C51.1729 42.0526 54.4621 45.3418 58.5195 45.3418Z" fill="#04498A"/>
</svg>

After

Width:  |  Height:  |  Size: 834 B

@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.03065 3.99764C8.26317 3.88054 8.54566 3.9383 8.71355 4.13726C9.23007 4.74936 9.89464 5.29352 10.6354 5.81785L9.72603 6.72725C9.50636 6.94692 9.50636 7.30308 9.72603 7.52275C9.9457 7.74242 10.3019 7.74242 10.5215 7.52275L11.585 6.45927C11.7918 6.59413 12.0017 6.72898 12.2136 6.8647L11.226 7.85225C11.0064 8.07192 11.0064 8.42808 11.226 8.64775C11.4457 8.86742 11.8019 8.86742 12.0215 8.64775L13.1465 7.52275C13.1582 7.51112 13.1692 7.49912 13.1796 7.48677C14.718 8.49195 16.279 9.63033 16.9168 11.2027C16.9411 11.2625 16.9622 11.3222 16.9803 11.3817L16.9806 11.3828C17.1127 11.8191 17.0754 12.2159 16.9345 12.5584C16.8058 12.8746 16.5209 13.1948 16.2772 13.4015C15.7345 13.8288 14.9919 14.0626 14.3247 14.0626L5.92164 14.0626C4.86348 14.0626 3.99982 14.0627 3.32006 13.9504C2.60001 13.8314 2.01027 13.5755 1.56067 13.0137C1.22687 12.5966 1.05311 12.0717 0.979841 11.5144C0.961047 11.3715 0.948942 11.227 0.942378 11.0826C0.934315 10.905 0.934423 10.7232 0.94166 10.5385C0.963373 9.98453 1.04925 9.40482 1.17116 8.8386C1.4956 7.33173 2.09664 5.82445 2.56332 4.97836C2.67903 4.76857 2.91492 4.6551 3.15104 4.69564C3.38717 4.73618 3.5717 4.92184 3.61081 5.1582C3.69759 5.68261 4.19703 6.0438 5.09532 6.21638C5.66248 6.32535 6.27655 6.33177 6.76992 6.29194C6.63744 5.73186 6.71839 5.252 6.96967 4.85529C7.27174 4.37839 7.79 4.11837 8.03065 3.99764ZM3.50395 12.8401C2.95615 12.7496 2.6605 12.5866 2.43949 12.3104C2.2163 12.1039 2.11095 11.5428 2.08618 11.2881C4.83108 12.1696 6.76679 12.4037 9.18511 11.6392C9.56462 11.5192 9.82978 11.4356 10.0297 11.3827C10.0996 11.3392 10.3337 11.2943 10.7118 11.4627C10.967 11.5706 11.3125 11.7316 11.7922 11.9553C12.8348 12.4415 14.2288 12.776 15.7098 12.4027C15.3837 12.6763 15.0127 12.9373 14.3252 12.9373H5.97152C4.85183 12.9373 4.08149 12.9355 3.50395 12.8401Z" fill="#EC1C2B"/>
<path d="M10.8513 5.60225L10.6354 5.81785C10.9407 6.03391 11.2589 6.24658 11.585 6.45927L11.6468 6.39775C11.8665 6.17808 11.8665 5.82192 11.6468 5.60225C11.4271 5.38258 11.071 5.38258 10.8513 5.60225Z" fill="#EC1C2B"/>
<path d="M12.3513 6.72725L12.2136 6.8647L12.3515 6.95237C12.5804 7.09893 12.8113 7.24671 13.0424 7.39677C13.0882 7.42655 13.1336 7.45675 13.1796 7.48677C13.3653 7.26576 13.3547 6.9352 13.1468 6.72725C12.9271 6.50758 12.571 6.50758 12.3513 6.72725Z" fill="#EC1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1,9 @@
<svg width="78" height="66" viewBox="0 0 78 66" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="52" width="7" height="14" rx="1" fill="#EEF0F1"/>
<rect x="11" y="8" width="7" height="58" rx="1" fill="#EEF0F1"/>
<rect x="22" y="42" width="7" height="24" rx="1" fill="#EEF0F1"/>
<rect x="33" y="25" width="7" height="41" rx="1" fill="#EEF0F1"/>
<rect x="44" y="57" width="7" height="9" rx="1" fill="#EEF0F1"/>
<rect x="55" y="36" width="7" height="30" rx="1" fill="#EEF0F1"/>
<rect x="66" y="50" width="7" height="16" rx="1" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 556 B

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.98294 0.937501L10.0171 0.937501C10.6943 0.937478 11.2658 0.937458 11.7187 0.990317C12.1912 1.04547 12.653 1.16885 13.0279 1.50686C13.1968 1.65917 13.3405 1.83674 13.4532 2.03335C13.6745 2.4193 13.7109 2.83 13.676 3.23706C14.4077 3.32152 15.0147 3.51453 15.4891 3.98974C15.9403 4.44178 16.1344 5.01085 16.2251 5.68681C16.3125 6.33802 16.3125 7.16608 16.3125 8.19364L16.3125 12.0323C16.3125 13.0598 16.3125 13.8879 16.2251 14.5391C16.1344 15.2151 15.9403 15.7841 15.4891 16.2362C15.0377 16.6883 14.4692 16.8829 13.7939 16.9739C13.1437 17.0614 12.3169 17.0614 11.2912 17.0614L6.70878 17.0614C5.68314 17.0614 4.85635 17.0614 4.20606 16.9739C3.5308 16.8829 2.96232 16.6883 2.51092 16.2362C2.05966 15.7841 1.8656 15.2151 1.77488 14.5391C1.68747 13.8879 1.68749 13.0599 1.6875 12.0323L1.6875 8.19363C1.68749 7.16609 1.68747 6.33801 1.77488 5.68681C1.8656 5.01085 2.05966 4.44178 2.51092 3.98974C2.98532 3.51453 3.59231 3.32152 4.32401 3.23706C4.28909 2.83 4.32552 2.4193 4.54679 2.03335C4.65951 1.83674 4.80316 1.65917 4.97208 1.50686C5.34697 1.16885 5.80878 1.04547 6.2813 0.990317C6.73419 0.937458 7.30573 0.937478 7.98294 0.937501ZM6.4505 2.40548C6.10231 2.44612 5.99439 2.51431 5.94431 2.55945C5.88697 2.61115 5.839 2.67071 5.80176 2.73567C5.77228 2.7871 5.7302 2.8956 5.7731 3.22603C5.81755 3.56848 5.93319 4.02061 6.11117 4.70847C6.25294 5.25636 6.34684 5.61587 6.44666 5.88513C6.54128 6.14037 6.61985 6.25188 6.69652 6.32481C6.75734 6.38266 6.82512 6.43385 6.89865 6.47724C6.99346 6.53318 7.1292 6.58098 7.41292 6.60821C7.60338 6.62648 7.83044 6.63336 8.12257 6.63595L8.66354 5.01303C8.79452 4.62008 9.21926 4.40771 9.61222 4.53869C10.0052 4.66968 10.2175 5.09442 10.0866 5.48738L9.70335 6.63701C10.0818 6.63557 10.3604 6.62996 10.5871 6.60821C10.8708 6.58098 11.0065 6.53318 11.1014 6.47724C11.1749 6.43385 11.2427 6.38266 11.3035 6.32481C11.3801 6.25188 11.4587 6.14037 11.5533 5.88513C11.6532 5.61587 11.7471 5.25636 11.8888 4.70847C12.0668 4.02061 12.1825 3.56848 12.2269 3.22603C12.2698 2.8956 12.2277 2.7871 12.1982 2.73567C12.161 2.67071 12.113 2.61115 12.0557 2.55945C12.0056 2.51431 11.8977 2.44612 11.5495 2.40548C11.1914 2.36368 10.706 2.3625 9.9734 2.3625H8.0266C7.29399 2.3625 6.80862 2.36368 6.4505 2.40548ZM7.5 12.75C7.08579 12.75 6.75 13.0858 6.75 13.5C6.75 13.9142 7.08579 14.25 7.5 14.25H10.5C10.9142 14.25 11.25 13.9142 11.25 13.5C11.25 13.0858 10.9142 12.75 10.5 12.75H7.5Z" fill="#FFA800"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -0,0 +1,9 @@
<svg width="78" height="66" viewBox="0 0 78 66" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="52" width="7" height="14" rx="1" fill="#EEF0F1"/>
<rect x="11" y="8" width="7" height="58" rx="1" fill="#EEF0F1"/>
<rect x="22" y="42" width="7" height="24" rx="1" fill="#EEF0F1"/>
<rect x="33" y="25" width="7" height="41" rx="1" fill="#EEF0F1"/>
<rect x="44" y="57" width="7" height="9" rx="1" fill="#EEF0F1"/>
<rect x="55" y="36" width="7" height="30" rx="1" fill="#EEF0F1"/>
<rect x="66" y="50" width="7" height="16" rx="1" fill="#FFA800"/>
</svg>

After

Width:  |  Height:  |  Size: 556 B

@ -3,6 +3,8 @@
"arabic": "العربية",
"login": "تسجيل الدخول",
"noDataAvailable": "لا توجد بيانات متاحة",
"noRatingAvailable": "لا يوجد تقييم متاح",
"doctorDoesNotHaveRating": "لم يحصل هذا الطبيب على أي تقييمات بعد.",
"confirm": "تأكيد",
"loadingText": "جاري التحميل، يرجى الانتظار...",
"kilometerUnit": "كم",
@ -187,7 +189,7 @@
"lastName": "اسم العائلة",
"female": "أنثى",
"male": "ذكر",
"preferredLanguage": "اللغة المفضلة *",
"preferredLanguage": "اللغة المفضلة",
"locationsRegister": "أين ترغب في إنشاء هذا الملف؟",
"ksa": "السعودية",
"dubai": "دبي",
@ -301,7 +303,7 @@
"vitalSignsSubTitle": "التقارير",
"myMedical": "نشط",
"myMedicalSubtitle": "الأدوية",
"myDoctor": "طبيبي",
"myDoctor": "أطبائي",
"myDoctorSubtitle": "القائمة",
"eye": "العين",
"eyeSubtitle": "القياس",
@ -363,7 +365,7 @@
"paymentOnline": "الخدمة",
"onlineCheckIn": "تسجيل الوصول عبر الإنترنت",
"myBalances": "رصيدي",
"myWallet": "محف<EFBFBD><EFBFBD>تي",
"myWallet": "محفظتي",
"balanceAmount": "مبلغ المحفظة",
"totalBalance": "إجمالي الرصيد",
"createAdvancedPayment": "إعادة شحن المحفظة",
@ -487,7 +489,7 @@
"services2": "الخدمات",
"cantSeeProfile": "لرؤية ملفك الطبي، يرجى تسجيل الدخول أو التسجيل الآن",
"loginRegisterNow": "تسجيل الدخول أو التسجيل الآن",
"hmgPharmacy": "صيدلية مجموعة الحبيب الطبية",
"hmgPharmacy": "صيدلية الحبيب",
"ecommerceSolution": "حلول التجارة الإلكترونية",
"comprehensive": "شامل",
"onlineConsulting": "استشارات عبر الإنترنت",
@ -858,7 +860,7 @@
"onboardingBody1": "ببضع نقرات فقط يمكنك استشارة الطبيب الذي تختاره.",
"onboardingHeading2": "الوصول إلى السجل الطبي بين يديك",
"onboardingBody2": "تتبع تاريخك الطبي بما في ذلك الفحوصات المخبرية، الوصفات الطبية، التأمين، وغيرها.",
"hmgHospitals": "مستشفيات مجموعة الحبيب الطبية",
"hmgHospitals": "مستشفيات الحبيب",
"hmcMedicalClinic": "مراكز مجموعة الحبيب الطبية",
"applyFilter": "تطبيق الفلتر",
"facilityAndLocation": "المرفق والموقع",
@ -908,6 +910,7 @@
"general": "عام",
"liveCare": "لايف كير",
"recentVisits": "الزيارات الأخيرة",
"favouriteDoctors": "الأطباء المفضلون",
"searchByClinic": "البحث حسب العيادة",
"tapToSelectClinic": "انقر لاختيار العيادة",
"searchByDoctor": "البحث حسب الطبيب",
@ -1584,10 +1587,26 @@
"reschedulingAppo": "إعادة جدولة الموعد، يرجى الانتظار...",
"invalidEligibility": "لا يمكنك إجراء الدفع عبر الإنترنت لأنك غير مؤهل لاستخدام الخدمة المقدمة.",
"invalidInsurance": "لا يمكنك إجراء الدفع عبر الإنترنت لأنه ليس لديك تأمين صالح.",
"continueCash": "تواصل نقدا",
"applewatch": "ساعة آبل",
"applehealthapplicationshouldbeinstalledinyourphone": "يجب تثبيت تطبيق Apple Health على هاتفك",
"unabletodetectapplicationinstalledpleasecomebackonceinstalled": "لا يمكننا اكتشاف التطبيق المثبت على جهازك. يرجى العودة إلى هنا بمجرد تثبيت هذا التطبيق.",
"applewatchshouldbeconnected": "يجب توصيل ساعة آبل",
"samsungwatch": "ساعة سامسونج",
"samsunghealthapplicationshouldbeinstalledinyourphone": "يجب تثبيت تطبيق Samsung Health على هاتفك",
"samsungwatchshouldbeconnected": "يجب توصيل ساعة سامسونج",
"huaweiwatch": "ساعة هواوي",
"huaweihealthapplicationshouldbeinstalledinyourphone": "يجب تثبيت تطبيق Huawei Health على هاتفك",
"huaweiwatchshouldbeconnected": "يجب توصيل ساعة هواوي",
"whoopwatch": "ساعة Whoop",
"whoophealthapplicationshouldbeinstalledinyourphone": "يجب تثبيت تطبيق Whoop Health على هاتفك",
"whoopwatchshouldbeconnected": "يجب توصيل ساعة Whoop",
"updatetheinformation": "سيتيح ذلك جمع أحدث المعلومات من ساعة آبل الخاصة بك",
"continueCash": "متابعة الدفع نقدًا",
"timeFor": "الوقت",
"hmgPolicies": "سياسات مجموعة الحبيب الطبية",
"darkMode": "المظهر الداكن",
"featureComingSoonDescription": "هذه الميزة ستتوفر قريباً. نحن نعمل جاهدين لإضافة ميزات أكثر تميزاً إلى التطبيق. انتظرونا لمتابعة التحديثات.",
"generateAiAnalysisResult": "قم بإجراء تحليل لهذا المختبر AI",
"ratings": "التقييمات",
"hmgPharmacyText": "صيدلية الحبيب، المتجر الصيدلاني الإلكتروني المتكامل الذي تقدمه لكم مجموعة خدمات الدكتور سليمان الحبيب الطبية.",
@ -1596,5 +1615,43 @@
"verifyInsurance": "التحقق من التأمين",
"tests": "تحليل",
"calendarPermissionAlert": "يرجى منح إذن الوصول إلى التقويم من إعدادات التطبيق لضبط تذكير تناول الدواء.",
"sortByLocation": "الترتيب حسب الموقع"
"sortByNearestLocation": "فرز حسب الأقرب إلى موقعك",
"giveLocationPermissionForNearestList": "يرجى منح إذن الوصول إلى الموقع من إعدادات التطبيق لعرض أقرب المواقع.",
"sortByLocation": "الترتيب حسب الموقع",
"timeForFirstReminder": "وقت التذكير الأول",
"reminderRemovalNote": "يمكنك إزالتها من التقويم الخاص بك لاحقاً عن طريق إيقاف تشغيل التذكير",
"communicationLanguage": "لغة التواصل",
"cmcServiceHeader": "فحص صحي شامل: تشخيص متقدم، معلومات صحية مفصلة",
"cmcServiceDescription": "احصل على معلومات تفصيلية عن صحتك من خلال خدمات التشخيص المتقدمة لدينا. افهم جسمك بشكل أفضل لمستقبل صحي.",
"eReferralServiceHeader": "نظام الإحالة الإلكترونية في مستشفى حبيب: تبسيط عملية إحالة المرضى",
"eReferralServiceDescription": "نُسهّل عملية نقل المرضى بسلاسة إلى مستشفى حبيب من خلال نظام الإحالة الإلكترونية الآمن لدينا. نضمن استمرارية الرعاية لكل مريض.",
"bloodDonationServiceHeader": "تبرع بالدم، أنقذ الأرواح. تبرعك يُحدث فرقاً.",
"bloodDonationServiceDescription": "تبرّع بالدم، وأنقذ الأرواح. تبرعك يبعث الأمل. انضم لحملة التبرع بالدم وكن شريان حياة للمحتاجين. كل قطرة تُحدث فرقًا!",
"healthTrackersServiceHeader": "تتبّع مؤشراتك الحيوية بسهولة ويسر",
"healthTrackersServiceDescription": "أدخل بياناتك لمراقبة معدل ضربات القلب وضغط الدم بشكل مستمر، بالإضافة إلى ملخصات دقيقة لأنشطتك اليومية. ابقَ على اطلاع وحسّن صحتك بسهولة.",
"waterConsumptionServiceHeader": "حافظ على رطوبتك، حافظ على صحتك. تتبع كمية الماء التي تشربها يومياً بكل سهولة.",
"waterConsumptionServiceDescription": "أروِ عطشك، وتابع صحتك. راقب كمية الماء التي تتناولها يومياً بكل سهولة باستخدام تطبيقنا سهل الاستخدام، مما يضمن لك الترطيب الأمثل والصحة الجيدة.",
"smartWatchServiceHeader": "قم بمزامنة ساعتك الذكية مع تطبيقات الصحة",
"smartWatchServiceDescription": "قم بتوصيل ساعتك الذكية بسلاسة بتطبيقنا الصحي لتتبع البيانات بسهولة والحصول على رؤى شخصية.",
"liveChatServiceHeader": "مساعدة الخبراء على مدار الساعة طوال أيام الأسبوع\n\nمساعدة",
"liveChatServiceDescription": "هل تحتاج إلى مساعدة؟ تتيح لك خدمة الدردشة المباشرة لدينا التواصل مع فريق دعم الخبراء للإجابة على أي أسئلة لديك حول الميزات أو الإعدادات أو استكشاف الأخطاء وإصلاحها.",
"emergencyServiceHeader": "تسجيل الوصول إلى قسم الطوارئ، أسرع من أي وقت مضى. اتصل بالإسعاف / فريق الاستجابة السريعة على الفور",
"emergencyServiceDescription": "هل تواجه حالة طبية طارئة؟ سيارات الإسعاف وفرق الاستجابة السريعة لدينا جاهزة على مدار الساعة. بالإضافة إلى ذلك، يمكنك تسجيل دخولك إلى قسم الطوارئ بسرعة لتلقي رعاية أسرع.",
"homeHealthCareServiceHeader": "صحتك، في أبهى صورها. رعاية فائقة الجودة، تصلك إلى عتبة دارك.",
"homeHealthCareServiceDescription": "نقدم لكم رعاية صحية عالية الجودة تصلكم إلى عتبة منزلكم. ممرضات ذوات خبرة يقدمون رعاية حانية في راحة منزلكم.",
"profileOnlyText": "الملف الشخصي",
"information": "معلومة",
"noFavouriteDoctors": "ليس لديك أي قائمة مفضلة حتى الآن",
"addDoctors": "إضافة الأطباء",
"favouriteList": "قائمة المفضلة",
"later": "لاحقاً",
"cancelAppointmentConfirmMessage": "هل أنت متأكد من رغبتك في إلغاء هذا الموعد؟",
"acknowledged": "معترف به",
"searchLabResults": "بحث نتائج المختبر",
"callForAssistance": "اتصل للحصول على المساعدة الفورية",
"oneWaySubtitle": "نقل من الموقع إلى المستشفى",
"twoWaySubtitle": "نقل من الموقع إلى المستشفى والعودة مرة أخرى",
"toHospitalSubtitle": "نقل من موقعك الحالي إلى المستشفى",
"fromHospitalSubtitle": "نقل من المستشفى إلى منزلك"
}

@ -3,6 +3,8 @@
"arabic": "Arabic",
"login": "Login",
"noDataAvailable": "No Data Available",
"noRatingAvailable": "No Rating Available",
"doctorDoesNotHaveRating": "This doctor does not have any ratings yet.",
"confirm": "Confirm",
"loadingText": "Loading, please wait...",
"kilometerUnit": "KM",
@ -187,7 +189,7 @@
"lastName": "Last Name",
"female": "Female",
"male": "Male",
"preferredLanguage": "Preferred Language *",
"preferredLanguage": "Preferred Language",
"locationsRegister": "Where do you want to create this file?",
"ksa": "KSA",
"dubai": "Dubai",
@ -300,7 +302,7 @@
"vitalSignsSubTitle": "Reports",
"myMedical": "Active",
"myMedicalSubtitle": "Medications",
"myDoctor": "My Doctor",
"myDoctor": "My Doctors",
"myDoctorSubtitle": "List",
"eye": "Eye",
"eyeSubtitle": "Measurement",
@ -902,6 +904,7 @@
"general": "General",
"liveCare": "LiveCare",
"recentVisits": "Recent Visits",
"favouriteDoctors": "Favourite Doctors",
"searchByClinic": "Search By Clinic",
"tapToSelectClinic": "Tap to select clinic",
"searchByDoctor": "Search By Doctor",
@ -1579,8 +1582,23 @@
"invalidEligibility": "You cannot make online payment because you are not eligible to use the provided service.",
"invalidInsurance": "You cannot make online payment because you do not have a valid insurance.",
"continueCash": "Continue as cash",
"applewatch": "Apple Watch",
"applehealthapplicationshouldbeinstalledinyourphone": "Apple Health application should be installed in your phone",
"unabletodetectapplicationinstalledpleasecomebackonceinstalled": "We are unable to detect the application installed in your device. Please come back here once you have installed this application.",
"applewatchshouldbeconnected": "Apple Watch should be connected",
"samsungwatch": "Samsung Watch",
"samsunghealthapplicationshouldbeinstalledinyourphone": "Samsung Health application should be installed in your phone",
"samsungwatchshouldbeconnected": "Samsung Watch should be connected",
"huaweiwatch": "Huawei Watch",
"huaweihealthapplicationshouldbeinstalledinyourphone": "Huawei Health application should be installed in your phone",
"huaweiwatchshouldbeconnected": "Huawei Watch should be connected",
"whoopwatch": "Whoop Watch",
"whoophealthapplicationshouldbeinstalledinyourphone": "Whoop Health application should be installed in your phone",
"whoopwatchshouldbeconnected": "Whoop Watch should be connected",
"updatetheinformation": "This will allow to gather the most up to date information from your apple watch",
"timeFor": "Time For",
"hmgPolicies": "HMG Policies",
"featureComingSoonDescription": "Feature is coming soon. We are actively working to bring more exciting features into the app. Stay tuned for updates.",
"darkMode": "Dark Mode",
"generateAiAnalysisResult": "Generate AI analysis for this result",
"ratings": "Ratings",
@ -1590,5 +1608,42 @@
"verifyInsurance": "Verify Insurance",
"tests": "tests",
"calendarPermissionAlert": "Please grant calendar access permission from app settings to set medication reminder.",
"sortByLocation": "Sort by location"
"timeForFirstReminder": "Time for 1st reminder",
"reminderRemovalNote": "You can remove it from your calendar later by switching off the reminder",
"sortByLocation": "Sort by location",
"sortByNearestLocation": "Sort by nearest to your location",
"giveLocationPermissionForNearestList": "Please grant location permission from app settings to see the nearest locations.",
"communicationLanguage": "Communication Language",
"cmcServiceHeader": "Complete Health Checkup: Advanced diagnostics, Detailed Health insights",
"cmcServiceDescription": "Get detailed insights into your health with our advanced diagnostics. Understand your body better for a healthier future.",
"eReferralServiceHeader": "HMG Hospital E-Referral: Streamlined patient referrals",
"eReferralServiceDescription": "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.",
"bloodDonationServiceHeader": "Give Blood, Save Lives. Your donation makes a difference.",
"bloodDonationServiceDescription": "Donate blood, empower lives. Your contribution creates hope. Join our blood drive and be a lifeline for those in need. Every drop counts!",
"healthTrackersServiceHeader": "Track Your Vitals with Ease and effortlessly ",
"healthTrackersServiceDescription": "Input your metrics for continuous heart rate monitoring, blood pressure and precise daily activity summaries. Stay informed and optimize your well-being with ease.",
"waterConsumptionServiceHeader": "Stay Hydrated, Stay Healthy. Track your daily water intake with ease.",
"waterConsumptionServiceDescription": "Quench your thirst, track your health. Effortlessly monitor your daily water intake with our user-friendly app, ensuring optimal hydration and well-being.",
"smartWatchServiceHeader": "Sync Your Smartwatch with Health Apps",
"smartWatchServiceDescription": "Seamlessly connect your smartwatch to our health app for effortless data tracking and personalized insights.",
"liveChatServiceHeader": "24/7 Expert\nAssistance",
"liveChatServiceDescription": "Need help ? Our live chat connects you with expert support for any questions about features, settings, or troubleshooting.",
"emergencyServiceHeader": "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly",
"emergencyServiceDescription": "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.",
"homeHealthCareServiceHeader": "Your Health, Elevated. Premium care, delivered to your doorstep.",
"homeHealthCareServiceDescription": "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.",
"profileOnlyText": "Profile",
"information": "Information",
"noFavouriteDoctors": "You don't have any favourite list yet",
"addDoctors": "Add Doctors",
"favouriteList": "Favourite List",
"later": "Later",
"cancelAppointmentConfirmMessage": "Are you sure you want to cancel this appointment?",
"acknowledged": "Acknowledged",
"searchLabResults": "Search lab results",
"callForAssistance": "Call for immediate assistance",
"oneWaySubtitle": "Pickup from location to hospital",
"twoWaySubtitle": "Round trip from location to hospital and back",
"toHospitalSubtitle": "Transfer from your current location to the hospital",
"fromHospitalSubtitle": "Transfer from the hospital back to your home"
}

@ -0,0 +1,543 @@
PODS:
- amazon_payfort (1.1.4):
- Flutter
- PayFortSDK
- audio_session (0.0.1):
- Flutter
- barcode_scan2 (0.0.1):
- Flutter
- SwiftProtobuf (~> 1.33)
- connectivity_plus (0.0.1):
- Flutter
- CryptoSwift (1.8.4)
- device_calendar (0.0.1):
- Flutter
- device_calendar_plus_ios (0.0.1):
- Flutter
- device_info_plus (0.0.1):
- Flutter
- DKImagePickerController/Core (4.3.9):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.9)
- DKImagePickerController/PhotoGallery (4.3.9):
- DKImagePickerController/Core
- DKPhotoGallery
- DKImagePickerController/Resource (4.3.9)
- DKPhotoGallery (0.0.19):
- DKPhotoGallery/Core (= 0.0.19)
- DKPhotoGallery/Model (= 0.0.19)
- DKPhotoGallery/Preview (= 0.0.19)
- DKPhotoGallery/Resource (= 0.0.19)
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Core (0.0.19):
- DKPhotoGallery/Model
- DKPhotoGallery/Preview
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Model (0.0.19):
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Preview (0.0.19):
- DKPhotoGallery/Model
- DKPhotoGallery/Resource
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Resource (0.0.19):
- SDWebImage
- SwiftyGif
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Firebase/Analytics (11.15.0):
- Firebase/Core
- Firebase/Core (11.15.0):
- Firebase/CoreOnly
- FirebaseAnalytics (~> 11.15.0)
- Firebase/CoreOnly (11.15.0):
- FirebaseCore (~> 11.15.0)
- Firebase/Messaging (11.15.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 11.15.0)
- firebase_analytics (11.6.0):
- Firebase/Analytics (= 11.15.0)
- firebase_core
- Flutter
- firebase_core (3.15.2):
- Firebase/CoreOnly (= 11.15.0)
- Flutter
- firebase_messaging (15.2.10):
- Firebase/Messaging (= 11.15.0)
- firebase_core
- Flutter
- FirebaseAnalytics (11.15.0):
- FirebaseAnalytics/Default (= 11.15.0)
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseAnalytics/Default (11.15.0):
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleAppMeasurement/Default (= 11.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseCore (11.15.0):
- FirebaseCoreInternal (~> 11.15.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1)
- FirebaseCoreInternal (11.15.0):
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- FirebaseInstallations (11.15.0):
- FirebaseCore (~> 11.15.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- PromisesObjC (~> 2.4)
- FirebaseMessaging (11.15.0):
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleDataTransport (~> 10.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Reachability (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- nanopb (~> 3.30910.0)
- FLAnimatedImage (1.0.17)
- Flutter (1.0.0)
- flutter_callkit_incoming (0.0.1):
- CryptoSwift
- Flutter
- flutter_inappwebview_ios (0.0.1):
- Flutter
- flutter_inappwebview_ios/Core (= 0.0.1)
- OrderedSet (~> 6.0.3)
- flutter_inappwebview_ios/Core (0.0.1):
- Flutter
- OrderedSet (~> 6.0.3)
- flutter_ios_voip_kit_karmm (0.8.0):
- Flutter
- flutter_local_notifications (0.0.1):
- Flutter
- flutter_nfc_kit (3.6.0):
- Flutter
- flutter_zoom_videosdk (0.0.1):
- Flutter
- ZoomVideoSDK/CptShare (= 2.1.10)
- ZoomVideoSDK/zm_annoter_dynamic (= 2.1.10)
- ZoomVideoSDK/zoomcml (= 2.1.10)
- ZoomVideoSDK/ZoomVideoSDK (= 2.1.10)
- fluttertoast (0.0.2):
- Flutter
- geolocator_apple (1.2.0):
- Flutter
- FlutterMacOS
- Google-Maps-iOS-Utils (5.0.0):
- GoogleMaps (~> 8.0)
- google_maps_flutter_ios (0.0.1):
- Flutter
- Google-Maps-iOS-Utils (< 7.0, >= 5.0)
- GoogleMaps (< 11.0, >= 8.4)
- GoogleAdsOnDeviceConversion (2.1.0):
- GoogleUtilities/Logger (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Core (11.15.0):
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Default (11.15.0):
- GoogleAdsOnDeviceConversion (= 2.1.0)
- GoogleAppMeasurement/Core (= 11.15.0)
- GoogleAppMeasurement/IdentitySupport (= 11.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/IdentitySupport (11.15.0):
- GoogleAppMeasurement/Core (= 11.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- GoogleMaps (8.4.0):
- GoogleMaps/Maps (= 8.4.0)
- GoogleMaps/Base (8.4.0)
- GoogleMaps/Maps (8.4.0):
- GoogleMaps/Base
- GoogleUtilities/AppDelegateSwizzler (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Privacy
- GoogleUtilities/Environment (8.1.0):
- GoogleUtilities/Privacy
- GoogleUtilities/Logger (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Privacy
- GoogleUtilities/MethodSwizzler (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilities/Network (8.1.0):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Privacy
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (8.1.0)":
- GoogleUtilities/Privacy
- GoogleUtilities/Privacy (8.1.0)
- GoogleUtilities/Reachability (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilities/UserDefaults (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- health (13.1.4):
- Flutter
- image_picker_ios (0.0.1):
- Flutter
- just_audio (0.0.1):
- Flutter
- FlutterMacOS
- local_auth_darwin (0.0.1):
- Flutter
- FlutterMacOS
- location (0.0.1):
- Flutter
- manage_calendar_events (0.0.1):
- Flutter
- map_launcher (0.0.1):
- Flutter
- MapboxCommon (23.11.0)
- MapboxCoreMaps (10.19.1):
- MapboxCommon (~> 23.11)
- MapboxCoreNavigation (2.19.0):
- MapboxDirections (~> 2.14)
- MapboxNavigationNative (< 207.0.0, >= 206.0.1)
- MapboxDirections (2.14.3):
- Polyline (~> 5.0)
- Turf (~> 2.8.0)
- MapboxMaps (10.19.0):
- MapboxCommon (= 23.11.0)
- MapboxCoreMaps (= 10.19.1)
- MapboxMobileEvents (= 2.0.0)
- Turf (= 2.8.0)
- MapboxMobileEvents (2.0.0)
- MapboxNavigation (2.19.0):
- MapboxCoreNavigation (= 2.19.0)
- MapboxMaps (~> 10.18)
- MapboxSpeech (~> 2.0)
- Solar-dev (~> 3.0)
- MapboxNavigationNative (206.2.2):
- MapboxCommon (~> 23.10)
- MapboxSpeech (2.1.1)
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0)
- nanopb/decode (3.30910.0)
- nanopb/encode (3.30910.0)
- network_info_plus (0.0.1):
- Flutter
- open_filex (0.0.2):
- Flutter
- OrderedSet (6.0.3)
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- PayFortSDK (3.2.1)
- permission_handler_apple (9.3.0):
- Flutter
- Polyline (5.1.0)
- PromisesObjC (2.4.0)
- SDWebImage (5.21.5):
- SDWebImage/Core (= 5.21.5)
- SDWebImage/Core (5.21.5)
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- Solar-dev (3.0.1)
- sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS
- SwiftProtobuf (1.33.3)
- SwiftyGif (5.4.5)
- Turf (2.8.0)
- url_launcher_ios (0.0.1):
- Flutter
- video_player_avfoundation (0.0.1):
- Flutter
- FlutterMacOS
- wakelock_plus (0.0.1):
- Flutter
- webview_flutter_wkwebview (0.0.1):
- Flutter
- FlutterMacOS
- ZoomVideoSDK/CptShare (2.1.10)
- ZoomVideoSDK/zm_annoter_dynamic (2.1.10)
- ZoomVideoSDK/zoomcml (2.1.10)
- ZoomVideoSDK/ZoomVideoSDK (2.1.10)
DEPENDENCIES:
- amazon_payfort (from `.symlinks/plugins/amazon_payfort/ios`)
- audio_session (from `.symlinks/plugins/audio_session/ios`)
- barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_calendar (from `.symlinks/plugins/device_calendar/ios`)
- device_calendar_plus_ios (from `.symlinks/plugins/device_calendar_plus_ios/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- FLAnimatedImage
- Flutter (from `Flutter`)
- flutter_callkit_incoming (from `.symlinks/plugins/flutter_callkit_incoming/ios`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_ios_voip_kit_karmm (from `.symlinks/plugins/flutter_ios_voip_kit_karmm/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_nfc_kit (from `.symlinks/plugins/flutter_nfc_kit/ios`)
- flutter_zoom_videosdk (from `.symlinks/plugins/flutter_zoom_videosdk/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
- google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`)
- health (from `.symlinks/plugins/health/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- just_audio (from `.symlinks/plugins/just_audio/darwin`)
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
- location (from `.symlinks/plugins/location/ios`)
- manage_calendar_events (from `.symlinks/plugins/manage_calendar_events/ios`)
- map_launcher (from `.symlinks/plugins/map_launcher/ios`)
- MapboxMaps (= 10.19.0)
- MapboxNavigation (= 2.19.0)
- network_info_plus (from `.symlinks/plugins/network_info_plus/ios`)
- open_filex (from `.symlinks/plugins/open_filex/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
SPEC REPOS:
trunk:
- CryptoSwift
- DKImagePickerController
- DKPhotoGallery
- Firebase
- FirebaseAnalytics
- FirebaseCore
- FirebaseCoreInternal
- FirebaseInstallations
- FirebaseMessaging
- FLAnimatedImage
- Google-Maps-iOS-Utils
- GoogleAdsOnDeviceConversion
- GoogleAppMeasurement
- GoogleDataTransport
- GoogleMaps
- GoogleUtilities
- MapboxCommon
- MapboxCoreMaps
- MapboxCoreNavigation
- MapboxDirections
- MapboxMaps
- MapboxMobileEvents
- MapboxNavigation
- MapboxNavigationNative
- MapboxSpeech
- nanopb
- OrderedSet
- PayFortSDK
- Polyline
- PromisesObjC
- SDWebImage
- Solar-dev
- SwiftProtobuf
- SwiftyGif
- Turf
- ZoomVideoSDK
EXTERNAL SOURCES:
amazon_payfort:
:path: ".symlinks/plugins/amazon_payfort/ios"
audio_session:
:path: ".symlinks/plugins/audio_session/ios"
barcode_scan2:
:path: ".symlinks/plugins/barcode_scan2/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
device_calendar:
:path: ".symlinks/plugins/device_calendar/ios"
device_calendar_plus_ios:
:path: ".symlinks/plugins/device_calendar_plus_ios/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
firebase_analytics:
:path: ".symlinks/plugins/firebase_analytics/ios"
firebase_core:
:path: ".symlinks/plugins/firebase_core/ios"
firebase_messaging:
:path: ".symlinks/plugins/firebase_messaging/ios"
Flutter:
:path: Flutter
flutter_callkit_incoming:
:path: ".symlinks/plugins/flutter_callkit_incoming/ios"
flutter_inappwebview_ios:
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
flutter_ios_voip_kit_karmm:
:path: ".symlinks/plugins/flutter_ios_voip_kit_karmm/ios"
flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_nfc_kit:
:path: ".symlinks/plugins/flutter_nfc_kit/ios"
flutter_zoom_videosdk:
:path: ".symlinks/plugins/flutter_zoom_videosdk/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
geolocator_apple:
:path: ".symlinks/plugins/geolocator_apple/darwin"
google_maps_flutter_ios:
:path: ".symlinks/plugins/google_maps_flutter_ios/ios"
health:
:path: ".symlinks/plugins/health/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
just_audio:
:path: ".symlinks/plugins/just_audio/darwin"
local_auth_darwin:
:path: ".symlinks/plugins/local_auth_darwin/darwin"
location:
:path: ".symlinks/plugins/location/ios"
manage_calendar_events:
:path: ".symlinks/plugins/manage_calendar_events/ios"
map_launcher:
:path: ".symlinks/plugins/map_launcher/ios"
network_info_plus:
:path: ".symlinks/plugins/network_info_plus/ios"
open_filex:
:path: ".symlinks/plugins/open_filex/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
SPEC CHECKSUMS:
amazon_payfort: 4ad7a3413acc1c4c4022117a80d18fee23c572d3
audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0
barcode_scan2: 4e4b850b112f4e29017833e4715f36161f987966
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90
device_calendar: b55b2c5406cfba45c95a59f9059156daee1f74ed
device_calendar_plus_ios: 2c04ad7643c6e697438216e33693b84e8ca45ded
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
firebase_analytics: 0e25ca1d4001ccedd40b4e5b74c0ec34e18f6425
firebase_core: 995454a784ff288be5689b796deb9e9fa3601818
firebase_messaging: f4a41dd102ac18b840eba3f39d67e77922d3f707
FirebaseAnalytics: 6433dfd311ba78084fc93bdfc145e8cb75740eae
FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09
FLAnimatedImage: bbf914596368867157cc71b38a8ec834b3eeb32b
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_callkit_incoming: cb8138af67cda6dd981f7101a5d709003af21502
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_ios_voip_kit_karmm: 371663476722afb631d5a13a39dee74c56c1abd0
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
flutter_nfc_kit: e1b71583eafd2c9650bc86844a7f2d185fb414f6
flutter_zoom_videosdk: 0f59e71685a03ddb0783ecc43bf3155b8599a7f5
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321
google_maps_flutter_ios: 3213e1e5f5588b6134935cb8fc59acb4e6d88377
GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64
GoogleAppMeasurement: 700dce7541804bec33db590a5c496b663fbe2539
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
health: 32d2fbc7f26f9a2388d1a514ce168adbfa5bda65
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed
local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb
location: 155caecf9da4f280ab5fe4a55f94ceccfab838f8
manage_calendar_events: fe1541069431af035ced925ebd9def8b4b271254
map_launcher: 8051ad5783913cafce93f2414c6858f2904fd8df
MapboxCommon: 119f3759f7dc9457f0695848108ab323eb643cb4
MapboxCoreMaps: ca17f67baced23f8c952166ac6314c35bad3f66c
MapboxCoreNavigation: 3be9990fae3ed732a101001746d0e3b4234ec023
MapboxDirections: d9ad8452e8927d95ed21e35f733834dbca7e0eb1
MapboxMaps: b7f29ec7c33f7dc6d2947c1148edce6db81db9a7
MapboxMobileEvents: d044b9edbe0ec7df60f6c2c9634fe9a7f449266b
MapboxNavigation: da9cf3d773ed5b0fa0fb388fccdaa117ee681f31
MapboxNavigationNative: 629e359f3d2590acd1ebbacaaf99e1a80ee57e42
MapboxSpeech: cd25ef99c3a3d2e0da72620ff558276ea5991a77
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
network_info_plus: cf61925ab5205dce05a4f0895989afdb6aade5fc
open_filex: 432f3cd11432da3e39f47fcc0df2b1603854eff1
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
PayFortSDK: 233eabe9a45601fdbeac67fa6e5aae46ed8faf82
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
Polyline: 2a1f29f87f8d9b7de868940f4f76deb8c678a5b1
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
Solar-dev: 4612dc9878b9fed2667d23b327f1d4e54e16e8d0
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
SwiftProtobuf: e1b437c8e31a4c5577b643249a0bb62ed4f02153
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
webview_flutter_wkwebview: 8ebf4fded22593026f7dbff1fbff31ea98573c8d
ZoomVideoSDK: 94e939820e57a075c5e712559f927017da0de06a
PODFILE CHECKSUM: 8235407385ddd5904afc2563d65406117a51993e
COCOAPODS: 1.16.2

@ -16,11 +16,11 @@ import GoogleMaps
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)
}
// 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

@ -200,7 +200,7 @@ class ApiClientImp implements ApiClient {
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 1231755;
// body['PatientID'] = 1018977;
// body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q";

@ -229,11 +229,12 @@ class ApiConsts {
static String getPatientBloodGroup = "services/PatientVarification.svc/REST/BloodDonation_GetBloodGroupDetails";
static String getPatientBloodAgreement = "Services/PatientVarification.svc/REST/CheckUserAgreementForBloodDonation";
static String getPatientBloodTypeNew = "Services/Patients.svc/REST/HIS_GetPatientBloodType_New";
static String getAiOverViewLabOrders = "Services/Patients.svc/REST/HMGAI_Lab_Analyze_Orders_API";
static String getAiOverViewLabOrder = "Services/Patients.svc/REST/HMGAI_Lab_Analyzer_API";
// ************ static values for Api ****************
static final double appVersionID = 20.5;
static final double appVersionID = 20.9;
// static final double appVersionID = 50.7;
static final int appChannelId = 3;
@ -468,6 +469,15 @@ var GET_DENTAL_DOCTORS_LIST_URL = "Services/Doctors.svc/REST/Dental_DoctorChiefC
//URL to get doctor free slots
var GET_DOCTOR_FREE_SLOTS = "Services/Doctors.svc/REST/GetDoctorFreeSlots";
//URL to check if doctor is favorite
var IS_FAVOURITE_DOCTOR = "Services/Patients.svc/REST/Patient_IsFavouriteDoctor";
//URL to get favorite doctors list
var GET_FAVOURITE_DOCTOR = "Services/Patients.svc/REST/Patient_GetFavouriteDoctor";
//URL to insert favorite doctor
var INSERT_FAVOURITE_DOCTOR = "Services/Patients.svc/REST/Patient_InsertFavouriteDoctor";
//URL to insert appointment
var INSERT_SPECIFIC_APPOINTMENT = "Services/Doctors.svc/REST/InsertSpecificAppointment";

@ -174,7 +174,7 @@ class AppAssets {
static const String warning = '$svgBasePath/warning.svg';
static const String share_location = '$svgBasePath/share_location.svg';
static const String to_arrow = '$svgBasePath/to_arrow.svg';
static const String dual_arrow = '$svgBasePath/to_arrow.svg';
static const String dual_arrow = '$svgBasePath/dual_arrow.svg';
static const String forward_arrow_medium = '$svgBasePath/forward_arrow_medium.svg';
static const String eReferral = '$svgBasePath/e-referral.svg';
static const String comprehensiveCheckup = '$svgBasePath/comprehensive_checkup.svg';
@ -182,6 +182,8 @@ class AppAssets {
static const String ic_rrt_vehicle = '$svgBasePath/ic_rrt_vehicle.svg';
static const String doctor_profile_rating_icon = '$svgBasePath/doctor_profile_rating_icon.svg';
static const String doctor_profile_reviews_icon = '$svgBasePath/doctor_profile_reviews_icon.svg';
static const String bookmark_icon = '$svgBasePath/bookmark_icon.svg';
static const String bookmark_filled_icon = '$svgBasePath/bookmark_filled_icon.svg';
static const String waiting_appointment_icon = '$svgBasePath/waitingAppo.svg';
static const String call_for_vitals = '$svgBasePath/call_for_vitals.svg';
static const String call_for_doctor = '$svgBasePath/call_for_doctor.svg';
@ -233,6 +235,21 @@ class AppAssets {
static const String forward_top_nav_icon = '$svgBasePath/forward_top_nav_icon.svg';
static const String back_top_nav_icon = '$svgBasePath/back_top_nav_icon.svg';
static const String bluetooth = '$svgBasePath/bluetooth.svg';
//smartwatch
static const String watchActivity = '$svgBasePath/watch_activity.svg';
static const String watchActivityTrailing = '$svgBasePath/watch_activity_trailing.svg';
static const String watchSteps= '$svgBasePath/watch_steps.svg';
static const String watchStepsTrailing= '$svgBasePath/watch_steps_trailing.svg';
static const String watchSleep= '$svgBasePath/watch_sleep.svg';
static const String watchSleepTrailing= '$svgBasePath/watch_sleep_trailing.svg';
static const String watchBmi= '$svgBasePath/watch_bmi.svg';
static const String watchBmiTrailing= '$svgBasePath/watch_bmi_trailing.svg';
static const String watchWeight= '$svgBasePath/watch_weight.svg';
static const String watchWeightTrailing= '$svgBasePath/watch_weight_trailing.svg';
static const String watchHeight= '$svgBasePath/watch_height.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';
@ -344,6 +361,15 @@ class AppAssets {
static const String homeHealthCareService = '$pngBasePath/home_health_care.png';
static const String pharmacyService = '$pngBasePath/pharmacy_service.png';
static const String bloodDonationService = '$pngBasePath/blood_donation_image.png';
static const String waterConsumptionService = '$pngBasePath/water_consumption_image.png';
static const String emergencyService = '$pngBasePath/emergency_services_image.png';
static const String cmcService = '$pngBasePath/cmc_services_image.png';
static const String eReferralService = '$pngBasePath/ereferral_services_image.png';
static const String carParkingService = '$pngBasePath/Carparking_services_image.png';
static const String smartWatchService = '$pngBasePath/smartwatch_services_image.png';
static const String healthTrackersService = '$pngBasePath/healthtrackers_services_image.png';
static const String livechatService = '$pngBasePath/livechat_services_image.png';
static const String maleImg = '$pngBasePath/male_img.png';
static const String femaleImg = '$pngBasePath/female_img.png';

@ -169,7 +169,7 @@ class AppState {
///this will be called if there is any problem in getting the user location
void resetLocation() {
userLong = 0.0;
userLong = 0.0;
userLat = 0.0;
}
setRatedVisible(bool value) {

@ -0,0 +1,18 @@
enum SmartWatchTypes{
apple,
samsung,
huawei,
whoop
}
class SmartwatchDetails {
final SmartWatchTypes watchType;
final String watchIcon;
final String smallIcon;
final String detailsTitle;
final String details;
final String secondTitle;
SmartwatchDetails(this.watchType, this.watchIcon, this.smallIcon, this.detailsTitle, this.details, this.secondTitle);
}

@ -101,9 +101,11 @@ class CalenderUtilsNew {
required String itemDescriptionN,
required String route,
Function(String)? onFailure,
String? prescriptionNumber}) async {
String? prescriptionNumber,
DateTime? scheduleDateTime,
}) async {
DateTime currentDay = DateTime.now();
DateTime actualDate = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 8, 0);
DateTime actualDate = scheduleDateTime??DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 8, 0);
print("the frequency is $frequencyNumber");
frequencyNumber ??= 2; //Some time frequency number is null so by default will be 2
int interval = calculateIntervalAsPerFrequency(frequencyNumber);

@ -1,7 +1,10 @@
import 'package:device_calendar/device_calendar.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:intl/intl.dart';
import '../app_state.dart' show AppState;
class DateUtil {
/// convert String To Date function
/// [date] String we want to convert
@ -198,6 +201,10 @@ class DateUtil {
}
}
static getMonthDayAsOfLang(int month){
return getIt.get<AppState>().isArabic()?getMonthArabic(month):getMonth(month);
}
/// get month by
/// [month] convert month number in to month name in Arabic
static getMonthArabic(int month) {
@ -268,6 +275,10 @@ class DateUtil {
return date ?? DateTime.now();
}
static getWeekDayAsOfLang(int weekDay){
return getIt.get<AppState>().isArabic()?getWeekDayArabic(weekDay):getWeekDayEnglish(weekDay);
}
/// get month by
/// [weekDay] convert week day in int to week day name
static getWeekDay(int weekDay) {
@ -580,6 +591,14 @@ class DateUtil {
return weekDayName; // Return as-is if not recognized
}
}
static String millisToHourMin(int milliseconds) {
int totalMinutes = (milliseconds / 60000).floor(); // convert ms min
int hours = totalMinutes ~/ 60; // integer division
int minutes = totalMinutes % 60; // remaining minutes
return '${hours} hr ${minutes} min';
}
}
extension OnlyDate on DateTime {

@ -15,37 +15,37 @@ class LoadingUtils {
static bool get isLoading => _isLoadingVisible;
static showFullScreenLoader({bool barrierDismissible = true, isSuccessDialog = false, String loadingText = "Loading, Please wait..."}) {
if (!_isLoadingVisible) {
_isLoadingVisible = true;
final context = _navigationService.navigatorKey.currentContext;
log("got the context in showFullScreenLoading");
if (context == null) return;
showDialog(
barrierDismissible: barrierDismissible,
context: context,
barrierColor: AppColors.blackColor.withOpacity(0.5),
useRootNavigator: false,
useSafeArea: false,
builder: (BuildContext context) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Material(
child: Center(
child: isSuccessDialog ? Utils.getSuccessWidget(loadingText: loadingText) : Utils.getLoadingWidget(loadingText: loadingText),
).paddingSymmetrical(24.w, 0),
),
);
}).then((value) {
_isLoadingVisible = false;
});
}
}
// static showFullScreenLoader({bool barrierDismissible = true, isSuccessDialog = false, String loadingText = "Loading, Please wait..."}) {
// if (!_isLoadingVisible) {
// _isLoadingVisible = true;
// final context = _navigationService.navigatorKey.currentContext;
// log("got the context in showFullScreenLoading");
// if (context == null) return;
//
// showDialog(
// barrierDismissible: barrierDismissible,
// context: context,
// barrierColor: AppColors.blackColor.withOpacity(0.5),
// useRootNavigator: false,
// useSafeArea: false,
// builder: (BuildContext context) {
// return Container(
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
// color: AppColors.whiteColor,
// borderRadius: 20.h,
// hasShadow: false,
// ),
// child: Material(
// child: Center(
// child: isSuccessDialog ? Utils.getSuccessWidget(loadingText: loadingText) : Utils.getLoadingWidget(loadingText: loadingText),
// ).paddingSymmetrical(24.w, 0),
// ),
// );
// }).then((value) {
// _isLoadingVisible = false;
// });
// }
// }
static hideFullScreenLoader() {
if (!_isLoadingVisible) return;

@ -152,9 +152,11 @@ class Utils {
static String getDayMonthYearDateFormatted(DateTime? dateTime) {
if (dateTime == null) return "";
return appState.isArabic()
? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}"
: "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}";
return
// appState.isArabic()
// ? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}"
// :
"${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}";
}
/// get month by
@ -382,7 +384,7 @@ class Utils {
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),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 8.h),
],
).center;
@ -396,7 +398,7 @@ class Utils {
Lottie.asset(AppAnimations.errorAnimation,
repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 8.h),
],
).center;
@ -405,6 +407,7 @@ class Utils {
static Widget getWarningWidget({
String? loadingText,
bool isShowActionButtons = false,
bool showOkButton = false,
Widget? bodyWidget,
Function? onConfirmTap,
Function? onCancelTap,
@ -457,7 +460,26 @@ class Utils {
),
],
)
: SizedBox.shrink(),
: showOkButton?
Row(
children: [
Expanded(
child: CustomButton(
text: LocaleKeys.ok.tr(),
onPressed: () async {
if (onConfirmTap != null) {
onConfirmTap();
}
},
backgroundColor: AppColors.bgGreenColor,
borderColor: AppColors.bgGreenColor,
textColor: Colors.white,
// icon: AppAssets.confirm,
),
),
],
)
:SizedBox.shrink(),
],
).center;
}
@ -748,21 +770,22 @@ class Utils {
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: 5.w,
children: [
Image.asset(AppAssets.mada, width: 25.h, height: 25.h),
Image.asset(AppAssets.mada, width: 35.h, height: 35.h),
Image.asset(
AppAssets.tamaraEng,
width: 25.h,
height: 25.h,
width: 35.h,
height: 35.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);
return Utils.buildSvgWithAssets(icon: AppAssets.tamara, width: 35.h, height: 35.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),
Image.asset(AppAssets.visa, width: 35.h, height: 35.h),
Image.asset(AppAssets.mastercard, width: 35.h, height: 25.h),
Image.asset(AppAssets.applePay, width: 35.h, height: 35.h),
],
);
}

@ -18,6 +18,7 @@ extension CapExtension on String {
String get needTranslation => this;
String get capitalizeFirstofEach => trim().isNotEmpty ? trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : "";
}
extension EmailValidator on String {
@ -72,13 +73,14 @@ extension EmailValidator on String {
int? maxlines,
FontStyle? fontStyle,
TextOverflow? textOverflow,
double letterSpacing = 0}) =>
double letterSpacing = 0, bool isEnglishOnly = false}) =>
Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: maxlines,
overflow: textOverflow,
style: TextStyle(
fontFamily: isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins',
fontSize: 9.f,
fontStyle: fontStyle ?? FontStyle.normal,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
@ -88,7 +90,7 @@ extension EmailValidator on String {
decorationColor: color ?? AppColors.blackColor),
);
Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0}) => Text(
Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0, bool isEnglishOnly = false,}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null,
@ -99,6 +101,7 @@ extension EmailValidator on String {
color: color ?? AppColors.blackColor,
letterSpacing: letterSpacing,
decoration: isUnderLine ? TextDecoration.underline : null,
fontFamily: isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins',
),
);
@ -293,17 +296,19 @@ extension EmailValidator on String {
style: TextStyle(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(
Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight, double? letterSpacing, bool isEnglishOnly = false,}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
fontFamily: (isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),
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(
Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing, bool isEnglishOnly = false,}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
fontFamily: (isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),
height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.f, letterSpacing: letterSpacing ?? -1, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
);

@ -45,6 +45,8 @@ abstract class AuthenticationRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> insertPatientDeviceData({required dynamic patientDeviceDataRequest});
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientDeviceData({required dynamic patientDeviceDataRequest});
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientBloodType();
}
class AuthenticationRepoImp implements AuthenticationRepo {
@ -656,4 +658,37 @@ class AuthenticationRepoImp implements AuthenticationRepo {
}
}
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientBloodType() async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getPatientBloodTypeNew,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
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()));
}
}
}

@ -31,6 +31,7 @@ import 'package:hmg_patient_app_new/features/authentication/models/resp_models/s
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.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/features/symptoms_checker/symptoms_checker_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';
@ -131,6 +132,8 @@ class AuthenticationViewModel extends ChangeNotifier {
pickedCountryByUAEUser = null;
_appState.setUserRegistrationPayload = RegistrationDataModelPayload();
_appState.setNHICUserData = CheckUserStatusResponseNHIC();
getIt.get<SymptomsCheckerViewModel>().setSelectedHeight(0);
getIt.get<SymptomsCheckerViewModel>().setSelectedWeight(0);
}
void onCountryChange(CountryEnum country) {
@ -624,8 +627,10 @@ class AuthenticationViewModel extends ChangeNotifier {
activation.list!.first.zipCode = selectedCountrySignup == CountryEnum.others ? '0' : selectedCountrySignup.countryCode;
_appState.setAuthenticatedUser(activation.list!.first);
_appState.setPrivilegeModelList(activation.list!.first.listPrivilege!);
// _appState.setUserBloodGroup = activation.patientBlodType ?? "N/A";
_appState.setUserBloodGroup = activation.patientBloodType ?? "N/A";
_appState.setUserBloodGroup = activation.patientBlodType ?? "N/A";
// Fetch patient blood type from new API
await getPatientBloodTypeNew();
}
// _appState.setUserBloodGroup = (activation.patientBlodType ?? "");
_appState.setAppAuthToken = activation.authenticationTokenId;
@ -860,17 +865,17 @@ class AuthenticationViewModel extends ChangeNotifier {
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.data is String) {
//TODO: This Section Need to Be Testing.
LoadingUtils.hideFullScreenLoader();
LoaderBottomSheet.hideLoader();
_dialogService.showExceptionBottomSheet(message: apiResponse.data, onOkPressed: () {}, onCancelPressed: () {});
//TODO: Here We Need to Show a Dialog Of Something in the case of Fail With OK and Cancel and the Display Variable WIll be result.
} else {
LoadingUtils.hideFullScreenLoader();
LoaderBottomSheet.hideLoader();
if (apiResponse.data["MessageStatus"] == 1) {
LoadingUtils.showFullScreenLoader(isSuccessDialog: true, loadingText: "Your medical file has been created successfully. \nPlease proceed to login.");
LoaderBottomSheet.showLoader(loadingText: "Your medical file has been created successfully. \nPlease proceed to login.");
//TODO: Here We Need to Show a Dialog Of Something in the case of Success.
await clearDefaultInputValues(); // This will Clear All Default Values Of User.
Future.delayed(Duration(seconds: 1), () {
LoadingUtils.hideFullScreenLoader();
LoaderBottomSheet.hideLoader();
// _navigationService.pushAndReplace(AppRoutes.loginScreen);
_navigationService.pushAndRemoveUntil(CustomPageRoute(page: LandingNavigation()), (r) => false);
_navigationService.push(CustomPageRoute(page: LoginScreen()));
@ -1153,4 +1158,29 @@ class AuthenticationViewModel extends ChangeNotifier {
_navigationService.pushAndReplace(AppRoutes.landingScreen);
}
}
Future<void> getPatientBloodTypeNew() async {
try {
final result = await _authenticationRepo.getPatientBloodType();
result.fold(
(failure) async {
// Log error but don't show to user, keep existing blood type
log("Failed to fetch blood type: ${failure.message}");
},
(apiResponse) {
if (apiResponse.messageStatus == 1 && apiResponse.data != null) {
// Extract blood type from response
String? bloodType = apiResponse.data['GetPatientBloodType'];
if (bloodType != null && bloodType.isNotEmpty) {
_appState.setUserBloodGroup = bloodType;
log("Blood type updated from new API: $bloodType");
}
}
},
);
} catch (e) {
log("Error calling getPatientBloodType: $e");
}
}
}

@ -107,6 +107,12 @@ abstract class BookAppointmentsRepo {
Function(String)? onError});
Future<Either<Failure, GenericApiModel<AppointmentNearestGateResponseModel>>> getAppointmentNearestGate({required int projectID, required int clinicID});
Future<Either<Failure, GenericApiModel<dynamic>>> isFavouriteDoctor(
{required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> insertFavouriteDoctor(
{required int patientID, required int projectID, required int clinicID, required int doctorID, required bool isActive, Function(dynamic)? onSuccess, Function(String)? onError});
}
class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@ -1133,4 +1139,86 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> isFavouriteDoctor(
{required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapRequest = {"PatientID": patientID, "ProjectID": projectID, "ClinicID": clinicID, "DoctorID": doctorID};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
IS_FAVOURITE_DOCTOR,
body: mapRequest,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
if (onError != null) {
onError(error);
}
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response["IsFavouriteDoctor"],
);
if (onSuccess != null) {
onSuccess(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<Either<Failure, GenericApiModel<dynamic>>> insertFavouriteDoctor(
{required int patientID, required int projectID, required int clinicID, required int doctorID, required bool isActive, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapRequest = {"PatientID": patientID, "ProjectID": projectID, "ClinicID": clinicID, "DoctorID": doctorID, "IsActive": isActive};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
INSERT_FAVOURITE_DOCTOR,
body: mapRequest,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
if (onError != null) {
onError(error);
}
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
);
if (onSuccess != null) {
onSuccess(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()));
}
}
}

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.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/location_util.dart';
@ -11,6 +12,7 @@ import 'package:hmg_patient_app_new/core/utils/loading_utils.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/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';
@ -98,6 +100,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isDoctorRatingDetailsLoading = false;
List<DoctorRateDetails> doctorDetailsList = [];
bool isFavouriteDoctor = false;
List<FreeSlot> slotsList = [];
List<TimeSlot> docFreeSlots = [];
List<TimeSlot> dayEvents = [];
@ -137,6 +141,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool applyFilters = false;
bool isWaitingAppointmentAvailable = false;
bool isPatientRescheduleAppointment = false;
bool isWaitingAppointmentSelected = false;
int waitingAppointmentProjectID = 0;
@ -148,6 +153,72 @@ class BookAppointmentsViewModel extends ChangeNotifier {
AppointmentNearestGateResponseModel? appointmentNearestGateResponseModel;
///variables for laser clinic
bool isLaserHospitalsLoading = false;
List<PatientDoctorAppointmentList> laserHospitalsList = [];
int laserHospitalHmgCount = 0;
int laserHospitalHmcCount = 0;
Future<void> getLaserHospitals({Function(dynamic)? onSuccess, Function(String)? onError}) async {
isLaserHospitalsLoading = true;
laserHospitalsList.clear();
laserHospitalHmgCount = 0;
laserHospitalHmcCount = 0;
notifyListeners();
final result = await bookAppointmentsRepo.getDoctorsList(253, 0, false, 0, '');
result.fold(
(failure) async {
isLaserHospitalsLoading = false;
notifyListeners();
onError?.call(failure.message);
},
(apiResponse) async {
if (apiResponse.messageStatus == 1) {
var doctorList = apiResponse.data!;
var regionList = await DoctorMapper.getMappedDoctor(
doctorList,
isArabic: _appState.isArabic(),
lat: _appState.userLat,
long: _appState.userLong,
);
var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0);
regionList = await DoctorMapper.sortList(isLocationEnabled, regionList);
// Flatten all hospitals across all regions into a single list
laserHospitalsList.clear();
Set<String> addedHospitals = {};
regionList.registeredDoctorMap?.forEach((region, regionData) {
for (var hospital in regionData?.hmgDoctorList ?? <PatientDoctorAppointmentList>[]) {
if (!addedHospitals.contains(hospital.filterName)) {
addedHospitals.add(hospital.filterName ?? '');
laserHospitalsList.add(hospital);
}
}
for (var hospital in regionData?.hmcDoctorList ?? <PatientDoctorAppointmentList>[]) {
if (!addedHospitals.contains(hospital.filterName)) {
addedHospitals.add(hospital.filterName ?? '');
laserHospitalsList.add(hospital);
}
}
});
laserHospitalHmgCount = laserHospitalsList.where((h) => h.isHMC != true).length;
laserHospitalHmcCount = laserHospitalsList.where((h) => h.isHMC == true).length;
isLaserHospitalsLoading = false;
notifyListeners();
onSuccess?.call(apiResponse);
} else {
isLaserHospitalsLoading = false;
notifyListeners();
onError?.call(apiResponse.errorMessage ?? 'Unknown error');
}
},
);
}
List<LaserCategoryType> femaleLaserCategory = [
LaserCategoryType(1, 'bodyString'),
LaserCategoryType(2, 'face'),
@ -155,7 +226,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
LaserCategoryType(11, 'retouch'),
];
List<LaserCategoryType> maleLaserCategory = [
LaserCategoryType(1, 'body'),
LaserCategoryType(1, 'bodyString'),
LaserCategoryType(2, 'face'),
LaserCategoryType(11, 'retouch'),
];
@ -287,6 +358,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
isContinueDentalPlan = false;
isChiefComplaintsListLoading = true;
isWaitingAppointmentSelected = false;
isPatientRescheduleAppointment = false;
bodyTypes = [maleLaserCategory, femaleLaserCategory];
// getLocation();
notifyListeners();
@ -425,6 +497,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
setIsPatientRescheduleAppointment(bool isPatientRescheduleAppointment) {
this.isPatientRescheduleAppointment = isPatientRescheduleAppointment;
notifyListeners();
}
void onTabChanged(int index) {
calculationID = null;
isGetDocForHealthCal = false;
@ -535,6 +612,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
//TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
Future<void> getDoctorsList({int projectID = 0, bool isNearest = true, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
filteredDoctorList.clear();
doctorsListGrouped.clear();
notifyListeners();
projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
final result =
await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, doctorName.isNotEmpty ? false : isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan);
@ -648,6 +728,17 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) {
doctorsProfileResponseModel = apiResponse.data!;
notifyListeners();
// Check if doctor is favorite after getting profile
if(_appState.isAuthenticated) {
checkIsFavouriteDoctor(
patientID: _appState.getAuthenticatedUser()!.patientId!,
projectID: doctorsProfileResponseModel.projectID ?? 0,
clinicID: doctorsProfileResponseModel.clinicID ?? 0,
doctorID: doctorsProfileResponseModel.doctorID ?? 0,
);
}
if (onSuccess != null) {
onSuccess(apiResponse);
}
@ -841,64 +932,114 @@ class BookAppointmentsViewModel extends ChangeNotifier {
print(failure);
onError!(failure.message);
},
(apiResponse) {
(apiResponse) async {
if (apiResponse.messageStatus == 2) {
// onError!(apiResponse);
LoadingUtils.hideFullScreenLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
loadingText: apiResponse.data["ErrorEndUserMessage"],
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
},
onConfirmTap: () async {
navigationService.pop();
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
originalClinicID: 0,
originalProjectID: 0,
appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
);
LoaderBottomSheet.showLoader(
loadingText: LocaleKeys.reschedulingAppo.tr(context: navigationService.navigatorKey.currentContext!),
);
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
if (isPatientRescheduleAppointment) {
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
originalClinicID: 0,
originalProjectID: 0,
appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
);
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
// LoaderBottomSheet.hideLoader();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
// LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoaderBottomSheet.hideLoader();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: LocaleKeys.bookingYourAppointment.tr());
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());
await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
navigationService.navigatorKey.currentContext!,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
showCommonBottomSheetWithoutHeight(
GetIt.instance<NavigationService>().navigatorKey.currentContext!,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
callBackFunc: () {
setIsPatientRescheduleAppointment(false);
Navigator.pushAndRemoveUntil(
navigationService.navigatorKey.currentContext!,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
},
isFullScreen: false,
isCloseButtonVisible: false,
isAutoDismiss: true
);
});
});
});
} else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
loadingText: apiResponse.data["ErrorEndUserMessage"],
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
},
onConfirmTap: () async {
navigationService.pop();
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
originalClinicID: 0,
originalProjectID: 0,
appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
);
LoaderBottomSheet.showLoader(
loadingText: LocaleKeys.reschedulingAppo.tr(context: navigationService.navigatorKey.currentContext!),
);
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
LoaderBottomSheet.hideLoader();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoaderBottomSheet.hideLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
showCommonBottomSheetWithoutHeight(
GetIt.instance<NavigationService>().navigatorKey.currentContext!,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
callBackFunc: () {
setIsPatientRescheduleAppointment(false);
Navigator.pushAndRemoveUntil(
navigationService.navigatorKey.currentContext!,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
},
isFullScreen: false,
isCloseButtonVisible: false,
isAutoDismiss: true
);
});
});
});
});
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
} else if (apiResponse.messageStatus == 1) {
if (apiResponse.data == null || apiResponse.data!.isEmpty) {
onError!("No free slots available".tr());
@ -935,7 +1076,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// onError!(apiResponse);
LoadingUtils.hideFullScreenLoader();
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
@ -971,22 +1112,28 @@ 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: LocaleKeys.bookingYourAppointment.tr());
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader();
LoaderBottomSheet.hideLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
navigationService.navigatorKey.currentContext!,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
});
showCommonBottomSheetWithoutHeight(
GetIt.instance<NavigationService>().navigatorKey.currentContext!,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
callBackFunc: () {
setIsPatientRescheduleAppointment(false);
Navigator.pushAndRemoveUntil(
navigationService.navigatorKey.currentContext!,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
},
isFullScreen: false,
isCloseButtonVisible: false,
isAutoDismiss: true
);
});
});
});
@ -1480,7 +1627,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
LoadingUtils.hideFullScreenLoader();
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
@ -1530,4 +1677,79 @@ class BookAppointmentsViewModel extends ChangeNotifier {
},
);
}
void toggleFavouriteDoctor() {
isFavouriteDoctor = !isFavouriteDoctor;
notifyListeners();
}
void setIsFavouriteDoctor(bool value) {
isFavouriteDoctor = value;
notifyListeners();
}
Future<void> checkIsFavouriteDoctor({required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.isFavouriteDoctor(
patientID: patientID,
projectID: projectID,
clinicID: clinicID,
doctorID: doctorID,
onSuccess: onSuccess,
onError: onError,
);
result.fold(
(failure) async {
if (onError != null) {
onError(failure.message);
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
if (onError != null) {
onError(apiResponse.errorMessage ?? "Failed to check favorite doctor");
}
} else if (apiResponse.messageStatus == 1) {
// Check the response for IsFavouriteDoctor flag
bool isFavorite = apiResponse.data;
setIsFavouriteDoctor(isFavorite);
if (onSuccess != null) {
onSuccess(apiResponse.data);
}
}
},
);
}
Future<void> insertFavouriteDoctor({required int patientID, required int projectID, required int clinicID, required int doctorID, required bool isActive, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.insertFavouriteDoctor(
patientID: patientID,
projectID: projectID,
clinicID: clinicID,
doctorID: doctorID,
isActive: isActive,
onSuccess: onSuccess,
onError: onError,
);
result.fold(
(failure) async {
if (onError != null) {
onError(failure.message);
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
if (onError != null) {
onError(apiResponse.errorMessage ?? "Failed to update favorite doctor");
}
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse.data);
}
}
},
);
}
}

@ -0,0 +1,81 @@
import 'dart:convert';
class GetFavoriteDoctorsListModel {
int? id;
int? projectId;
int? clinicId;
int? doctorId;
int? patientId;
bool? patientOutSa;
bool? isActive;
String? createdOn;
dynamic modifiedOn;
String? doctorImageUrl;
String? doctorName;
String? doctorTitle;
String? nationalityFlagUrl;
String? nationalityId;
String? nationalityName;
List<String>? speciality;
GetFavoriteDoctorsListModel({
this.id,
this.projectId,
this.clinicId,
this.doctorId,
this.patientId,
this.patientOutSa,
this.isActive,
this.createdOn,
this.modifiedOn,
this.doctorImageUrl,
this.doctorName,
this.doctorTitle,
this.nationalityFlagUrl,
this.nationalityId,
this.nationalityName,
this.speciality,
});
factory GetFavoriteDoctorsListModel.fromRawJson(String str) => GetFavoriteDoctorsListModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory GetFavoriteDoctorsListModel.fromJson(Map<String, dynamic> json) => GetFavoriteDoctorsListModel(
id: json["ID"],
projectId: json["ProjectID"],
clinicId: json["ClinicID"],
doctorId: json["DoctorID"],
patientId: json["PatientID"],
patientOutSa: json["PatientOutSA"],
isActive: json["IsActive"],
createdOn: json["CreatedOn"],
modifiedOn: json["ModifiedOn"],
doctorImageUrl: json["DoctorImageURL"],
doctorName: json["DoctorName"],
doctorTitle: json["DoctorTitle"],
nationalityFlagUrl: json["NationalityFlagURL"],
nationalityId: json["NationalityID"],
nationalityName: json["NationalityName"],
speciality: json["Speciality"] == null ? [] : List<String>.from(json["Speciality"]!.map((x) => x)),
);
Map<String, dynamic> toJson() => {
"ID": id,
"ProjectID": projectId,
"ClinicID": clinicId,
"DoctorID": doctorId,
"PatientID": patientId,
"PatientOutSA": patientOutSa,
"IsActive": isActive,
"CreatedOn": createdOn,
"ModifiedOn": modifiedOn,
"DoctorImageURL": doctorImageUrl,
"DoctorName": doctorName,
"DoctorTitle": doctorTitle,
"NationalityFlagURL": nationalityFlagUrl,
"NationalityID": nationalityId,
"NationalityName": nationalityName,
"Speciality": speciality == null ? [] : List<dynamic>.from(speciality!.map((x) => x)),
};
}

@ -61,4 +61,14 @@ class LaserBodyPart {
data['CategoryNameN'] = this.categoryNameN;
return data;
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is LaserBodyPart &&
runtimeType == other.runtimeType &&
id == other.id;
@override
int get hashCode => id.hashCode;
}

@ -530,6 +530,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
void setTransportationOption(PatientERTransportationMethod item) {
selectedTransportOption = item;
notifyListeners();
}
void updateCallingPlace(AmbulanceCallingPlace? value) {

@ -919,8 +919,6 @@ class HmgServicesRepoImp implements HmgServicesRepo {
@override
Future<Either<Failure, GenericApiModel<List<VitalSignResModel>>>> getPatientVitalSign() async {
Map<String, dynamic> requestBody = {
};
try {

@ -48,13 +48,14 @@ class VitalSignUiModel {
);
}
if (s.contains('low')) {
final Color yellowBg = AppColors.warningColor.withValues(alpha: 0.12);
// Warning for both low and overweight/underweight BMI, since they can indicate potential health issues.
if (s.contains('low') || s.contains('underweight') || s.contains('overweight')) {
final Color yellowBg = AppColors.highAndLow.withValues(alpha: 0.12);
return VitalSignUiModel(
iconBg: yellowBg,
iconFg: AppColors.warningColor,
iconFg: AppColors.highAndLow,
chipBg: yellowBg,
chipFg: AppColors.warningColor,
chipFg: AppColors.highAndLow,
);
}
@ -91,12 +92,27 @@ class VitalSignUiModel {
}
static String bmiStatus(dynamic bmi) {
if (bmi == null) return 'N/A';
final double bmiValue = double.tryParse(bmi.toString()) ?? 0;
if (bmiValue < 18.5) return 'Underweight';
if (bmiValue < 25) return 'Normal';
if (bmiValue < 30) return 'Overweight';
return 'High';
String bmiStatus = 'Normal';
final double bmiResult = double.tryParse(bmi.toString()) ?? 0;
if (bmiResult >= 30) {
bmiStatus = "High";
} else if (bmiResult < 30 && bmiResult >= 25) {
bmiStatus = "Overweight";
} else if (bmiResult < 25 && bmiResult >= 18.5) {
bmiStatus = "Normal";
} else if (bmiResult < 18.5) {
bmiStatus = "Underweight";
}
// if (bmi == null) return 'N/A';
// final double bmiValue = double.tryParse(bmi.toString()) ?? 0;
// if (bmiValue < 18.5) return 'Underweight';
// if (bmiValue < 25) return 'Normal';
// if (bmiValue < 30) return 'Overweight';
// return 'High';
return bmiStatus;
}
}

@ -4,6 +4,7 @@ import 'dart:core';
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/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
@ -71,6 +72,9 @@ class LabViewModel extends ChangeNotifier {
List<TestDetails> uniqueTestsList = [];
List<String> indexedCharacterForUniqueTest = [];
List<TestDetails> filteredUniqueTestsList = [];
List<String> filteredIndexedCharacterForUniqueTest = [];
double maxY = 0.0;
double minY = double.infinity;
double maxX = double.infinity;
@ -84,6 +88,9 @@ class LabViewModel extends ChangeNotifier {
LabOrderResponseByAi? labOrderResponseByAi;
LabOrdersResponseByAi? labOrdersResponseByAi;
bool isLabAIAnalysisNeedsToBeShown = true;
bool isLabResultsHistoryShowMore = false;
LabViewModel({required this.labRepo, required this.errorHandlerService, required this.navigationService});
initLabProvider() {
@ -93,6 +100,8 @@ class LabViewModel extends ChangeNotifier {
labOrderTests.clear();
isLabOrdersLoading = true;
isLabResultsLoading = true;
isLabAIAnalysisNeedsToBeShown = true;
isLabResultsHistoryShowMore = false;
patientLabOrdersByClinic.clear();
patientLabOrdersByHospital.clear();
patientLabOrdersViewList.clear();
@ -111,6 +120,16 @@ class LabViewModel extends ChangeNotifier {
notifyListeners();
}
setIsLabAIAnalysisNeedsToBeShown(bool isLabAIAnalysisNeedsToBeShown) {
this.isLabAIAnalysisNeedsToBeShown = isLabAIAnalysisNeedsToBeShown;
notifyListeners();
}
setIsLabResultsHistoryShowMore() {
isLabResultsHistoryShowMore = !isLabResultsHistoryShowMore;
notifyListeners();
}
void setIsSortByClinic(bool value) {
isSortByClinic = value;
patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
@ -218,7 +237,8 @@ class LabViewModel extends ChangeNotifier {
final clinicMap = <String, List<PatientLabOrdersResponseModel>>{};
final hospitalMap = <String, List<PatientLabOrdersResponseModel>>{};
if (query.isEmpty) {
// filteredLabOrders = List.from(patientLabOrders); // reset
filteredLabOrders = List.from(patientLabOrders); // reset
filteredUniqueTestsList = List.from(uniqueTestsList);
for (var order in patientLabOrders) {
final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
clinicMap.putIfAbsent(clinicKey, () => []).add(order);
@ -234,7 +254,10 @@ class LabViewModel extends ChangeNotifier {
final descriptions = order.testDetails?.map((d) => d.description?.toLowerCase()).toList() ?? [];
return descriptions.any((desc) => desc != null && desc.contains(query.toLowerCase()));
}).toList();
// patientLabOrders = filteredLabOrders;
filteredUniqueTestsList = uniqueTestsList.where((test) {
final desc = test.description?.toLowerCase() ?? '';
return desc.contains(query.toLowerCase());
}).toList();
for (var order in filteredLabOrders) {
final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
clinicMap.putIfAbsent(clinicKey, () => []).add(order);
@ -246,6 +269,14 @@ class LabViewModel extends ChangeNotifier {
patientLabOrdersByHospital = hospitalMap.values.toList();
patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
}
// Rebuild filtered indexed characters
filteredIndexedCharacterForUniqueTest = [];
for (var test in filteredUniqueTestsList) {
String label = test.description ?? "";
if (label.isEmpty) continue;
if (filteredIndexedCharacterForUniqueTest.contains(label[0].toLowerCase())) continue;
filteredIndexedCharacterForUniqueTest.add(label[0].toLowerCase());
}
notifyListeners();
}
@ -280,6 +311,10 @@ class LabViewModel extends ChangeNotifier {
for (var element in uniqueTests) {
labOrderTests.add(element.description ?? "");
}
// Initialize filtered lists with full data
filteredUniqueTestsList = List.from(uniqueTestsList);
filteredIndexedCharacterForUniqueTest = List.from(indexedCharacterForUniqueTest);
}
Future<void> getLabResultsByAppointmentNo(
@ -387,7 +422,8 @@ class LabViewModel extends ChangeNotifier {
LoaderBottomSheet.hideLoader();
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
var recentThree = sort(apiResponse.data!);
var sortedResult = sort(apiResponse.data!);
var recentThree = sortedResult.take(3).toList();
mainLabResults = recentThree;
double highRefrenceValue = double.negativeInfinity;
@ -395,11 +431,12 @@ class LabViewModel extends ChangeNotifier {
double lowRefenceValue = double.infinity;
String? flagForLowReferenceRange;
recentThree.reversed.forEach((element) {
sortedResult.toList().reversed.forEach((element) {
try {
var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
var resultValue = double.parse(element.resultValue!);
var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? "");
// var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? "");
var transformedValue = resultValue;
if (resultValue > maxY) {
maxY = resultValue;
maxX = maxY;
@ -431,9 +468,9 @@ class LabViewModel extends ChangeNotifier {
highRefrenceValue = maxY;
lowRefenceValue = minY;
}
//
if (minY > lowRefenceValue) {
minY = lowRefenceValue - 25;
minY = lowRefenceValue - getInterval();
}
this.flagForHighReferenceRange = flagForHighReferenceRange;
@ -442,12 +479,16 @@ class LabViewModel extends ChangeNotifier {
lowTransformedReferenceValue = double.parse(transformValueInRange(lowRefenceValue, flagForLowReferenceRange ?? "").toStringAsFixed(1));
this.highRefrenceValue = double.parse(highRefrenceValue.toStringAsFixed(1));
this.lowRefenceValue = double.parse(lowRefenceValue.toStringAsFixed(1));
if (maxY < highRefrenceValue) {
if(maxY<highRefrenceValue){
maxY = highRefrenceValue;
maxX = maxY;
}
maxY += 25;
minY -= 25;
// if (maxY < highRefrenceValue) {
// minY = highRefrenceValue - getInterval();
// }
// maxY += 25;
// minY -= 25;
LabResult recentResult = recentThree.first;
recentResult.uOM = unitOfMeasure;
checkIfGraphShouldBeDisplayed(recentResult);
@ -464,6 +505,17 @@ class LabViewModel extends ChangeNotifier {
},
);
}
num getInterval() {
// return .1;
var maxX = maxY;
if(maxX<1) return .2;
if(maxX >=1.0 && maxX < 5.0) return .3;
if(maxX >=5.0 && maxX < 10.0) return 1.5;
if(maxX >=10.0 && maxX < 50.0) return 2.5;
if(maxX >=50.0 && maxX < 100.0) return 5;
if(maxX >=100.0 && maxX < 200.0) return 10;
return 15;
}
void checkIfGraphShouldBeDisplayed(LabResult recentResult) {
shouldShowGraph = recentResult.checkIfGraphShouldBeDisplayed();
@ -586,7 +638,8 @@ class LabViewModel extends ChangeNotifier {
try {
var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
var resultValue = double.parse(element.resultValue!);
var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? "");
// var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? "");
var transformedValue = double.parse(element.resultValue!);
if (resultValue > maxY) {
maxY = resultValue;
}
@ -769,7 +822,13 @@ class LabViewModel extends ChangeNotifier {
Future<void> getAiOverviewLabOrders({required PatientLabOrdersResponseModel labOrder, required String loadingText}) async {
// LoadingUtils.showFullScreenLoader(loadingText: "Loading and analysing your data,\nPlease be patient and let the AI do the magic. \nPlease be patient, This might take some time.");
LoadingUtils.showFullScreenLoader(loadingText: loadingText);
// LoadingUtils.showFullScreenLoader(loadingText: loadingText);
LoaderBottomSheet.showLoader(
loadingText: loadingText,
showCloseButton: true,
onCloseTap: () {
setIsLabAIAnalysisNeedsToBeShown(false);
});
List<Map<String, dynamic>> results = [];
Map<String, dynamic> orderData = {"order_date": labOrder.orderDate ?? "", "clinic": labOrder.clinicDescription ?? "", "doctor": labOrder.doctorName ?? "", "results": []};
List<Map<String, dynamic>> testResults = [];
@ -792,16 +851,20 @@ class LabViewModel extends ChangeNotifier {
result.fold(
(failure) async {
LoadingUtils.hideFullScreenLoader();
await errorHandlerService.handleError(failure: failure);
if (isLabAIAnalysisNeedsToBeShown) {
await errorHandlerService.handleError(failure: failure);
}
},
(apiResponse) {
LoadingUtils.hideFullScreenLoader();
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
labOrdersResponseByAi = apiResponse.data;
navigationService.push(
MaterialPageRoute(builder: (_) => LabAiAnalysisDetailedPage()),
);
if (isLabAIAnalysisNeedsToBeShown) {
labOrdersResponseByAi = apiResponse.data;
navigationService.push(
MaterialPageRoute(builder: (_) => LabAiAnalysisDetailedPage()),
);
}
}
},
);
@ -810,7 +873,12 @@ class LabViewModel extends ChangeNotifier {
}
Future<void> getAiOverviewSingleLabResult({required String langId, required LabResult recentLabResult, required String loadingText}) async {
LoaderBottomSheet.showLoader(loadingText: loadingText);
LoaderBottomSheet.showLoader(
loadingText: loadingText,
showCloseButton: true,
onCloseTap: () {
setIsLabAIAnalysisNeedsToBeShown(false);
});
List<Map<String, dynamic>> results = [];
results.add({
"Description": recentLabResult.description ?? '',
@ -820,19 +888,24 @@ class LabViewModel extends ChangeNotifier {
"ReferanceRange": recentLabResult.referanceRange ?? '',
});
var payload = {"patient_id": currentlySelectedPatientOrder!.patientID, "language_id": langId, "lab_results": results};
// var payload = {"patient_id": currentlySelectedPatientOrder!.patientID, "language_id": langId, "lab_results": results};
var payload = {"patient_id": getIt.get<AppState>().getAuthenticatedUser()!.patientId, "language_id": langId, "lab_results": results};
final result = await labRepo.getPatientAiOverViewLabOrder(payload);
result.fold(
(failure) async {
LoaderBottomSheet.hideLoader();
await errorHandlerService.handleError(failure: failure);
if (isLabAIAnalysisNeedsToBeShown) {
await errorHandlerService.handleError(failure: failure);
}
},
(apiResponse) {
LoaderBottomSheet.hideLoader();
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
labOrderResponseByAi = apiResponse.data;
notifyListeners();
if (isLabAIAnalysisNeedsToBeShown) {
labOrderResponseByAi = apiResponse.data;
notifyListeners();
}
}
},
);

@ -43,9 +43,20 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
int hmgCount = 0;
int hmcCount = 0;
RegionBottomSheetType regionBottomSheetType = RegionBottomSheetType.FOR_REGION;
bool sortByLocation = false;
AppointmentViaRegionViewmodel({required this.navigationService,required this.appState});
void initSortByLocation() {
sortByLocation = (appState.userLat != 0.0) && (appState.userLong != 0.0);
notifyListeners();
}
void setSortByLocation(bool value) {
sortByLocation = value;
notifyListeners();
}
void setSelectedRegionId(String? regionId) {
selectedRegionId = regionId;
notifyListeners();
@ -76,6 +87,8 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
hmcCount = registeredDoctorMap.hmcSize;
hmgCount = registeredDoctorMap.hmgSize;
hospitalList!.sort((a, b) => num.parse(a.distanceInKMs!).compareTo(num.parse(b.distanceInKMs!)));
getDisplayList();
}
@ -122,6 +135,7 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
setFacility(null);
setBottomSheetType(RegionBottomSheetType.FOR_REGION);
setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION);
sortByLocation = false;
}
void setHospitalModel(PatientDoctorAppointmentList? hospital) {

@ -73,6 +73,7 @@ class PatientAppointmentHistoryResponseModel {
num? patientShare;
num? patientShareWithTax;
num? patientTaxAmount;
String? doctorNationalityFlagURL;
PatientAppointmentHistoryResponseModel({
this.setupID,
@ -148,6 +149,7 @@ class PatientAppointmentHistoryResponseModel {
this.patientShare,
this.patientShareWithTax,
this.patientTaxAmount,
this.doctorNationalityFlagURL,
});
PatientAppointmentHistoryResponseModel.fromJson(Map<String, dynamic> json) {
@ -235,6 +237,7 @@ class PatientAppointmentHistoryResponseModel {
patientShare = json['PatientShare'];
patientShareWithTax = json['PatientShareWithTax'];
patientTaxAmount = json['PatientTaxAmount'];
doctorNationalityFlagURL = json['DoctorNationalityFlagURL'];
}
Map<String, dynamic> toJson() {

@ -8,6 +8,7 @@ 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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_favorite_doctors_list.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/rate_appointment_resp_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
@ -51,6 +52,8 @@ abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientDoctorsList();
Future<Either<Failure, GenericApiModel<List<GetFavoriteDoctorsListModel>>>> getFavouriteDoctorsList();
Future<Either<Failure, GenericApiModel<dynamic>>> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
Future<Either<Failure, GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>>> getTamaraInstallmentsDetails();
@ -510,7 +513,10 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
try {
final list = response['PatientDoctorAppointmentResultList'];
final appointmentsList = list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientAppointmentHistoryResponseModel>();
List<PatientAppointmentHistoryResponseModel> appointmentsList =
list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientAppointmentHistoryResponseModel>();
// appointmentsList.removeWhere((element) => element.isActiveDoctorProfile == false);
apiResponse = GenericApiModel<List<PatientAppointmentHistoryResponseModel>>(
messageStatus: messageStatus,
@ -531,6 +537,56 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
}
}
@override
Future<Either<Failure, GenericApiModel<List<GetFavoriteDoctorsListModel>>>> getFavouriteDoctorsList() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<GetFavoriteDoctorsListModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_FAVOURITE_DOCTOR,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['Patient_GetFavouriteDoctorList'];
if (list == null || list.isEmpty) {
apiResponse = GenericApiModel<List<GetFavoriteDoctorsListModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: [],
);
return;
}
final appointmentsList = (list as List).map((item) => GetFavoriteDoctorsListModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetFavoriteDoctorsListModel>();
appointmentsList.removeWhere((element) => element.isActive == false);
apiResponse = GenericApiModel<List<GetFavoriteDoctorsListModel>>(
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<Either<Failure, GenericApiModel>> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}) async {
Map<String, dynamic> requestBody = {

@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.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/features/book_appointments/models/resp_models/get_favorite_doctors_list.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
@ -37,6 +38,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
bool isAppointmentPatientShareLoading = false;
bool isTimeLineAppointmentsLoading = false;
bool isPatientMyDoctorsLoading = false;
bool isPatientFavouriteDoctorsLoading = false;
bool isFavouriteDoctorsDataFetched = false;
bool isAppointmentDataToBeLoaded = true;
@ -64,6 +67,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
List<PatientAppointmentHistoryResponseModel> patientMyDoctorsList = [];
List<GetFavoriteDoctorsListModel> patientFavouriteDoctorsList = [];
List<PatientAppointmentHistoryResponseModel> patientEyeMeasurementsAppointmentsHistoryList = [];
// Grouping by Clinic and Hospital
@ -89,6 +94,10 @@ class MyAppointmentsViewModel extends ChangeNotifier {
selectedTabIndex = index;
start = null;
end = null;
// if (index == 0) {
// filteredAppointmentList.clear();
// filteredAppointmentList.addAll(patientAppointmentsHistoryList);
// }
notifyListeners();
}
@ -659,6 +668,51 @@ class MyAppointmentsViewModel extends ChangeNotifier {
);
}
Future<void> getPatientFavouriteDoctors({bool forceRefresh = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
// If data is already fetched and not forcing refresh, skip API call
if (isFavouriteDoctorsDataFetched && !forceRefresh) {
return;
}
isPatientFavouriteDoctorsLoading = true;
patientFavouriteDoctorsList.clear();
notifyListeners();
final result = await myAppointmentsRepo.getFavouriteDoctorsList();
result.fold(
(failure) async {
isPatientFavouriteDoctorsLoading = false;
notifyListeners();
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
isPatientFavouriteDoctorsLoading = false;
notifyListeners();
} else if (apiResponse.messageStatus == 1) {
patientFavouriteDoctorsList = apiResponse.data!;
isFavouriteDoctorsDataFetched = true;
isPatientFavouriteDoctorsLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
// Method to force refresh favorite doctors list
void refreshFavouriteDoctors() {
isFavouriteDoctorsDataFetched = false;
getPatientFavouriteDoctors(forceRefresh: true);
}
// Method to reset favorite doctors cache
void resetFavouriteDoctorsCache() {
isFavouriteDoctorsDataFetched = false;
}
Future<void> insertLiveCareVIDARequest(
{required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.insertLiveCareVIDARequest(clientRequestID: clientRequestID, patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);

@ -12,6 +12,10 @@ abstract class NotificationsRepo {
required int pagingSize,
required int currentPage,
});
Future<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> markAsRead({
required int notificationID,
});
}
class NotificationsRepoImp implements NotificationsRepo {
@ -75,4 +79,38 @@ class NotificationsRepoImp implements NotificationsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> markAsRead({required int notificationID}) async {
Map<String, dynamic> mapDevice = {"NotificationPoolID": notificationID};
try {
GenericApiModel<List<NotificationResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
PUSH_NOTIFICATION_SET_MESSAGES_FROM_POOL_AS_READ,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<List<NotificationResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: [],
);
} 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()));
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save