watch integration ui #183

Open
taha.alam wants to merge 17 commits from watch_integration into master

@ -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")
}
@ -191,6 +192,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")

@ -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

@ -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

@ -1580,8 +1580,24 @@
"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": "المظهر الداكن"
"darkMode": "المظهر الداكن",
"featureComingSoonDescription": "هذه الميزة ستتوفر قريباً. نحن نعمل جاهدين لإضافة ميزات أكثر تميزاً إلى التطبيق. انتظرونا لمتابعة التحديثات."
}

@ -1574,7 +1574,22 @@
"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",
"darkMode": "Dark Mode"
"darkMode": "Dark Mode",
"featureComingSoonDescription": "Feature is coming soon. We are actively working to bring more exciting features into the app. Stay tuned for updates."
}

@ -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

@ -231,6 +231,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';

@ -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);
}

@ -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 {

@ -405,6 +405,7 @@ class Utils {
static Widget getWarningWidget({
String? loadingText,
bool isShowActionButtons = false,
bool showOkButton = false,
Widget? bodyWidget,
Function? onConfirmTap,
Function? onCancelTap,
@ -457,7 +458,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;
}

@ -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 {

@ -0,0 +1,134 @@
import 'dart:math';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:intl/intl.dart';
import 'model/Vitals.dart';
enum Durations {
daily("daily"),
weekly("weekly"),
monthly("monthly"),
halfYearly("halfYearly"),
yearly("yearly");
final String value;
const Durations(this.value);
}
class HealthDataTransformation {
Map<String, List<DataPoint>> transformVitalsToDataPoints(VitalsWRTType vitals, String filterType, String selectedSection,) {
final Map<String, List<DataPoint>> dataPointMap = {};
Map<String, List<Vitals>> data = vitals.getVitals();
// Group data based on the filter type
Map<String, List<Vitals>> groupedData = {};
// List<List<Vitals> > items = data.values.toList();
List<String> keys = data.keys.toList();
var count = 0;
List<Vitals> item = data[selectedSection] ?? [];
// for(var item in items) {
List<DataPoint> dataPoints = [];
for (var vital in item) {
String key = "";
if (vital.value == "" || vital.timestamp == "") continue;
var parseDate = DateTime.parse(vital.timestamp);
var currentDate = normalizeToStartOfDay(DateTime.now());
if (filterType == Durations.daily.value) {
if(isBetweenInclusive(parseDate, currentDate, DateTime.now())) {
key = DateFormat('yyyy-MM-dd HH').format(DateTime.parse(vital.timestamp));
groupedData.putIfAbsent(key, () => []).add(vital);
}// Group by hour
} else if (filterType == Durations.weekly.value) {
if(isBetweenInclusive(parseDate, currentDate.subtract(Duration(days: 7)), DateTime.now())) {
key = DateFormat('yyyy-MM-dd').format(DateTime.parse(vital.timestamp));
groupedData.putIfAbsent(key, () => []).add(vital);
} // Group by day
} else if (filterType == Durations.monthly.value) {
if(isBetweenInclusive(parseDate, currentDate.subtract(Duration(days: 30)), DateTime.now())) {
print("the value for the monthly filter is ${vital.value} with the timestamp ${vital.timestamp} and the current date is $currentDate and the parse date is $parseDate");
key = DateFormat('yyyy-MM-dd').format(DateTime.parse(vital.timestamp));
groupedData.putIfAbsent(key, () => []).add(vital);
} // Group by day
} else if (filterType == Durations.halfYearly.value || filterType == Durations.yearly.value) {
if(isBetweenInclusive(parseDate, currentDate.subtract(Duration(days: filterType == Durations.halfYearly.value?180: 365)), DateTime.now())) {
key = DateFormat('yyyy-MM').format(DateTime.parse(vital.timestamp));
groupedData.putIfAbsent(key, () => []).add(vital);
} // Group by month
} else {
throw ArgumentError('Invalid filter type');
}
}
print("the size of groupData is ${groupedData.values.length}");
// Process grouped data
groupedData.forEach((key, values) {
double sum = values.fold(0, (acc, v) => acc + num.parse(v.value));
double mean = sum / values.length;
if(selectedSection == "bodyOxygen" || selectedSection == "bodyTemperature") {
mean = sum / values.length;
}else {
mean = sum;
}
double finalValue = mean;
print("the final value is $finalValue for the key $key with the original values ${values.map((v) => v.value).toList()} and uom is ${values.first.unitOfMeasure}");
dataPoints.add(DataPoint(
value: smartScale(finalValue),
label: key,
actualValue: finalValue.toStringAsFixed(2),
displayTime: key,
unitOfMeasurement:values.first.unitOfMeasure ,
time: DateTime.parse(values.first.timestamp),
));
});
dataPointMap[filterType] = dataPoints;
// }
return dataPointMap;
}
double smartScale(double number) {
// if (number <= 0) return 0;
// final _random = Random();
// double ratio = number / 100;
//
// double scalingFactor = ratio > 1 ? 100 / number : 100;
//
// double result = (number / 100) * scalingFactor;
// print("the ratio is ${ratio.toInt()+1}");
// double max = (100+_random.nextInt(ratio.toInt()+10)).toDouble();
//
// return result.clamp(0, max);
if (number <= 0) return 0;
final random = Random();
// Smooth compression scaling
double baseScaled = number <20 ? number:100 * (number / (number + 100));
// Random factor between 0.9 and 1.1 (±10%)
double randomFactor = number <20 ? random.nextDouble() * 1.5: 0.9 + random.nextDouble() * 0.2;
double result = baseScaled * randomFactor;
return result.clamp(0, 100);
}
DateTime normalizeToStartOfDay(DateTime date) {
return DateTime(date.year, date.month, date.day);
}
bool isBetweenInclusive(
DateTime target,
DateTime start,
DateTime end,
) {
return !normalizeToStartOfDay(target).isBefore(start) && !normalizeToStartOfDay(target).isAfter(end);
}
}

@ -1,6 +1,18 @@
import 'package:flutter/foundation.dart';
import 'package:health/health.dart';
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import '../../core/common_models/data_points.dart';
import '../../core/dependencies.dart';
import '../../presentation/smartwatches/activity_detail.dart' show ActivityDetails;
import '../../presentation/smartwatches/smart_watch_activity.dart' show SmartWatchActivity;
import '../../services/navigation_service.dart' show NavigationService;
import 'HealthDataTransformation.dart';
import 'model/Vitals.dart';
class HealthProvider with ChangeNotifier {
final HealthService _healthService = HealthService();
@ -10,13 +22,27 @@ class HealthProvider with ChangeNotifier {
String selectedTimeRange = '7D';
int selectedTabIndex = 0;
String selectedWatchType = 'apple';
SmartWatchTypes? selectedWatchType ;
String selectedWatchURL = 'assets/images/png/smartwatches/apple-watch-5.jpg';
HealthDataTransformation healthDataTransformation = HealthDataTransformation();
String selectedSection = "";
Map<String, List<DataPoint>> daily = {};
Map<String, List<DataPoint>> weekly = {};
Map<String, List<DataPoint>> monthly = {};
Map<String, List<DataPoint>> halgyearly = {};
Map<String, List<DataPoint>> yearly = {};
Map<String, List<DataPoint>> selectedData = {};
Durations selectedDuration = Durations.daily;
VitalsWRTType? vitals;
double? averageValue;
String? averageValueString;
setSelectedWatchType(String type, String imageURL) {
setSelectedWatchType(SmartWatchTypes type, String imageURL) {
selectedWatchType = type;
selectedWatchURL = imageURL;
notifyListeners();
_healthService.addWatchHelper(type);
}
void onTabChanged(int index) {
@ -40,9 +66,7 @@ class HealthProvider with ChangeNotifier {
final startTime = _getStartDate();
final endTime = DateTime.now();
healthData = await _healthService.getAllHealthData(startTime, endTime);
isLoading = false;
notifyListeners();
} catch (e) {
@ -91,4 +115,176 @@ class HealthProvider with ChangeNotifier {
return DateTime.now().subtract(const Duration(days: 7));
}
}
void initDevice() async {
LoaderBottomSheet.showLoader();
notifyListeners();
final result = await _healthService.initDevice();
isLoading = false;
LoaderBottomSheet.hideLoader();
if (result.isError) {
error = 'Error initializing device: ${result.asError}';
} else {
LoaderBottomSheet.showLoader();
await getVitals();
// LoaderBottomSheet.hideLoader();
// await Future.delayed(Duration(seconds: 5));
getIt.get<NavigationService>().pushPage(page: SmartWatchActivity());
print('Device initialized successfully');
}
notifyListeners();
}
Future<void> getVitals() async {
final result = await _healthService.getVitals();
vitals = result;
LoaderBottomSheet.hideLoader();
notifyListeners();
}
mapValuesForFilters(
Durations filter,
String selectedSection,
) {
if (vitals == null) return {};
switch (filter) {
case Durations.daily:
if (daily.isNotEmpty) {
selectedData = daily;
break;
}
selectedData = daily = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.daily.value, selectedSection);
break;
case Durations.weekly:
if (weekly.isNotEmpty) {
selectedData = weekly;
break;
}
selectedData = weekly = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.weekly.value, selectedSection);
break;
case Durations.monthly:
if (monthly.isNotEmpty) {
selectedData = monthly;
break;
}
selectedData = monthly = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.monthly.value, selectedSection);
break;
case Durations.halfYearly:
if (halgyearly.isNotEmpty) {
selectedData = halgyearly;
break;
}
selectedData = halgyearly = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.halfYearly.value, selectedSection);
break;
case Durations.yearly:
if (yearly.isNotEmpty) {
selectedData = yearly;
break;
}
selectedData = yearly = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.yearly.value, selectedSection);
break;
default:
{}
;
}
notifyListeners();
}
void navigateToDetails(String value, {required String sectionName, required String uom}) {
getIt.get<NavigationService>().pushPage(page: ActivityDetails(selectedActivity: value, sectionName:sectionName, uom: uom,));
}
void saveSelectedSection(String value) {
// if(selectedSection == value) return;
selectedSection = value;
}
void deleteDataIfSectionIsDifferent(String value) {
// if(selectedSection == value){
// return;
// }
daily.clear();
weekly.clear();
halgyearly.clear();
monthly.clear();
yearly.clear();
selectedSection = "";
selectedSection = "";
averageValue = null;
averageValueString = null;
selectedDuration = Durations.daily;
}
void fetchData() {
// if(selectedSection == value) return;
mapValuesForFilters(selectedDuration, selectedSection);
getAverageForData();
transformValueIfSleepIsSelected();
}
void setDurations(Durations duration) {
selectedDuration = duration;
}
void getAverageForData() {
if (selectedData.isEmpty) {
averageValue = 0.0;
notifyListeners();
return;
}
double total = 0;
int count = 0;
selectedData.forEach((key, dataPoints) {
for (var dataPoint in dataPoints) {
total += num.parse(dataPoint.actualValue);
count++;
}
});
print("total count is $count and total is $total");
averageValue = count > 0 ? total / count : null;
notifyListeners();
}
void transformValueIfSleepIsSelected() {
if (selectedSection != "sleep") return;
if (averageValue == null) {
averageValueString = null;
return;
}
averageValueString = DateUtil.millisToHourMin(averageValue?.toInt() ?? 0);
averageValue = null;
notifyListeners();
}
String firstNonEmptyValue(List<Vitals> dataPoints) {
try {
return dataPoints.firstWhere((dp) => dp.value != null && dp.value!.trim().isNotEmpty).value;
} catch (e) {
return "0"; // no non-empty value found
}
}
String sumOfNonEmptyData(List<Vitals> list) {
final now = DateTime.now().toLocal();
final today = DateTime(now.year, now.month, now.day);
var data = double.parse(list
.where((dp) {
final localDate = DateTime.parse(dp.timestamp);
final normalized = DateTime(localDate.year, localDate.month, localDate.day);
return normalized.isAtSameMomentAs(today);
})
.fold("0", (sum, dp) => (double.parse(sum) + double.parse(dp.value)).toString())
.toString());
var formattedString = data.toStringAsFixed(2);
if (formattedString.endsWith('.00')) {
return formattedString.substring(0, formattedString.length - 3);
}
return formattedString;
}
}

@ -1,9 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:health/health.dart';
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/model/Vitals.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/create_watch_helper.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart';
import 'package:permission_handler/permission_handler.dart';
import 'health_utils.dart';
import 'package:async/async.dart';
class HealthService {
static final HealthService _instance = HealthService._internal();
@ -14,6 +22,8 @@ class HealthService {
final Health health = Health();
WatchHelper? watchHelper;
final List<HealthDataType> _healthMetrics = [
HealthDataType.HEART_RATE,
// HealthDataType.STEPS,
@ -161,4 +171,42 @@ class HealthService {
return [];
}
}
void addWatchHelper(SmartWatchTypes watchType){
watchHelper = CreateWatchHelper.getWatchName(watchType) ;
}
Future<Result<bool>> initDevice() async {
if(watchHelper == null){
return Result.error('No watch helper found');
}
return await watchHelper!.initDevice();
}
Future<VitalsWRTType?> getVitals() async {
if (watchHelper == null) {
print('No watch helper found');
return null;
}
try {
await watchHelper!.getHeartRate();
await watchHelper!.getSleep();
await watchHelper!.getSteps();
await watchHelper!.getActivity();
await watchHelper!.getBodyTemperature();
await watchHelper!.getDistance();
await watchHelper!.getBloodOxygen();
Result<dynamic> data = await watchHelper!.retrieveData();
if(data.isError) {
print('Unable to get the data');
}
var response = jsonDecode(data.asValue?.value?.toString()?.trim().replaceAll("\n", "")??"");
VitalsWRTType vitals = VitalsWRTType.fromMap(response);
log("the data is ${vitals}");
return vitals;
}catch(e){
print('Error getting heart rate: $e');
}
return null;
}
}

@ -0,0 +1,103 @@
class Vitals {
String value;
final String timestamp;
final String unitOfMeasure;
Vitals({
required this.value,
required this.timestamp,
this.unitOfMeasure = "",
});
factory Vitals.fromMap(Map<dynamic, dynamic> map) {
return Vitals(
value: map['value'] ?? "",
timestamp: map['timeStamp'] ?? "",
unitOfMeasure: map['uom'] ?? "",
);
}
toString(){
return "{\"value\": \"$value\", \"timeStamp\": \"$timestamp\", \"uom\": \"$unitOfMeasure\"}";
}
}
class VitalsWRTType {
final List<Vitals> heartRate;
final List<Vitals> sleep;
final List<Vitals> step;
final List<Vitals> distance;
final List<Vitals> activity;
final List<Vitals> bodyOxygen;
final List<Vitals> bodyTemperature;
double maxHeartRate = double.negativeInfinity;
double maxSleep = double.negativeInfinity;
double maxStep= double.negativeInfinity;
double maxActivity = double.negativeInfinity;
double maxBloodOxygen = double.negativeInfinity;
double maxBodyTemperature = double.negativeInfinity;
VitalsWRTType({required this.distance, required this.bodyOxygen, required this.bodyTemperature, required this.heartRate, required this.sleep, required this.step, required this.activity});
factory VitalsWRTType.fromMap(Map<dynamic, dynamic> map) {
List<Vitals> activity = [];
List<Vitals> steps = [];
List<Vitals> sleeps = [];
List<Vitals> heartRate = [];
List<Vitals> bodyOxygen = [];
List<Vitals> distance = [];
List<Vitals> bodyTemperature = [];
map["activity"].forEach((element) {
element["uom"] = "Kcal";
var data = Vitals.fromMap(element);
activity.add(data);
});
map["steps"].forEach((element) {
element["uom"] = "";
steps.add(Vitals.fromMap(element));
});
map["sleep"].forEach((element) {
element["uom"] = "hr";
sleeps.add(Vitals.fromMap(element));
});
map["heartRate"].forEach((element) {
element["uom"] = "bpm";
heartRate.add(Vitals.fromMap(element));
});
map["bloodOxygen"].forEach((element) {
element["uom"] = "";
bodyOxygen.add(Vitals.fromMap(element));
});
map["bodyTemperature"].forEach((element) {
element["uom"] = "C";
bodyTemperature.add(Vitals.fromMap(element));
});
map["distance"].forEach((element) {
element["uom"] = "km";
var data = Vitals.fromMap(element);
data.value = (double.parse(data.value)/1000).toStringAsFixed(2);
distance.add(data);
});
return VitalsWRTType(bodyTemperature: bodyTemperature, bodyOxygen: bodyOxygen, heartRate: heartRate, sleep: sleeps, step: steps, activity: activity, distance: distance);
}
Map<String, List<Vitals>> getVitals() {
return {
"heartRate": heartRate ,
"sleep": sleep,
"steps": step,
"activity": activity,
"bodyOxygen": bodyOxygen,
"bodyTemperature": bodyTemperature,
"distance": distance,
};
}
}

@ -0,0 +1,90 @@
import 'dart:async';
import 'package:async/async.dart';
import 'package:flutter/services.dart';
class SamsungPlatformChannel {
final MethodChannel _channel = MethodChannel('samsung_watch');
Future<Result<bool>> initDevice() async {
try{
await _channel.invokeMethod('init');
return Result.value(true);
}catch(e){
return Result.error(e);
}
}
Future<Result<bool>> getRequestedPermission() async {
try{
await _channel.invokeMethod('getPermission');
return Result.value(true);
}catch(e){
return Result.error(e);
}
}
Future<Result<bool>> getHeartRate() async {
try{
await _channel.invokeMethod('getHeartRate');
return Result.value(true);
}catch(e){
return Result.error(e);
}
}
Future<Result<bool>> getSleep() async {
try{
await _channel.invokeMethod('getSleepData');
return Result.value(true);
}catch(e){
return Result.error(e);
}
}
Future<Result<bool>> getSteps() async {
try{
await _channel.invokeMethod('steps');
return Result.value(true);
}catch(e){
return Result.error(e);
}
}
Future<Result<bool>> getActivity() async {
try{
await _channel.invokeMethod('activitySummary');
return Result.value(true);
}catch(e){
return Result.error(e);
}
}
Future<Result<dynamic>> retrieveData() async {
try{
return Result.value(await _channel.invokeMethod('retrieveData'));
}catch(e){
return Result.error(e);
}
}
Future<Result<dynamic>> getBloodOxygen() async {
try{
return Result.value(await _channel.invokeMethod('bloodOxygen'));
}catch(e){
return Result.error(e);
}
}
Future<Result<dynamic>> getBodyTemperature() async {
try{
return Result.value(await _channel.invokeMethod('bodyTemperature'));
}catch(e){
return Result.error(e);
}
}
Future<FutureOr<void>> getDistance() async {
try{
return Result.value(await _channel.invokeMethod('distance'));
}catch(e){
return Result.error(e);
}
}
}

@ -0,0 +1,22 @@
import 'dart:io';
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/health_connect_helper.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/huawei_watch_connecter.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/samsung_health.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart';
class CreateWatchHelper {
static WatchHelper getWatchName(SmartWatchTypes watchType) {
/// if running device is ios
if(Platform.isIOS) return HealthConnectHelper();
switch(watchType){
case SmartWatchTypes.samsung:
return SamsungHealth();
case SmartWatchTypes.huawei:
return HuaweiHealthDataConnector();
default:
return SamsungHealth();
}
}
}

@ -0,0 +1,188 @@
import 'dart:async';
import 'dart:io';
import 'package:async/src/result/result.dart';
import 'package:flutter/material.dart';
import 'package:health/health.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart' show WatchHelper;
import 'package:permission_handler/permission_handler.dart';
import '../model/Vitals.dart';
class HealthConnectHelper extends WatchHelper {
final Health health = Health();
final List<HealthDataType> _healthPermissions = [
HealthDataType.ACTIVE_ENERGY_BURNED,
HealthDataType.HEART_RATE,
HealthDataType.STEPS,
HealthDataType.BLOOD_OXYGEN,
HealthDataType.BODY_TEMPERATURE,
HealthDataType.DISTANCE_WALKING_RUNNING,
HealthDataType.TOTAL_CALORIES_BURNED
];
Map<String , List<Vitals>> mappedData = {};
@override
FutureOr<void> getHeartRate() async {
try {
final types = HealthDataType.HEART_RATE;
final endDate = DateTime.now();
// final startDate = endDate.subtract(Duration(days: 365));
final startDate = endDate.subtract(Duration(days: 365));
final data = await getHeartData(startDate, endDate, types);
addDataToMap("heartRate",data );
} catch (e) {
print('Error getting heart rate: $e');
}
}
@override
FutureOr<void> getSleep() async {
try {
final types = HealthDataType.SLEEP_IN_BED;
final endDate = DateTime.now();
final startDate = endDate.subtract(Duration(days: 365));
final data = await getData(startDate, endDate, types);
addDataToMap("sleep",data );
} catch (e) {
print('Error getting sleep data: $e');
}
}
@override
FutureOr<void> getSteps() async {
try {
final types = HealthDataType.STEPS;
final endDate = DateTime.now();
final startDate = endDate.subtract(Duration(days: 365));
final data = await getData(startDate, endDate, types);
addDataToMap("steps",data );
debugPrint('Steps Data: $data');
} catch (e) {
debugPrint('Error getting steps: $e');
}
}
@override
Future<void> getActivity() async {
try {
final types = HealthDataType.ACTIVE_ENERGY_BURNED;
final endDate = DateTime.now();
final startDate = endDate.subtract(Duration(days: 365));
final data = await getData(startDate, endDate, types);
addDataToMap("activity",data );
debugPrint('Activity Data: $data');
} catch (e) {
debugPrint('Error getting activity: $e');
}
}
@override
Future<dynamic> retrieveData() async {
return Result.value(getMappedData());
}
@override
Future<dynamic> getBloodOxygen() async {
try {
final types = HealthDataType.BLOOD_OXYGEN;
final endDate = DateTime.now();
final startDate = endDate.subtract(Duration(days: 365));
final data = await getData(startDate, endDate, types);
addDataToMapBloodOxygen("bloodOxygen", data);
} catch (e) {
debugPrint('Error getting blood oxygen: $e');
}
}
@override
Future<dynamic> getBodyTemperature() async {
try {
final types = HealthDataType.BODY_TEMPERATURE;
final endDate = DateTime.now();
final startDate = endDate.subtract(Duration(days: 365));
final data = await getData(startDate, endDate, types);
addDataToMap("bodyTemperature",data );
} catch (e) {
debugPrint('Error getting body temp erature: $e');
}
}
@override
FutureOr<void> getDistance() async {
try {
final types = HealthDataType.DISTANCE_WALKING_RUNNING;
final endDate = DateTime.now();
final startDate = endDate.subtract(Duration(days: 365));
final data = await getData(startDate, endDate, types);
addDataToMap("distance",data );
} catch (e) {
debugPrint('Error getting distance: $e');
}
}
@override
Future<Result<bool>> initDevice() async {
try {
final types = _healthPermissions;
final granted = await health.requestAuthorization(types);
await Permission.activityRecognition.request();
await Permission.location.request();
await Health().requestHealthDataHistoryAuthorization();
return Result.value(granted);
} catch (e) {
debugPrint('Authorization error: $e');
return Result.error(false);
}
}
getData(startTime, endTime,type) async {
return await health.getHealthIntervalDataFromTypes(
startDate: startTime,
endDate: endTime,
types: [type],
interval: 3600,
);
}
void addDataToMap(String s, data) {
mappedData[s] = [];
for (var point in data) {
if (point.value is NumericHealthValue) {
final numericValue = (point.value as NumericHealthValue).numericValue;
Vitals vitals = Vitals(
value: (point.value as NumericHealthValue).numericValue.toStringAsFixed(2),
timestamp: point.dateFrom.toString()
);
mappedData[s]?.add(vitals);
}
}
}
void addDataToMapBloodOxygen(String s, data) {
mappedData[s] = [];
for (var point in data) {
if (point.value is NumericHealthValue) {
final numericValue = (point.value as NumericHealthValue).numericValue;
point.value = NumericHealthValue(
numericValue: numericValue * 100,
);
Vitals vitals = Vitals(value: (point.value as NumericHealthValue).numericValue.toStringAsFixed(2), timestamp: point.dateFrom.toString());
mappedData[s]?.add(vitals);
}
}
}
getMappedData() {
return " { \"heartRate\": ${mappedData["heartRate"] ?? []}, \"sleep\": ${mappedData["sleep"] ?? []}, \"steps\": ${mappedData["steps"] ?? []}, \"activity\": ${mappedData["activity"] ?? []}, \"bloodOxygen\": ${mappedData["bloodOxygen"] ?? []}, \"bodyTemperature\": ${mappedData["bodyTemperature"] ?? []}, \"distance\": ${mappedData["distance"] ?? []} }";
}
getHeartData(DateTime startDate, DateTime endDate, HealthDataType types) async {
return await health.getHealthDataFromTypes(
startTime: startDate,
endTime: endDate,
types: [types],
);
}
}

@ -0,0 +1,86 @@
import 'dart:async';
import 'package:async/src/result/result.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart';
import 'package:huawei_health/huawei_health.dart';
class HuaweiHealthDataConnector extends WatchHelper{
final MethodChannel _channel = MethodChannel('huawei_watch');
@override
Future<Result<bool>> initDevice() async{
try{
await _channel.invokeMethod('init');
}catch(e){
}
// List of scopes to ask for authorization.
// Note: These scopes should also be authorized on the Huawei Developer Console.
List<Scope> scopes = [
Scope.HEALTHKIT_STEP_READ, Scope.HEALTHKIT_OXYGEN_SATURATION_READ, // View and store height and weight data in Health Service Kit.
Scope.HEALTHKIT_HEARTRATE_READ, Scope.HEALTHKIT_SLEEP_READ,
Scope.HEALTHKIT_BODYTEMPERATURE_READ, Scope.HEALTHKIT_CALORIES_READ
];
try {
bool? result = await SettingController.getHealthAppAuthorization();
debugPrint(
'Granted Scopes for result == is $result}',
);
return Result.value(true);
} catch (e) {
debugPrint('Error on authorization, Error:${e.toString()}');
return Result.error(false);
}
}
@override
Future<void> getActivity() async {
DataType dataTypeResult = await SettingController.readDataType(
DataType.DT_CONTINUOUS_STEPS_DELTA.name
);
}
@override
Future getBloodOxygen() {
throw UnimplementedError();
}
@override
Future getBodyTemperature() {
throw UnimplementedError();
}
@override
FutureOr<void> getHeartRate() {
throw UnimplementedError();
}
@override
FutureOr<void> getSleep() {
throw UnimplementedError();
}
@override
FutureOr<void> getSteps() {
throw UnimplementedError();
}
@override
Future retrieveData() {
throw UnimplementedError();
}
@override
FutureOr<void> getDistance() {
// TODO: implement getDistance
throw UnimplementedError();
}
}

@ -0,0 +1,98 @@
import 'dart:async';
import 'package:async/src/result/result.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/platform_channel/samsung_platform_channel.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart' show WatchHelper;
class SamsungHealth extends WatchHelper {
final SamsungPlatformChannel platformChannel = SamsungPlatformChannel();
@override
FutureOr<void> getHeartRate() async {
try {
await platformChannel.getHeartRate();
}catch(e){
print('Error getting heart rate: $e');
}
}
@override
Future<Result<bool>> initDevice() async {
var result = await platformChannel.initDevice();
if(result.isError){
return result;
}
return await platformChannel.getRequestedPermission();
}
@override
FutureOr<void> getSleep() async {
try {
await platformChannel.getSleep();
}catch(e){
print('Error getting heart rate: $e');
}
}
@override
FutureOr<void> getSteps() async{
try {
await platformChannel.getSteps();
}catch(e){
print('Error getting heart rate: $e');
}
}
@override
Future<void> getActivity() async{
try {
await platformChannel.getActivity();
}catch(e){
print('Error getting heart rate: $e');
}
}
@override
Future<dynamic> retrieveData() async{
try {
return await platformChannel.retrieveData();
}catch(e){
print('Error getting heart rate: $e');
}
}
@override
Future<dynamic> getBloodOxygen() async{
try {
return await platformChannel.getBloodOxygen();
}catch(e){
print('Error getting heart rate: $e');
}
}
@override
Future<dynamic> getBodyTemperature() async {
try {
return await platformChannel.getBodyTemperature();
}catch(e){
print('Error getting heart rate: $e');
}
}
@override
FutureOr<void> getDistance() async{
try {
return await platformChannel.getDistance();
}catch(e){
print('Error getting heart rate: $e');
}
}
}

@ -0,0 +1,14 @@
import 'dart:async';
import 'package:async/async.dart';
abstract class WatchHelper {
Future<Result<bool>> initDevice();
FutureOr<void> getHeartRate();
FutureOr<void> getSleep();
FutureOr<void> getSteps();
FutureOr<void> getDistance();
Future<void> getActivity();
Future<dynamic> retrieveData();
Future<dynamic> getBodyTemperature();
Future<dynamic> getBloodOxygen();
}

@ -1574,8 +1574,23 @@ abstract class LocaleKeys {
static const invalidEligibility = 'invalidEligibility';
static const invalidInsurance = 'invalidInsurance';
static const continueCash = 'continueCash';
static const applewatch = 'applewatch';
static const applehealthapplicationshouldbeinstalledinyourphone = 'applehealthapplicationshouldbeinstalledinyourphone';
static const unabletodetectapplicationinstalledpleasecomebackonceinstalled = 'unabletodetectapplicationinstalledpleasecomebackonceinstalled';
static const applewatchshouldbeconnected = 'applewatchshouldbeconnected';
static const samsungwatch = 'samsungwatch';
static const samsunghealthapplicationshouldbeinstalledinyourphone = 'samsunghealthapplicationshouldbeinstalledinyourphone';
static const samsungwatchshouldbeconnected = 'samsungwatchshouldbeconnected';
static const huaweiwatch = 'huaweiwatch';
static const huaweihealthapplicationshouldbeinstalledinyourphone = 'huaweihealthapplicationshouldbeinstalledinyourphone';
static const huaweiwatchshouldbeconnected = 'huaweiwatchshouldbeconnected';
static const whoopwatch = 'whoopwatch';
static const whoophealthapplicationshouldbeinstalledinyourphone = 'whoophealthapplicationshouldbeinstalledinyourphone';
static const whoopwatchshouldbeconnected = 'whoopwatchshouldbeconnected';
static const updatetheinformation = 'updatetheinformation';
static const timeFor = 'timeFor';
static const hmgPolicies = 'hmgPolicies';
static const darkMode = 'darkMode';
static const featureComingSoonDescription = 'featureComingSoonDescription';
}

@ -294,11 +294,11 @@ class ServicesPage extends StatelessWidget {
true,
route: null,
onTap: () async {
if (getIt.get<AppState>().isAuthenticated) {
// if (getIt.get<AppState>().isAuthenticated) {
getIt.get<NavigationService>().pushPageRoute(AppRoutes.smartWatches);
} else {
await getIt.get<AuthenticationViewModel>().onLoginPressed();
}
// } else {
// await getIt.get<AuthenticationViewModel>().onLoginPressed();
// }
},
// route: AppRoutes.huaweiHealthExample,
),

@ -0,0 +1,359 @@
import 'dart:math';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.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/smartwatch_health_data/health_provider.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/graph/CustomBarGraph.dart';
import 'package:intl/intl.dart' show DateFormat;
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/HealthDataTransformation.dart' as durations;
import 'package:dartz/dartz.dart' show Tuple2;
import '../../core/utils/date_util.dart';
class ActivityDetails extends StatefulWidget {
final String selectedActivity;
final String sectionName;
final String uom;
const ActivityDetails({super.key, required this.selectedActivity, required this.sectionName, required this.uom});
@override
State<ActivityDetails> createState() => _ActivityDetailsState();
}
class _ActivityDetailsState extends State<ActivityDetails> {
int index = 0;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: "All Health Data".needTranslation,
child: Column(
spacing: 16.h,
children: [
periodSelectorView((index) {}),
activityDetails(),
DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
child:
activityGraph().paddingOnly(left: 16.w, right: 16.w, top: 32.h, bottom: 16.h),)
],
).paddingSymmetrical(24.w, 24.h),
),
);
}
Widget periodSelectorView(Function(int) onItemSelected) {
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
child: Row(
children: [
Expanded(
child: CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "D"),
CustomTabBarModel(null, "W"),
CustomTabBarModel(null, "M"),
CustomTabBarModel(null, "6M"),
CustomTabBarModel(null, "Y"),
// CustomTabBarModel(null, "Completed".needTranslation),
],
shouldTabExpanded: true,
onTabChange: (index) {
switch (index) {
case 0:
context.read<HealthProvider>().setDurations(durations.Durations.daily);
break;
case 1:
context.read<HealthProvider>().setDurations(durations.Durations.weekly);
break;
case 2:
context.read<HealthProvider>().setDurations(durations.Durations.monthly);
break;
case 3:
context.read<HealthProvider>().setDurations(durations.Durations.halfYearly);
break;
case 4:
context.read<HealthProvider>().setDurations(durations.Durations.yearly);
break;
}
context.read<HealthProvider>().fetchData();
},
),
),
],
).paddingSymmetrical(4.w, 4.h));
}
Widget activityDetails() {
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h, hasShadow: true),
child: Column(
spacing: 8.h,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
widget.sectionName.capitalizeFirstofEach.toText32(weight: FontWeight.w600, color: AppColors.textColor),
"Average".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)
],
),
Selector<HealthProvider, Tuple2<double?, String?>>(
selector: (_, model) => Tuple2(model.averageValue, model.averageValueString),
builder: (_, data, __) {
var averageAsDouble = data.value1;
var averageAsString = data.value2;
return Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
spacing: 4.w,
children: [
(averageAsDouble?.toStringAsFixed(2) ?? averageAsString ?? "N/A").toText24(color: AppColors.textGreenColor, fontWeight: FontWeight.w600),
Visibility(
visible: averageAsDouble != null || averageAsString != null,
child: widget.uom.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500)
)
],
);
})
],
).paddingSymmetrical(16.w, 16.h));
}
Widget activityGraph() {
// final _random = Random();
//
// int randomBP() => 100 + _random.nextInt(51); // 100150
// final List<DataPoint> data6Months = List.generate(6, (index) {
// final date = DateTime.now().subtract(Duration(days: 30 * (5 - index)));
//
// final value = randomBP();
//
// return DataPoint(
// value: value.toDouble(),
// label: value.toString(),
// actualValue: value.toString(),
// displayTime: DateFormat('MMM').format(date),
// unitOfMeasurement: 'mmHg',
// time: date,
// );
// });
// final List<DataPoint> data12Months = List.generate(12, (index) {
// final date = DateTime.now().subtract(Duration(days: 30 * (11 - index)));
//
// final value = randomBP();
//
// return DataPoint(
// value: value.toDouble(),
// label: value.toString(),
// actualValue: value.toString(),
// displayTime: DateFormat('MMM').format(date),
// unitOfMeasurement: 'mmHg',
// time: date,
// );
// });
//
// List<DataPoint> data =[];
// if(index == 0){
// data = data6Months;
// } else if(index == 1){
// data = data12Months;
// } else
// data = [
// DataPoint(
// value: 128,
// label: "128",
// actualValue: '128',
// displayTime: 'Sun',
// unitOfMeasurement: 'mmHg',
// time: DateTime.now().subtract(const Duration(days: 6)),
// ),
// DataPoint(
// value: 135,
// label: "135",
// actualValue: '135',
// displayTime: 'Mon',
// unitOfMeasurement: 'mmHg',
// time: DateTime.now().subtract(const Duration(days: 5)),
// ),
// DataPoint(
// value: 122,
// label: "122",
// actualValue: '122',
// displayTime: 'Tue',
// unitOfMeasurement: 'mmHg',
// time: DateTime.now().subtract(const Duration(days: 4)),
// ),
// DataPoint(
// value: 140,
// label: "140",
// actualValue: '140',
// displayTime: 'Wed',
// unitOfMeasurement: 'mmHg',
// time: DateTime.now().subtract(const Duration(days: 3)),
// ),
// DataPoint(
// value: 118,
// label: "118",
// actualValue: '118',
// displayTime: 'Thu',
// unitOfMeasurement: 'mmHg',
// time: DateTime.now().subtract(const Duration(days: 2)),
// ),
// DataPoint(
// value: 125,
// label: "125",
// actualValue: '125',
// displayTime: 'Fri',
// unitOfMeasurement: 'mmHg',
// time: DateTime.now().subtract(const Duration(days: 1)),
// ),
// DataPoint(
// value: 130,
// label: "130",
// actualValue: '130',
// displayTime: 'Sat',
// unitOfMeasurement: 'mmHg',
// time: DateTime.now(),
// ),23
// ];
return Selector<HealthProvider, Map<String, List<DataPoint>>?>(
selector: (_, model) => model.selectedData,
builder: (_, data, __) {
if (context.read<HealthProvider>().selectedData.values.toList().first?.isEmpty == true) return SizedBox();
return CustomBarChart(
dataPoints: context.read<HealthProvider>().selectedData.values.toList().first,
height: 300.h,
maxY: 150,
barColor: AppColors.bgGreenColor,
barWidth: getBarWidth(),
barRadius: BorderRadius.circular(8),
bottomLabelColor: Colors.black,
bottomLabelSize: 12,
leftLabelInterval: .1,
leftLabelReservedSize: 20,
// Left axis label formatter (Y-axis)
leftLabelFormatter: (value) {
var labelValue = double.tryParse(value.toStringAsFixed(0));
if (labelValue == null) return SizedBox.shrink();
// if (labelValue == 0 || labelValue == 150 / 2 || labelValue == 150) {
// return Text(
// labelValue.toStringAsFixed(0),
// style: const TextStyle(
// color: Colors.black26,
// fontSize: 11,
// ),
// );
// }
return SizedBox.shrink();
},
/// for the handling of the sleep time
getTooltipItem: (widget.selectedActivity == "sleep")
? (data) {
return BarTooltipItem(
'${DateUtil. millisToHourMin(num.parse(data.actualValue).toInt())}\n${DateFormat('dd MMM, yyyy').format(data.time)}',
TextStyle(
color: Colors.black,
fontSize: 12.f,
fontWeight: FontWeight.w500,
),
);
}
: null,
// Bottom axis label formatter (X-axis - Days)
bottomLabelFormatter: (value, dataPoints) {
final index = value.toInt();
print("value is $value");
print("the index is $index");
print("the dataPoints.length is ${dataPoints.length}");
var bottomText = "";
var date = dataPoints[index].time;
print("the time is ${date}");
switch (context.read<HealthProvider>().selectedDuration) {
case durations.Durations.daily:
bottomText = getHour(date).toString();
break;
case durations.Durations.weekly:
bottomText = getDayName(date)[0];
break;
case durations.Durations.monthly:
case durations.Durations.halfYearly:
case durations.Durations.yearly:
bottomText = getMonthName(date)[0];
}
return Text(
bottomText,
style: const TextStyle(
color: Colors.grey,
fontSize: 11,
),
);
return const Text('');
},
verticalInterval: 1 / context.read<HealthProvider>().selectedData.values.toList().first.length,
getDrawingVerticalLine: (value) {
return FlLine(
color: AppColors.greyColor,
strokeWidth: 1,
);
},
showGridLines: true);
});
}
//todo remove these from here
String getDayName(DateTime date) {
return DateUtil.getWeekDayAsOfLang(date.weekday);
}
String getHour(DateTime date) {
return date.hour.toString().padLeft(2, '0').toString();
}
static String getMonthName(DateTime date) {
return DateUtil.getMonthDayAsOfLang(date.month);
}
double getBarWidth() {
var duration = context.read<HealthProvider>().selectedDuration;
switch(duration){
case durations.Durations.daily:
return 26.w;
case durations.Durations.weekly:
return 26.w;
case durations.Durations.monthly:
return 6.w;
case durations.Durations.halfYearly:
return 26.w;
case durations.Durations.yearly:
return 18.w;
}
}
}

@ -0,0 +1,252 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/smartwatch_health_data/health_provider.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/activity_detail.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/HealthDataTransformation.dart' as durations;
import '../../core/utils/date_util.dart' show DateUtil;
class SmartWatchActivity extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: "All Health Data".needTranslation,
child: Column(
spacing: 16.h,
children: [
resultItem(
leadingIcon: AppAssets.watchActivity,
title: "Activity Calories".needTranslation,
description: "Activity rings give you a quick visual reference of how active you are each day. ".needTranslation,
trailingIcon: AppAssets.watchActivityTrailing,
result: context.read<HealthProvider>().sumOfNonEmptyData(context.read<HealthProvider>().vitals?.activity??[]),
unitsOfMeasure: "Kcal"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("activity");
context.read<HealthProvider>().saveSelectedSection("activity");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("activity", sectionName:"Activity Calories", uom: "Kcal");
}),
resultItem(
leadingIcon: AppAssets.watchSteps,
title: "Steps".needTranslation,
description: "Step count is the number of steps you take throughout the day.".needTranslation,
trailingIcon: AppAssets.watchStepsTrailing,
result: context.read<HealthProvider>().sumOfNonEmptyData(context.read<HealthProvider>().vitals?.step??[]),
unitsOfMeasure: "Steps"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("steps");
context.read<HealthProvider>().saveSelectedSection("steps");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("steps", sectionName: "Steps", uom: "Steps");
}),
resultItem(
leadingIcon: AppAssets.watchSteps,
title: "Distance Covered".needTranslation,
description: "Step count is the distance you take throughout the day.".needTranslation,
trailingIcon: AppAssets.watchStepsTrailing,
result: context.read<HealthProvider>().sumOfNonEmptyData(context.read<HealthProvider>().vitals?.distance??[]),
unitsOfMeasure: "Km"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("distance");
context.read<HealthProvider>().saveSelectedSection("distance");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("distance", sectionName: "Distance Covered", uom: "km");
}),
resultItem(
leadingIcon: AppAssets.watchSleep,
title: "Sleep Score".needTranslation,
description: "This will keep track of how much hours you sleep in a day".needTranslation,
trailingIcon: AppAssets.watchSleepTrailing,
result: DateUtil.millisToHourMin(int.parse(context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.sleep??[]))).split(" ")[0],
unitsOfMeasure: "hr",
resultSecondValue: DateUtil.millisToHourMin(int.parse(context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.sleep??[]))).split(" ")[2],
unitOfSecondMeasure: "min"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("sleep");
context.read<HealthProvider>().saveSelectedSection("sleep");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("sleep", sectionName:"Sleep Score",uom:"");
}),
resultItem(
leadingIcon: AppAssets.watchWeight,
title: "Blood Oxygen".needTranslation,
description: "This will calculate your Blood Oxygen to keep track and update history".needTranslation,
trailingIcon: AppAssets.watchWeightTrailing,
result: context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.bodyOxygen??[], ),
unitsOfMeasure: "%"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("bodyOxygen");
context.read<HealthProvider>().saveSelectedSection("bodyOxygen");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("bodyOxygen", uom: "%", sectionName:"Blood Oxygen" );
}),
resultItem(
leadingIcon: AppAssets.watchWeight,
title: "Body temperature".needTranslation,
description: "This will calculate your Body temprerature to keep track and update history".needTranslation,
trailingIcon: AppAssets.watchWeightTrailing,
result: context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.bodyTemperature??[]),
unitsOfMeasure: "C"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("bodyTemperature");
context.read<HealthProvider>().saveSelectedSection("bodyTemperature");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("bodyTemperature" , sectionName: "Body temperature".capitalizeFirstofEach, uom: "C");
}),
],
).paddingSymmetrical(24.w, 24.h),
));
}
Widget resultItem({
required String leadingIcon,
required String title,
required String description,
required String trailingIcon,
required String result,
required String unitsOfMeasure,
String? resultSecondValue,
String? unitOfSecondMeasure
}) {
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
child: Row(
spacing: 16.w,
children: [
Expanded(
child:Column(
spacing: 8.h,
children: [
Row(
spacing: 8.w,
children: [
Utils.buildSvgWithAssets(icon: leadingIcon, height: 16.h, width: 14.w),
title.toText16( weight: FontWeight.w600, color: AppColors.textColor),
],
),
description.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
spacing: 2.h,
children: [
result.toText21(weight: FontWeight.w600, color: AppColors.textColor),
unitsOfMeasure.toText10(weight: FontWeight.w500, color:AppColors.greyTextColor ),
if(resultSecondValue != null)
Visibility(
visible: resultSecondValue != null ,
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
spacing: 2.h,
children: [
SizedBox(width: 2.w,),
resultSecondValue.toText21(weight: FontWeight.w600, color: AppColors.textColor),
unitOfSecondMeasure!.toText10(weight: FontWeight.w500, color:AppColors.greyTextColor )
],
),
)
],
),
],
) ,
),
Utils.buildSvgWithAssets(icon: trailingIcon, width: 72.w, height: 72.h),
],
).paddingSymmetrical(16.w, 16.h)
);
}
}

@ -3,18 +3,25 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/route_extensions.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/smartwatch_health_data/health_provider.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/smartwatch_instructions_page.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/widgets/supported_watches_list.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:provider/provider.dart';
import '../../core/utils/utils.dart';
class SmartwatchHomePage extends StatelessWidget {
const SmartwatchHomePage({super.key});
@ -80,7 +87,15 @@ class SmartwatchHomePage extends StatelessWidget {
CustomButton(
text: LocaleKeys.select.tr(context: context),
onPressed: () {
context.read<HealthProvider>().setSelectedWatchType("apple", "assets/images/png/smartwatches/apple-watch-5.jpg");
context.read<HealthProvider>().setSelectedWatchType(SmartWatchTypes.apple, "assets/images/png/smartwatches/apple-watch-5.jpg");
getIt.get<NavigationService>().pushPage(page: SmartwatchInstructionsPage(
smartwatchDetails: SmartwatchDetails(SmartWatchTypes.apple,
"assets/images/png/smartwatches/apple-watch-5.jpg",
AppAssets.bluetooth,
LocaleKeys.applehealthapplicationshouldbeinstalledinyourphone.tr(context: context),
LocaleKeys.unabletodetectapplicationinstalledpleasecomebackonceinstalled.tr(context: context),
LocaleKeys.applewatchshouldbeconnected.tr(context: context)),
));
},
backgroundColor: AppColors.primaryRedColor.withAlpha(40),
borderColor: AppColors.primaryRedColor.withAlpha(0),
@ -105,8 +120,15 @@ class SmartwatchHomePage extends StatelessWidget {
CustomButton(
text: LocaleKeys.select.tr(context: context),
onPressed: () {
context.read<HealthProvider>().setSelectedWatchType("samsung", "assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg");
},
context.read<HealthProvider>().setSelectedWatchType(SmartWatchTypes.samsung, "assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg");
getIt.get<NavigationService>().pushPage(page: SmartwatchInstructionsPage(
smartwatchDetails: SmartwatchDetails(SmartWatchTypes.samsung,
"assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg",
AppAssets.bluetooth,
LocaleKeys.samsunghealthapplicationshouldbeinstalledinyourphone.tr(context: context),
LocaleKeys.unabletodetectapplicationinstalledpleasecomebackonceinstalled.tr(context: context),
LocaleKeys.samsungwatchshouldbeconnected.tr(context: context)),
)); },
backgroundColor: AppColors.primaryRedColor.withAlpha(40),
borderColor: AppColors.primaryRedColor.withAlpha(0),
textColor: AppColors.primaryRedColor,
@ -130,7 +152,16 @@ class SmartwatchHomePage extends StatelessWidget {
CustomButton(
text: LocaleKeys.select.tr(context: context),
onPressed: () {
context.read<HealthProvider>().setSelectedWatchType("huawei", "assets/images/png/smartwatches/Huawei_Watch.png");
// context.read<HealthProvider>().setSelectedWatchType(SmartWatchTypes.huawei, "assets/images/png/smartwatches/Huawei_Watch.png");
// getIt.get<NavigationService>().pushPage(page: SmartwatchInstructionsPage(
// smartwatchDetails: SmartwatchDetails(SmartWatchTypes.huawei,
// "assets/images/png/smartwatches/Huawei_Watch.png",
// AppAssets.bluetooth,
// LocaleKeys.huaweihealthapplicationshouldbeinstalledinyourphone.tr(context: context),
// LocaleKeys.unabletodetectapplicationinstalledpleasecomebackonceinstalled.tr(context: context),
// LocaleKeys.huaweiwatchshouldbeconnected.tr(context: context)),
// ));
showUnavailableDialog(context);
},
backgroundColor: AppColors.primaryRedColor.withAlpha(40),
borderColor: AppColors.primaryRedColor.withAlpha(0),
@ -155,7 +186,17 @@ class SmartwatchHomePage extends StatelessWidget {
CustomButton(
text: LocaleKeys.select.tr(context: context),
onPressed: () {
context.read<HealthProvider>().setSelectedWatchType("whoop", "assets/images/png/smartwatches/Whoop_Watch.png");
showUnavailableDialog(context);
// context.read<HealthProvider>().setSelectedWatchType(SmartWatchTypes.whoop, "assets/images/png/smartwatches/Whoop_Watch.png");
// getIt.get<NavigationService>().pushPage(page: SmartwatchInstructionsPage(
// smartwatchDetails: SmartwatchDetails(SmartWatchTypes.whoop,
// "assets/images/png/smartwatches/Whoop_Watch.png",
// AppAssets.bluetooth,
// LocaleKeys.whoophealthapplicationshouldbeinstalledinyourphone.tr(context: context),
// LocaleKeys.unabletodetectapplicationinstalledpleasecomebackonceinstalled.tr(context: context),
// LocaleKeys.whoopwatchshouldbeconnected.tr(context: context)),
// ));
},
backgroundColor: AppColors.primaryRedColor.withAlpha(40),
borderColor: AppColors.primaryRedColor.withAlpha(0),
@ -177,4 +218,23 @@ class SmartwatchHomePage extends StatelessWidget {
),
);
}
void showUnavailableDialog(BuildContext context) {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: LocaleKeys.featureComingSoonDescription.tr(context: context),
isShowActionButtons: false,
showOkButton: true,
onConfirmTap: () async {
context.pop();
}
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}

@ -1,15 +1,26 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/smart_watch_activity.dart' show SmartWatchActivity;
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:provider/provider.dart';
import '../../core/utils/utils.dart';
import '../../features/smartwatch_health_data/health_provider.dart' show HealthProvider;
class SmartwatchInstructionsPage extends StatelessWidget {
const SmartwatchInstructionsPage({super.key});
final SmartwatchDetails smartwatchDetails;
const SmartwatchInstructionsPage({super.key, required this.smartwatchDetails});
@override
Widget build(BuildContext context) {
@ -25,6 +36,7 @@ class SmartwatchInstructionsPage extends StatelessWidget {
child: CustomButton(
text: LocaleKeys.getStarted.tr(context: context),
onPressed: () {
context.read<HealthProvider>().initDevice();
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
@ -35,8 +47,55 @@ class SmartwatchInstructionsPage extends StatelessWidget {
height: 50.h,
).paddingSymmetrical(24.w, 30.h),
),
child: SingleChildScrollView(),
child: Column(
mainAxisSize: MainAxisSize.max,
spacing: 18.h,
children: [
Image.asset(smartwatchDetails.watchIcon, fit: BoxFit.contain, height: 280.h,width: 280.w,),
DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
child: Column(
children: [
watchContentDetails(
title: smartwatchDetails.detailsTitle,
description: smartwatchDetails.details,
icon: smartwatchDetails.smallIcon,
descriptionTextColor: AppColors.primaryRedColor
),
Divider(
color: AppColors.dividerColor,
thickness: 1.h,
).paddingOnly(top: 16.h, bottom: 16.h),
watchContentDetails(
title: smartwatchDetails.secondTitle,
description: LocaleKeys.updatetheinformation.tr(),
icon: AppAssets.bluetooth,
descriptionTextColor: AppColors.greyTextColor
),
],
).paddingSymmetrical(16.w, 16.h),
)
],
).paddingSymmetrical(24.w, 16.h),
),
);
}
Widget watchContentDetails({required String title, required String description, required String icon, required Color descriptionTextColor}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [
DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
child: Utils.buildSvgWithAssets(icon: icon, width: 40.w, height: 40.h),
),
title.toText16(weight: FontWeight.w600, color: AppColors.textColor),
description.toText12(fontWeight: FontWeight.w500, color: descriptionTextColor)
],
);
}
}

@ -41,6 +41,7 @@ import '../features/monthly_reports/monthly_reports_repo.dart';
import '../features/monthly_reports/monthly_reports_view_model.dart';
import '../features/qr_parking/qr_parking_view_model.dart';
import '../presentation/parking/paking_page.dart';
import '../presentation/smartwatches/smartwatch_instructions_page.dart';
import '../services/error_handler_service.dart';
class AppRoutes {

@ -57,7 +57,7 @@ class _SplashScreenState extends State<SplashPage> {
await notificationService.initialize(onNotificationClick: (payload) {
// Handle notification click here
});
//ZoomService().initializeZoomSDK();
ZoomService().initializeZoomSDK();
if (isAppOpenedFromCall) {
navigateToTeleConsult();
} else {

@ -300,6 +300,7 @@ extension AppColorsContext on BuildContext {
// Shimmer
Color get shimmerBaseColor => _isDark ? AppColors.dark.shimmerBaseColor : const Color(0xFFE0E0E0);
Color get shimmerHighlightColor => _isDark ? AppColors.dark.shimmerHighlightColor : const Color(0xFFF5F5F5);
static const Color tooltipColor = Color(0xFF1AACACAC);
// Aliases
Color get bgScaffoldColor => scaffoldBgColor;

@ -21,6 +21,7 @@ class CustomTabBar extends StatefulWidget {
final Color? inActiveTextColor;
final Color? inActiveBackgroundColor;
final Function(int)? onTabChange;
final bool shouldTabExpanded;
const CustomTabBar({
super.key,
@ -31,6 +32,7 @@ class CustomTabBar extends StatefulWidget {
this.activeBackgroundColor,
this.inActiveBackgroundColor,
this.onTabChange,
this.shouldTabExpanded = false
});
@override
@ -62,6 +64,11 @@ class CustomTabBarState extends State<CustomTabBar> {
final resolvedActiveBgColor = widget.activeBackgroundColor ?? AppColors.lightGrayBGColor;
final resolvedInActiveBgColor = widget.inActiveBackgroundColor ?? AppColors.whiteColor;
late Widget parentWidget;
if(widget.shouldTabExpanded){
return Row(
children:List.generate(widget.tabs.length, (index)=>myTab(widget.tabs[index], index, resolvedActiveTextColor, resolvedInActiveTextColor, resolvedActiveBgColor, resolvedInActiveBgColor).expanded),
);
}
if (widget.tabs.length > 3) {
parentWidget = ListView.separated(

@ -0,0 +1,250 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
/// A customizable bar chart widget using `fl_chart`.
///
/// Displays a bar chart with configurable axis labels, colors, and data points.
/// Useful for visualizing comparative data, categories, or grouped values.
///
/// **Parameters:**
/// - [dataPoints]: List of `DataPoint` objects to plot.
/// - [secondaryDataPoints]: Optional list for grouped bars (e.g., comparison data).
/// - [leftLabelFormatter]: Function to build left axis labels.
/// - [bottomLabelFormatter]: Function to build bottom axis labels.
/// - [width]: Optional width of the chart.
/// - [height]: Required height of the chart.
/// - [maxY], [maxX], [minX]: Axis bounds.
/// - [barColor]: Color of the bars.
/// - [secondaryBarColor]: Color of the secondary bars.
/// - [barRadius]: Border radius for bar corners.
/// - [barWidth]: Width of each bar.
/// - [bottomLabelColor]: Color of bottom axis labels.
/// - [bottomLabelSize]: Font size for bottom axis labels.
/// - [bottomLabelFontWeight]: Font weight for bottom axis labels.
/// - [leftLabelInterval]: Interval between left axis labels.
/// - [leftLabelReservedSize]: Reserved space for left axis labels.
/// - [showBottomTitleDates]: Whether to show bottom axis labels.
/// - [isFullScreeGraph]: Whether the graph is fullscreen.
/// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting.
///
/// Example usage:
/// ```dart
/// CustomBarChart(
/// dataPoints: sampleData,
/// leftLabelFormatter: (value) => ...,
/// bottomLabelFormatter: (value, dataPoints) => ...,
/// height: 300,
/// maxY: 100,
/// )
class CustomBarChart extends StatelessWidget {
final List<DataPoint> dataPoints;
final List<DataPoint>? secondaryDataPoints; // For grouped bar charts
final double? width;
final double height;
final double? maxY;
final double? maxX;
final double? minX;
Color? barColor;
final Color? secondaryBarColor;
Color? barGridColor;
Color? bottomLabelColor;
final double? bottomLabelSize;
final FontWeight? bottomLabelFontWeight;
final double? leftLabelInterval;
final double? leftLabelReservedSize;
final double? bottomLabelReservedSize;
final bool? showGridLines;
final GetDrawingGridLine? getDrawingVerticalLine;
final double? verticalInterval;
final double? minY;
final BorderRadius? barRadius;
final double barWidth;
final BarTooltipItem Function(DataPoint)? getTooltipItem;
/// Creates the left label and provides it to the chart
final Widget Function(double) leftLabelFormatter;
final Widget Function(double, List<DataPoint>) bottomLabelFormatter;
final bool showBottomTitleDates;
final bool isFullScreeGraph;
final bool makeGraphBasedOnActualValue;
CustomBarChart(
{super.key,
required this.dataPoints,
this.secondaryDataPoints,
required this.leftLabelFormatter,
this.width,
required this.height,
this.maxY,
this.maxX,
this.showBottomTitleDates = true,
this.isFullScreeGraph = false,
this.secondaryBarColor,
this.bottomLabelFontWeight = FontWeight.w500,
this.bottomLabelSize,
this.leftLabelInterval,
this.leftLabelReservedSize,
this.bottomLabelReservedSize,
this.makeGraphBasedOnActualValue = false,
required this.bottomLabelFormatter,
this.minX,
this.showGridLines = false,
this.getDrawingVerticalLine,
this.verticalInterval,
this.minY,
this.barRadius,
this.barWidth = 16,
this.getTooltipItem,
this.barColor ,
this.barGridColor ,
this.bottomLabelColor,
});
@override
Widget build(BuildContext context) {
barColor ??= AppColors.bgGreenColor;
barGridColor ??= AppColors.graphGridColor;
bottomLabelColor ??= AppColors.textColor;
return Material(
color: Colors.white,
child: SizedBox(
width: width,
height: height,
child: BarChart(
BarChartData(
minY: minY ?? 0,
maxY: maxY,
barTouchData: BarTouchData(
handleBuiltInTouches: true,
touchCallback: (FlTouchEvent event, BarTouchResponse? touchResponse) {
// Let fl_chart handle the touch
},
touchTooltipData: BarTouchTooltipData(
getTooltipColor: (_)=>AppColorsContext.tooltipColor,
getTooltipItem: (group, groupIndex, rod, rodIndex) {
final dataPoint = dataPoints[groupIndex];
if(getTooltipItem != null) {
return getTooltipItem!(dataPoint);
}
return BarTooltipItem(
'${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""}\n${DateFormat('dd MMM, yyyy').format(dataPoint.time)}',
TextStyle(
color: Colors.black,
fontSize: 12.f,
fontWeight: FontWeight.w500,
),
);
},
),
enabled: true,
),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: leftLabelReservedSize ?? 80,
interval: leftLabelInterval ?? .1,
getTitlesWidget: (value, _) {
return leftLabelFormatter(value);
},
),
),
bottomTitles: AxisTitles(
axisNameSize: 20,
sideTitles: SideTitles(
showTitles: showBottomTitleDates,
reservedSize: bottomLabelReservedSize ?? 30,
getTitlesWidget: (value, _) {
return bottomLabelFormatter(value, dataPoints);
},
interval: 1,
),
),
topTitles: AxisTitles(),
rightTitles: AxisTitles(),
),
borderData: FlBorderData(
show: true,
border: const Border(
bottom: BorderSide.none,
left: BorderSide(color: Colors.grey, width: .5),
right: BorderSide.none,
top: BorderSide.none,
),
),
barGroups: _buildBarGroups(dataPoints),
gridData: FlGridData(
show: showGridLines ?? true,
drawHorizontalLine: false,
verticalInterval: verticalInterval,
getDrawingVerticalLine: getDrawingVerticalLine ??
(value) {
return FlLine(
color: barGridColor,
strokeWidth: 1,
dashArray: [5, 5],
);
},
)),
),
),
);
}
/// Builds bar chart groups from data points
List<BarChartGroupData> _buildBarGroups(List<DataPoint> dataPoints) {
return dataPoints.asMap().entries.map((entry) {
final index = entry.key;
final dataPoint = entry.value;
double value = (makeGraphBasedOnActualValue)
? double.tryParse(dataPoint.actualValue) ?? 0.0
: dataPoint.value;
final barRods = <BarChartRodData>[
BarChartRodData(
toY: value,
color: barColor,
width: barWidth,
borderRadius: barRadius ?? BorderRadius.circular(6),
// backDrawRodData: BackgroundBarChartRodData(
// show: true,
// toY: maxY,
// color: Colors.grey[100],
// ),
),
];
// Add secondary bar if provided (for grouped bar charts)
if (secondaryDataPoints != null && index < secondaryDataPoints!.length) {
final secondaryDataPoint = secondaryDataPoints![index];
double secondaryValue = (makeGraphBasedOnActualValue)
? double.tryParse(secondaryDataPoint.actualValue) ?? 0.0
: secondaryDataPoint.value;
barRods.add(
BarChartRodData(
toY: secondaryValue,
color: secondaryBarColor ?? AppColors.blueColor,
width: barWidth,
borderRadius: barRadius ?? BorderRadius.circular(6),
),
);
}
return BarChartGroupData(
x: index,
barRods: barRods,
barsSpace: 8.w
);
}).toList();
}
}

File diff suppressed because it is too large Load Diff

@ -91,7 +91,7 @@ dependencies:
location: ^8.0.1
gms_check: ^1.0.4
huawei_location: ^6.14.2+301
# huawei_health: ^6.16.0+300
huawei_health: ^6.15.0+300
intl: ^0.20.2
flutter_widget_from_html: ^0.17.1
huawei_map: ^6.12.0+301

Loading…
Cancel
Save