Merge branch 'master' into fatima

# Conflicts:
#	lib/core/dependencies.dart
#	lib/main.dart
#	lib/presentation/hmg_services/services_page.dart
#	lib/presentation/medical_file/medical_file_page.dart
#	lib/routes/app_routes.dart
pull/152/head
Fatimah.Alshammari 3 months ago
commit 20e4f82df3

@ -156,16 +156,16 @@ dependencies {
implementation("com.intuit.ssp:ssp-android:1.1.0")
implementation("com.intuit.sdp:sdp-android:1.1.0")
// implementation("com.github.bumptech.glide:glide:4.16.0")
// annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.github.bumptech.glide:glide:4.16.0")
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.mapbox.maps:android:11.5.0")
// implementation("com.mapbox.maps:android:11.4.0")
// AARs
// implementation(files("libs/PenNavUI.aar"))
// implementation(files("libs/Penguin.aar"))
// implementation(files("libs/PenguinRenderer.aar"))
implementation(files("libs/PenNavUI.aar"))
implementation(files("libs/Penguin.aar"))
implementation(files("libs/PenguinRenderer.aar"))
implementation("com.github.kittinunf.fuel:fuel:2.3.1")
implementation("com.github.kittinunf.fuel:fuel-android:2.3.1")
@ -180,9 +180,11 @@ dependencies {
implementation("com.google.android.material:material:1.12.0")
implementation("pl.droidsonroids.gif:android-gif-drawable:1.2.25")
implementation("com.mapbox.mapboxsdk:mapbox-sdk-turf:7.3.1")
androidTestImplementation("androidx.test:core:1.6.1")
implementation("com.whatsapp.otp:whatsapp-otp-android-sdk:0.1.0")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
// implementation(project(":vitalSignEngine"))
}

Binary file not shown.

Binary file not shown.

@ -49,7 +49,7 @@
<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_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" />
@ -58,6 +58,13 @@
<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"

@ -1,6 +1,51 @@
package com.ejada.hmg
import android.app.PendingIntent
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.ejada.hmg.penguin.PenguinInPlatformBridge
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity : 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()
}
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()
}
}

@ -0,0 +1,61 @@
package com.ejada.hmg.penguin
import com.ejada.hmg.MainActivity
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.ejada.hmg.penguin.PenguinView
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import com.ejada.hmg.PermissionManager.HostNotificationPermissionManager
import com.ejada.hmg.PermissionManager.HostBgLocationManager
import com.ejada.hmg.PermissionManager.HostGpsStateManager
import io.flutter.plugin.common.MethodChannel
class PenguinInPlatformBridge(
private var flutterEngine: FlutterEngine,
private var mainActivity: MainActivity
) {
private lateinit var channel: MethodChannel
companion object {
private const val CHANNEL = "launch_penguin_ui"
}
@RequiresApi(Build.VERSION_CODES.O)
fun create() {
// openTok = OpenTok(mainActivity, flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
when (call.method) {
"launchPenguin" -> {
print("the platform channel is being called")
if (HostNotificationPermissionManager.isNotificationPermissionGranted(mainActivity))
else HostNotificationPermissionManager.requestNotificationPermission(mainActivity)
HostBgLocationManager.requestLocationBackgroundPermission(mainActivity)
HostGpsStateManager.requestLocationPermission(mainActivity)
val args = call.arguments as Map<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.ejada.hmg.PermissionManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.HandlerThread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
/**
* This preferences for app level
*/
public class AppPreferences {
public static final String PREF_NAME = "PenguinINUI_AppPreferences";
public static final int MODE = Context.MODE_PRIVATE;
public static final String campusIdKey = "campusId";
public static final String LANG = "Lang";
public static final String settingINFO = "SETTING-INFO";
public static final String userName = "userName";
public static final String passWord = "passWord";
private static HandlerThread handlerThread;
private static Handler handler;
static {
handlerThread = new HandlerThread("PreferencesHandlerThread");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
public static SharedPreferences getPreferences(final Context context) {
return context.getSharedPreferences(AppPreferences.PREF_NAME, AppPreferences.MODE);
}
public static SharedPreferences.Editor getEditor(final Context context) {
return getPreferences(context).edit();
}
public static void writeInt(final Context context, final String key, final int value) {
handler.post(() -> {
SharedPreferences.Editor editor = getEditor(context);
editor.putInt(key, value);
editor.apply();
});
}
public static int readInt(final Context context, final String key, final int defValue) {
Callable<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.ejada.hmg.PermissionManager;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.peng.pennavmap.PlugAndPlaySDK;
import com.peng.pennavmap.R;
import com.peng.pennavmap.enums.InitializationErrorType;
/**
* Manages background location permission requests and handling for the application.
*/
public class HostBgLocationManager {
/**
* Request code for background location permission
*/
public static final int REQUEST_ACCESS_BACKGROUND_LOCATION_CODE = 301;
/**
* Request code for navigating to app settings
*/
private static final int REQUEST_CODE_SETTINGS = 11234;
/**
* Alert dialog for denied permissions
*/
private static AlertDialog deniedAlertDialog;
/**
* Checks if the background location permission has been granted.
*
* @param context the context of the application or activity
* @return true if the permission is granted, false otherwise
*/
public static boolean isLocationBackgroundGranted(Context context) {
return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Requests the background location permission from the user.
*
* @param activity the activity from which the request is made
*/
public static void requestLocationBackgroundPermission(Activity activity) {
// Check if the ACCESS_BACKGROUND_LOCATION permission is already granted
if (!isLocationBackgroundGranted(activity)) {
// Permission is not granted, so request it
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
REQUEST_ACCESS_BACKGROUND_LOCATION_CODE);
}
}
/**
* Displays a dialog prompting the user to grant the background location permission.
*
* @param activity the activity where the dialog is displayed
*/
public static void showLocationBackgroundPermission(Activity activity) {
AlertDialog alertDialog = new AlertDialog.Builder(activity)
.setCancelable(false)
.setMessage(activity.getString(R.string.com_penguin_nav_ui_geofence_alert_msg))
.setPositiveButton(activity.getString(R.string.com_penguin_nav_ui_go_to_settings), (dialog, which) -> {
if (activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
HostBgLocationManager.requestLocationBackgroundPermission(activity);
} else {
openAppSettings(activity);
}
if (dialog != null) {
dialog.dismiss();
}
})
.setNegativeButton(activity.getString(R.string.com_penguin_nav_ui_later), (dialog, which) -> {
dialog.cancel();
})
.create();
alertDialog.show();
}
/**
* Handles the scenario where permissions are denied by the user.
* Displays a dialog to guide the user to app settings or exit the activity.
*
* @param activity the activity where the dialog is displayed
*/
public static synchronized void handlePermissionsDenied(Activity activity) {
if (deniedAlertDialog != null && deniedAlertDialog.isShowing()) {
deniedAlertDialog.dismiss();
}
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setCancelable(false)
.setMessage(activity.getString(R.string.com_penguin_nav_ui_permission_denied_dialog_msg))
.setNegativeButton(activity.getString(R.string.com_penguin_nav_ui_cancel), (dialogInterface, i) -> {
if (PlugAndPlaySDK.externalPenNavUIDelegate != null) {
PlugAndPlaySDK.externalPenNavUIDelegate.onPenNavInitializationError(
InitializationErrorType.permissions.getTypeKey(),
InitializationErrorType.permissions);
}
activity.finish();
})
.setPositiveButton(activity.getString(R.string.com_penguin_nav_ui_go_settings), (dialogInterface, i) -> {
dialogInterface.dismiss();
openAppSettings(activity);
});
deniedAlertDialog = builder.create();
deniedAlertDialog.show();
}
/**
* Opens the application's settings screen to allow the user to modify permissions.
*
* @param activity the activity from which the settings screen is launched
*/
private static void openAppSettings(Activity activity) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
intent.setData(uri);
if (intent.resolveActivity(activity.getPackageManager()) != null) {
activity.startActivityForResult(intent, REQUEST_CODE_SETTINGS);
}
}
}

@ -0,0 +1,68 @@
package com.ejada.hmg.PermissionManager;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.peng.pennavmap.managers.permissions.managers.BgLocationManager;
public class HostGpsStateManager {
private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
public boolean checkGPSEnabled(Activity activity) {
LocationManager gpsStateManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
return gpsStateManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
public static boolean isGpsGranted(Activity activity) {
return BgLocationManager.isLocationBackgroundGranted(activity)
|| ContextCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED;
}
/**
* Checks if the location permission is granted.
*
* @param activity the Activity context
* @return true if permission is granted, false otherwise
*/
public static boolean isLocationPermissionGranted(Activity activity) {
return ContextCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED;
}
/**
* Requests the location permission.
*
* @param activity the Activity context
*/
public static void requestLocationPermission(Activity activity) {
ActivityCompat.requestPermissions(
activity,
new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
},
LOCATION_PERMISSION_REQUEST_CODE
);
}
}

@ -0,0 +1,73 @@
package com.ejada.hmg.PermissionManager;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
public class HostNotificationPermissionManager {
private static final int REQUEST_NOTIFICATION_PERMISSION = 100;
/**
* Checks if the notification permission is granted.
*
* @return true if the notification permission is granted, false otherwise.
*/
public static boolean isNotificationPermissionGranted(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
try {
return ActivityCompat.checkSelfPermission(activity, android.Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED;
} catch (Exception e) {
// Handle cases where the API is unavailable
e.printStackTrace();
return NotificationManagerCompat.from(activity).areNotificationsEnabled();
}
} else {
// Permissions were not required below Android 13 for notifications
return NotificationManagerCompat.from(activity).areNotificationsEnabled();
}
}
/**
* Requests the notification permission.
*/
public static void requestNotificationPermission(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (!isNotificationPermissionGranted(activity)) {
ActivityCompat.requestPermissions(activity,
new String[]{android.Manifest.permission.POST_NOTIFICATIONS},
REQUEST_NOTIFICATION_PERMISSION);
}
}
}
/**
* Handles the result of the permission request.
*
* @param requestCode The request code passed in requestPermissions().
* @param permissions The requested permissions.
* @param grantResults The grant results for the corresponding permissions.
*/
public static boolean handlePermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (permissions.length > 0 &&
permissions[0].equals(android.Manifest.permission.POST_NOTIFICATIONS) &&
grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted
System.out.println("Notification permission granted.");
return true;
} else {
// Permission denied
System.out.println("Notification permission denied.");
return false;
}
}
}

@ -0,0 +1,28 @@
package com.ejada.hmg.PermissionManager
import android.Manifest
import android.os.Build
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.ejada.hmg.PermissionManager
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
class PermissionManager(
private val context: Context,
val listener: PermissionListener,
private val requestCode: Int,
vararg permissions: String
) {
private val permissionsArray = permissions
interface PermissionListener {
fun onPermissionGranted()
fun onPermissionDenied()
}
fun arePermissionsGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
permissionsArray.all {
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
} else {
true
}
}
fun requestPermissions(activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityCompat.requestPermissions(activity, permissionsArray, requestCode)
}
}
fun handlePermissionsResult(requestCode: Int, permissions: Array<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.ejada.hmg.PermissionManager
// PermissionResultReceiver.kt
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
class PermissionResultReceiver(
private val callback: (Boolean) -> Unit
) : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val granted = intent?.getBooleanExtra("PERMISSION_GRANTED", false) ?: false
callback(granted)
}
}

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

@ -0,0 +1,97 @@
package com.ejada.hmg.penguin
import android.content.Context
import com.google.gson.Gson
import com.peng.pennavmap.PlugAndPlaySDK
import com.peng.pennavmap.connections.ApiController
import com.peng.pennavmap.interfaces.RefIdDelegate
import com.peng.pennavmap.models.TokenModel
import com.peng.pennavmap.models.postmodels.PostToken
import com.peng.pennavmap.utils.AppSharedData
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import android.util.Log
class PenguinNavigator() {
fun navigateTo(mContext: Context, refID: String, delegate: RefIdDelegate,clientID : String,clientKey : String ) {
val postToken = PostToken(clientID, clientKey)
getToken(mContext, postToken, object : RefIdDelegate {
override fun onRefByIDSuccess(PoiId: String?) {
Log.e("navigateTo", "PoiId is+++++++ $PoiId")
PlugAndPlaySDK.navigateTo(mContext, refID, object : RefIdDelegate {
override fun onRefByIDSuccess(PoiId: String?) {
Log.e("navigateTo", "PoiId 2is+++++++ $PoiId")
delegate.onRefByIDSuccess(refID)
}
override fun onGetByRefIDError(error: String?) {
delegate.onRefByIDSuccess(error)
}
})
}
override fun onGetByRefIDError(error: String?) {
delegate.onRefByIDSuccess(error)
}
})
}
fun getToken(mContext: Context, postToken: PostToken?, apiTokenCallBack: RefIdDelegate) {
try {
// Create the API call
val purposesCall: Call<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.ejada.hmg.penguin
import android.app.Activity
import android.content.Context
import android.content.Context.RECEIVER_EXPORTED
import android.content.IntentFilter
import android.graphics.Color
import android.os.Build
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.Toast
import androidx.annotation.RequiresApi
import com.ejada.hmg.PermissionManager.PermissionManager
import com.ejada.hmg.PermissionManager.PermissionResultReceiver
import com.ejada.hmg.MainActivity
import com.ejada.hmg.PermissionManager.PermissionHelper
import com.peng.pennavmap.PlugAndPlayConfiguration
import com.peng.pennavmap.PlugAndPlaySDK
import com.peng.pennavmap.enums.InitializationErrorType
import com.peng.pennavmap.interfaces.PenNavUIDelegate
import com.peng.pennavmap.utils.Languages
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.platform.PlatformView
import com.ejada.hmg.penguin.PenguinNavigator
import com.peng.pennavmap.interfaces.PIEventsDelegate
import com.peng.pennavmap.interfaces.PILocationDelegate
import com.peng.pennavmap.interfaces.RefIdDelegate
import com.peng.pennavmap.models.LocationMessage
import com.peng.pennavmap.models.PIReportIssue
import java.util.ArrayList
import penguin.com.pennav.renderer.PIRendererSettings
/**
* Custom PlatformView for displaying Penguin UI components within a Flutter app.
* Implements `PlatformView` for rendering the view, `MethodChannel.MethodCallHandler` for handling method calls,
* and `PenNavUIDelegate` for handling SDK events.
*/
@RequiresApi(Build.VERSION_CODES.O)
internal class PenguinView(
context: Context,
id: Int,
val creationParams: Map<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)
}
/**
* 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")
}
}

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

@ -19,5 +19,5 @@
<string name="GEOFENCE_REQUEST_TOO_FREQUENT">
Geofence requests happened too frequently.
</string>
<!-- <string name="mapbox_access_token" translatable="false">sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg</string>-->
<string name="mapbox_access_token" translatable="false">pk.eyJ1IjoicndhaWQiLCJhIjoiY2x5cGo4aHNjMGNsbTJyc2djempobGQxaSJ9.RCaC6WrUt4A4YnZNfxnONQ</string>
</resources>

File diff suppressed because one or more lines are too long

@ -18,7 +18,7 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
// id("com.android.application") version "8.7.3" apply false
// id("com.android.application") version "8.9.3" apply false
id("com.android.application") version "8.9.3" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.85417 0C4.41186 0 0 4.41186 0 9.85417C0 15.2965 4.41186 19.7083 9.85417 19.7083C15.2965 19.7083 19.7083 15.2965 19.7083 9.85417C19.7083 4.41186 15.2965 0 9.85417 0ZM10.7708 6.1875C10.7708 5.68124 10.3604 5.27083 9.85417 5.27083C9.34791 5.27083 8.9375 5.68124 8.9375 6.1875V8.9375H6.1875C5.68124 8.9375 5.27083 9.34791 5.27083 9.85417C5.27083 10.3604 5.68124 10.7708 6.1875 10.7708H8.9375V13.5208C8.9375 14.0271 9.34791 14.4375 9.85417 14.4375C10.3604 14.4375 10.7708 14.0271 10.7708 13.5208V10.7708H13.5208C14.0271 10.7708 14.4375 10.3604 14.4375 9.85417C14.4375 9.34791 14.0271 8.9375 13.5208 8.9375H10.7708V6.1875Z" fill="#2B353E"/>
</svg>

After

Width:  |  Height:  |  Size: 790 B

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 13C4 8.58172 7.58172 5 12 5C16.4183 5 20 8.58172 20 13C20 13.5523 20.4477 14 21 14C21.5523 14 22 13.5523 22 13C22 7.47715 17.5228 3 12 3C6.47715 3 2 7.47715 2 13C2 13.5523 2.44772 14 3 14C3.55228 14 4 13.5523 4 13Z" fill="#D48D05"/>
<path d="M12 9C12.5523 9 13 9.44772 13 10L13 15.1707C14.1652 15.5825 15 16.6938 15 18C15 19.6569 13.6569 21 12 21C10.3431 21 9 19.6569 9 18C9 16.6938 9.83481 15.5825 11 15.1707L11 10C11 9.44772 11.4477 9 12 9Z" fill="#D48D05"/>
</svg>

After

Width:  |  Height:  |  Size: 576 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

@ -0,0 +1,5 @@
<svg width="44" height="32" viewBox="0 0 44 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1H31.2656L28.4282 31H7.6206L1 1Z" stroke="#8F9AA3" stroke-width="2" stroke-linejoin="round" stroke-dasharray="4 4"/>
<path d="M31.2656 3.14062H36.9404C40.1561 3.14062 42.6152 5.92634 42.6152 9.5692V15.9978C42.6152 19.6406 40.1561 22.4263 36.9404 22.4263H33.1572" stroke="#8F9AA3" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4 4"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.9171 10.1057C20.1307 9.99999 19.1234 9.99999 17.8411 10H17.7741C16.4918 9.99999 15.4846 9.99999 14.6982 10.1057C13.8923 10.2141 13.253 10.4406 12.7506 10.943C12.2482 11.4453 12.0217 12.0847 11.9133 12.8906C11.8076 13.6769 11.8076 14.6842 11.8076 15.9665V16.0335C11.8076 17.3158 11.8076 18.3231 11.9133 19.1094C12.0217 19.9153 12.2482 20.5547 12.7506 21.057C13.253 21.5594 13.8923 21.7859 14.6982 21.8943C15.4846 22 16.4918 22 17.7741 22H17.8411C19.1234 22 20.1307 22 20.9171 21.8943C21.7229 21.7859 22.3623 21.5594 22.8646 21.057C23.367 20.5547 23.5936 19.9153 23.7019 19.1094C23.8076 18.3231 23.8076 17.3158 23.8076 16.0335V15.9665C23.8076 14.6842 23.8076 13.6769 23.7019 12.8906C23.5936 12.0847 23.367 11.4453 22.8646 10.943C22.3623 10.4406 21.7229 10.2141 20.9171 10.1057ZM18.393 13.6585C18.393 13.3352 18.1309 13.0732 17.8076 13.0732C17.4843 13.0732 17.2223 13.3352 17.2223 13.6585V15.4146H15.4662C15.1429 15.4146 14.8808 15.6767 14.8808 16C14.8808 16.3233 15.1429 16.5854 15.4662 16.5854H17.2223V18.3415C17.2223 18.6648 17.4843 18.9268 17.8076 18.9268C18.1309 18.9268 18.393 18.6648 18.393 18.3415V16.5854H20.1491C20.4724 16.5854 20.7344 16.3233 20.7344 16C20.7344 15.6767 20.4724 15.4146 20.1491 15.4146H18.393V13.6585Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -0,0 +1,4 @@
<svg width="44" height="32" viewBox="0 0 44 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1H31.2656L28.4282 31H7.6206L1 1Z" stroke="#2E3039" stroke-width="2" stroke-linejoin="round"/>
<path d="M31.2656 3.14062H36.9404C40.1561 3.14062 42.6152 5.92634 42.6152 9.5692V15.9978C42.6152 19.6406 40.1561 22.4263 36.9404 22.4263H33.1572" stroke="#2E3039" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 438 B

@ -0,0 +1,5 @@
<svg width="44" height="32" viewBox="0 0 44 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1H31.2656L28.4282 31H7.6206L1 1Z" stroke="#2E3039" stroke-width="2" stroke-linejoin="round"/>
<path d="M31.2656 3.14062H36.9404C40.1561 3.14062 42.6152 5.92634 42.6152 9.5692V15.9978C42.6152 19.6406 40.1561 22.4263 36.9404 22.4263H33.1572" stroke="#2E3039" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.80762 14.561C14.141 11.4732 20.4743 22.2805 26.8076 19.1927V30H9.91873L7.80762 14.561Z" fill="#4EB5FF"/>
</svg>

After

Width:  |  Height:  |  Size: 555 B

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.582 1.25C18.1253 1.25 17.7623 1.44409 17.4564 1.6775C17.4426 1.68802 17.4288 1.69876 17.4149 1.70972C17.1988 1.87986 17.0908 1.96493 17.0829 2.09948C17.0749 2.23403 17.1799 2.33908 17.39 2.54917L21.4509 6.61006C21.661 6.82016 21.7661 6.92521 21.9006 6.91724C22.0352 6.90928 22.1202 6.80127 22.2904 6.58526C22.3013 6.57135 22.3121 6.5575 22.3226 6.54371C22.556 6.23778 22.7501 5.87482 22.7501 5.41811C22.7501 4.96139 22.556 4.59843 22.3226 4.2925C22.1683 4.0902 21.9664 3.87507 21.7389 3.64395L22.4645 2.91831C22.8461 2.53666 22.8461 1.91789 22.4645 1.53624C22.0828 1.15459 21.4641 1.15459 21.0824 1.53624L20.3568 2.26187C20.1254 2.03412 19.9101 1.83199 19.7076 1.6775C19.4017 1.44409 19.0387 1.25 18.582 1.25Z" fill="#8F9AA3"/>
<path d="M15.8025 2.55451C15.4544 2.41032 15.0732 2.37717 14.7099 2.45499C14.3791 2.52586 14.1459 2.7182 14.0063 2.85774L12.9207 3.94343C12.7811 4.08298 12.5888 4.31616 12.5179 4.64697C12.4401 5.01026 12.4732 5.39146 12.6174 5.73957C12.7033 5.94681 12.8276 6.11274 12.9534 6.25694L14.8296 8.13314C14.6603 8.37678 14.4295 8.56478 14.1399 8.69315C11.6325 9.80468 9.80541 11.6318 8.69388 14.1392C8.56556 14.4286 8.37765 14.6594 8.13414 14.8286L6.2565 12.951C6.2565 12.951 5.94637 12.7009 5.73913 12.6151C5.39102 12.4709 5.00982 12.4377 4.64653 12.5155C4.31572 12.5864 4.08254 12.7787 3.94299 12.9183L2.8573 14.004C2.71776 14.1435 2.52542 14.3767 2.45455 14.7075C2.37673 15.0708 2.40988 15.452 2.55407 15.8001C2.63991 16.0074 2.76424 16.1733 2.89002 16.3175L7.68129 21.1088C7.82549 21.2345 7.99142 21.3589 8.19866 21.4447C8.54677 21.5889 8.92797 21.6221 9.29126 21.5442C9.62207 21.4734 9.85525 21.281 9.9948 21.1415L11.0805 20.0558C11.22 19.9162 11.4124 19.6831 11.4832 19.3522C11.5611 18.989 11.5279 18.6078 11.3837 18.2597C11.2979 18.0524 11.1736 17.8865 11.0478 17.7423L9.55828 16.2528C9.96485 15.9054 10.2931 15.4666 10.5223 14.9497C11.4326 12.8962 12.8969 11.4319 14.9504 10.5215C15.4675 10.2923 15.9063 9.96398 16.2537 9.5573L17.7446 11.0482C17.7446 11.0482 18.0548 11.2983 18.262 11.3842C18.6101 11.5284 18.9913 11.5615 19.3546 11.4837C19.6854 11.4128 19.9186 11.2205 20.0582 11.0809L21.1438 9.99524C21.2834 9.85569 21.4757 9.62251 21.5466 9.2917C21.6244 8.92841 21.5913 8.54721 21.4471 8.1991C21.3612 7.99186 21.2369 7.82593 21.1111 7.68173L16.3199 2.89046C16.1757 2.76468 16.0097 2.64035 15.8025 2.55451Z" fill="#8F9AA3"/>
<path d="M5.41914 22.7492C5.87586 22.7492 6.23882 22.5551 6.54475 22.3217C6.55854 22.3112 6.57238 22.3005 6.58629 22.2895C6.80231 22.1194 6.91032 22.0343 6.91828 21.8997C6.92624 21.7652 6.8212 21.6601 6.6111 21.45L2.55022 17.3892C2.34012 17.1791 2.23508 17.074 2.10053 17.082C1.96598 17.0899 1.8809 17.1979 1.71076 17.414C1.69981 17.4279 1.68906 17.4417 1.67854 17.4555C1.44513 17.7614 1.25104 18.1244 1.25104 18.5811C1.25104 19.0378 1.44513 19.4008 1.67854 19.7067C1.83274 19.9088 2.03441 20.1237 2.26163 20.3546L1.53697 21.0793C1.15532 21.4609 1.15532 22.0797 1.53697 22.4613C1.91862 22.843 2.53739 22.843 2.91904 22.4613L3.64369 21.7367C3.8753 21.9647 4.09087 22.1671 4.29354 22.3217C4.59947 22.5551 4.96243 22.7492 5.41914 22.7492Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -0,0 +1,3 @@
<svg width="18" height="24" viewBox="0 0 18 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.00124 0C11.3713 0 13.5174 0.164601 15.0979 0.443605C15.8881 0.58307 16.5336 0.748865 17.0233 0.953187C17.2682 1.05523 17.4752 1.16174 17.6522 1.31444C17.795 1.43765 17.9265 1.61567 17.9745 1.83394C17.997 1.90181 18.0048 1.9739 17.9972 2.04501L17.0012 11.996L15.9975 22.043C15.9672 22.5395 15.5942 22.827 15.1929 23.0391C14.7917 23.2511 14.276 23.417 13.658 23.5565C12.4221 23.8362 10.7723 24.0027 8.95569 23.9999C7.13906 23.9971 5.49826 23.8251 4.27681 23.5411C3.6661 23.3993 3.16459 23.2334 2.77121 23.0177C2.57471 22.9099 2.40312 22.7875 2.26148 22.6271C2.11978 22.4665 2.00417 22.247 2.0038 21.998V22.0511L0.00409739 2.04501C-0.00616554 1.96529 0.00292169 1.88427 0.030564 1.80879C0.0830907 1.60168 0.215339 1.43369 0.352891 1.3149C0.529744 1.16221 0.734914 1.05546 0.979786 0.953417C1.46953 0.748945 2.11501 0.58315 2.90522 0.443836C4.48567 0.164907 6.63123 0 9.00124 0ZM9.00124 0.99817C6.67896 0.99817 4.57689 1.17015 3.08229 1.4337C2.33498 1.56561 1.73987 1.72046 1.36969 1.875C1.25708 1.92187 1.21402 1.95496 1.14902 1.99426C1.21402 2.03395 1.25557 2.07167 1.36969 2.11929C1.73965 2.27312 2.33498 2.4308 3.08229 2.56267C4.57689 2.82648 6.67896 2.9982 9.00124 2.9982C11.3236 2.9982 13.4256 2.82629 14.9202 2.56267C15.6675 2.43076 16.2705 2.27346 16.6406 2.11929C16.7547 2.07167 16.7959 2.03395 16.8613 1.99426C16.7963 1.95458 16.7532 1.92187 16.6406 1.875C16.2706 1.72042 15.6675 1.56557 14.9202 1.4337C13.4256 1.16989 11.3236 0.99817 9.00124 0.99817ZM16.8886 3.08794C16.4149 3.27049 15.8253 3.42253 15.0979 3.55092C13.5174 3.82985 11.3713 3.99429 9.00124 3.99429C6.63123 3.99429 4.48503 3.82992 2.90458 3.55092C2.17718 3.42242 1.58755 3.27037 1.11391 3.08794L1.90087 10.9552C2.34156 10.75 2.91315 10.5837 3.60563 10.4454C5.01028 10.1646 6.90406 9.99623 9.00124 9.99623C11.0985 9.99623 13 10.1644 14.4046 10.4454C15.0971 10.5841 15.6612 10.75 16.1017 10.9552L16.8886 3.08794ZM9.00124 11.0002C6.95818 11.0002 5.10756 11.1661 3.79897 11.4279C3.14469 11.5586 2.62265 11.7139 2.30704 11.8634C2.19594 11.9159 2.14793 11.9563 2.09427 11.996C2.14793 12.0357 2.19518 12.0754 2.30704 12.1287C2.62258 12.278 3.14469 12.4332 3.79897 12.5642C5.10756 12.8258 6.95818 12.994 9.00124 12.994C11.0443 12.994 12.8969 12.8261 14.2055 12.5642C14.8597 12.4334 15.3798 12.2778 15.6954 12.1287C15.8077 12.0758 15.8565 12.0361 15.9101 11.996C15.8561 11.9563 15.8062 11.9155 15.6954 11.8634C15.3799 11.7141 14.8597 11.5588 14.2055 11.4279C12.8969 11.1663 11.0443 11.0002 9.00124 11.0002ZM13.9145 18.4944C13.7965 18.493 13.6817 18.5333 13.5906 18.6084C13.4994 18.6834 13.4378 18.7882 13.4165 18.9043L12.9811 21.0901L11.2997 21.5099C11.2352 21.525 11.1744 21.5526 11.1206 21.5913C11.0669 21.63 11.0214 21.6789 10.9867 21.7353C10.952 21.7917 10.9288 21.8544 10.9184 21.9198C10.9081 21.9852 10.9108 22.052 10.9264 22.1164C10.942 22.1807 10.9702 22.2413 11.0093 22.2947C11.0484 22.3481 11.0978 22.3931 11.1544 22.4273C11.2111 22.4616 11.2739 22.4842 11.3394 22.494C11.4049 22.5038 11.4717 22.5007 11.5359 22.4845L13.5277 21.9826C13.6193 21.9598 13.7025 21.9116 13.7679 21.8435C13.8332 21.7754 13.8779 21.6902 13.8969 21.5978L14.3988 19.1036C14.414 19.0349 14.4145 18.9636 14.4004 18.8946C14.3862 18.8257 14.3576 18.7606 14.3165 18.7034C14.2754 18.6463 14.2227 18.5985 14.1618 18.5631C14.101 18.5278 14.0333 18.5055 13.9633 18.4981C13.9471 18.496 13.9308 18.4949 13.9145 18.4944Z" fill="#4EB5FF"/>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 2C3.55229 2 4 2.44772 4 3L4 14C4 15.6782 4.00213 16.8362 4.11923 17.7072C4.17092 18.0917 4.23383 18.5232 4.45131 18.8563C4.60954 18.5142 4.76255 18.1696 4.91553 17.8252C5.29664 16.967 5.67762 16.1092 6.13888 15.2891C6.58328 14.499 7.11953 13.7058 7.77477 13.1011C8.43871 12.4882 9.28219 12.0189 10.3058 12.0189C11.6975 12.0189 12.5705 12.9129 13.1308 13.4867L13.1883 13.5455C13.8415 14.2124 14.1538 14.4717 14.6132 14.4717C15.0859 14.4717 15.4115 14.2943 15.733 13.9424C16.1205 13.5181 16.396 12.9986 16.6658 12.49C16.7134 12.4003 16.7608 12.3109 16.8086 12.2224C17.5056 10.9328 18.5501 9 20.9995 9C21.5518 9 21.9995 9.44772 21.9995 10C21.9995 10.5523 21.5518 11 20.9995 11C19.8601 11 19.3296 11.7648 18.5475 13.2114L18.4738 13.348C18.1403 13.9674 17.7401 14.7105 17.2097 15.2911C16.5941 15.9651 15.7622 16.4717 14.6132 16.4717C13.2476 16.4717 12.3831 15.5847 11.8309 15.0182C11.7865 14.9725 11.7412 14.925 11.6952 14.8766C11.3159 14.4783 10.8784 14.0189 10.3058 14.0189C9.93769 14.0189 9.55589 14.1788 9.13128 14.5707C8.69797 14.9707 8.28242 15.5577 7.88206 16.2696C7.45308 17.0322 7.09983 17.8308 6.7465 18.6295C6.56362 19.043 6.38072 19.4565 6.18729 19.865C6.22215 19.8708 6.25731 19.876 6.2928 19.8808C7.16378 19.9979 8.32182 20 10 20H21C21.5523 20 22 20.4477 22 21C22 21.5523 21.5523 22 21 22H9.928C8.33933 22 7.04616 22.0001 6.0263 21.8629C4.96232 21.7199 4.04736 21.4113 3.31802 20.682C2.58869 19.9526 2.28011 19.0377 2.13706 17.9737C1.99995 16.9539 1.99997 15.6607 2 14.0721L2 3C2 2.44772 2.44772 2 3 2Z" fill="#2E3039"/>
<path d="M8 4C8.55229 4 9 3.55228 9 3C9 2.44772 8.55229 2 8 2L7 2C6.44772 2 6 2.44772 6 3C6 3.55228 6.44772 4 7 4L8 4Z" fill="#2E3039"/>
<path d="M11 8C11.5523 8 12 7.55228 12 7C12 6.44772 11.5523 6 11 6H7C6.44772 6 6 6.44772 6 7C6 7.55228 6.44772 8 7 8L11 8Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,3 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 8.0625C0 12.5153 3.60975 16.125 8.0625 16.125C12.5153 16.125 16.125 12.5153 16.125 8.0625C16.125 3.60975 12.5153 0 8.0625 0C3.60975 0 0 3.60975 0 8.0625ZM11.5695 5.25974C11.8748 5.53949 11.895 6.01423 11.6153 6.31948L7.49026 10.8195C7.35226 10.9702 7.158 11.058 6.954 11.0625C6.74925 11.067 6.552 10.9875 6.40725 10.8428L4.53225 8.96777C4.239 8.67527 4.239 8.19973 4.53225 7.90723C4.82475 7.61398 5.30026 7.61398 5.59276 7.90723L6.91425 9.228L10.5097 5.30552C10.7895 5.00027 11.2643 4.97999 11.5695 5.25974Z" fill="#18C273"/>
</svg>

After

Width:  |  Height:  |  Size: 681 B

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.8271 3.35515C16.8461 1.52662 14.1719 2.25537 12.5563 3.46861C12.4618 3.5396 12.3778 3.60261 12.3027 3.6585C12.1487 3.77312 12.0717 3.83043 11.9724 3.83043C11.873 3.83043 11.796 3.77312 11.642 3.6585C11.5669 3.60262 11.4829 3.5396 11.3884 3.46861C9.77284 2.25537 7.09861 1.52662 4.11763 3.35515C2.63347 4.26554 1.61665 5.88968 1.31555 7.86051C1.27161 8.14807 1.24965 8.29185 1.33944 8.39639C1.42923 8.50094 1.58234 8.50092 1.88856 8.50089L5.14448 8.50059C5.55656 8.49886 6.25061 8.49594 6.92509 8.74131C7.18957 8.83753 7.42861 8.9636 7.64109 9.10184C7.94838 9.30175 8.10203 9.40171 8.22623 9.37305C8.35044 9.34439 8.43459 9.20413 8.60289 8.92363L9.32823 7.71474C9.78963 6.94573 10.6276 6.48278 11.5242 6.50152C12.4208 6.52027 13.2387 7.01784 13.6676 7.80546L14.9648 10.1879C15.0479 10.3405 15.0895 10.4168 15.1603 10.4589C15.2311 10.501 15.318 10.501 15.4918 10.501H15.972C17.3527 10.501 18.472 11.6203 18.472 13.001C18.472 14.3817 17.3527 15.501 15.972 15.501L14.8556 15.5031C14.4407 15.5127 13.1951 15.5415 12.106 14.705C12.0741 14.6805 12.0429 14.6555 12.0124 14.6302C11.6751 14.3499 11.5064 14.2097 11.3704 14.2328C11.2345 14.2559 11.1366 14.419 10.9409 14.7453L10.6157 15.2872C10.1827 16.0089 9.41568 16.4642 8.5748 16.4989C7.73393 16.5335 6.93207 16.1427 6.44122 15.4591L5.21484 13.7511C5.12681 13.6285 5.0828 13.5672 5.01833 13.5341C4.95385 13.501 4.87839 13.501 4.72747 13.501L3.23319 13.501C2.80205 13.501 2.58648 13.501 2.49975 13.6503C2.41302 13.7997 2.51637 13.9808 2.72307 14.3432C3.7896 16.2127 5.46319 18.1083 7.86568 19.8865C9.31705 20.9614 10.3823 21.7503 11.9723 21.7503C13.5624 21.7503 14.6276 20.9614 16.079 19.8865C20.2586 16.7929 22.2322 13.3443 22.6412 10.2951C23.0481 7.2606 21.8883 4.61952 19.8271 3.35515Z" fill="#18C273"/>
<path d="M12.3509 8.5218C12.1794 8.20675 11.8522 8.00772 11.4936 8.00022C11.1349 7.99272 10.7997 8.1779 10.6152 8.48551L8.39777 12.1812L7.72833 11.2488C7.71432 11.2293 7.69962 11.2103 7.68425 11.1919C7.66865 11.1731 7.65154 11.1522 7.63294 11.1295C7.4192 10.8684 7.00838 10.3666 6.41298 10.15C5.9948 9.99782 5.53719 9.99883 5.08287 9.99984L4.97266 10L2.97266 10C2.42037 10 1.97266 10.4477 1.97266 11C1.97266 11.5523 2.42037 12 2.97266 12H4.97266C5.60991 12 5.68621 12.0141 5.72762 12.0289C5.73136 12.0307 5.76392 12.0476 5.83105 12.1122C5.91437 12.1924 5.9964 12.2905 6.12473 12.4445L7.66036 14.5832C7.8567 14.8567 8.17744 15.013 8.51379 14.9992C8.85014 14.9853 9.15695 14.8032 9.33015 14.5145L11.4306 11.0138L12.2854 12.5838C12.295 12.6013 12.305 12.6186 12.3156 12.6356L12.3573 12.7026C12.5191 12.9635 12.7156 13.2804 13.0204 13.5144C13.6863 14.0259 14.4768 14.0096 14.863 14.0016C14.9044 14.0008 14.9412 14 14.9727 14H15.9727C16.5249 14 16.9727 13.5523 16.9727 13C16.9727 12.4477 16.5249 12 15.9727 12H14.9727C14.9119 12 14.8581 12.0003 14.8096 12.0005C14.6334 12.0015 14.5272 12.002 14.416 11.9872C14.3192 11.9743 14.276 11.9545 14.248 11.9352C14.2434 11.9296 14.2355 11.9197 14.2239 11.9037C14.1807 11.8444 14.1277 11.7611 14.0275 11.601L12.3509 8.5218Z" fill="#18C273"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.083 8.3418C16.1804 8.26933 16.3034 8.26171 16.4092 8.32129C16.4449 8.34147 16.4818 8.37722 16.5537 8.44922C18.2206 9.92249 19.4021 12.5001 18.3545 14.9922C18.0491 15.7186 17.3429 16.2001 16.5498 16.2002L16.2725 16.1982C16.045 16.1964 15.9309 16.1958 15.8486 16.2598C15.7666 16.3238 15.7397 16.4344 15.6855 16.6553L14.7207 20.5879C14.4117 21.8473 13.2957 22.7461 11.998 22.7461C10.7005 22.746 9.5844 21.8473 9.27539 20.5879L8.31152 16.6553C8.25731 16.4342 8.2297 16.3238 8.14746 16.2598C8.06513 16.1957 7.95135 16.1964 7.72363 16.1982L7.44727 16.2002C6.6541 16.2002 5.94699 15.7187 5.6416 14.9922C4.59407 12.5 5.77638 9.92243 7.44336 8.44922C7.51481 8.37768 7.55044 8.34145 7.58594 8.32129C7.69173 8.26141 7.81555 8.26931 7.91309 8.3418C7.94584 8.36621 7.98081 8.41097 8.0498 8.5C8.14907 8.62808 8.25089 8.75011 8.35254 8.86426C8.78359 9.34823 9.29846 9.79001 9.86914 10.124C10.4188 10.4457 11.1568 10.7461 11.998 10.7461C12.8393 10.7461 13.5773 10.4457 14.127 10.124C14.6976 9.79001 15.2125 9.34823 15.6436 8.86426C15.7452 8.75013 15.847 8.62806 15.9463 8.5C16.0154 8.41081 16.0502 8.36627 16.083 8.3418ZM12 1.25C14.0711 1.25 15.75 2.92893 15.75 5C15.75 6.07361 15.1945 7.1188 14.5254 7.87012C14.1835 8.25405 13.7876 8.58923 13.3711 8.83301C12.9615 9.07273 12.4873 9.25 12 9.25C11.5127 9.25 11.0385 9.07273 10.6289 8.83301C10.2124 8.58923 9.81654 8.25405 9.47461 7.87012C8.80554 7.1188 8.25 6.07361 8.25 5C8.25 2.92893 9.92893 1.25 12 1.25Z" fill="#0B85F7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.5378 19.5227C15.7479 19.3066 15.8082 18.9857 15.6909 18.7081C15.5736 18.4304 15.3014 18.25 15 18.25H13L13 5.75L15 5.75C15.3014 5.75 15.5736 5.56957 15.6909 5.29193C15.8082 5.01429 15.7479 4.69339 15.5378 4.47726C15.4246 4.36071 15.2441 4.13501 14.9904 3.81197C14.7621 3.52108 14.4399 3.11067 14.1661 2.78758C13.8723 2.44084 13.5478 2.08603 13.2266 1.81244C13.0657 1.67538 12.888 1.54343 12.6999 1.44275C12.5177 1.34525 12.2758 1.25 12 1.25C11.7242 1.25 11.4824 1.34525 11.3002 1.44275C11.112 1.54343 10.9344 1.67538 10.7734 1.81244C10.4522 2.08603 10.1277 2.44084 9.8339 2.78758C9.56013 3.11067 9.23797 3.52107 9.00963 3.81197C8.7559 4.13501 8.57549 4.36071 8.46221 4.47726C8.25213 4.69339 8.19185 5.01429 8.30917 5.29193C8.42649 5.56957 8.69861 5.75 9.00002 5.75L11 5.75L11 18.25H9.00002C8.69861 18.25 8.42649 18.4304 8.30917 18.7081C8.19185 18.9857 8.25213 19.3066 8.46221 19.5227C8.57549 19.6393 8.7559 19.865 9.00963 20.188C9.23797 20.4789 9.56014 20.8893 9.8339 21.2124C10.1277 21.5592 10.4522 21.914 10.7734 22.1876C10.9344 22.3246 11.112 22.4566 11.3002 22.5573C11.4824 22.6547 11.7242 22.75 12 22.75C12.2758 22.75 12.5177 22.6547 12.6999 22.5572C12.888 22.4566 13.0657 22.3246 13.2266 22.1876C13.5478 21.914 13.8723 21.5592 14.1661 21.2124C14.4399 20.8893 14.7621 20.4789 14.9904 20.188C15.2441 19.865 15.4246 19.6393 15.5378 19.5227Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,8 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 4C2.44772 4 2 4.44772 2 5C2 5.55228 2.44772 6 3 6L5 6C5.55228 6 6 5.55229 6 5C6 4.44772 5.55228 4 5 4L3 4Z" fill="#2E3039"/>
<path d="M9 4C8.44772 4 8 4.44772 8 5C8 5.55228 8.44772 6 9 6L21 6C21.5523 6 22 5.55229 22 5C22 4.44772 21.5523 4 21 4L9 4Z" fill="#2E3039"/>
<path d="M8 12C8 11.4477 8.44772 11 9 11L21 11C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13L9 13C8.44772 13 8 12.5523 8 12Z" fill="#2E3039"/>
<path d="M3 11C2.44772 11 2 11.4477 2 12C2 12.5523 2.44772 13 3 13H5C5.55228 13 6 12.5523 6 12C6 11.4477 5.55228 11 5 11L3 11Z" fill="#2E3039"/>
<path d="M8 19C8 18.4477 8.44772 18 9 18L21 18C21.5523 18 22 18.4477 22 19C22 19.5523 21.5523 20 21 20L9 20C8.44772 20 8 19.5523 8 19Z" fill="#2E3039"/>
<path d="M3 18C2.44772 18 2 18.4477 2 19C2 19.5523 2.44772 20 3 20H5C5.55228 20 6 19.5523 6 19C6 18.4477 5.55228 18 5 18H3Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 975 B

@ -0,0 +1,3 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.125 0C4.53312 0 0 4.53312 0 10.125C0 15.7169 4.53312 20.25 10.125 20.25C15.7169 20.25 20.25 15.7169 20.25 10.125C20.25 4.53312 15.7169 0 10.125 0ZM6.35756 9.18314C5.83738 9.18314 5.4157 9.60482 5.4157 10.125C5.4157 10.6452 5.83738 11.0669 6.35756 11.0669H13.8924C14.4126 11.0669 14.8343 10.6452 14.8343 10.125C14.8343 9.60482 14.4126 9.18314 13.8924 9.18314H6.35756Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 541 B

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.9998 1.25C7.54394 1.25 3.92765 4.84151 3.92761 9.27697C3.92752 10.3087 3.85814 11.0873 3.38247 11.7872C3.31632 11.8831 3.22855 12.0032 3.13265 12.1345C2.9661 12.3625 2.77503 12.624 2.63009 12.8505C2.37428 13.2503 2.12435 13.7324 2.03843 14.2942C1.75812 16.127 3.05031 17.3136 4.33721 17.8454C8.86992 19.7182 15.1296 19.7182 19.6623 17.8454C20.9492 17.3136 22.2414 16.127 21.9611 14.2942C21.8752 13.7324 21.6252 13.2503 21.3694 12.8505C21.2245 12.624 21.0334 12.3625 20.8669 12.1345C20.771 12.0033 20.6832 11.8832 20.6171 11.7873C20.1414 11.0874 20.072 10.3088 20.0719 9.27703C20.0719 4.84155 16.4556 1.25 11.9998 1.25Z" fill="#8F9AA3"/>
<path d="M11.9982 22.7477C13.0185 22.7477 13.974 22.4563 14.7773 21.9511C15.4904 21.5027 15.8469 21.2785 15.7222 20.9057C15.5974 20.533 15.1054 20.5747 14.1215 20.6582C12.7143 20.7776 11.2821 20.7776 9.87493 20.6582C8.89101 20.5747 8.39905 20.533 8.27429 20.9057C8.14953 21.2784 8.50605 21.5027 9.2191 21.9511C10.0225 22.4563 10.978 22.7477 11.9982 22.7477Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,9 @@
<svg width="102" height="85" viewBox="0 0 102 85" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M35.6143 34.2284C37.6104 34.2284 39.2286 32.696 39.2286 30.8056C39.2286 28.9153 37.6104 27.3828 35.6143 27.3828C33.6182 27.3828 32 28.9153 32 30.8056C32 32.696 33.6182 34.2284 35.6143 34.2284Z" fill="#CBE7FF" fill-opacity="0.7"/>
<path d="M2.40952 61.6106C3.74026 61.6106 4.81904 60.589 4.81904 59.3287C4.81904 58.0685 3.74026 57.0469 2.40952 57.0469C1.07878 57.0469 0 58.0685 0 59.3287C0 60.589 1.07878 61.6106 2.40952 61.6106Z" fill="#CBE7FF" fill-opacity="0.6"/>
<path d="M11.4452 85.0015C13.1087 85.0015 14.4571 83.7245 14.4571 82.1492C14.4571 80.5739 13.1087 79.2969 11.4452 79.2969C9.78182 79.2969 8.43335 80.5739 8.43335 82.1492C8.43335 83.7245 9.78182 85.0015 11.4452 85.0015Z" fill="#CBE7FF" fill-opacity="0.5"/>
<path d="M72.6927 32.3844C74.3561 32.3844 75.7046 31.1073 75.7046 29.532C75.7046 27.9567 74.3561 26.6797 72.6927 26.6797C71.0293 26.6797 69.6808 27.9567 69.6808 29.532C69.6808 31.1073 71.0293 32.3844 72.6927 32.3844Z" fill="#CBE7FF" fill-opacity="0.7"/>
<path d="M99.4095 53.0559C100.74 53.0559 101.819 52.0343 101.819 50.7741C101.819 49.5138 100.74 48.4922 99.4095 48.4922C98.0788 48.4922 97 49.5138 97 50.7741C97 52.0343 98.0788 53.0559 99.4095 53.0559Z" fill="#CBE7FF" fill-opacity="0.6"/>
<path d="M88.8681 77.0097C90.8642 77.0097 92.4824 75.4772 92.4824 73.5869C92.4824 71.6965 90.8642 70.1641 88.8681 70.1641C86.872 70.1641 85.2538 71.6965 85.2538 73.5869C85.2538 75.4772 86.872 77.0097 88.8681 77.0097Z" fill="#CBE7FF" fill-opacity="0.5"/>
<path d="M13.4095 4.56373C14.7403 4.56373 15.819 3.54211 15.819 2.28187C15.819 1.02163 14.7403 0 13.4095 0C12.0788 0 11 1.02163 11 2.28187C11 3.54211 12.0788 4.56373 13.4095 4.56373Z" fill="#D7EEFF" fill-opacity="0.8"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -0,0 +1,3 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.75 0C4.81294 0 0 4.81294 0 10.75C0 16.6871 4.81294 21.5 10.75 21.5C16.6871 21.5 21.5 16.6871 21.5 10.75C21.5 4.81294 16.6871 0 10.75 0ZM10.7416 5C8.94576 5 7.48808 6.45407 7.48808 8.25C7.48808 10.0459 8.94576 11.5 10.7416 11.5C12.5375 11.5 13.9952 10.0459 13.9952 8.25C13.9952 6.45407 12.5375 5 10.7416 5ZM15.7909 15.2302C13.1235 12.3502 8.32472 12.4987 5.71382 15.2256L5.52631 15.4131C5.38104 15.5584 5.30172 15.7569 5.30688 15.9623C5.31204 16.1677 5.40122 16.362 5.5536 16.4998C6.92769 17.7423 8.7513 18.5 10.7501 18.5C12.7489 18.5 14.5725 17.7423 15.9466 16.4998C16.099 16.362 16.1882 16.1677 16.1933 15.9623C16.1985 15.7569 16.1192 15.5584 15.9739 15.4131L15.7909 15.2302Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 851 B

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.9999 3.125C10.9999 2.57272 11.4476 2.125 11.9999 2.125C12.5522 2.125 12.9999 2.57272 12.9999 3.125V5.25648L13.6002 5.6567C13.6063 5.64329 13.6125 5.62994 13.6188 5.61666C13.7792 5.27762 14.0126 4.95992 14.3466 4.72784C14.6864 4.4917 15.0865 4.375 15.5234 4.375C17.3868 4.375 19.2054 5.74706 20.5136 7.78194C21.844 9.8515 22.75 12.745 22.75 16.125C22.75 18.5571 21.9318 20.0447 20.8453 20.9014C19.7942 21.7302 18.6097 21.875 18.0144 21.875C16.8144 21.875 15.5948 21.6036 14.6787 20.7304C13.7563 19.8512 13.2788 18.4938 13.2788 16.625C13.2842 16.2851 13.3181 15.4637 13.4101 14.8979C13.5229 14.0671 13.7417 12.9986 14.1873 12.0938C13.709 10.979 13.3028 9.33793 13.2548 7.89942C13.254 7.87588 13.2533 7.85231 13.2527 7.82873L11.9999 6.99352L10.7473 7.82858C10.7467 7.85222 10.746 7.87583 10.7452 7.89942C10.6973 9.33793 10.291 10.979 9.81277 12.0938C10.2583 12.9986 10.4772 14.0671 10.5899 14.8979C10.682 15.4637 10.7158 16.2851 10.7213 16.625C10.7213 18.4938 10.2438 19.8512 9.32137 20.7304C8.40522 21.6036 7.18561 21.875 5.98563 21.875C5.39035 21.875 4.20581 21.7302 3.15476 20.9014C2.06825 20.0447 1.25 18.5571 1.25 16.125C1.25 12.745 2.15607 9.8515 3.48648 7.78194C4.79459 5.74706 6.61322 4.375 8.47665 4.375C8.91351 4.375 9.3136 4.4917 9.65343 4.72784C9.98742 4.95992 10.2208 5.27762 10.3812 5.61666C10.3875 5.6299 10.3937 5.64322 10.3997 5.65659L10.9999 5.25648V3.125Z" fill="#18C273"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.46313 15.0645C2.87679 15.044 3.22877 15.3627 3.24927 15.7764C3.35231 17.8551 3.66017 18.906 4.3772 19.623C5.09424 20.34 6.14517 20.6479 8.22388 20.751C8.63755 20.7715 8.95629 21.1234 8.93579 21.5371C8.91528 21.9508 8.56333 22.2695 8.14966 22.249C6.02926 22.1439 4.4621 21.829 3.31665 20.6836C2.17121 19.5382 1.85633 17.971 1.75122 15.8506C1.73072 15.4369 2.04943 15.085 2.46313 15.0645ZM21.5374 15.0645C21.951 15.085 22.2698 15.4369 22.2493 15.8506C22.1442 17.971 21.8292 19.5382 20.6838 20.6836C19.5384 21.829 17.9712 22.1439 15.8508 22.249C15.4371 22.2695 15.0852 21.9508 15.0647 21.5371C15.0442 21.1234 15.3629 20.7715 15.7766 20.751C17.8553 20.6479 18.9063 20.3401 19.6233 19.623C20.3403 18.906 20.6482 17.855 20.7512 15.7764C20.7717 15.3627 21.1236 15.044 21.5374 15.0645ZM12.0002 3.90039C13.1582 3.90046 14.0999 4.84198 14.0999 6C14.0998 6.84086 13.6022 7.56538 12.887 7.90039H13.136C14.762 7.90045 16.0999 9.11395 16.0999 10.624L16.0999 13.9297C16.0998 14.2828 15.7901 14.5546 15.427 14.5547H14.8362L14.4954 19.5156C14.4719 19.8513 14.1707 20.0996 13.8235 20.0996H10.1956C9.84928 20.0994 9.54928 19.8523 9.52466 19.5176V19.5166L9.16626 14.5547H8.57349C8.21034 14.5547 7.90065 14.2828 7.90063 13.9297L7.90063 10.624C7.90063 9.11391 9.23851 7.90039 10.8645 7.90039H11.1135C10.3982 7.56541 9.90065 6.84092 9.90063 6C9.90063 4.84194 10.8422 3.90039 12.0002 3.90039ZM14.3948 19.5078C14.3902 19.5747 14.3712 19.6378 14.342 19.6953C14.3561 19.6675 14.3685 19.6387 14.3772 19.6084L14.3958 19.5078L14.7434 14.4551L14.3948 19.5078ZM10.8645 9.14941C9.96391 9.14941 9.24536 9.81932 9.24536 10.624L9.24536 13.3057H9.79517C10.1415 13.3058 10.4415 13.5529 10.4661 13.8877V13.8887L10.8245 18.8506H13.1926L13.5334 13.8896C13.5569 13.5537 13.858 13.3047 14.2053 13.3047H14.7542L14.7542 10.624C14.7542 9.81935 14.0367 9.14947 13.136 9.14941H10.8645ZM8.00903 14.0225C8.00955 14.0251 8.01043 14.0277 8.01099 14.0303C8.00412 13.9978 8.00024 13.964 8.00024 13.9297L8.00903 14.0225ZM8.14966 1.75098C8.56334 1.73051 8.91529 2.04921 8.93579 2.46289C8.95628 2.87656 8.63754 3.22849 8.22388 3.24902C6.14515 3.35206 5.09424 3.65995 4.3772 4.37695C3.66016 5.09399 3.35231 6.14489 3.24927 8.22363C3.22877 8.6373 2.87679 8.95598 2.46313 8.93555C2.04943 8.91504 1.73072 8.56312 1.75122 8.14941C1.85633 6.02899 2.1712 4.46186 3.31665 3.31641C4.4621 2.17099 6.02924 1.85608 8.14966 1.75098ZM15.8508 1.75098C17.9712 1.85609 19.5384 2.17098 20.6838 3.31641C21.8292 4.46185 22.1442 6.02903 22.2493 8.14941C22.2698 8.56308 21.951 8.91498 21.5374 8.93555C21.1236 8.95605 20.7717 8.63734 20.7512 8.22363C20.6482 6.14495 20.3403 5.09399 19.6233 4.37695C18.9063 3.65993 17.8553 3.35207 15.7766 3.24902C15.3629 3.22852 15.0442 2.87659 15.0647 2.46289C15.0852 2.04919 15.4371 1.73047 15.8508 1.75098ZM12.0002 5.09961C11.504 5.09961 11.0999 5.50374 11.0999 6C11.0999 6.49625 11.504 6.90039 12.0002 6.90039C12.4964 6.90032 12.9006 6.49622 12.9006 6C12.9006 5.50378 12.4965 5.09968 12.0002 5.09961ZM10.0364 6.37793C10.0371 6.38156 10.0386 6.38505 10.0393 6.38867C10.0273 6.32802 10.0173 6.26657 10.011 6.2041L10.0364 6.37793Z" fill="#2B353E"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@ -0,0 +1,6 @@
<svg width="67" height="74" viewBox="0 0 67 74" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M66.6078 32.2697C66.1744 30.4738 65.1769 29.2328 63.7991 28.7779C63.3117 28.6169 62.7964 28.5751 62.2712 28.6277C63.2293 24.3157 64.7466 19.7654 64.5501 15.5873C64.1645 7.35177 58.7442 0.579242 41.8415 0.0148163C31.0287 -0.346213 20.1447 5.91597 20.6036 16.0287C20.8633 21.7671 21.9015 25.0884 22.6027 28.1906L22.5955 28.6872C21.9433 28.5588 21.3005 28.5802 20.7048 28.7779C19.3245 29.2328 18.327 30.4738 17.8953 32.2697C17.4945 33.929 17.6233 35.8995 18.2581 37.8187C19.2879 40.9412 21.4426 43.2444 23.6866 43.6967C24.8086 48.1266 26.7007 52.0335 29.1423 55.1157L28.172 72.4976C28.1488 72.947 28.4967 73.3275 28.9447 73.3489C28.9581 73.3504 28.9715 73.3518 28.9853 73.3518C29.4158 73.3518 29.7757 73.0131 29.7974 72.5777L30.6851 56.8736C33.9673 60.2504 37.9975 62.271 42.3673 62.271C46.7608 62.271 50.8119 60.229 54.1046 56.8167L54.9949 72.5777C55.0166 73.0131 55.3771 73.3518 55.8074 73.3518C55.8211 73.3518 55.8345 73.3504 55.848 73.3489C56.296 73.3275 56.6436 72.947 56.6204 72.4976L55.646 55.0476C58.0739 51.9631 59.9511 48.0599 61.0666 43.6372C63.217 43.0688 65.2513 40.8234 66.2445 37.8187C66.8779 35.8995 67.0069 33.9304 66.6078 32.2697ZM19.8023 37.3084C19.2636 35.6723 19.1472 34.0197 19.4762 32.6514C19.7725 31.421 20.3896 30.5942 21.2152 30.3207C21.6323 30.184 22.0894 30.1988 22.5741 30.3493L22.5226 34.373C22.5226 36.9688 22.7961 39.47 23.2684 41.8561C21.862 41.1861 20.5169 39.474 19.8023 37.3084ZM42.3673 60.6429C32.3223 60.6429 24.1495 48.8586 24.1495 34.3838L24.2782 24.4253C24.2957 23.0704 23.6463 18.5731 28.6563 18.5731C35.6019 18.5731 35.2 23.0896 41.772 23.0896C48.4944 23.0896 49.0054 18.4254 55.848 18.4254C61.0748 18.4254 60.4495 23.4953 60.4698 25.083L60.5874 34.373C60.5874 48.8586 52.4154 60.6429 42.3673 60.6429ZM64.6994 37.3084C64.028 39.3398 62.8019 40.9779 61.4919 41.7277C61.9508 39.3764 62.2157 36.9159 62.2157 34.3635L62.1642 30.287C62.5604 30.2003 62.9383 30.2071 63.2873 30.3221C64.1118 30.5942 64.7292 31.421 65.0258 32.6514C65.3557 34.0197 65.2381 35.6723 64.6994 37.3084Z" fill="#2B353E"/>
<path d="M14.4426 56.2374C14.4426 59.3856 11.8899 61.9383 8.74195 61.9383C5.59398 61.9383 3.04248 59.3856 3.04248 56.2374C3.04248 53.0909 5.59398 50.5382 8.74195 50.5382C11.8899 50.5382 14.4426 53.0909 14.4426 56.2374Z" fill="#EC1B2A"/>
<path d="M8.74187 64.9804C3.92094 64.9804 0 61.058 0 56.2374C0 53.2991 1.50647 50.5382 3.9587 48.9287V19.423C3.9587 16.7865 6.10398 14.6387 8.74187 14.6387C11.3809 14.6387 13.5276 16.7865 13.5276 19.423V48.9302C15.9784 50.5382 17.4849 53.2991 17.4849 56.2374C17.4849 61.058 13.5617 64.9804 8.74187 64.9804ZM8.74187 16.1909C6.95934 16.1909 5.51009 17.6405 5.51009 19.423V49.807L5.1256 50.0307C2.92082 51.3189 1.55138 53.6967 1.55138 56.2374C1.55138 60.2029 4.77773 63.4293 8.74187 63.4293C12.706 63.4293 15.9326 60.2029 15.9326 56.2374C15.9326 53.6982 14.5644 51.3189 12.361 50.0318L11.9751 49.807V19.423C11.9751 17.6405 10.5258 16.1909 8.74187 16.1909Z" fill="#2B353E"/>
<path d="M10.4569 51.7886H7.02686V25.6216H10.4569V51.7886Z" fill="#EC1B2A"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1459 1.27079C12.8412 1.24999 12.47 1.24999 12.0253 1.25C11.5806 1.24999 11.1588 1.24999 10.8541 1.27079C10.5368 1.29245 10.2372 1.33914 9.94665 1.45953C9.27321 1.73863 8.73814 2.2737 8.45905 2.94713C8.33865 3.23764 8.29196 3.53731 8.2703 3.85456C8.2495 4.15932 8.2495 4.53054 8.24951 4.97522L8.24951 12.6414C7.0263 13.6949 6.25 15.2569 6.25 17C6.25 20.1756 8.82436 22.75 12 22.75C15.1756 22.75 17.75 20.1756 17.75 17C17.75 15.2569 16.9737 13.6949 15.7505 12.6414L15.7505 4.97524C15.7505 4.53055 15.7505 4.15933 15.7297 3.85456C15.708 3.53731 15.6614 3.23764 15.541 2.94713C15.2619 2.2737 14.7268 1.73863 14.0534 1.45953C13.7628 1.33914 13.4632 1.29245 13.1459 1.27079ZM12 7C12.5523 7 13 7.44772 13 8L13 14.4375C14.0243 14.8375 14.75 15.834 14.75 17C14.75 18.5188 13.5188 19.75 12 19.75C10.4812 19.75 9.25 18.5188 9.25 17C9.25 15.834 9.97566 14.8375 11 14.4375L11 8C11 7.44772 11.4477 7 12 7Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,34 @@
<svg width="143" height="315" viewBox="0 0 143 315" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_7567_21248)">
<path d="M91.32 30.3281H64.357C56.9114 30.3281 50.8755 35.6934 50.8755 42.3118V52.5836C50.8755 59.202 56.9114 64.5673 64.357 64.5673H91.32C98.7656 64.5673 104.801 59.202 104.801 52.5836V42.3118C104.801 35.6934 98.7656 30.3281 91.32 30.3281Z" fill="#F9FDFF"/>
<path d="M96.5104 12.7188H58.0881C51.7221 12.7188 46.5614 17.5366 46.5614 23.4796C46.5614 29.4227 51.7221 34.2405 58.0881 34.2405H96.5104C102.876 34.2405 108.037 29.4227 108.037 23.4796C108.037 17.5366 102.876 12.7188 96.5104 12.7188Z" fill="#CFEDFF"/>
<path d="M87.3176 64.5625H68.084C41.5279 64.5625 20 83.9067 20 107.769V280.595C20 304.457 41.5279 323.802 68.084 323.802H87.3176C113.874 323.802 135.402 304.457 135.402 280.595V107.769C135.402 83.9067 113.874 64.5625 87.3176 64.5625Z" fill="url(#paint0_linear_7567_21248)"/>
<mask id="mask0_7567_21248" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="22" y="91" width="112" height="229">
<path d="M91.626 91.9531H63.7756C40.7036 91.9531 22 109.616 22 131.403V280.438C22 302.225 40.7036 319.888 63.7756 319.888H91.626C114.698 319.888 133.402 302.225 133.402 280.438V131.403C133.402 109.616 114.698 91.9531 91.626 91.9531Z" fill="white"/>
</mask>
<g mask="url(#mask0_7567_21248)">
<path d="M133.402 92H20.9685V320H133.402V92Z" fill="url(#paint1_linear_7567_21248)"/>
</g>
<path d="M122.696 130.671C125.516 130.671 127.802 128.506 127.802 125.835C127.802 123.165 125.516 121 122.696 121C119.877 121 117.591 123.165 117.591 125.835C117.591 128.506 119.877 130.671 122.696 130.671Z" fill="#CBE7FF" fill-opacity="0.7"/>
<path d="M63.6143 207.228C65.6104 207.228 67.2286 205.696 67.2286 203.806C67.2286 201.915 65.6104 200.383 63.6143 200.383C61.6182 200.383 60 201.915 60 203.806C60 205.696 61.6182 207.228 63.6143 207.228Z" fill="#CBE7FF" fill-opacity="0.7"/>
<path d="M30.4095 234.611C31.7403 234.611 32.819 233.589 32.819 232.329C32.819 231.069 31.7403 230.047 30.4095 230.047C29.0788 230.047 28 231.069 28 232.329C28 233.589 29.0788 234.611 30.4095 234.611Z" fill="#CBE7FF" fill-opacity="0.6"/>
<path d="M39.4452 258.002C41.1087 258.002 42.4571 256.725 42.4571 255.149C42.4571 253.574 41.1087 252.297 39.4452 252.297C37.7818 252.297 36.4333 253.574 36.4333 255.149C36.4333 256.725 37.7818 258.002 39.4452 258.002Z" fill="#CBE7FF" fill-opacity="0.5"/>
<path d="M100.693 205.384C102.356 205.384 103.705 204.107 103.705 202.532C103.705 200.957 102.356 199.68 100.693 199.68C99.0293 199.68 97.6808 200.957 97.6808 202.532C97.6808 204.107 99.0293 205.384 100.693 205.384Z" fill="#CBE7FF" fill-opacity="0.7"/>
<path d="M127.41 226.056C128.74 226.056 129.819 225.034 129.819 223.774C129.819 222.514 128.74 221.492 127.41 221.492C126.079 221.492 125 222.514 125 223.774C125 225.034 126.079 226.056 127.41 226.056Z" fill="#CBE7FF" fill-opacity="0.6"/>
<path d="M116.868 250.01C118.864 250.01 120.482 248.477 120.482 246.587C120.482 244.697 118.864 243.164 116.868 243.164C114.872 243.164 113.254 244.697 113.254 246.587C113.254 248.477 114.872 250.01 116.868 250.01Z" fill="#CBE7FF" fill-opacity="0.5"/>
<path d="M41.4095 177.564C42.7403 177.564 43.819 176.542 43.819 175.282C43.819 174.022 42.7403 173 41.4095 173C40.0788 173 39 174.022 39 175.282C39 176.542 40.0788 177.564 41.4095 177.564Z" fill="#D7EEFF" fill-opacity="0.8"/>
</g>
<defs>
<linearGradient id="paint0_linear_7567_21248" x1="77.7008" y1="29.9973" x2="77.7008" y2="323.802" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9FDFF"/>
<stop offset="1" stop-color="#CBE7FF"/>
</linearGradient>
<linearGradient id="paint1_linear_7567_21248" x1="77.185" y1="65.1765" x2="77.185" y2="333.412" gradientUnits="userSpaceOnUse">
<stop stop-color="#DAF2FF"/>
<stop offset="1" stop-color="#AEDCFF"/>
</linearGradient>
<clipPath id="clip0_7567_21248">
<rect width="143" height="315" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6439 1.25L13.3561 1.25C14.259 1.24997 15.0211 1.24994 15.6249 1.32042C16.255 1.39396 16.8707 1.55847 17.3706 2.00915C17.5958 2.21222 17.7873 2.44899 17.9376 2.71113C18.2326 3.22573 18.2812 3.77334 18.2347 4.31609C19.2103 4.4287 20.0196 4.68604 20.6521 5.31966C21.2538 5.92238 21.5125 6.68113 21.6335 7.58241C21.75 8.4507 21.75 9.55477 21.75 10.9249V16.043C21.75 17.4131 21.75 18.5172 21.6335 19.3855C21.5125 20.2867 21.2538 21.0455 20.6521 21.6482C20.0502 22.2511 19.2923 22.5105 18.3919 22.6318C17.5249 22.7486 16.4225 22.7486 15.055 22.7485L8.94504 22.7485C7.57752 22.7486 6.47513 22.7486 5.60808 22.6318C4.70774 22.5105 3.94976 22.2511 3.3479 21.6482C2.74621 21.0455 2.48747 20.2867 2.3665 19.3855C2.24997 18.5172 2.24998 17.4131 2.25 16.0431L2.25 10.9248C2.24998 9.55478 2.24997 8.45069 2.3665 7.58241C2.48747 6.68113 2.74621 5.92238 3.3479 5.31966C3.98043 4.68604 4.78975 4.4287 5.76534 4.31608C5.71879 3.77334 5.76736 3.22573 6.06239 2.71113C6.21268 2.44899 6.40422 2.21222 6.62944 2.00915C7.1293 1.55847 7.74503 1.39396 8.37506 1.32042C8.97893 1.24994 9.74097 1.24997 10.6439 1.25ZM8.60067 3.2073C8.13641 3.26149 7.99252 3.35241 7.92575 3.4126C7.8493 3.48154 7.78534 3.56095 7.73568 3.64756C7.69637 3.71613 7.64027 3.8608 7.69746 4.30137C7.75673 4.75797 7.91092 5.36081 8.14823 6.27796C8.33725 7.00848 8.46246 7.48783 8.59555 7.84684C8.72171 8.18716 8.82647 8.33584 8.9287 8.43308C9.00978 8.51021 9.10016 8.57847 9.1982 8.63631C9.32461 8.71091 9.5056 8.77465 9.88389 8.81094C10.1378 8.83531 10.4406 8.84448 10.8301 8.84793L11.5514 6.68404C11.726 6.1601 12.2924 5.87694 12.8163 6.05159C13.3402 6.22624 13.6234 6.79256 13.4488 7.3165L12.9378 8.84935C13.4424 8.84743 13.8138 8.83994 14.1161 8.81094C14.4944 8.77465 14.6754 8.71091 14.8018 8.63631C14.8998 8.57847 14.9902 8.51021 15.0713 8.43308C15.1735 8.33584 15.2783 8.18716 15.4045 7.84684C15.5375 7.48783 15.6628 7.00848 15.8518 6.27796C16.0891 5.36081 16.2433 4.75797 16.3025 4.30137C16.3597 3.8608 16.3036 3.71613 16.2643 3.64756C16.2147 3.56095 16.1507 3.48153 16.0742 3.4126C16.0075 3.35241 15.8636 3.26149 15.3993 3.2073C14.9218 3.15157 14.2747 3.15 13.2979 3.15L10.7021 3.15C9.72532 3.15 9.07816 3.15157 8.60067 3.2073ZM10 17C9.44772 17 9 17.4477 9 18C9 18.5523 9.44772 19 10 19H14C14.5523 19 15 18.5523 15 18C15 17.4477 14.5523 17 14 17L10 17Z" fill="#18C273"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6439 1.25L13.3561 1.25C14.259 1.24997 15.0211 1.24994 15.6249 1.32042C16.255 1.39396 16.8707 1.55847 17.3706 2.00915C17.5958 2.21222 17.7873 2.44899 17.9376 2.71113C18.2326 3.22573 18.2812 3.77334 18.2347 4.31609C19.2103 4.4287 20.0196 4.68604 20.6521 5.31966C21.2538 5.92238 21.5125 6.68113 21.6335 7.58241C21.75 8.4507 21.75 9.55477 21.75 10.9249V16.043C21.75 17.4131 21.75 18.5172 21.6335 19.3855C21.5125 20.2867 21.2538 21.0455 20.6521 21.6482C20.0502 22.2511 19.2923 22.5105 18.3919 22.6318C17.5249 22.7486 16.4225 22.7486 15.055 22.7485L8.94504 22.7485C7.57752 22.7486 6.47513 22.7486 5.60808 22.6318C4.70774 22.5105 3.94976 22.2511 3.3479 21.6482C2.74621 21.0455 2.48747 20.2867 2.3665 19.3855C2.24997 18.5172 2.24998 17.4131 2.25 16.0431L2.25 10.9248C2.24998 9.55478 2.24997 8.45069 2.3665 7.58241C2.48747 6.68113 2.74621 5.92238 3.3479 5.31966C3.98043 4.68604 4.78975 4.4287 5.76534 4.31608C5.71879 3.77334 5.76736 3.22573 6.06239 2.71113C6.21268 2.44899 6.40422 2.21222 6.62944 2.00915C7.1293 1.55847 7.74503 1.39396 8.37506 1.32042C8.97893 1.24994 9.74097 1.24997 10.6439 1.25ZM8.60067 3.2073C8.13641 3.26149 7.99252 3.35241 7.92575 3.4126C7.8493 3.48154 7.78534 3.56095 7.73568 3.64756C7.69637 3.71613 7.64027 3.8608 7.69746 4.30137C7.75673 4.75797 7.91092 5.36081 8.14823 6.27796C8.33725 7.00848 8.46246 7.48783 8.59555 7.84684C8.72171 8.18716 8.82647 8.33584 8.9287 8.43308C9.00978 8.51021 9.10016 8.57847 9.1982 8.63631C9.32461 8.71091 9.5056 8.77465 9.88389 8.81094C10.1378 8.83531 10.4406 8.84448 10.8301 8.84793L11.5514 6.68404C11.726 6.1601 12.2924 5.87694 12.8163 6.05159C13.3402 6.22624 13.6234 6.79256 13.4488 7.3165L12.9378 8.84935C13.4424 8.84743 13.8138 8.83994 14.1161 8.81094C14.4944 8.77465 14.6754 8.71091 14.8018 8.63631C14.8998 8.57847 14.9902 8.51021 15.0713 8.43308C15.1735 8.33584 15.2783 8.18716 15.4045 7.84684C15.5375 7.48783 15.6628 7.00848 15.8518 6.27796C16.0891 5.36081 16.2433 4.75797 16.3025 4.30137C16.3597 3.8608 16.3036 3.71613 16.2643 3.64756C16.2147 3.56095 16.1507 3.48153 16.0742 3.4126C16.0075 3.35241 15.8636 3.26149 15.3993 3.2073C14.9218 3.15157 14.2747 3.15 13.2979 3.15L10.7021 3.15C9.72532 3.15 9.07816 3.15157 8.60067 3.2073ZM10 17C9.44772 17 9 17.4477 9 18C9 18.5523 9.44772 19 10 19H14C14.5523 19 15 18.5523 15 18C15 17.4477 14.5523 17 14 17L10 17Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1,3 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.0625 16.125C3.6097 16.125 0 12.5153 0 8.0625C0 3.6097 3.6097 0 8.0625 0C12.5153 0 16.125 3.6097 16.125 8.0625C16.125 12.5153 12.5153 16.125 8.0625 16.125ZM8.0625 4.3125C8.47671 4.3125 8.8125 4.64829 8.8125 5.0625V8.43749H8.93401C9.15181 8.43742 9.38226 8.43734 9.56413 8.45912C9.69673 8.47499 10.1846 8.53481 10.4097 8.99153C10.635 9.4486 10.3763 9.85561 10.3061 9.96553C10.2094 10.1169 10.0646 10.2914 9.92764 10.4564L9.90413 10.4848C9.68853 10.7449 9.43178 11.0409 9.1743 11.2764C9.04572 11.394 8.89699 11.5151 8.73553 11.6109C8.58584 11.6996 8.34945 11.8125 8.0625 11.8125C7.77556 11.8125 7.53916 11.6996 7.38947 11.6109C7.22801 11.5151 7.07929 11.394 6.95071 11.2764C6.69322 11.0409 6.43647 10.7449 6.22087 10.4848L6.19736 10.4564C6.06041 10.2914 5.91565 10.1169 5.81894 9.96552C5.74871 9.85561 5.49001 9.4486 5.71529 8.99153C5.94038 8.53481 6.42827 8.47499 6.56087 8.45912C6.74274 8.43734 6.9732 8.43742 7.191 8.43749H7.3125L7.3125 5.0625C7.3125 4.64829 7.64829 4.3125 8.0625 4.3125Z" fill="#FF9E15"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

@ -0,0 +1,118 @@
//
// MainFlutterVC.swift
// Runner
//
// Created by ZiKambrani on 25/03/1442 AH.
//
import UIKit
import Flutter
import NetworkExtension
import SystemConfiguration.CaptiveNetwork
class MainFlutterVC: FlutterViewController {
override func viewDidLoad() {
super.viewDidLoad()
// flutterMethodChannel?.setMethodCallHandler { (methodCall, result) in
//
// if methodCall.method == "connectHMGInternetWifi"{
// self.connectHMGInternetWifi(methodCall:methodCall, result: result)
//
// }else if methodCall.method == "connectHMGGuestWifi"{
// self.connectHMGGuestWifi(methodCall:methodCall, result: result)
//
// }else if methodCall.method == "isHMGNetworkAvailable"{
// self.isHMGNetworkAvailable(methodCall:methodCall, result: result)
//
// }else if methodCall.method == "registerHmgGeofences"{
// self.registerHmgGeofences(result: result)
// }
//
// print("")
// }
//
// FlutterText.with(key: "errorConnectingHmgNetwork") { (localized) in
// print(localized)
// }
}
// Connect HMG Wifi and Internet
func connectHMGInternetWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
guard let pateintId = (methodCall.arguments as? [Any])?.first as? String
else { return assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'") }
HMG_Internet.shared.connect(patientId: pateintId) { (status, message) in
result(status ? 1 : 0)
if status{
self.showMessage(title:"Congratulations", message:message)
}else{
self.showMessage(title:"Ooops,", message:message)
}
}
}
// Connect HMG-Guest for App Access
func connectHMGGuestWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
HMG_GUEST.shared.connect() { (status, message) in
result(status ? 1 : 0)
if status{
self.showMessage(title:"Congratulations", message:message)
}else{
self.showMessage(title:"Ooops,", message:message)
}
}
}
func isHMGNetworkAvailable(methodCall:FlutterMethodCall ,result: @escaping FlutterResult) -> Bool{
guard let ssid = methodCall.arguments as? String else {
assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'")
return false
}
let queue = DispatchQueue.init(label: "com.hmg.wifilist")
NEHotspotHelper.register(options: nil, queue: queue) { (command) in
print(command)
if(command.commandType == NEHotspotHelperCommandType.filterScanList) {
if let networkList = command.networkList{
for network in networkList{
print(network.ssid)
}
}
}
}
return false
}
// Message Dailog
func showMessage(title:String, message:String){
DispatchQueue.main.async {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert )
alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
self.present(alert, animated: true) {
}
}
}
// Register Geofence
func registerHmgGeofences(result: @escaping FlutterResult){
flutterMethodChannel?.invokeMethod("getGeofencePreferenceKey", arguments: nil){ geoFencesJsonString in
if let jsonString = geoFencesJsonString as? String{
let allZones = GeoZoneModel.list(from: jsonString)
HMG_Geofence().register(geoZones: allZones)
}else{
}
}
}
}

@ -0,0 +1,22 @@
//
// API.swift
// Runner
//
// Created by ZiKambrani on 04/04/1442 AH.
//
import UIKit
fileprivate let DOMAIN = "https://uat.hmgwebservices.com"
fileprivate let SERVICE = "Services/Patients.svc/REST"
fileprivate let BASE_URL = "\(DOMAIN)/\(SERVICE)"
struct API {
static let WIFI_CREDENTIALS = "\(BASE_URL)/Hmg_SMS_Get_By_ProjectID_And_PatientID"
}
//struct API {
// static let WIFI_CREDENTIALS = FlutterConstants.WIFI_CREDENTIALS_URL
// static let LOG_GEOFENCE = FlutterConstants.LOG_GEOFENCE_URL
//}

@ -0,0 +1,150 @@
//
// Extensions.swift
// Runner
//
// Created by ZiKambrani on 04/04/1442 AH.
//
import UIKit
extension String{
func toUrl() -> URL?{
return URL(string: self)
}
func removeSpace() -> String?{
return self.replacingOccurrences(of: " ", with: "")
}
}
extension Date{
func toString(format:String) -> String{
let df = DateFormatter()
df.dateFormat = format
return df.string(from: self)
}
}
extension Dictionary{
func merge(dict:[String:Any?]) -> [String:Any?]{
var self_ = self as! [String:Any?]
dict.forEach { (kv) in
self_.updateValue(kv.value, forKey: kv.key)
}
return self_
}
}
extension Bundle {
func certificate(named name: String) -> SecCertificate {
let cerURL = self.url(forResource: name, withExtension: "cer")!
let cerData = try! Data(contentsOf: cerURL)
let cer = SecCertificateCreateWithData(nil, cerData as CFData)!
return cer
}
func identity(named name: String, password: String) -> SecIdentity {
let p12URL = self.url(forResource: name, withExtension: "p12")!
let p12Data = try! Data(contentsOf: p12URL)
var importedCF: CFArray? = nil
let options = [kSecImportExportPassphrase as String: password]
let err = SecPKCS12Import(p12Data as CFData, options as CFDictionary, &importedCF)
precondition(err == errSecSuccess)
let imported = importedCF! as NSArray as! [[String:AnyObject]]
precondition(imported.count == 1)
return (imported[0][kSecImportItemIdentity as String]!) as! SecIdentity
}
}
extension SecCertificate{
func trust() -> Bool?{
var optionalTrust: SecTrust?
let policy = SecPolicyCreateBasicX509()
let status = SecTrustCreateWithCertificates([self] as AnyObject,
policy,
&optionalTrust)
guard status == errSecSuccess else { return false}
let trust = optionalTrust!
let stat = optionalTrust?.evaluateAllowing(rootCertificates: [self])
return stat
}
func secTrustObject() -> SecTrust?{
var optionalTrust: SecTrust?
let policy = SecPolicyCreateBasicX509()
let status = SecTrustCreateWithCertificates([self] as AnyObject,
policy,
&optionalTrust)
return optionalTrust
}
}
extension SecTrust {
func evaluate() -> Bool {
var trustResult: SecTrustResultType = .invalid
let err = SecTrustEvaluate(self, &trustResult)
guard err == errSecSuccess else { return false }
return [.proceed, .unspecified].contains(trustResult)
}
func evaluateAllowing(rootCertificates: [SecCertificate]) -> Bool {
// Apply our custom root to the trust object.
var err = SecTrustSetAnchorCertificates(self, rootCertificates as CFArray)
guard err == errSecSuccess else { return false }
// Re-enable the system's built-in root certificates.
err = SecTrustSetAnchorCertificatesOnly(self, false)
guard err == errSecSuccess else { return false }
// Run a trust evaluation and only allow the connection if it succeeds.
return self.evaluate()
}
}
extension UIView{
func show(){
self.alpha = 0.0
self.isHidden = false
UIView.animate(withDuration: 0.25, animations: {
self.alpha = 1
}) { (complete) in
}
}
func hide(){
UIView.animate(withDuration: 0.25, animations: {
self.alpha = 0.0
}) { (complete) in
self.isHidden = true
}
}
}
extension UIViewController{
func showAlert(withTitle: String, message: String){
let alert = UIAlertController(title: withTitle, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
present(alert, animated: true) {
}
}
}

@ -0,0 +1,36 @@
//
// FlutterConstants.swift
// Runner
//
// Created by ZiKambrani on 22/12/2020.
//
import UIKit
class FlutterConstants{
static var LOG_GEOFENCE_URL:String?
static var WIFI_CREDENTIALS_URL:String?
static var DEFAULT_HTTP_PARAMS:[String:Any?]?
class func set(){
// (FiX) Take a start with FlutterMethodChannel (kikstart)
/* First call to flutter method is not returning the correct value (Always returning 'NSObject') then after it wroking fine and returning correct value*/
FlutterText.with(key: "test") { (test) in
flutterMethodChannel?.invokeMethod("getDefaultHttpParameters", arguments: nil){ (response) in
if let defaultHTTPParams = response as? [String:Any?]{
DEFAULT_HTTP_PARAMS = defaultHTTPParams
}
}
flutterMethodChannel?.invokeMethod("getLogGeofenceFullUrl", arguments:nil){ (response) in
if let url = response as? String{
LOG_GEOFENCE_URL = url
}
}
}
}
}

@ -0,0 +1,67 @@
//
// GeoZoneModel.swift
// Runner
//
// Created by ZiKambrani on 13/12/2020.
//
import UIKit
import CoreLocation
class GeoZoneModel{
var geofenceId:Int = -1
var description:String = ""
var descriptionN:String?
var latitude:String?
var longitude:String?
var radius:Int?
var type:Int?
var projectID:Int?
var imageURL:String?
var isCity:String?
func identifier() -> String{
return "\(geofenceId)_hmg"
}
func message() -> String{
return description
}
func toRegion(locationManager:CLLocationManager) -> CLCircularRegion?{
if let rad = radius, let lat = latitude?.removeSpace(), let long = longitude?.removeSpace(),
let radius_d = Double("\(rad)"), let lat_d = Double(lat), let long_d = Double(long){
let coordinate = CLLocationCoordinate2D(latitude: lat_d, longitude: long_d)
let validatedRadius = min(radius_d, locationManager.maximumRegionMonitoringDistance)
let region = CLCircularRegion(center: coordinate, radius: validatedRadius, identifier: identifier())
region.notifyOnExit = true
region.notifyOnEntry = true
return region
}
return nil
}
class func from(json:[String:Any]) -> GeoZoneModel{
let model = GeoZoneModel()
model.geofenceId = json["GEOF_ID"] as? Int ?? 0
model.radius = json["Radius"] as? Int
model.projectID = json["ProjectID"] as? Int
model.type = json["Type"] as? Int
model.description = json["Description"] as? String ?? ""
model.descriptionN = json["DescriptionN"] as? String
model.latitude = json["Latitude"] as? String
model.longitude = json["Longitude"] as? String
model.imageURL = json["ImageURL"] as? String
model.isCity = json["IsCity"] as? String
return model
}
class func list(from jsonString:String) -> [GeoZoneModel]{
let value = dictionaryArray(from: jsonString)
let geoZones = value.map { GeoZoneModel.from(json: $0) }
return geoZones
}
}

@ -0,0 +1,119 @@
//
// GlobalHelper.swift
// Runner
//
// Created by ZiKambrani on 29/03/1442 AH.
//
import UIKit
func dictionaryArray(from:String) -> [[String:Any]]{
if let data = from.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] ?? []
} catch {
print(error.localizedDescription)
}
}
return []
}
func dictionary(from:String) -> [String:Any]?{
if let data = from.data(using: .utf8) {
do {
return try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
let HmgLocalNotificationCategoryIdentifier = "hmg.local.notification"
func showNotification(identifier:String? = nil, title:String?, subtitle:String?, message:String?, sound:UNNotificationSound = UNNotificationSound.default, categoryIdentifier:String = HmgLocalNotificationCategoryIdentifier){
DispatchQueue.main.async {
let notificationContent = UNMutableNotificationContent()
notificationContent.categoryIdentifier = categoryIdentifier
if identifier != nil { notificationContent.categoryIdentifier = identifier! }
if title != nil { notificationContent.title = title! }
if subtitle != nil { notificationContent.body = message! }
if message != nil { notificationContent.subtitle = subtitle! }
notificationContent.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "\(Date().timeIntervalSinceNow)", content: notificationContent, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error: \(error)")
}
}
}
}
func appLanguageCode() -> Int{
let lang = UserDefaults.standard.string(forKey: "language") ?? "ar"
return lang == "ar" ? 2 : 1
}
func userProfile() -> [String:Any?]?{
var userProf = UserDefaults.standard.string(forKey: "flutter.imei-user-data")
if(userProf == nil){
userProf = UserDefaults.standard.string(forKey: "flutter.user-profile")
}
return dictionary(from: userProf ?? "{}")
}
fileprivate let defaultHTTPParams:[String : Any?] = [
"ZipCode" : "966",
"VersionID" : 5.8,
"Channel" : 3,
"LanguageID" : appLanguageCode(),
"IPAdress" : "10.20.10.20",
"generalid" : "Cs2020@2016$2958",
"PatientOutSA" : 0,
"SessionID" : nil,
"isDentalAllowedBackend" : false,
"DeviceTypeID" : 2
]
func httpPostRequest(urlString:String, jsonBody:[String:Any?], completion:((Bool,[String:Any]?)->Void)?){
var json: [String: Any?] = jsonBody
json = json.merge(dict: defaultHTTPParams)
let jsonData = try? JSONSerialization.data(withJSONObject: json)
// create post request
let url = URL(string: urlString)!
var request = URLRequest(url: url)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("*/*", forHTTPHeaderField: "Accept")
request.httpMethod = "POST"
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any], let status = responseJSON["MessageStatus"] as? Int{
print(responseJSON)
if status == 1{
completion?(true,responseJSON)
}else{
completion?(false,responseJSON)
}
}else{
completion?(false,nil)
}
}
task.resume()
}

@ -0,0 +1,94 @@
import Foundation
import FLAnimatedImage
var flutterMethodChannelPenguinIn:FlutterMethodChannel? = nil
fileprivate var mainViewController:MainFlutterVC!
class HMGPenguinInPlatformBridge{
private let channelName = "launch_penguin_ui"
private static var shared_:HMGPenguinInPlatformBridge?
class func initialize(flutterViewController:MainFlutterVC){
shared_ = HMGPenguinInPlatformBridge()
mainViewController = flutterViewController
shared_?.openChannel()
}
func shared() -> HMGPenguinInPlatformBridge{
assert((HMGPenguinInPlatformBridge.shared_ != nil), "HMGPenguinInPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
return HMGPenguinInPlatformBridge.shared_!
}
private func openChannel(){
flutterMethodChannelPenguinIn = FlutterMethodChannel(name: channelName, binaryMessenger: mainViewController.binaryMessenger)
flutterMethodChannelPenguinIn?.setMethodCallHandler { (methodCall, result) in
print("Called function \(methodCall.method)")
if let arguments = methodCall.arguments as Any? {
if methodCall.method == "launchPenguin"{
print("====== launchPenguinView Launched =========")
self.launchPenguinView(arguments: arguments, result: result)
}
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "Storyboard name is required", details: nil))
}
}
}
private func launchPenguinView(arguments: Any, result: @escaping FlutterResult) {
let penguinView = PenguinView(
frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height),
viewIdentifier: 0,
arguments: arguments,
binaryMessenger: mainViewController.binaryMessenger
)
let penguinUIView = penguinView.view()
penguinUIView.frame = mainViewController.view.bounds
penguinUIView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mainViewController.view.addSubview(penguinUIView)
guard let args = arguments as? [String: Any],
let loaderImageData = args["loaderImage"] as? FlutterStandardTypedData else {
print("loaderImage data not found in arguments")
result(FlutterError(code: "ARGUMENT_ERROR", message: "Missing loaderImage data", details: nil))
return
}
let loadingOverlay = UIView(frame: UIScreen.main.bounds)
loadingOverlay.backgroundColor = UIColor.black.withAlphaComponent(0.5) // Semi-transparent overlay
loadingOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Display the GIF using FLAnimatedImage
let animatedImage = FLAnimatedImage(animatedGIFData: loaderImageData.data)
let gifImageView = FLAnimatedImageView()
gifImageView.animatedImage = animatedImage
gifImageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
gifImageView.center = loadingOverlay.center
gifImageView.contentMode = .scaleAspectFit
loadingOverlay.addSubview(gifImageView)
if let window = UIApplication.shared.windows.first {
window.addSubview(loadingOverlay)
} else {
print("Error: Main window not found")
}
penguinView.onSuccess = {
// Hide and remove the loader
DispatchQueue.main.async {
loadingOverlay.removeFromSuperview()
}
}
result(nil)
}
}

@ -0,0 +1,140 @@
//
// HMGPlatformBridge.swift
// Runner
//
// Created by ZiKambrani on 14/12/2020.
//
import UIKit
import NetworkExtension
import SystemConfiguration.CaptiveNetwork
var flutterMethodChannel:FlutterMethodChannel? = nil
fileprivate var mainViewController:MainFlutterVC!
class HMGPlatformBridge{
private let channelName = "HMG-Platform-Bridge"
private static var shared_:HMGPlatformBridge?
class func initialize(flutterViewController:MainFlutterVC){
shared_ = HMGPlatformBridge()
mainViewController = flutterViewController
shared_?.openChannel()
}
func shared() -> HMGPlatformBridge{
assert((HMGPlatformBridge.shared_ != nil), "HMGPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
return HMGPlatformBridge.shared_!
}
private func openChannel(){
flutterMethodChannel = FlutterMethodChannel(name: channelName, binaryMessenger: mainViewController.binaryMessenger)
flutterMethodChannel?.setMethodCallHandler { (methodCall, result) in
print("Called function \(methodCall.method)")
if methodCall.method == "connectHMGInternetWifi"{
self.connectHMGInternetWifi(methodCall:methodCall, result: result)
}else if methodCall.method == "connectHMGGuestWifi"{
self.connectHMGGuestWifi(methodCall:methodCall, result: result)
}else if methodCall.method == "isHMGNetworkAvailable"{
self.isHMGNetworkAvailable(methodCall:methodCall, result: result)
}else if methodCall.method == "registerHmgGeofences"{
self.registerHmgGeofences(result: result)
}else if methodCall.method == "unRegisterHmgGeofences"{
self.unRegisterHmgGeofences(result: result)
}
print("")
}
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
FlutterConstants.set()
}
}
// Connect HMG Wifi and Internet
func connectHMGInternetWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
guard let pateintId = (methodCall.arguments as? [Any])?.first as? String
else { return assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'") }
HMG_Internet.shared.connect(patientId: pateintId) { (status, message) in
result(status ? 1 : 0)
if status{
self.showMessage(title:"Congratulations", message:message)
}else{
self.showMessage(title:"Ooops,", message:message)
}
}
}
// Connect HMG-Guest for App Access
func connectHMGGuestWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
HMG_GUEST.shared.connect() { (status, message) in
result(status ? 1 : 0)
if status{
self.showMessage(title:"Congratulations", message:message)
}else{
self.showMessage(title:"Ooops,", message:message)
}
}
}
func isHMGNetworkAvailable(methodCall:FlutterMethodCall ,result: @escaping FlutterResult) -> Bool{
guard let ssid = methodCall.arguments as? String else {
assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'")
return false
}
let queue = DispatchQueue.init(label: "com.hmg.wifilist")
NEHotspotHelper.register(options: nil, queue: queue) { (command) in
print(command)
if(command.commandType == NEHotspotHelperCommandType.filterScanList) {
if let networkList = command.networkList{
for network in networkList{
print(network.ssid)
}
}
}
}
return false
}
// Message Dailog
func showMessage(title:String, message:String){
DispatchQueue.main.async {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert )
alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
mainViewController.present(alert, animated: true) {
}
}
}
// Register Geofence
func registerHmgGeofences(result: @escaping FlutterResult){
flutterMethodChannel?.invokeMethod("getGeoZones", arguments: nil){ geoFencesJsonString in
if let jsonString = geoFencesJsonString as? String{
let allZones = GeoZoneModel.list(from: jsonString)
HMG_Geofence.shared().register(geoZones: allZones)
result(true)
}else{
}
}
}
// Register Geofence
func unRegisterHmgGeofences(result: @escaping FlutterResult){
HMG_Geofence.shared().unRegisterAll()
result(true)
}
}

@ -0,0 +1,183 @@
//
// HMG_Geofence.swift
// Runner
//
// Created by ZiKambrani on 13/12/2020.
//
import UIKit
import CoreLocation
fileprivate var df = DateFormatter()
fileprivate var transition = ""
enum Transition:Int {
case entry = 1
case exit = 2
func name() -> String{
return self.rawValue == 1 ? "Enter" : "Exit"
}
}
class HMG_Geofence:NSObject{
var geoZones:[GeoZoneModel]?
var locationManager:CLLocationManager!{
didSet{
// https://developer.apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati
locationManager.allowsBackgroundLocationUpdates = true
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.activityType = .other
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
// locationManager.distanceFilter = 500
// locationManager.startMonitoringSignificantLocationChanges()
}
}
private static var shared_:HMG_Geofence?
class func shared() -> HMG_Geofence{
if HMG_Geofence.shared_ == nil{
HMG_Geofence.initGeofencing()
}
return shared_!
}
class func initGeofencing(){
shared_ = HMG_Geofence()
shared_?.locationManager = CLLocationManager()
}
func register(geoZones:[GeoZoneModel]){
self.geoZones = geoZones
let monitoredRegions_ = monitoredRegions()
self.geoZones?.forEach({ (zone) in
if let region = zone.toRegion(locationManager: locationManager){
if let already = monitoredRegions_.first(where: {$0.identifier == zone.identifier()}){
debugPrint("Already monitering region: \(already)")
}else{
startMonitoring(region: region)
}
}else{
debugPrint("Invalid region: \(zone.latitude ?? "invalid_latitude"),\(zone.longitude ?? "invalid_longitude"),r\(zone.radius ?? 0) | \(zone.identifier())")
}
})
}
func monitoredRegions() -> Set<CLRegion>{
return locationManager.monitoredRegions
}
func unRegisterAll(){
for region in locationManager.monitoredRegions {
locationManager.stopMonitoring(for: region)
}
}
}
// CLLocationManager Delegates
extension HMG_Geofence : CLLocationManagerDelegate{
func startMonitoring(region: CLCircularRegion) {
if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
return
}
if CLLocationManager.authorizationStatus() != .authorizedAlways {
let message = """
Your geotification is saved but will only be activated once you grant
HMG permission to access the device location.
"""
debugPrint(message)
}
locationManager.startMonitoring(for: region)
locationManager.requestState(for: region)
debugPrint("Starts monitering region: \(region)")
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
debugPrint("didEnterRegion: \(region)")
if region is CLCircularRegion {
handleEvent(for: region,transition: .entry, location: manager.location)
}
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
debugPrint("didExitRegion: \(region)")
if region is CLCircularRegion {
handleEvent(for: region,transition: .exit, location: manager.location)
}
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
debugPrint("didDetermineState: \(state)")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
debugPrint("didUpdateLocations: \(locations)")
}
}
// Helpers
extension HMG_Geofence{
func handleEvent(for region: CLRegion!, transition:Transition, location:CLLocation?) {
if let userProfile = userProfile(){
notifyUser(forRegion: region, transition: transition, location: locationManager.location, userProfile: userProfile)
notifyServer(forRegion: region, transition: transition, location: locationManager.location, userProfile: userProfile)
}
}
func geoZone(by id: String) -> GeoZoneModel? {
var zone:GeoZoneModel? = nil
if let zones_ = geoZones{
zone = zones_.first(where: { $0.identifier() == id})
}else{
// let jsonArray = UserDefaults.standard.string(forKey: "hmg-geo-fences")
}
return zone
}
func notifyUser(forRegion:CLRegion, transition:Transition, location:CLLocation?, userProfile:[String:Any?]){
if let patientId = userProfile["PatientID"] as? Int{
}
}
func notifyServer(forRegion:CLRegion, transition:Transition, location:CLLocation?, userProfile:[String:Any?]){
if let patientId = userProfile["PatientID"] as? Int{
if let idString = forRegion.identifier.split(separator: "_").first, let idInt = Int(idString){
let body:[String:Any] = [
"PointsID":idInt,
"GeoType":transition.rawValue,
"PatientID":patientId
]
var logs = UserDefaults.init(suiteName: "GeoFenceLog")?.dictionary(forKey: "GEOFENCE_LOGS") ?? [:]
var geo = (logs[forRegion.identifier] as? [String]) ?? []
let url = "https://hmgwebservices.com/Services/Patients.svc/REST/GeoF_InsertPatientFileInfo"
httpPostRequest(urlString: url, jsonBody: body){ (status,json) in
let status_ = status ? "Notified successfully:" : "Failed to notify:"
showNotification(title: transition.name(), subtitle: forRegion.identifier, message: status_)
geo.append("\(status_) \(transition.name()) at \(Date().toString(format: "dd/MMM/yyy hh:mm:ss"))")
logs.updateValue( geo, forKey: forRegion.identifier)
UserDefaults.init(suiteName: "GeoFenceLog")?.set(logs, forKey: "GEOFENCE_LOGS")
}
}
}
}
}

@ -0,0 +1,22 @@
//
// LocalizedFromFlutter.swift
// Runner
//
// Created by ZiKambrani on 10/04/1442 AH.
//
import UIKit
class FlutterText{
class func with(key:String,completion: @escaping (String)->Void){
flutterMethodChannel?.invokeMethod("localizedValue", arguments: key, result: { (result) in
if let localized = result as? String{
completion(localized)
}else{
completion(key)
}
})
}
}

@ -0,0 +1,61 @@
//
// HMGPlatformBridge.swift
// Runner
//
// Created by ZiKambrani on 14/12/2020.
//
import UIKit
import NetworkExtension
import SystemConfiguration.CaptiveNetwork
fileprivate var openTok:OpenTok?
class OpenTokPlatformBridge : NSObject{
private var methodChannel:FlutterMethodChannel? = nil
private var mainViewController:MainFlutterVC!
private static var shared_:OpenTokPlatformBridge?
class func initialize(flutterViewController:MainFlutterVC, registrar:FlutterPluginRegistrar?){
shared_ = OpenTokPlatformBridge()
shared_?.mainViewController = flutterViewController
shared_?.openChannel()
openTok = OpenTok(mainViewController: flutterViewController, registrar: registrar)
}
func shared() -> OpenTokPlatformBridge{
assert((OpenTokPlatformBridge.shared_ != nil), "OpenTokPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
return OpenTokPlatformBridge.shared_!
}
private func openChannel(){
methodChannel = FlutterMethodChannel(name: "OpenTok-Platform-Bridge", binaryMessenger: mainViewController.binaryMessenger)
methodChannel?.setMethodCallHandler { (call, result) in
print("Called function \(call.method)")
switch(call.method) {
case "initSession":
openTok?.initSession(call: call, result: result)
case "swapCamera":
openTok?.swapCamera(call: call, result: result)
case "toggleAudio":
openTok?.toggleAudio(call: call, result: result)
case "toggleVideo":
openTok?.toggleVideo(call: call, result: result)
case "hangupCall":
openTok?.hangupCall(call: call, result: result)
default:
result(FlutterMethodNotImplemented)
}
print("")
}
}
}

@ -0,0 +1,76 @@
//
// PenguinModel.swift
// Runner
//
// Created by Amir on 06/08/2024.
//
import Foundation
// Define the model class
struct PenguinModel {
let baseURL: String
let dataURL: String
let dataServiceName: String
let positionURL: String
let clientKey: String
let storyboardName: String
let mapBoxKey: String
let clientID: String
let positionServiceName: String
let username: String
let isSimulationModeEnabled: Bool
let isShowUserName: Bool
let isUpdateUserLocationSmoothly: Bool
let isEnableReportIssue: Bool
let languageCode: String
let clinicID: String
let patientID: String
let projectID: String
// Initialize the model from a dictionary
init?(from dictionary: [String: Any]) {
guard
let baseURL = dictionary["baseURL"] as? String,
let dataURL = dictionary["dataURL"] as? String,
let dataServiceName = dictionary["dataServiceName"] as? String,
let positionURL = dictionary["positionURL"] as? String,
let clientKey = dictionary["clientKey"] as? String,
let storyboardName = dictionary["storyboardName"] as? String,
let mapBoxKey = dictionary["mapBoxKey"] as? String,
let clientID = dictionary["clientID"] as? String,
let positionServiceName = dictionary["positionServiceName"] as? String,
let username = dictionary["username"] as? String,
let isSimulationModeEnabled = dictionary["isSimulationModeEnabled"] as? Bool,
let isShowUserName = dictionary["isShowUserName"] as? Bool,
let isUpdateUserLocationSmoothly = dictionary["isUpdateUserLocationSmoothly"] as? Bool,
let isEnableReportIssue = dictionary["isEnableReportIssue"] as? Bool,
let languageCode = dictionary["languageCode"] as? String,
let clinicID = dictionary["clinicID"] as? String,
let patientID = dictionary["patientID"] as? String,
let projectID = dictionary["projectID"] as? String
else {
print("Initialization failed due to missing or invalid keys.")
return nil
}
self.baseURL = baseURL
self.dataURL = dataURL
self.dataServiceName = dataServiceName
self.positionURL = positionURL
self.clientKey = clientKey
self.storyboardName = storyboardName
self.mapBoxKey = mapBoxKey
self.clientID = clientID
self.positionServiceName = positionServiceName
self.username = username
self.isSimulationModeEnabled = isSimulationModeEnabled
self.isShowUserName = isShowUserName
self.isUpdateUserLocationSmoothly = isUpdateUserLocationSmoothly
self.isEnableReportIssue = isEnableReportIssue
self.languageCode = languageCode
self.clinicID = clinicID
self.patientID = patientID
self.projectID = projectID
}
}

@ -0,0 +1,57 @@
import PenNavUI
import UIKit
class PenguinNavigator {
private var config: PenguinModel
init(config: PenguinModel) {
self.config = config
}
private func logError(_ message: String) {
// Centralized logging function
print("PenguinSDKNavigator Error: \(message)")
}
func navigateToPOI( referenceId:String,completion: @escaping (Bool, String?) -> Void) {
PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey) { [weak self] token, error in
if let error = error {
let errorMessage = "Token error while getting the for Navigate to method"
completion(false, "Failed to get token: \(errorMessage)")
print("Failed to get token: \(errorMessage)")
return
}
guard let token = token else {
completion(false, "Token is nil")
print("Token is nil")
return
}
print("Token Generated")
print(token);
}
}
private func handleNavigation(referenceId: String, token: String, completion: @escaping (Bool, String?) -> Void) {
DispatchQueue.main.async {
PenNavUIManager.shared.setToken(token: token)
PenNavUIManager.shared.navigate(to: referenceId) { [weak self] _, navError in
guard let self = self else { return }
if let navError = navError {
self.logError("Navigation error: Reference ID invalid")
completion(false, "Navigation error: \(navError.localizedDescription)")
return
}
// Navigation successful
completion(true, nil)
}
}
}
}

@ -0,0 +1,31 @@
//
// BlueGpsPlugin.swift
// Runner
//
// Created by Penguin .
//
//import Foundation
//import Flutter
//
///**
// * A Flutter plugin for integrating Penguin SDK functionality.
// * This class registers a view factory with the Flutter engine to create native views.
// */
//class PenguinPlugin: NSObject, FlutterPlugin {
//
// /**
// * Registers the plugin with the Flutter engine.
// *
// * @param registrar The [FlutterPluginRegistrar] used to register the plugin.
// * This method is called when the plugin is initialized, and it sets up the communication
// * between Flutter and native code.
// */
// public static func register(with registrar: FlutterPluginRegistrar) {
// // Create an instance of PenguinViewFactory with the binary messenger from the registrar
// let factory = PenguinViewFactory(messenger: registrar.messenger())
//
// // Register the view factory with a unique ID for use in Flutter code
// registrar.register(factory, withId: "penguin_native")
// }
//}

@ -0,0 +1,445 @@
//
// BlueGpsView.swift
// Runner
//
// Created by Penguin.
//
import Foundation
import UIKit
import Flutter
import PenNavUI
import Foundation
import Flutter
import UIKit
/**
* A custom Flutter platform view for displaying Penguin UI components.
* This class integrates with the Penguin navigation SDK and handles UI events.
*/
class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitializationDelegate
{
// The main view displayed within the platform view
private var _view: UIView
private var model: PenguinModel?
private var methodChannel: FlutterMethodChannel
var onSuccess: (() -> Void)?
/**
* Initializes the PenguinView with the provided parameters.
*
* @param frame The frame of the view, specifying its size and position.
* @param viewId A unique identifier for this view instance.
* @param args Optional arguments provided for creating the view.
* @param messenger The [FlutterBinaryMessenger] used for communication with Dart.
*/
init(
frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?,
binaryMessenger messenger: FlutterBinaryMessenger?
) {
_view = UIView()
methodChannel = FlutterMethodChannel(name: "launch_penguin_ui", binaryMessenger: messenger!)
super.init()
// Get the screen's width and height to set the view's frame
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
// Uncomment to set the background color of the view
// _view.backgroundColor = UIColor.red
// Set the frame of the view to cover the entire screen
_view.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
print("========Inside Penguin View ========")
print(args)
guard let arguments = args as? [String: Any] else {
print("Error: Arguments are not in the expected format.")
return
}
print("===== i got tha Args=======")
// Initialize the model from the arguments
if let penguinModel = PenguinModel(from: arguments) {
self.model = penguinModel
initPenguin(args: penguinModel)
} else {
print("Error: Failed to initialize PenguinModel from arguments ")
}
// Initialize the Penguin SDK with required configurations
// initPenguin( arguments: args)
}
/**
* Initializes the Penguin SDK with custom configuration settings.
*/
func initPenguin(args: PenguinModel) {
// Set the initialization delegate to handle SDK initialization events
PenNavUIManager.shared.initializationDelegate = self
// Configure the Penguin SDK with necessary parameters
PenNavUIManager.shared
.setClientKey(args.clientKey)
.setClientID(args.clientID)
.setUsername(args.username)
.setSimulationModeEnabled(isEnable: args.isSimulationModeEnabled)
.setBaseURL(dataURL: args.dataURL, positionURL: args.positionURL)
.setServiceName(dataServiceName: args.dataServiceName, positionServiceName: args.positionServiceName)
.setIsShowUserName(args.isShowUserName)
.setIsUpdateUserLocationSmoothly(args.isUpdateUserLocationSmoothly)
.setEnableReportIssue(enable: args.isEnableReportIssue)
.setLanguage(args.languageCode)
.setBackButtonVisibility(true)
.build()
}
/**
* Returns the main view associated with this platform view.
*
* @return The UIView instance that represents this platform view.
*/
func view() -> UIView {
return _view
}
// MARK: - PIEventsDelegate Methods
/**
* Called when the Penguin UI is dismissed.
*/
func onPenNavUIDismiss() {
// Handle UI dismissal if needed
print("====== onPenNavUIDismiss =========")
self.view().removeFromSuperview()
}
/**
* Called when a report issue is generated.
*
* @param issue The type of issue reported.
*/
func onReportIssue(_ issue: PenNavUI.IssueType) {
// Handle report issue events if needed
print("====== onReportIssueError =========")
methodChannel.invokeMethod("onReportIssue", arguments: ["issueType": issue])
}
/**
* Called when the Penguin UI setup is successful.
*/
func onPenNavSuccess() {
print("====== onPenNavSuccess =========")
onSuccess?()
methodChannel.invokeMethod("onPenNavSuccess", arguments: nil)
// Obtain the FlutterViewController instance
let controller: FlutterViewController = UIApplication.shared.windows.first?.rootViewController as! FlutterViewController
print("====== after controller onPenNavSuccess =========")
// Set the events delegate to handle SDK events
PenNavUIManager.shared.eventsDelegate = self
print("====== after eventsDelegate onPenNavSuccess =========")
// Present the Penguin UI on top of the Flutter view controller
PenNavUIManager.shared.present(root: controller, view: _view)
print("====== after present onPenNavSuccess =========")
print(model?.clinicID)
print("====== after present onPenNavSuccess =========")
guard let config = self.model else {
print("Error: Config Model is nil")
return
}
guard let clinicID = self.model?.clinicID,
let clientID = self.model?.clientID, !clientID.isEmpty else {
print("Error: Config Client ID is nil or empty")
return
}
let navigator = PenguinNavigator(config: config)
PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey) { [weak self] token, error in
if let error = error {
let errorMessage = "Token error while getting the for Navigate to method"
print("Failed to get token: \(errorMessage)")
return
}
guard let token = token else {
print("Token is nil")
return
}
print("Token Generated")
print(token);
self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in
if success {
print("Navigation successful")
} else {
print("Navigation failed: \(errorMessage ?? "Unknown error")")
}
}
print("====== after Token onPenNavSuccess =========")
}
}
private func handleNavigation(clinicID: String, token: String, completion: @escaping (Bool, String?) -> Void) {
DispatchQueue.main.async {
PenNavUIManager.shared.setToken(token: token)
PenNavUIManager.shared.navigate(to: clinicID)
completion(true,nil)
}
}
/**
* Called when there is an initialization error with the Penguin UI.
*
* @param errorType The type of initialization error.
* @param errorDescription A description of the error.
*/
func onPenNavInitializationError(errorType: PenNavUI.PenNavUIError, errorDescription: String) {
// Handle initialization errors if needed
print("onPenNavInitializationErrorType: \(errorType.rawValue)")
print("onPenNavInitializationError: \(errorDescription)")
}
}

@ -0,0 +1,59 @@
//
// BlueGpsViewFactory.swift
// Runner
//
// Created by Penguin .
//
import Foundation
import Flutter
/**
* A factory class for creating instances of [PenguinView].
* This class implements `FlutterPlatformViewFactory` to create and manage native views.
*/
class PenguinViewFactory: NSObject, FlutterPlatformViewFactory {
// The binary messenger used for communication with the Flutter engine
private var messenger: FlutterBinaryMessenger
/**
* Initializes the PenguinViewFactory with the given messenger.
*
* @param messenger The [FlutterBinaryMessenger] used to communicate with Dart code.
*/
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
/**
* Creates a new instance of [PenguinView].
*
* @param frame The frame of the view, specifying its size and position.
* @param viewId A unique identifier for this view instance.
* @param args Optional arguments provided for creating the view.
* @return An instance of [PenguinView] configured with the provided parameters.
*/
func create(
withFrame frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?
) -> FlutterPlatformView {
return PenguinView(
frame: frame,
viewIdentifier: viewId,
arguments: args,
binaryMessenger: messenger)
}
/**
* Returns the codec used for encoding and decoding method channel arguments.
* This method is required when `arguments` in `create` is not `nil`.
*
* @return A [FlutterMessageCodec] instance used for serialization.
*/
public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}

@ -11,11 +11,23 @@
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
478CFA942E638C8E0064F3D7 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */; };
61243B492EC5FA3700D46FA0 /* PenguinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B422EC5FA3700D46FA0 /* PenguinModel.swift */; };
61243B4C2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B3D2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift */; };
61243B502EC5FA3700D46FA0 /* PenguinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B452EC5FA3700D46FA0 /* PenguinView.swift */; };
61243B542EC5FA3700D46FA0 /* PenguinPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B442EC5FA3700D46FA0 /* PenguinPlugin.swift */; };
61243B562EC5FA3700D46FA0 /* PenguinNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B432EC5FA3700D46FA0 /* PenguinNavigator.swift */; };
61243B572EC5FA3700D46FA0 /* PenguinViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B462EC5FA3700D46FA0 /* PenguinViewFactory.swift */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
766D8CB62EC60BE600D05E07 /* Penguin.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB32EC60BE600D05E07 /* Penguin.xcframework */; };
766D8CB72EC60BE600D05E07 /* Penguin.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB32EC60BE600D05E07 /* Penguin.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
766D8CB82EC60BE600D05E07 /* PenNavUI.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */; };
766D8CB92EC60BE600D05E07 /* PenNavUI.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
766D8CBA2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */; };
766D8CBB2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
B976FB9C47411C32B24D5E01 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACE60DF9393168FD748550B3 /* Pods_Runner.framework */; };
DE1EF78253E64BE86845D2CC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -35,6 +47,9 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
766D8CB72EC60BE600D05E07 /* Penguin.xcframework in Embed Frameworks */,
766D8CBB2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Embed Frameworks */,
766D8CB92EC60BE600D05E07 /* PenNavUI.xcframework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@ -49,9 +64,18 @@
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
478CFA952E6E20A60064F3D7 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
61243B3D2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HMGPenguinInPlatformBridge.swift; sourceTree = "<group>"; };
61243B422EC5FA3700D46FA0 /* PenguinModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinModel.swift; sourceTree = "<group>"; };
61243B432EC5FA3700D46FA0 /* PenguinNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinNavigator.swift; sourceTree = "<group>"; };
61243B442EC5FA3700D46FA0 /* PenguinPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinPlugin.swift; sourceTree = "<group>"; };
61243B452EC5FA3700D46FA0 /* PenguinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinView.swift; sourceTree = "<group>"; };
61243B462EC5FA3700D46FA0 /* PenguinViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinViewFactory.swift; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7595037DD52211B91157B0F3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
766D8CB32EC60BE600D05E07 /* Penguin.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Penguin.xcframework; path = Frameworks/Penguin.xcframework; sourceTree = "<group>"; };
766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PenNavUI.xcframework; path = Frameworks/PenNavUI.xcframework; sourceTree = "<group>"; };
766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PenguinINRenderer.xcframework; path = Frameworks/PenguinINRenderer.xcframework; sourceTree = "<group>"; };
769C9BF82E6F106D009F68A9 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
8E12CEEB8E334EE22D5259D7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
@ -62,7 +86,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
ACE60DF9393168FD748550B3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D6BB17A036DF7FCE75271203 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -71,7 +95,10 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B976FB9C47411C32B24D5E01 /* Pods_Runner.framework in Frameworks */,
766D8CB62EC60BE600D05E07 /* Penguin.xcframework in Frameworks */,
766D8CBA2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Frameworks */,
766D8CB82EC60BE600D05E07 /* PenNavUI.xcframework in Frameworks */,
DE1EF78253E64BE86845D2CC /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -86,6 +113,37 @@
path = RunnerTests;
sourceTree = "<group>";
};
61243B412EC5FA3700D46FA0 /* Helper */ = {
isa = PBXGroup;
children = (
61243B3D2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift */,
);
path = Helper;
sourceTree = "<group>";
};
61243B472EC5FA3700D46FA0 /* Penguin */ = {
isa = PBXGroup;
children = (
61243B422EC5FA3700D46FA0 /* PenguinModel.swift */,
61243B432EC5FA3700D46FA0 /* PenguinNavigator.swift */,
61243B442EC5FA3700D46FA0 /* PenguinPlugin.swift */,
61243B452EC5FA3700D46FA0 /* PenguinView.swift */,
61243B462EC5FA3700D46FA0 /* PenguinViewFactory.swift */,
);
path = Penguin;
sourceTree = "<group>";
};
766D8CB22EC60BE600D05E07 /* Frameworks */ = {
isa = PBXGroup;
children = (
766D8CB32EC60BE600D05E07 /* Penguin.xcframework */,
766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */,
766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */,
D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
79DD2093A1D9674C94359FC8 /* Pods */ = {
isa = PBXGroup;
children = (
@ -115,7 +173,7 @@
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
79DD2093A1D9674C94359FC8 /* Pods */,
A07D637C76A0ABB38659D189 /* Frameworks */,
766D8CB22EC60BE600D05E07 /* Frameworks */,
);
sourceTree = "<group>";
};
@ -131,6 +189,8 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
61243B412EC5FA3700D46FA0 /* Helper */,
61243B472EC5FA3700D46FA0 /* Penguin */,
769C9BF82E6F106D009F68A9 /* RunnerDebug.entitlements */,
478CFA952E6E20A60064F3D7 /* Runner.entitlements */,
478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */,
@ -146,14 +206,6 @@
path = Runner;
sourceTree = "<group>";
};
A07D637C76A0ABB38659D189 /* Frameworks */ = {
isa = PBXGroup;
children = (
ACE60DF9393168FD748550B3 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -362,6 +414,12 @@
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
61243B492EC5FA3700D46FA0 /* PenguinModel.swift in Sources */,
61243B4C2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift in Sources */,
61243B502EC5FA3700D46FA0 /* PenguinView.swift in Sources */,
61243B542EC5FA3700D46FA0 /* PenguinPlugin.swift in Sources */,
61243B562EC5FA3700D46FA0 /* PenguinNavigator.swift in Sources */,
61243B572EC5FA3700D46FA0 /* PenguinViewFactory.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

@ -1,7 +1,7 @@
import Flutter
import UIKit
//import FirebaseCore
//import FirebaseMessaging
import FirebaseCore
import FirebaseMessaging
import GoogleMaps
@main
@objc class AppDelegate: FlutterAppDelegate {
@ -10,11 +10,18 @@ import GoogleMaps
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng")
// FirebaseApp.configure()
FirebaseApp.configure()
initializePlatformChannels()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func initializePlatformChannels(){
if let mainViewController = window?.rootViewController as? FlutterViewController{ // platform initialization suppose to be in foreground
HMGPenguinInPlatformBridge.initialize(flutterViewController: mainViewController)
}
}
override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken:Data){
// Messaging.messaging().apnsToken = deviceToken
super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)

@ -0,0 +1,118 @@
//
// MainFlutterVC.swift
// Runner
//
// Created by ZiKambrani on 25/03/1442 AH.
//
import UIKit
import Flutter
import NetworkExtension
import SystemConfiguration.CaptiveNetwork
class MainFlutterVC: FlutterViewController {
override func viewDidLoad() {
super.viewDidLoad()
// flutterMethodChannel?.setMethodCallHandler { (methodCall, result) in
//
// if methodCall.method == "connectHMGInternetWifi"{
// self.connectHMGInternetWifi(methodCall:methodCall, result: result)
//
// }else if methodCall.method == "connectHMGGuestWifi"{
// self.connectHMGGuestWifi(methodCall:methodCall, result: result)
//
// }else if methodCall.method == "isHMGNetworkAvailable"{
// self.isHMGNetworkAvailable(methodCall:methodCall, result: result)
//
// }else if methodCall.method == "registerHmgGeofences"{
// self.registerHmgGeofences(result: result)
// }
//
// print("")
// }
//
// FlutterText.with(key: "errorConnectingHmgNetwork") { (localized) in
// print(localized)
// }
}
// Connect HMG Wifi and Internet
func connectHMGInternetWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
guard let pateintId = (methodCall.arguments as? [Any])?.first as? String
else { return assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'") }
HMG_Internet.shared.connect(patientId: pateintId) { (status, message) in
result(status ? 1 : 0)
if status{
self.showMessage(title:"Congratulations", message:message)
}else{
self.showMessage(title:"Ooops,", message:message)
}
}
}
// Connect HMG-Guest for App Access
func connectHMGGuestWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
HMG_GUEST.shared.connect() { (status, message) in
result(status ? 1 : 0)
if status{
self.showMessage(title:"Congratulations", message:message)
}else{
self.showMessage(title:"Ooops,", message:message)
}
}
}
func isHMGNetworkAvailable(methodCall:FlutterMethodCall ,result: @escaping FlutterResult) -> Bool{
guard let ssid = methodCall.arguments as? String else {
assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'")
return false
}
let queue = DispatchQueue.init(label: "com.hmg.wifilist")
NEHotspotHelper.register(options: nil, queue: queue) { (command) in
print(command)
if(command.commandType == NEHotspotHelperCommandType.filterScanList) {
if let networkList = command.networkList{
for network in networkList{
print(network.ssid)
}
}
}
}
return false
}
// Message Dailog
func showMessage(title:String, message:String){
DispatchQueue.main.async {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert )
alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
self.present(alert, animated: true) {
}
}
}
// Register Geofence
func registerHmgGeofences(result: @escaping FlutterResult){
flutterMethodChannel?.invokeMethod("getGeofencePreferenceKey", arguments: nil){ geoFencesJsonString in
if let jsonString = geoFencesJsonString as? String{
let allZones = GeoZoneModel.list(from: jsonString)
HMG_Geofence().register(geoZones: allZones)
}else{
}
}
}
}

@ -0,0 +1,22 @@
//
// API.swift
// Runner
//
// Created by ZiKambrani on 04/04/1442 AH.
//
import UIKit
fileprivate let DOMAIN = "https://uat.hmgwebservices.com"
fileprivate let SERVICE = "Services/Patients.svc/REST"
fileprivate let BASE_URL = "\(DOMAIN)/\(SERVICE)"
struct API {
static let WIFI_CREDENTIALS = "\(BASE_URL)/Hmg_SMS_Get_By_ProjectID_And_PatientID"
}
//struct API {
// static let WIFI_CREDENTIALS = FlutterConstants.WIFI_CREDENTIALS_URL
// static let LOG_GEOFENCE = FlutterConstants.LOG_GEOFENCE_URL
//}

@ -0,0 +1,150 @@
//
// Extensions.swift
// Runner
//
// Created by ZiKambrani on 04/04/1442 AH.
//
import UIKit
extension String{
func toUrl() -> URL?{
return URL(string: self)
}
func removeSpace() -> String?{
return self.replacingOccurrences(of: " ", with: "")
}
}
extension Date{
func toString(format:String) -> String{
let df = DateFormatter()
df.dateFormat = format
return df.string(from: self)
}
}
extension Dictionary{
func merge(dict:[String:Any?]) -> [String:Any?]{
var self_ = self as! [String:Any?]
dict.forEach { (kv) in
self_.updateValue(kv.value, forKey: kv.key)
}
return self_
}
}
extension Bundle {
func certificate(named name: String) -> SecCertificate {
let cerURL = self.url(forResource: name, withExtension: "cer")!
let cerData = try! Data(contentsOf: cerURL)
let cer = SecCertificateCreateWithData(nil, cerData as CFData)!
return cer
}
func identity(named name: String, password: String) -> SecIdentity {
let p12URL = self.url(forResource: name, withExtension: "p12")!
let p12Data = try! Data(contentsOf: p12URL)
var importedCF: CFArray? = nil
let options = [kSecImportExportPassphrase as String: password]
let err = SecPKCS12Import(p12Data as CFData, options as CFDictionary, &importedCF)
precondition(err == errSecSuccess)
let imported = importedCF! as NSArray as! [[String:AnyObject]]
precondition(imported.count == 1)
return (imported[0][kSecImportItemIdentity as String]!) as! SecIdentity
}
}
extension SecCertificate{
func trust() -> Bool?{
var optionalTrust: SecTrust?
let policy = SecPolicyCreateBasicX509()
let status = SecTrustCreateWithCertificates([self] as AnyObject,
policy,
&optionalTrust)
guard status == errSecSuccess else { return false}
let trust = optionalTrust!
let stat = optionalTrust?.evaluateAllowing(rootCertificates: [self])
return stat
}
func secTrustObject() -> SecTrust?{
var optionalTrust: SecTrust?
let policy = SecPolicyCreateBasicX509()
let status = SecTrustCreateWithCertificates([self] as AnyObject,
policy,
&optionalTrust)
return optionalTrust
}
}
extension SecTrust {
func evaluate() -> Bool {
var trustResult: SecTrustResultType = .invalid
let err = SecTrustEvaluate(self, &trustResult)
guard err == errSecSuccess else { return false }
return [.proceed, .unspecified].contains(trustResult)
}
func evaluateAllowing(rootCertificates: [SecCertificate]) -> Bool {
// Apply our custom root to the trust object.
var err = SecTrustSetAnchorCertificates(self, rootCertificates as CFArray)
guard err == errSecSuccess else { return false }
// Re-enable the system's built-in root certificates.
err = SecTrustSetAnchorCertificatesOnly(self, false)
guard err == errSecSuccess else { return false }
// Run a trust evaluation and only allow the connection if it succeeds.
return self.evaluate()
}
}
extension UIView{
func show(){
self.alpha = 0.0
self.isHidden = false
UIView.animate(withDuration: 0.25, animations: {
self.alpha = 1
}) { (complete) in
}
}
func hide(){
UIView.animate(withDuration: 0.25, animations: {
self.alpha = 0.0
}) { (complete) in
self.isHidden = true
}
}
}
extension UIViewController{
func showAlert(withTitle: String, message: String){
let alert = UIAlertController(title: withTitle, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
present(alert, animated: true) {
}
}
}

@ -0,0 +1,36 @@
//
// FlutterConstants.swift
// Runner
//
// Created by ZiKambrani on 22/12/2020.
//
import UIKit
class FlutterConstants{
static var LOG_GEOFENCE_URL:String?
static var WIFI_CREDENTIALS_URL:String?
static var DEFAULT_HTTP_PARAMS:[String:Any?]?
class func set(){
// (FiX) Take a start with FlutterMethodChannel (kikstart)
/* First call to flutter method is not returning the correct value (Always returning 'NSObject') then after it wroking fine and returning correct value*/
FlutterText.with(key: "test") { (test) in
flutterMethodChannel?.invokeMethod("getDefaultHttpParameters", arguments: nil){ (response) in
if let defaultHTTPParams = response as? [String:Any?]{
DEFAULT_HTTP_PARAMS = defaultHTTPParams
}
}
flutterMethodChannel?.invokeMethod("getLogGeofenceFullUrl", arguments:nil){ (response) in
if let url = response as? String{
LOG_GEOFENCE_URL = url
}
}
}
}
}

@ -0,0 +1,67 @@
//
// GeoZoneModel.swift
// Runner
//
// Created by ZiKambrani on 13/12/2020.
//
import UIKit
import CoreLocation
class GeoZoneModel{
var geofenceId:Int = -1
var description:String = ""
var descriptionN:String?
var latitude:String?
var longitude:String?
var radius:Int?
var type:Int?
var projectID:Int?
var imageURL:String?
var isCity:String?
func identifier() -> String{
return "\(geofenceId)_hmg"
}
func message() -> String{
return description
}
func toRegion(locationManager:CLLocationManager) -> CLCircularRegion?{
if let rad = radius, let lat = latitude?.removeSpace(), let long = longitude?.removeSpace(),
let radius_d = Double("\(rad)"), let lat_d = Double(lat), let long_d = Double(long){
let coordinate = CLLocationCoordinate2D(latitude: lat_d, longitude: long_d)
let validatedRadius = min(radius_d, locationManager.maximumRegionMonitoringDistance)
let region = CLCircularRegion(center: coordinate, radius: validatedRadius, identifier: identifier())
region.notifyOnExit = true
region.notifyOnEntry = true
return region
}
return nil
}
class func from(json:[String:Any]) -> GeoZoneModel{
let model = GeoZoneModel()
model.geofenceId = json["GEOF_ID"] as? Int ?? 0
model.radius = json["Radius"] as? Int
model.projectID = json["ProjectID"] as? Int
model.type = json["Type"] as? Int
model.description = json["Description"] as? String ?? ""
model.descriptionN = json["DescriptionN"] as? String
model.latitude = json["Latitude"] as? String
model.longitude = json["Longitude"] as? String
model.imageURL = json["ImageURL"] as? String
model.isCity = json["IsCity"] as? String
return model
}
class func list(from jsonString:String) -> [GeoZoneModel]{
let value = dictionaryArray(from: jsonString)
let geoZones = value.map { GeoZoneModel.from(json: $0) }
return geoZones
}
}

@ -0,0 +1,119 @@
//
// GlobalHelper.swift
// Runner
//
// Created by ZiKambrani on 29/03/1442 AH.
//
import UIKit
func dictionaryArray(from:String) -> [[String:Any]]{
if let data = from.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] ?? []
} catch {
print(error.localizedDescription)
}
}
return []
}
func dictionary(from:String) -> [String:Any]?{
if let data = from.data(using: .utf8) {
do {
return try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
let HmgLocalNotificationCategoryIdentifier = "hmg.local.notification"
func showNotification(identifier:String? = nil, title:String?, subtitle:String?, message:String?, sound:UNNotificationSound = UNNotificationSound.default, categoryIdentifier:String = HmgLocalNotificationCategoryIdentifier){
DispatchQueue.main.async {
let notificationContent = UNMutableNotificationContent()
notificationContent.categoryIdentifier = categoryIdentifier
if identifier != nil { notificationContent.categoryIdentifier = identifier! }
if title != nil { notificationContent.title = title! }
if subtitle != nil { notificationContent.body = message! }
if message != nil { notificationContent.subtitle = subtitle! }
notificationContent.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "\(Date().timeIntervalSinceNow)", content: notificationContent, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error: \(error)")
}
}
}
}
func appLanguageCode() -> Int{
let lang = UserDefaults.standard.string(forKey: "language") ?? "ar"
return lang == "ar" ? 2 : 1
}
func userProfile() -> [String:Any?]?{
var userProf = UserDefaults.standard.string(forKey: "flutter.imei-user-data")
if(userProf == nil){
userProf = UserDefaults.standard.string(forKey: "flutter.user-profile")
}
return dictionary(from: userProf ?? "{}")
}
fileprivate let defaultHTTPParams:[String : Any?] = [
"ZipCode" : "966",
"VersionID" : 5.8,
"Channel" : 3,
"LanguageID" : appLanguageCode(),
"IPAdress" : "10.20.10.20",
"generalid" : "Cs2020@2016$2958",
"PatientOutSA" : 0,
"SessionID" : nil,
"isDentalAllowedBackend" : false,
"DeviceTypeID" : 2
]
func httpPostRequest(urlString:String, jsonBody:[String:Any?], completion:((Bool,[String:Any]?)->Void)?){
var json: [String: Any?] = jsonBody
json = json.merge(dict: defaultHTTPParams)
let jsonData = try? JSONSerialization.data(withJSONObject: json)
// create post request
let url = URL(string: urlString)!
var request = URLRequest(url: url)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("*/*", forHTTPHeaderField: "Accept")
request.httpMethod = "POST"
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any], let status = responseJSON["MessageStatus"] as? Int{
print(responseJSON)
if status == 1{
completion?(true,responseJSON)
}else{
completion?(false,responseJSON)
}
}else{
completion?(false,nil)
}
}
task.resume()
}

@ -0,0 +1,94 @@
import Foundation
import FLAnimatedImage
var flutterMethodChannelPenguinIn:FlutterMethodChannel? = nil
fileprivate var mainViewController:FlutterViewController!
class HMGPenguinInPlatformBridge{
private let channelName = "launch_penguin_ui"
private static var shared_:HMGPenguinInPlatformBridge?
class func initialize(flutterViewController:FlutterViewController){
shared_ = HMGPenguinInPlatformBridge()
mainViewController = flutterViewController
shared_?.openChannel()
}
func shared() -> HMGPenguinInPlatformBridge{
assert((HMGPenguinInPlatformBridge.shared_ != nil), "HMGPenguinInPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
return HMGPenguinInPlatformBridge.shared_!
}
private func openChannel(){
flutterMethodChannelPenguinIn = FlutterMethodChannel(name: channelName, binaryMessenger: mainViewController.binaryMessenger)
flutterMethodChannelPenguinIn?.setMethodCallHandler { (methodCall, result) in
print("Called function \(methodCall.method)")
if let arguments = methodCall.arguments as Any? {
if methodCall.method == "launchPenguin"{
print("====== launchPenguinView Launched =========")
self.launchPenguinView(arguments: arguments, result: result)
}
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "Storyboard name is required", details: nil))
}
}
}
private func launchPenguinView(arguments: Any, result: @escaping FlutterResult) {
let penguinView = PenguinView(
frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height),
viewIdentifier: 0,
arguments: arguments,
binaryMessenger: mainViewController.binaryMessenger
)
let penguinUIView = penguinView.view()
penguinUIView.frame = mainViewController.view.bounds
penguinUIView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mainViewController.view.addSubview(penguinUIView)
let args = arguments as? [String: Any]
// let loaderImageData = args["loaderImage"] as? FlutterStandardTypedData else {
// print("loaderImage data not found in arguments")
// result(FlutterError(code: "ARGUMENT_ERROR", message: "Missing loaderImage data", details: nil))
// return
// }
// let loadingOverlay = UIView(frame: UIScreen.main.bounds)
// loadingOverlay.backgroundColor = UIColor.black.withAlphaComponent(0.5) // Semi-transparent overlay
// loadingOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Display the GIF using FLAnimatedImage
// let animatedImage = FLAnimatedImage(animatedGIFData: loaderImageData.data)
// let gifImageView = FLAnimatedImageView()
// gifImageView.animatedImage = animatedImage
// gifImageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
// gifImageView.center = loadingOverlay.center
// gifImageView.contentMode = .scaleAspectFit
// loadingOverlay.addSubview(gifImageView)
// if let window = UIApplication.shared.windows.first {
// window.addSubview(loadingOverlay)
//
// } else {
// print("Error: Main window not found")
// }
penguinView.onSuccess = {
// Hide and remove the loader
// DispatchQueue.main.async {
// loadingOverlay.removeFromSuperview()
//
// }
}
result(nil)
}
}

@ -0,0 +1,140 @@
//
// HMGPlatformBridge.swift
// Runner
//
// Created by ZiKambrani on 14/12/2020.
//
import UIKit
import NetworkExtension
import SystemConfiguration.CaptiveNetwork
var flutterMethodChannel:FlutterMethodChannel? = nil
fileprivate var mainViewController:MainFlutterVC!
class HMGPlatformBridge{
private let channelName = "HMG-Platform-Bridge"
private static var shared_:HMGPlatformBridge?
class func initialize(flutterViewController:MainFlutterVC){
shared_ = HMGPlatformBridge()
mainViewController = flutterViewController
shared_?.openChannel()
}
func shared() -> HMGPlatformBridge{
assert((HMGPlatformBridge.shared_ != nil), "HMGPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
return HMGPlatformBridge.shared_!
}
private func openChannel(){
flutterMethodChannel = FlutterMethodChannel(name: channelName, binaryMessenger: mainViewController.binaryMessenger)
flutterMethodChannel?.setMethodCallHandler { (methodCall, result) in
print("Called function \(methodCall.method)")
if methodCall.method == "connectHMGInternetWifi"{
self.connectHMGInternetWifi(methodCall:methodCall, result: result)
}else if methodCall.method == "connectHMGGuestWifi"{
self.connectHMGGuestWifi(methodCall:methodCall, result: result)
}else if methodCall.method == "isHMGNetworkAvailable"{
self.isHMGNetworkAvailable(methodCall:methodCall, result: result)
}else if methodCall.method == "registerHmgGeofences"{
self.registerHmgGeofences(result: result)
}else if methodCall.method == "unRegisterHmgGeofences"{
self.unRegisterHmgGeofences(result: result)
}
print("")
}
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
FlutterConstants.set()
}
}
// Connect HMG Wifi and Internet
func connectHMGInternetWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
guard let pateintId = (methodCall.arguments as? [Any])?.first as? String
else { return assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'") }
HMG_Internet.shared.connect(patientId: pateintId) { (status, message) in
result(status ? 1 : 0)
if status{
self.showMessage(title:"Congratulations", message:message)
}else{
self.showMessage(title:"Ooops,", message:message)
}
}
}
// Connect HMG-Guest for App Access
func connectHMGGuestWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){
HMG_GUEST.shared.connect() { (status, message) in
result(status ? 1 : 0)
if status{
self.showMessage(title:"Congratulations", message:message)
}else{
self.showMessage(title:"Ooops,", message:message)
}
}
}
func isHMGNetworkAvailable(methodCall:FlutterMethodCall ,result: @escaping FlutterResult) -> Bool{
guard let ssid = methodCall.arguments as? String else {
assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'")
return false
}
let queue = DispatchQueue.init(label: "com.hmg.wifilist")
NEHotspotHelper.register(options: nil, queue: queue) { (command) in
print(command)
if(command.commandType == NEHotspotHelperCommandType.filterScanList) {
if let networkList = command.networkList{
for network in networkList{
print(network.ssid)
}
}
}
}
return false
}
// Message Dailog
func showMessage(title:String, message:String){
DispatchQueue.main.async {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert )
alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: nil))
mainViewController.present(alert, animated: true) {
}
}
}
// Register Geofence
func registerHmgGeofences(result: @escaping FlutterResult){
flutterMethodChannel?.invokeMethod("getGeoZones", arguments: nil){ geoFencesJsonString in
if let jsonString = geoFencesJsonString as? String{
let allZones = GeoZoneModel.list(from: jsonString)
HMG_Geofence.shared().register(geoZones: allZones)
result(true)
}else{
}
}
}
// Register Geofence
func unRegisterHmgGeofences(result: @escaping FlutterResult){
HMG_Geofence.shared().unRegisterAll()
result(true)
}
}

@ -0,0 +1,183 @@
//
// HMG_Geofence.swift
// Runner
//
// Created by ZiKambrani on 13/12/2020.
//
import UIKit
import CoreLocation
fileprivate var df = DateFormatter()
fileprivate var transition = ""
enum Transition:Int {
case entry = 1
case exit = 2
func name() -> String{
return self.rawValue == 1 ? "Enter" : "Exit"
}
}
class HMG_Geofence:NSObject{
var geoZones:[GeoZoneModel]?
var locationManager:CLLocationManager!{
didSet{
// https://developer.apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati
locationManager.allowsBackgroundLocationUpdates = true
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.activityType = .other
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
// locationManager.distanceFilter = 500
// locationManager.startMonitoringSignificantLocationChanges()
}
}
private static var shared_:HMG_Geofence?
class func shared() -> HMG_Geofence{
if HMG_Geofence.shared_ == nil{
HMG_Geofence.initGeofencing()
}
return shared_!
}
class func initGeofencing(){
shared_ = HMG_Geofence()
shared_?.locationManager = CLLocationManager()
}
func register(geoZones:[GeoZoneModel]){
self.geoZones = geoZones
let monitoredRegions_ = monitoredRegions()
self.geoZones?.forEach({ (zone) in
if let region = zone.toRegion(locationManager: locationManager){
if let already = monitoredRegions_.first(where: {$0.identifier == zone.identifier()}){
debugPrint("Already monitering region: \(already)")
}else{
startMonitoring(region: region)
}
}else{
debugPrint("Invalid region: \(zone.latitude ?? "invalid_latitude"),\(zone.longitude ?? "invalid_longitude"),r\(zone.radius ?? 0) | \(zone.identifier())")
}
})
}
func monitoredRegions() -> Set<CLRegion>{
return locationManager.monitoredRegions
}
func unRegisterAll(){
for region in locationManager.monitoredRegions {
locationManager.stopMonitoring(for: region)
}
}
}
// CLLocationManager Delegates
extension HMG_Geofence : CLLocationManagerDelegate{
func startMonitoring(region: CLCircularRegion) {
if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
return
}
if CLLocationManager.authorizationStatus() != .authorizedAlways {
let message = """
Your geotification is saved but will only be activated once you grant
HMG permission to access the device location.
"""
debugPrint(message)
}
locationManager.startMonitoring(for: region)
locationManager.requestState(for: region)
debugPrint("Starts monitering region: \(region)")
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
debugPrint("didEnterRegion: \(region)")
if region is CLCircularRegion {
handleEvent(for: region,transition: .entry, location: manager.location)
}
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
debugPrint("didExitRegion: \(region)")
if region is CLCircularRegion {
handleEvent(for: region,transition: .exit, location: manager.location)
}
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
debugPrint("didDetermineState: \(state)")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
debugPrint("didUpdateLocations: \(locations)")
}
}
// Helpers
extension HMG_Geofence{
func handleEvent(for region: CLRegion!, transition:Transition, location:CLLocation?) {
if let userProfile = userProfile(){
notifyUser(forRegion: region, transition: transition, location: locationManager.location, userProfile: userProfile)
notifyServer(forRegion: region, transition: transition, location: locationManager.location, userProfile: userProfile)
}
}
func geoZone(by id: String) -> GeoZoneModel? {
var zone:GeoZoneModel? = nil
if let zones_ = geoZones{
zone = zones_.first(where: { $0.identifier() == id})
}else{
// let jsonArray = UserDefaults.standard.string(forKey: "hmg-geo-fences")
}
return zone
}
func notifyUser(forRegion:CLRegion, transition:Transition, location:CLLocation?, userProfile:[String:Any?]){
if let patientId = userProfile["PatientID"] as? Int{
}
}
func notifyServer(forRegion:CLRegion, transition:Transition, location:CLLocation?, userProfile:[String:Any?]){
if let patientId = userProfile["PatientID"] as? Int{
if let idString = forRegion.identifier.split(separator: "_").first, let idInt = Int(idString){
let body:[String:Any] = [
"PointsID":idInt,
"GeoType":transition.rawValue,
"PatientID":patientId
]
var logs = UserDefaults.init(suiteName: "GeoFenceLog")?.dictionary(forKey: "GEOFENCE_LOGS") ?? [:]
var geo = (logs[forRegion.identifier] as? [String]) ?? []
let url = "https://hmgwebservices.com/Services/Patients.svc/REST/GeoF_InsertPatientFileInfo"
httpPostRequest(urlString: url, jsonBody: body){ (status,json) in
let status_ = status ? "Notified successfully:" : "Failed to notify:"
showNotification(title: transition.name(), subtitle: forRegion.identifier, message: status_)
geo.append("\(status_) \(transition.name()) at \(Date().toString(format: "dd/MMM/yyy hh:mm:ss"))")
logs.updateValue( geo, forKey: forRegion.identifier)
UserDefaults.init(suiteName: "GeoFenceLog")?.set(logs, forKey: "GEOFENCE_LOGS")
}
}
}
}
}

@ -0,0 +1,22 @@
//
// LocalizedFromFlutter.swift
// Runner
//
// Created by ZiKambrani on 10/04/1442 AH.
//
import UIKit
class FlutterText{
class func with(key:String,completion: @escaping (String)->Void){
flutterMethodChannel?.invokeMethod("localizedValue", arguments: key, result: { (result) in
if let localized = result as? String{
completion(localized)
}else{
completion(key)
}
})
}
}

@ -0,0 +1,61 @@
//
// HMGPlatformBridge.swift
// Runner
//
// Created by ZiKambrani on 14/12/2020.
//
import UIKit
import NetworkExtension
import SystemConfiguration.CaptiveNetwork
fileprivate var openTok:OpenTok?
class OpenTokPlatformBridge : NSObject{
private var methodChannel:FlutterMethodChannel? = nil
private var mainViewController:MainFlutterVC!
private static var shared_:OpenTokPlatformBridge?
class func initialize(flutterViewController:MainFlutterVC, registrar:FlutterPluginRegistrar?){
shared_ = OpenTokPlatformBridge()
shared_?.mainViewController = flutterViewController
shared_?.openChannel()
openTok = OpenTok(mainViewController: flutterViewController, registrar: registrar)
}
func shared() -> OpenTokPlatformBridge{
assert((OpenTokPlatformBridge.shared_ != nil), "OpenTokPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.")
return OpenTokPlatformBridge.shared_!
}
private func openChannel(){
methodChannel = FlutterMethodChannel(name: "OpenTok-Platform-Bridge", binaryMessenger: mainViewController.binaryMessenger)
methodChannel?.setMethodCallHandler { (call, result) in
print("Called function \(call.method)")
switch(call.method) {
case "initSession":
openTok?.initSession(call: call, result: result)
case "swapCamera":
openTok?.swapCamera(call: call, result: result)
case "toggleAudio":
openTok?.toggleAudio(call: call, result: result)
case "toggleVideo":
openTok?.toggleVideo(call: call, result: result)
case "hangupCall":
openTok?.hangupCall(call: call, result: result)
default:
result(FlutterMethodNotImplemented)
}
print("")
}
}
}

@ -0,0 +1,77 @@
//
// PenguinModel.swift
// Runner
//
// Created by Amir on 06/08/2024.
//
import Foundation
// Define the model class
struct PenguinModel {
let baseURL: String
let dataURL: String
let dataServiceName: String
let positionURL: String
let clientKey: String
let storyboardName: String
let mapBoxKey: String
let clientID: String
let positionServiceName: String
let username: String
let isSimulationModeEnabled: Bool
let isShowUserName: Bool
let isUpdateUserLocationSmoothly: Bool
let isEnableReportIssue: Bool
let languageCode: String
let clinicID: String
let patientID: String
let projectID: Int
// Initialize the model from a dictionary
init?(from dictionary: [String: Any]) {
guard
let baseURL = dictionary["baseURL"] as? String,
let dataURL = dictionary["dataURL"] as? String,
let dataServiceName = dictionary["dataServiceName"] as? String,
let positionURL = dictionary["positionURL"] as? String,
let clientKey = dictionary["clientKey"] as? String,
let storyboardName = dictionary["storyboardName"] as? String,
let mapBoxKey = dictionary["mapBoxKey"] as? String,
let clientID = dictionary["clientID"] as? String,
let positionServiceName = dictionary["positionServiceName"] as? String,
let username = dictionary["username"] as? String,
let isSimulationModeEnabled = dictionary["isSimulationModeEnabled"] as? Bool,
let isShowUserName = dictionary["isShowUserName"] as? Bool,
let isUpdateUserLocationSmoothly = dictionary["isUpdateUserLocationSmoothly"] as? Bool,
let isEnableReportIssue = dictionary["isEnableReportIssue"] as? Bool,
let languageCode = dictionary["languageCode"] as? String,
let clinicID = dictionary["clinicID"] as? String,
let patientID = dictionary["patientID"] as? String,
let projectID = dictionary["projectID"] as? Int
else {
print("Initialization failed due to missing or invalid keys.")
return nil
}
self.baseURL = baseURL
self.dataURL = dataURL
self.dataServiceName = dataServiceName
self.positionURL = positionURL
self.clientKey = clientKey
self.storyboardName = storyboardName
self.mapBoxKey = mapBoxKey
self.clientID = clientID
self.positionServiceName = positionServiceName
self.username = username
self.isSimulationModeEnabled = isSimulationModeEnabled
self.isShowUserName = isShowUserName
self.isUpdateUserLocationSmoothly = isUpdateUserLocationSmoothly
self.isEnableReportIssue = isEnableReportIssue
self.languageCode = languageCode
self.clinicID = clinicID
self.patientID = patientID
self.projectID = projectID
}
}

@ -0,0 +1,57 @@
import PenNavUI
import UIKit
class PenguinNavigator {
private var config: PenguinModel
init(config: PenguinModel) {
self.config = config
}
private func logError(_ message: String) {
// Centralized logging function
print("PenguinSDKNavigator Error: \(message)")
}
func navigateToPOI( referenceId:String,completion: @escaping (Bool, String?) -> Void) {
PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey, showProgress: true) { [weak self] token, error in
if let error = error {
let errorMessage = "Token error while getting the for Navigate to method"
completion(false, "Failed to get token: \(errorMessage)")
print("Failed to get token: \(errorMessage)")
return
}
guard let token = token else {
completion(false, "Token is nil")
print("Token is nil")
return
}
print("Token Generated")
print(token);
}
}
private func handleNavigation(referenceId: String, token: String, completion: @escaping (Bool, String?) -> Void) {
DispatchQueue.main.async {
PenNavUIManager.shared.setToken(token: token)
PenNavUIManager.shared.navigate(to: referenceId) { [weak self] _, navError in
guard let self = self else { return }
if let navError = navError {
self.logError("Navigation error: Reference ID invalid")
completion(false, "Navigation error: \(navError.localizedDescription)")
return
}
// Navigation successful
completion(true, nil)
}
}
}
}

@ -0,0 +1,31 @@
//
// BlueGpsPlugin.swift
// Runner
//
// Created by Penguin .
//
//import Foundation
//import Flutter
//
///**
// * A Flutter plugin for integrating Penguin SDK functionality.
// * This class registers a view factory with the Flutter engine to create native views.
// */
//class PenguinPlugin: NSObject, FlutterPlugin {
//
// /**
// * Registers the plugin with the Flutter engine.
// *
// * @param registrar The [FlutterPluginRegistrar] used to register the plugin.
// * This method is called when the plugin is initialized, and it sets up the communication
// * between Flutter and native code.
// */
// public static func register(with registrar: FlutterPluginRegistrar) {
// // Create an instance of PenguinViewFactory with the binary messenger from the registrar
// let factory = PenguinViewFactory(messenger: registrar.messenger())
//
// // Register the view factory with a unique ID for use in Flutter code
// registrar.register(factory, withId: "penguin_native")
// }
//}

@ -0,0 +1,462 @@
//
// BlueGpsView.swift
// Runner
//
// Created by Penguin.
//
import Foundation
import UIKit
import Flutter
import PenNavUI
import PenguinINRenderer
import Foundation
import Flutter
import UIKit
/**
* A custom Flutter platform view for displaying Penguin UI components.
* This class integrates with the Penguin navigation SDK and handles UI events.
*/
class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitializationDelegate
{
// The main view displayed within the platform view
private var _view: UIView
private var model: PenguinModel?
private var methodChannel: FlutterMethodChannel
var onSuccess: (() -> Void)?
/**
* Initializes the PenguinView with the provided parameters.
*
* @param frame The frame of the view, specifying its size and position.
* @param viewId A unique identifier for this view instance.
* @param args Optional arguments provided for creating the view.
* @param messenger The [FlutterBinaryMessenger] used for communication with Dart.
*/
init(
frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?,
binaryMessenger messenger: FlutterBinaryMessenger?
) {
_view = UIView()
methodChannel = FlutterMethodChannel(name: "launch_penguin_ui", binaryMessenger: messenger!)
super.init()
// Get the screen's width and height to set the view's frame
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
// Uncomment to set the background color of the view
// _view.backgroundColor = UIColor.red
// Set the frame of the view to cover the entire screen
_view.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
print("========Inside Penguin View ========")
print(args)
guard let arguments = args as? [String: Any] else {
print("Error: Arguments are not in the expected format.")
return
}
print("===== i got tha Args=======")
// Initialize the model from the arguments
if let penguinModel = PenguinModel(from: arguments) {
self.model = penguinModel
initPenguin(args: penguinModel)
} else {
print("Error: Failed to initialize PenguinModel from arguments ")
}
// Initialize the Penguin SDK with required configurations
// initPenguin( arguments: args)
}
/**
* Initializes the Penguin SDK with custom configuration settings.
*/
func initPenguin(args: PenguinModel) {
// Set the initialization delegate to handle SDK initialization events
PenNavUIManager.shared.initializationDelegate = self
// Configure the Penguin SDK with necessary parameters
PIRendererSettings.styleUri = "mapbox://styles/rwaid/cm3h30b36007v01qz7ik8a0sk"
PenNavUIManager.shared
.setClientKey(args.clientKey)
.setClientID(args.clientID)
.setUsername(args.username)
.setSimulationModeEnabled(isEnable: args.isSimulationModeEnabled)
.setBaseURL(dataURL: args.dataURL, positionURL: args.positionURL)
.setServiceName(dataServiceName: args.dataServiceName, positionServiceName: args.positionServiceName)
.setIsShowUserName(args.isShowUserName)
.setIsUpdateUserLocationSmoothly(args.isUpdateUserLocationSmoothly)
.setEnableReportIssue(enable: args.isEnableReportIssue)
.setLanguage(args.languageCode)
.setBackButtonVisibility(visible: true)
.setCampusID(args.projectID)
.build()
}
/**
* Returns the main view associated with this platform view.
*
* @return The UIView instance that represents this platform view.
*/
func view() -> UIView {
return _view
}
// MARK: - PIEventsDelegate Methods
/**
* Called when the Penguin UI is dismissed.
*/
func onPenNavUIDismiss() {
// Handle UI dismissal if needed
print("====== onPenNavUIDismiss =========")
self.view().removeFromSuperview()
}
/**
* Called when a report issue is generated.
*
* @param issue The type of issue reported.
*/
func onReportIssue(_ issue: PenNavUI.IssueType) {
// Handle report issue events if needed
print("====== onReportIssueError =========")
methodChannel.invokeMethod("onReportIssue", arguments: ["issueType": issue])
}
/**
* Called when the Penguin UI setup is successful.
*/
// func onPenNavInitializationSuccess() {
// isInitilized = true
// if let referenceId = referenceId {
// navigator?.navigateToPOI(referenceId: referenceId){ [self] success, errorMessage in
//
// channel?.invokeMethod(PenguinMethod.navigateToPOI.rawValue, arguments: errorMessage)
//
// }
// }
//
// channel?.invokeMethod(PenguinMethod.onPenNavSuccess.rawValue, arguments: nil)
// }
func onPenNavInitializationSuccess() {
print("====== onPenNavSuccess =========")
onSuccess?()
methodChannel.invokeMethod("onPenNavSuccess", arguments: nil)
// Obtain the FlutterViewController instance
let controller: FlutterViewController = UIApplication.shared.windows.first?.rootViewController as! FlutterViewController
print("====== after controller onPenNavSuccess =========")
_view = UIView(frame: UIScreen.main.bounds)
_view.backgroundColor = .clear
controller.view.addSubview(_view)
// Set the events delegate to handle SDK events
PenNavUIManager.shared.eventsDelegate = self
print("====== after eventsDelegate onPenNavSuccess =========")
// Present the Penguin UI on top of the Flutter view controller
PenNavUIManager.shared.present(root: controller, view: _view)
print("====== after present onPenNavSuccess =========")
print(model?.clinicID)
print("====== after present onPenNavSuccess =========")
guard let config = self.model else {
print("Error: Config Model is nil")
return
}
guard let clinicID = self.model?.clinicID,
let clientID = self.model?.clientID, !clientID.isEmpty else {
print("Error: Config Client ID is nil or empty")
return
}
let navigator = PenguinNavigator(config: config)
PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey, showProgress: false) { [weak self] token, error in
if let error = error {
let errorMessage = "Token error while getting the for Navigate to method"
print("Failed to get token: \(errorMessage)")
return
}
guard let token = token else {
print("Token is nil")
return
}
print("Token Generated")
print(token);
self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in
if success {
print("Navigation successful")
} else {
print("Navigation failed: \(errorMessage ?? "Unknown error")")
}
}
print("====== after Token onPenNavSuccess =========")
}
}
private func handleNavigation(clinicID: String, token: String, completion: @escaping (Bool, String?) -> Void) {
DispatchQueue.main.async {
PenNavUIManager.shared.setToken(token: token)
PenNavUIManager.shared.navigate(to: clinicID)
completion(true,nil)
}
}
/**
* Called when there is an initialization error with the Penguin UI.
*
* @param errorType The type of initialization error.
* @param errorDescription A description of the error.
*/
func onPenNavInitializationError(errorType: PenNavUI.PenNavUIError, errorDescription: String) {
// Handle initialization errors if needed
print("onPenNavInitializationErrorType: \(errorType.rawValue)")
print("onPenNavInitializationError: \(errorDescription)")
}
}

@ -0,0 +1,59 @@
//
// BlueGpsViewFactory.swift
// Runner
//
// Created by Penguin .
//
import Foundation
import Flutter
/**
* A factory class for creating instances of [PenguinView].
* This class implements `FlutterPlatformViewFactory` to create and manage native views.
*/
class PenguinViewFactory: NSObject, FlutterPlatformViewFactory {
// The binary messenger used for communication with the Flutter engine
private var messenger: FlutterBinaryMessenger
/**
* Initializes the PenguinViewFactory with the given messenger.
*
* @param messenger The [FlutterBinaryMessenger] used to communicate with Dart code.
*/
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
/**
* Creates a new instance of [PenguinView].
*
* @param frame The frame of the view, specifying its size and position.
* @param viewId A unique identifier for this view instance.
* @param args Optional arguments provided for creating the view.
* @return An instance of [PenguinView] configured with the provided parameters.
*/
func create(
withFrame frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?
) -> FlutterPlatformView {
return PenguinView(
frame: frame,
viewIdentifier: viewId,
arguments: args,
binaryMessenger: messenger)
}
/**
* Returns the codec used for encoding and decoding method channel arguments.
* This method is required when `arguments` in `create` is not `nil`.
*
* @return A [FlutterMessageCodec] instance used for serialization.
*/
public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.in-app-payments</key>
<array>
<string>merchant.com.hmgwebservices</string>
<string>merchant.com.hmgwebservices.uat</string>
</array>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>
</dict>
</plist>

@ -19,7 +19,7 @@ abstract class ApiClient {
Future<void> post(
String endPoint, {
required Map<String, dynamic> body,
required dynamic body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny,
@ -27,6 +27,8 @@ abstract class ApiClient {
bool isRCService,
bool isPaymentServices,
bool bypassConnectionCheck,
Map<String, String> apiHeaders,
bool isBodyPlainText,
});
Future<void> get(
@ -89,7 +91,7 @@ class ApiClientImp implements ApiClient {
@override
post(
String endPoint, {
required Map<String, dynamic> body,
required dynamic body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = false,
@ -97,6 +99,8 @@ class ApiClientImp implements ApiClient {
bool isRCService = false,
bool isPaymentServices = false,
bool bypassConnectionCheck = true,
Map<String, String>? apiHeaders,
bool isBodyPlainText = false,
}) async {
String url;
if (isExternal) {
@ -110,80 +114,84 @@ class ApiClientImp implements ApiClient {
}
// try {
var user = _appState.getAuthenticatedUser();
Map<String, String> headers = {'Content-Type': 'application/json', 'Accept': 'application/json'};
if (!isExternal) {
String? token = _appState.appAuthToken;
Map<String, String> headers = apiHeaders ?? {'Content-Type': 'application/json', 'Accept': 'application/json'};
if (body.containsKey('SetupID')) {
body['SetupID'] = body.containsKey('SetupID') ? body['SetupID'] ?? body[''] : SETUP_ID;
} else {}
// When isBodyPlainText is true, skip all body manipulation and use body as-is
if (!isBodyPlainText) {
if (!isExternal) {
String? token = _appState.appAuthToken;
if (body.containsKey('isDentalAllowedBackend')) {
body['isDentalAllowedBackend'] =
body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
}
if (body.containsKey('SetupID')) {
body['SetupID'] = body.containsKey('SetupID') ? body['SetupID'] ?? body[''] : SETUP_ID;
} else {}
if (!body.containsKey('IsPublicRequest')) {
// if (!body.containsKey('PatientType')) {
if (user != null && user.patientType != null) {
body['PatientType'] = user.patientType;
} else {
body['PatientType'] = PATIENT_TYPE.toString();
if (body.containsKey('isDentalAllowedBackend')) {
body['isDentalAllowedBackend'] =
body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
}
if (user != null && user.patientType != null) {
body['PatientTypeID'] = user.patientType;
} else {
body['PatientType'] = PATIENT_TYPE_ID.toString();
}
if (!body.containsKey('IsPublicRequest')) {
// if (!body.containsKey('PatientType')) {
if (user != null && user.patientType != null) {
body['PatientType'] = user.patientType;
} else {
body['PatientType'] = PATIENT_TYPE.toString();
}
if (user != null && user.patientType != null) {
body['PatientTypeID'] = user.patientType;
} else {
body['PatientType'] = PATIENT_TYPE_ID.toString();
}
if (user != null) {
body['TokenID'] = body['TokenID'] ?? token;
if (user != null) {
body['TokenID'] = body['TokenID'] ?? token;
body['PatientID'] = body['PatientID'] ?? user.patientId;
body['PatientID'] = body['PatientID'] ?? user.patientId;
body['PatientOutSA'] = body.containsKey('PatientOutSA') ? body['PatientOutSA'] ?? user.outSa : user.outSa;
body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
body['PatientOutSA'] = body.containsKey('PatientOutSA') ? body['PatientOutSA'] ?? user.outSa : user.outSa;
body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
}
// else {
// body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
//
// }
}
// else {
// body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
//
// }
}
}
// request.versionID = VERSION_ID;
// request.channel = CHANNEL;
// request.iPAdress = IP_ADDRESS;
// request.generalid = GENERAL_ID;
// request.languageID = (languageID == 'ar' ? 1 : 2);
// request.patientOutSA = (request.zipCode == '966' || request.zipCode == '+966') ? 0 : 1;
// body['VersionID'] = ApiConsts.appVersionID.toString();
if (!isExternal) {
body['VersionID'] = ApiConsts.appVersionID.toString();
body['Channel'] = ApiConsts.appChannelId.toString();
body['IPAdress'] = ApiConsts.appIpAddress;
body['generalid'] = ApiConsts.appGeneralId;
body['LanguageID'] = _appState.getLanguageID().toString();
body['Latitude'] = _appState.userLat.toString();
body['Longitude'] = _appState.userLong.toString();
body['DeviceTypeID'] = _appState.deviceTypeID;
if (_appState.appAuthToken.isNotEmpty) {
body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken;
// request.versionID = VERSION_ID;
// request.channel = CHANNEL;
// request.iPAdress = IP_ADDRESS;
// request.generalid = GENERAL_ID;
// request.languageID = (languageID == 'ar' ? 1 : 2);
// request.patientOutSA = (request.zipCode == '966' || request.zipCode == '+966') ? 0 : 1;
// body['VersionID'] = ApiConsts.appVersionID.toString();
if (!isExternal) {
body['VersionID'] = ApiConsts.appVersionID.toString();
body['Channel'] = ApiConsts.appChannelId.toString();
body['IPAdress'] = ApiConsts.appIpAddress;
body['generalid'] = ApiConsts.appGeneralId;
body['LanguageID'] = _appState.getLanguageID().toString();
body['Latitude'] = _appState.userLat.toString();
body['Longitude'] = _appState.userLong.toString();
body['DeviceTypeID'] = _appState.deviceTypeID;
if (_appState.appAuthToken.isNotEmpty) {
body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken;
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 1018977;
// body['PatientTypeID'] = 1;
//
// body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q";
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 1018977;
// body['PatientTypeID'] = 1;
//
// body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q";
body.removeWhere((key, value) => value == null);
}
body.removeWhere((key, value) => value == null);
final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck);
if (!networkStatus) {
@ -196,12 +204,13 @@ class ApiClientImp implements ApiClient {
return;
}
final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers);
// Handle body encoding based on isBodyPlainText flag
final dynamic requestBody = isBodyPlainText ? body : json.encode(body);
final response = await http.post(Uri.parse(url.trim()), body: requestBody, headers: headers);
final int statusCode = response.statusCode;
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
// log("response.body: ${response.body}");
// log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) {
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);

@ -334,6 +334,8 @@ var GET_PATIENT_SHARE_FOR_WALKIN_APPOINTMENT = 'Services/Doctors.svc/REST/GetChe
var CAN_PAY_FOR_FOR_WALKIN_APPOINTMENT = 'Services/Doctors.svc/REST/CanPayForWalkinAppointment';
var GET_APPOINTMENT_NEAREST_GATE = 'Services/OUTPs.svc/REST/getGateByProjectIDandClinicID';
//URL to get medicine and pharmacies list
var CHANNEL = 3;
var GENERAL_ID = 'Cs2020@2016\$2958';
@ -437,14 +439,6 @@ var RATE_DOCTOR_RESPONSE = 'Services/OUTPs.svc/REST/insertAppointmentQuestionRat
var GET_PATIENT_ALLERGIES = 'Services/Patients.svc/REST/GetPatientAllergies';
// H2O
var H2O_GET_USER_PROGRESS = "Services/H2ORemainder.svc/REST/H2O_GetUserProgress";
var H2O_INSERT_USER_ACTIVITY = "Services/H2ORemainder.svc/REST/H2O_InsertUserActivity";
var H2O_GET_USER_DETAIL = "Services/H2ORemainder.svc/REST/H2O_GetUserDetails_New";
var H2O_UPDATE_USER_DETAIL = "Services/H2ORemainder.svc/REST/H2O_UpdateUserDetails_New";
var H2O_UNDO_USER_ACTIVITY = "Services/H2ORemainder.svc/REST/H2o_UndoUserActivity";
//E_Referral Services
// Encillary Orders
var GET_ANCILLARY_ORDERS = 'Services/Doctors.svc/REST/GetOnlineAncillaryOrderList';
@ -670,25 +664,6 @@ var addPayFortApplePayResponse = "Services/PayFort_Serv.svc/REST/AddResponse";
// Auth Provider Consts
const String INSERT_DEVICE_IMEI = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI';
const String SELECT_DEVICE_IMEI = 'Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI';
const String CHECK_PATIENT_AUTH = 'Services/Authentication.svc/REST/CheckPatientAuthentication';
const GET_MOBILE_INFO = 'Services/Authentication.svc/REST/GetMobileLoginInfo';
const FORGOT_PASSWORD = 'Services/Authentication.svc/REST/CheckActivationCodeForSendFileNo';
const CHECK_PATIENT_FOR_REGISTRATION = "Services/Authentication.svc/REST/CheckPatientForRegisteration";
const CHECK_USER_STATUS = "Services/NHIC.svc/REST/GetPatientInfo";
const REGISTER_USER = 'Services/Authentication.svc/REST/PatientRegistration';
const LOGGED_IN_USER_URL = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo';
const FORGOT_PATIENT_ID = 'Services/Authentication.svc/REST/SendPatientIDSMSByMobileNumber';
const DASHBOARD = 'Services/Patients.svc/REST/PatientDashboard';
const PROFILE_SETTING = 'Services/Patients.svc/REST/GetPateintInfoForUpdate';
const SAVE_SETTING = 'Services/Patients.svc/REST/UpdatePateintInfo';
const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_InsertUpdate';
var ER_CREATE_ADVANCE_PAYMENT = "services/Doctors.svc/REST/ER_CreateAdvancePaymentForClinic";
//family Files
@ -858,12 +833,11 @@ class ApiConsts {
// SYMPTOMS CHECKER
static final String getBodySymptomsByName = '$symptomsCheckerApi/GetBodySymptomsByName';
static final String getRiskFactors = '$symptomsCheckerApi/GetRiskFactors';
static final String getGeneralSuggestion = '$symptomsCheckerApi/GetGeneralSggestion';
static final String getSuggestions = '$symptomsCheckerApi/GetSuggestion';
static final String diagnosis = '$symptomsCheckerApi/diagnosis';
static final String explain = '$symptomsCheckerApi/explain';
//E-REFERRAL SERVICES
static final getAllRelationshipTypes = "Services/Patients.svc/REST/GetAllRelationshipTypes";
static final sendActivationCodeForEReferral = 'Services/Authentication.svc/REST/SendActivationCodeForEReferral';
static final checkActivationCodeForEReferral = 'Services/Authentication.svc/REST/CheckActivationCodeForEReferral';
@ -871,6 +845,14 @@ class ApiConsts {
static final createEReferral = "Services/Patients.svc/REST/CreateEReferral";
static final getEReferrals = "Services/Patients.svc/REST/GetEReferrals";
//WATER CONSUMPTION
static String h2oGetUserProgress = "Services/H2ORemainder.svc/REST/H2O_GetUserProgress";
static String h2oInsertUserActivity = "Services/H2ORemainder.svc/REST/H2O_InsertUserActivity";
static String h2oInsertUserDetailsNew = "Services/H2ORemainder.svc/REST/H2O_InsertUserDetails_New";
static String h2oGetUserDetail = "Services/H2ORemainder.svc/REST/H2O_GetUserDetails_New";
static String h2oUpdateUserDetail = "Services/H2ORemainder.svc/REST/H2O_UpdateUserDetails_New";
static String h2oUndoUserActivity = "Services/H2ORemainder.svc/REST/H2o_UndoUserActivity";
// ************ static values for Api ****************
static final double appVersionID = 50.3;
static final int appChannelId = 3;
@ -882,3 +864,34 @@ class ApiConsts {
class ApiKeyConstants {
static final String googleMapsApiKey = 'AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng';
}
//flutter: URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_InsertUserActivity
// flutter: {"IdentificationNo":"2530976584","MobileNumber":"504278212","QuantityIntake":200,"VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
// flutter: response.body:
// flutter: {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":true,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":[{"QuantityConsumed":200.00,"PercentageConsumed":9.41,"PercentageLeft":90.59,"QuantityLimit":2125.00}],"UserProgressForWeekData":null,"UserProgressHistoryData":[{"Quantity":200.000,"CreatedDate":"\/Date(1766911222217+0300)\/"}],"VerificationCode":null,"isSMSSent":false}
// URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2o_UndoUserActivity
// flutter: {"Progress":1,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
// flutter: response.body:
// flutter: {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":[{"QuantityConsumed":0.00,"PercentageConsumed":0.00,"PercentageLeft":100.00,"QuantityLimit":2125.00}],"UserProgressForWeekData":null,"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
// Progress":2 means weekly data
// flutter: URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_GetUserProgress
// flutter: {"Progress":2,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
// flutter: response.body:
// [log] {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":null,"UserProgressForWeekData":[{"DayNumber":1,"DayDate":null,"DayName":"Sunday","PercentageConsumed":0},{"DayNumber":7,"DayDate":null,"DayName":"Saturday","PercentageConsumed":0},{"DayNumber":6,"DayDate":null,"DayName":"Friday","PercentageConsumed":0},{"DayNumber":5,"DayDate":null,"DayName":"Thursday","PercentageConsumed":0},{"DayNumber":4,"DayDate":null,"DayName":"Wednesday","PercentageConsumed":0},{"DayNumber":3,"DayDate":null,"DayName":"Tuesday","PercentageConsumed":0},{"DayNumber":2,"DayDate":null,"DayName":"Monday","PercentageConsumed":0}],"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
// Progress":1 means daily data
//URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_GetUserProgress
// flutter: {"Progress":1,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
// flutter: response.body:
// flutter: {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":[{"QuantityConsumed":0.00,"PercentageConsumed":0.00,"PercentageLeft":100.00,"QuantityLimit":2125.00}],"UserProgressForWeekData":null,"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
// Progress":1 means monthly data
// flutter: URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_GetUserProgress
// flutter: {"Progress":3,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
// flutter: response.body:
// [log] {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":[{"MonthNumber":1,"MonthName":"January","PercentageConsumed":0},{"MonthNumber":2,"MonthName":"February","PercentageConsumed":0},{"MonthNumber":3,"MonthName":"March","PercentageConsumed":0},{"MonthNumber":4,"MonthName":"April","PercentageConsumed":0},{"MonthNumber":5,"MonthName":"May","PercentageConsumed":0},{"MonthNumber":6,"MonthName":"June","PercentageConsumed":0},{"MonthNumber":7,"MonthName":"July","PercentageConsumed":0},{"MonthNumber":8,"MonthName":"August","PercentageConsumed":0},{"MonthNumber":9,"MonthName":"September","PercentageConsumed":0},{"MonthNumber":10,"MonthName":"October","PercentageConsumed":0},{"MonthNumber":11,"MonthName":"November","PercentageConsumed":0},{"MonthNumber":12,"MonthName":"December","PercentageConsumed":0}],"UserProgressForTodayData":null,"UserProgressForWeekData":null,"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}

@ -246,7 +246,29 @@ class AppAssets {
static const String rotateIcon = '$svgBasePath/rotate_icon.svg';
static const String refreshIcon = '$svgBasePath/refresh.svg';
static const String homeBorderedIcon = '$svgBasePath/home_bordered.svg';
static const String symptomCheckerIcon = '$svgBasePath/symptom_checker_icon.svg';
static const String symptomCheckerBottomIcon = '$svgBasePath/symptom_bottom_icon.svg';
// Water Monitor
static const String waterBottle = '$svgBasePath/water_bottle.svg';
static const String cupAdd = '$svgBasePath/cup_add.svg';
static const String cupFilled = '$svgBasePath/cup_filled.svg';
static const String waterBottleOuterBubbles = '$svgBasePath/outer_bubbles.svg';
static const String cupEmpty = '$svgBasePath/cup_empty.svg';
static const String dumbellIcon = '$svgBasePath/dumbell_icon.svg';
static const String weightScaleIcon = '$svgBasePath/weight_scale_icon.svg';
static const String heightIcon = '$svgBasePath/height_icon.svg';
static const String profileIcon = '$svgBasePath/profile_icon.svg';
static const String notificationIconGrey = '$svgBasePath/notification_icon_grey.svg';
static const String minimizeIcon = '$svgBasePath/minimize_icon.svg';
static const String addIconDark = '$svgBasePath/add_icon_dark.svg';
static const String glassIcon = '$svgBasePath/glass_icon.svg';
static const String graphIcon = '$svgBasePath/graph_icon.svg';
static const String listIcon = '$svgBasePath/list_icon.svg';
static const String yellowArrowDownIcon = '$svgBasePath/yellow_arrow_down_icon.svg';
static const String greenTickIcon = '$svgBasePath/green_tick_icon.svg';
// PNGS
static const String bloodSugar = '$svgBasePath/bloodsugar.svg';
@ -264,6 +286,19 @@ class AppAssets {
static const String covid19icon = '$svgBasePath/covid_19.svg';
//vital sign
static const String heartRate = '$svgBasePath/heart_rate.svg';
static const String respRate = '$svgBasePath/resp_rate.svg';
static const String weightVital = '$svgBasePath/weight_2.svg';
static const String bmiVital = '$svgBasePath/bmi_2.svg';
static const String heightVital = '$svgBasePath/height_2.svg';
static const String bloodPressure = '$svgBasePath/blood_pressure.svg';
static const String temperature = '$svgBasePath/temperature.svg';
// PNGS //
static const String hmgLogo = '$pngBasePath/hmg_logo.png';
static const String liveCareService = '$pngBasePath/livecare_service.png';
@ -289,7 +324,7 @@ class AppAssets {
static const String fullBodyFront = '$pngBasePath/full_body_front.png';
static const String fullBodyBack = '$pngBasePath/full_body_back.png';
static const String bmiFullBody = '$pngBasePath/bmi_image_1.png';
}

@ -63,6 +63,7 @@ class CacheConst {
static const String pharmacyAutorzieToken = 'PHARMACY_AUTORZIE_TOKEN';
static const String h2oUnit = 'H2O_UNIT';
static const String h2oReminder = 'H2O_REMINDER';
static const String waterReminderEnabled = 'WATER_REMINDER_ENABLED';
static const String livecareClinicData = 'LIVECARE_CLINIC_DATA';
static const String doctorScheduleDateSel = 'DOCTOR_SCHEDULE_DATE_SEL';
static const String appointmentHistoryMedical = 'APPOINTMENT_HISTORY_MEDICAL';
@ -74,6 +75,7 @@ class CacheConst {
static const String patientOccupationList = 'patient-occupation-list';
static const String hasEnabledQuickLogin = 'has-enabled-quick-login';
static const String quickLoginEnabled = 'quick-login-enabled';
static const String isMonthlyReportEnabled = 'is-monthly-report-enabled';
static const String zoomRoomID = 'zoom-room-id';
static String isAppOpenedFromCall = "is_app_opened_from_call";

@ -1,26 +1,26 @@
///class used to provide value for the [DynamicResultChart] to plot the values
class DataPoint {
///values that is displayed on the graph and dot is plotted on this
final double value;
///label shown on the bottom of the graph
String label;
String referenceValue;
String actualValue;
String? unitOfMeasurement ;
String? unitOfMeasurement;
DateTime time;
String displayTime;
DataPoint(
{required this.value,
required this.label,
required this.referenceValue,
required this.actualValue,
required this.time,
required this.displayTime,
this.unitOfMeasurement
});
DataPoint({
required this.value,
required this.label,
required this.actualValue,
required this.time,
required this.displayTime,
this.unitOfMeasurement,
this.referenceValue = '',
});
@override
String toString() {

@ -1,4 +1,5 @@
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
@ -31,11 +32,14 @@ import 'package:hmg_patient_app_new/features/location/location_repo.dart';
import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/monthly_reports/monthly_reports_repo.dart';
import 'package:hmg_patient_app_new/features/monthly_report/monthly_report_repo.dart';
import 'package:hmg_patient_app_new/features/monthly_report/monthly_report_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_rating_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_repo.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart';
@ -49,7 +53,8 @@ import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_r
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_repo.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
// import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_repo.dart';
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart';
import 'package:hmg_patient_app_new/presentation/monthly_reports/monthly_reports_page.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
@ -59,6 +64,7 @@ import 'package:hmg_patient_app_new/services/firebase_service.dart';
import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/services/notification_service.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:local_auth/local_auth.dart';
import 'package:logger/web.dart';
@ -112,6 +118,13 @@ class AppDependencies {
final sharedPreferences = await SharedPreferences.getInstance();
getIt.registerLazySingleton<CacheService>(() => CacheServiceImp(sharedPreferences: sharedPreferences, loggerService: getIt()));
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
getIt.registerLazySingleton<NotificationService>(() => NotificationServiceImp(
flutterLocalNotificationsPlugin: flutterLocalNotificationsPlugin,
loggerService: getIt(),
));
getIt.registerLazySingleton<ApiClient>(() => ApiClientImp(appState: getIt()));
getIt.registerLazySingleton<LocalAuthService>(
() => LocalAuthService(loggerService: getIt<LoggerService>(), localAuth: getIt<LocalAuthentication>()),
@ -137,6 +150,9 @@ class AppDependencies {
getIt.registerLazySingleton<HmgServicesRepo>(() => HmgServicesRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<SymptomsCheckerRepo>(() => SymptomsCheckerRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<BloodDonationRepo>(() => BloodDonationRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<WaterMonitorRepo>(() => WaterMonitorRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MyInvoicesRepo>(() => MyInvoicesRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MonthlyReportRepo>(() => MonthlyReportRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ActivePrescriptionsRepo>(() => ActivePrescriptionsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<TermsConditionsRepo>(() => TermsConditionsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerFactory<TermsConditionsViewModel>(() => TermsConditionsViewModel(termsConditionsRepo: getIt<TermsConditionsRepo>(), errorHandlerService: getIt<ErrorHandlerService>(),
@ -151,7 +167,6 @@ class AppDependencies {
),
);
// ViewModels
// Global/shared VMs LazySingleton
@ -161,25 +176,25 @@ class AppDependencies {
() => RadiologyViewModel(radiologyRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
);
getIt.registerLazySingleton<PrescriptionsViewModel>(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton<PrescriptionsViewModel>(
() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton<InsuranceViewModel>(() => InsuranceViewModel(insuranceRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<MyAppointmentsViewModel>(() => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
getIt.registerLazySingleton<MyAppointmentsViewModel>(
() => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
getIt.registerLazySingleton<AppointmentRatingViewModel>(() => AppointmentRatingViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
getIt.registerLazySingleton<AppointmentRatingViewModel>(
() => AppointmentRatingViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
getIt.registerLazySingleton<PayfortViewModel>(
() => PayfortViewModel(
payfortRepo: getIt(),
errorHandlerService: getIt(),
),
() => PayfortViewModel(payfortRepo: getIt(), errorHandlerService: getIt()),
);
getIt.registerLazySingleton<HabibWalletViewModel>(
() => HabibWalletViewModel(
habibWalletRepo: getIt(),
errorHandlerService: getIt(),
errorHandlerService: getIt()
),
);
@ -192,7 +207,12 @@ class AppDependencies {
getIt.registerLazySingleton<BookAppointmentsViewModel>(
() => BookAppointmentsViewModel(
bookAppointmentsRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), myAppointmentsViewModel: getIt(), locationUtils: getIt(), dialogService: getIt()),
bookAppointmentsRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt(),
myAppointmentsViewModel: getIt(),
locationUtils: getIt(),
dialogService: getIt()),
);
getIt.registerLazySingleton<ImmediateLiveCareViewModel>(
@ -206,8 +226,15 @@ class AppDependencies {
getIt.registerLazySingleton<AuthenticationViewModel>(
() => AuthenticationViewModel(
authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()),
authenticationRepo: getIt(),
cacheService: getIt(),
navigationService: getIt(),
dialogService: getIt(),
appState: getIt(),
errorHandlerService: getIt(),
localAuthService: getIt()),
);
getIt.registerLazySingleton<ProfileSettingsViewModel>(() => ProfileSettingsViewModel());
getIt.registerLazySingleton<DateRangeSelectorRangeViewModel>(() => DateRangeSelectorRangeViewModel());
@ -220,7 +247,14 @@ class AppDependencies {
getIt.registerLazySingleton<EmergencyServicesViewModel>(
() => EmergencyServicesViewModel(
locationUtils: getIt(), navServices: getIt(), emergencyServicesRepo: getIt(), appState: getIt(), errorHandlerService: getIt(), appointmentRepo: getIt(), dialogService: getIt()),
locationUtils: getIt(),
navServices: getIt(),
emergencyServicesRepo: getIt(),
appState: getIt(),
errorHandlerService: getIt(),
appointmentRepo: getIt(),
dialogService: getIt(),
),
);
getIt.registerLazySingleton<LocationViewModel>(
@ -233,45 +267,55 @@ class AppDependencies {
getIt.registerLazySingleton<HealthCalcualtorViewModel>(() => HealthCalcualtorViewModel());
getIt.registerLazySingleton<TodoSectionViewModel>(
() => TodoSectionViewModel(todoSectionRepo: getIt(), errorHandlerService: getIt()),
getIt.registerLazySingleton<TodoSectionViewModel>(() => TodoSectionViewModel(todoSectionRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<SymptomsCheckerViewModel>(
() => SymptomsCheckerViewModel(
errorHandlerService: getIt(),
symptomsCheckerRepo: getIt(),
appState: getIt(),
),
);
getIt.registerLazySingleton<SymptomsCheckerViewModel>(() => SymptomsCheckerViewModel(errorHandlerService: getIt(), symptomsCheckerRepo: getIt()));
getIt.registerLazySingleton<HmgServicesViewModel>(
() => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
() => HmgServicesViewModel(
bookAppointmentsRepo: getIt(),
hmgServicesRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt(),
),
);
getIt.registerLazySingleton<BloodDonationViewModel>(
() => BloodDonationViewModel(bloodDonationRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt()),
() => BloodDonationViewModel(
bloodDonationRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt(),
dialogService: getIt(),
appState: getIt(),
),
);
getIt.registerLazySingleton<HealthProvider>(
() => HealthProvider(),
);
getIt.registerLazySingleton<HealthProvider>(() => HealthProvider());
getIt.registerLazySingleton<WaterMonitorViewModel>(() => WaterMonitorViewModel(waterMonitorRepo: getIt()));
getIt.registerLazySingleton<MyInvoicesViewModel>(() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton<MonthlyReportViewModel>(() => MonthlyReportViewModel(errorHandlerService: getIt(), monthlyReportRepo: getIt()));
getIt.registerLazySingleton<ActivePrescriptionsViewModel>(
() => ActivePrescriptionsViewModel(
errorHandlerService: getIt(),
activePrescriptionsRepo: getIt()
errorHandlerService: getIt(),
activePrescriptionsRepo: getIt()
),
);
getIt.registerFactory<QrParkingViewModel>(
() => QrParkingViewModel(
qrParkingRepo: getIt<QrParkingRepo>(),
errorHandlerService: getIt<ErrorHandlerService>(),
cacheService: getIt<CacheService>(),
cacheService: getIt<CacheService>(),
),
);
// Screen-specific VMs Factory
// getIt.registerFactory<BookAppointmentsViewModel>(
// () => BookAppointmentsViewModel(
// bookAppointmentsRepo: getIt(),
// dialogService: getIt(),
// errorHandlerService: getIt(),
// ),
// );
}
}

@ -12,8 +12,9 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:huawei_location/huawei_location.dart' as HmsLocation show FusedLocationProviderClient, Location, LocationSettingsRequest, LocationRequest;
import 'package:location/location.dart' show Location, PermissionStatus, LocationData;
import 'package:huawei_location/huawei_location.dart' as HmsLocation
show FusedLocationProviderClient, Location, LocationSettingsRequest, LocationRequest;
import 'package:location/location.dart' show Location;
import 'package:permission_handler/permission_handler.dart' show Permission, PermissionListActions, PermissionStatusGetters, openAppSettings;
class LocationUtils {
@ -59,37 +60,22 @@ class LocationUtils {
// }
void getLocation(
{Function(LatLng)? onSuccess,
VoidCallback? onFailure,
bool isShowConfirmDialog = false,
VoidCallback? onLocationDeniedForever}) async {
{Function(LatLng)? onSuccess, VoidCallback? onFailure, bool isShowConfirmDialog = false, VoidCallback? onLocationDeniedForever}) async {
this.isShowConfirmDialog = isShowConfirmDialog;
if (Platform.isIOS) {
getCurrentLocation(
onFailure: onFailure,
onSuccess: onSuccess,
onLocationDeniedForever: onLocationDeniedForever);
getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess, onLocationDeniedForever: onLocationDeniedForever);
return;
}
if (await isGMSDevice ?? true) {
getCurrentLocation(
onFailure: onFailure,
onSuccess: onSuccess,
onLocationDeniedForever: onLocationDeniedForever);
getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess, onLocationDeniedForever: onLocationDeniedForever);
return;
}
getHMSLocation(
onFailure: onFailure,
onSuccess: onSuccess,
onLocationDeniedForever: onLocationDeniedForever);
getHMSLocation(onFailure: onFailure, onSuccess: onSuccess, onLocationDeniedForever: onLocationDeniedForever);
}
void getCurrentLocation(
{Function(LatLng)? onSuccess,
VoidCallback? onFailure,
VoidCallback? onLocationDeniedForever}) async {
void getCurrentLocation({Function(LatLng)? onSuccess, VoidCallback? onFailure, VoidCallback? onLocationDeniedForever}) async {
var location = Location();
bool isLocationEnabled = await location.serviceEnabled();
@ -113,14 +99,12 @@ class LocationUtils {
}
} else if (permissionGranted == LocationPermission.deniedForever) {
appState.resetLocation();
if(onLocationDeniedForever == null && isShowConfirmDialog){
if (onLocationDeniedForever == null && isShowConfirmDialog) {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
loadingText:
"Please grant location permission from app settings to see better results"
.needTranslation,
loadingText: "Please grant location permission from app settings to see better results".needTranslation,
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
@ -253,10 +237,7 @@ class LocationUtils {
appState.userLong = locationData.longitude;
}
void getHMSLocation(
{VoidCallback? onFailure,
Function(LatLng p1)? onSuccess,
VoidCallback? onLocationDeniedForever}) async {
void getHMSLocation({VoidCallback? onFailure, Function(LatLng p1)? onSuccess, VoidCallback? onLocationDeniedForever}) async {
try {
var location = Location();
HmsLocation.FusedLocationProviderClient locationService = HmsLocation.FusedLocationProviderClient()..initFusedLocationService();
@ -279,14 +260,12 @@ class LocationUtils {
permissionGranted = await Geolocator.requestPermission();
if (permissionGranted == LocationPermission.deniedForever) {
appState.resetLocation();
if(onLocationDeniedForever == null && isShowConfirmDialog){
if (onLocationDeniedForever == null && isShowConfirmDialog) {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
loadingText:
"Please grant location permission from app settings to see better results"
.needTranslation,
loadingText: "Please grant location permission from app settings to see better results".needTranslation,
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
@ -311,7 +290,7 @@ class LocationUtils {
HmsLocation.Location data = await locationService.getLastLocation();
if (data.latitude == null || data.longitude == null) {
appState.resetLocation();
appState.resetLocation();
HmsLocation.LocationRequest request = HmsLocation.LocationRequest()
..priority = HmsLocation.LocationRequest.PRIORITY_HIGH_ACCURACY
..interval = 1000 // 1 second

@ -14,19 +14,20 @@ class PostParamsModel {
String? sessionID;
String? setupID;
PostParamsModel(
{this.versionID,
this.channel,
this.languageID,
this.logInTokenID,
this.tokenID,
this.language,
this.ipAddress,
this.generalId,
this.latitude,
this.longitude,
this.deviceTypeID,
this.sessionID});
PostParamsModel({
this.versionID,
this.channel,
this.languageID,
this.logInTokenID,
this.tokenID,
this.language,
this.ipAddress,
this.generalId,
this.latitude,
this.longitude,
this.deviceTypeID,
this.sessionID,
});
PostParamsModel.fromJson(Map<String, dynamic> json) {
versionID = json['VersionID'];

@ -6,8 +6,6 @@ class DateUtil {
/// convert String To Date function
/// [date] String we want to convert
static DateTime convertStringToDate(String? date) {
if (date == null) return DateTime.now();
if (date.isEmpty) return DateTime.now();
@ -522,6 +520,64 @@ class DateUtil {
}
return "";
}
/// Get short month name from full month name
/// [monthName] Full month name like "January"
/// Returns short form like "Jan"
static String getShortMonthName(String monthName) {
switch (monthName.toLowerCase()) {
case 'january':
return 'Jan';
case 'february':
return 'Feb';
case 'march':
return 'Mar';
case 'april':
return 'Apr';
case 'may':
return 'May';
case 'june':
return 'Jun';
case 'july':
return 'Jul';
case 'august':
return 'Aug';
case 'september':
return 'Sep';
case 'october':
return 'Oct';
case 'november':
return 'Nov';
case 'december':
return 'Dec';
default:
return monthName; // Return as-is if not recognized
}
}
/// Get short weekday name from full weekday name
/// [weekDayName] Full weekday name like "Monday"
/// Returns short form like "Mon"
static String getShortWeekDayName(String weekDayName) {
switch (weekDayName.toLowerCase().trim()) {
case 'monday':
return 'Mon';
case 'tuesday':
return 'Tue';
case 'wednesday':
return 'Wed';
case 'thursday':
return 'Thu';
case 'friday':
return 'Fri';
case 'saturday':
return 'Sat';
case 'sunday':
return 'Sun';
default:
return weekDayName; // Return as-is if not recognized
}
}
}
extension OnlyDate on DateTime {

@ -1,191 +0,0 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
class LocalNotification {
Function(String payload)? _onNotificationClick;
static LocalNotification? _instance;
static LocalNotification? getInstance() {
return _instance;
}
static init({required Function(String payload) onNotificationClick}) {
if (_instance == null) {
_instance = LocalNotification();
_instance?._onNotificationClick = onNotificationClick;
_instance?._initialize();
} else {
// assert(false,(){
// //TODO fix it
// "LocalNotification Already Initialized";
// });
}
}
_initialize() async {
try {
var initializationSettingsAndroid = new AndroidInitializationSettings('app_icon');
var initializationSettingsIOS = DarwinInitializationSettings();
var initializationSettings = InitializationSettings(android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) {
switch (notificationResponse.notificationResponseType) {
case NotificationResponseType.selectedNotification:
// selectNotificationStream.add(notificationResponse.payload);
break;
case NotificationResponseType.selectedNotificationAction:
// if (notificationResponse.actionId == navigationActionId) {
// selectNotificationStream.add(notificationResponse.payload);
// }
break;
}
},
// onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
);
} catch (ex) {
print(ex.toString());
}
// flutterLocalNotificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse notificationResponse)
// {
// switch (notificationResponse.notificationResponseType) {
// case NotificationResponseType.selectedNotification:
// // selectNotificationStream.add(notificationResponse.payload);
// break;
// case NotificationResponseType.selectedNotificationAction:
// // if (notificationResponse.actionId == navigationActionId) {
// // selectNotificationStream.add(notificationResponse.payload);
// }
// // break;
// },}
//
// ,
//
// );
}
// void notificationTapBackground(NotificationResponse notificationResponse) {
// // ignore: avoid_print
// print('notification(${notificationResponse.id}) action tapped: '
// '${notificationResponse.actionId} with'
// ' payload: ${notificationResponse.payload}');
// if (notificationResponse.input?.isNotEmpty ?? false) {
// // ignore: avoid_print
// print('notification action tapped with input: ${notificationResponse.input}');
// }
// }
var _random = new Random();
_randomNumber({int from = 100000}) {
return _random.nextInt(from);
}
_vibrationPattern() {
var vibrationPattern = Int64List(4);
vibrationPattern[0] = 0;
vibrationPattern[1] = 1000;
vibrationPattern[2] = 5000;
vibrationPattern[3] = 2000;
return vibrationPattern;
}
Future? showNow({required String title, required String subtitle, required String payload}) {
Future.delayed(Duration(seconds: 1)).then((result) async {
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'com.hmg.local_notification',
'HMG',
channelDescription: 'HMG',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
vibrationPattern: _vibrationPattern(),
ongoing: true,
autoCancel: false,
usesChronometer: true,
when: DateTime.now().millisecondsSinceEpoch - 120 * 1000,
);
var iOSPlatformChannelSpecifics = DarwinNotificationDetails();
var platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(25613, title, subtitle, platformChannelSpecifics, payload: payload).catchError((err) {
print(err);
});
});
}
Future scheduleNotification({required DateTime scheduledNotificationDateTime, required String title, required String description}) async {
///vibrationPattern
var vibrationPattern = Int64List(4);
vibrationPattern[0] = 0;
vibrationPattern[1] = 1000;
vibrationPattern[2] = 5000;
vibrationPattern[3] = 2000;
// var androidPlatformChannelSpecifics = AndroidNotificationDetails('active-prescriptions', 'ActivePrescriptions',
// channelDescription: 'ActivePrescriptionsDescription',
// // icon: 'secondary_icon',
// sound: RawResourceAndroidNotificationSound('slow_spring_board'),
//
// ///change it to be as ionic
// // largeIcon: DrawableResourceAndroidBitmap('sample_large_icon'),///change it to be as ionic
// vibrationPattern: vibrationPattern,
// enableLights: true,
// color: const Color.fromARGB(255, 255, 0, 0),
// ledColor: const Color.fromARGB(255, 255, 0, 0),
// ledOnMs: 1000,
// ledOffMs: 500);
// var iOSPlatformChannelSpecifics = DarwinNotificationDetails(sound: 'slow_spring_board.aiff');
// /change it to be as ionic
// var platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics);
// await flutterLocalNotificationsPlugin.schedule(0, title, description, scheduledNotificationDateTime, platformChannelSpecifics);
}
///Repeat notification every day at approximately 10:00:00 am
Future showDailyAtTime() async {
// var time = Time(10, 0, 0);
// var androidPlatformChannelSpecifics = AndroidNotificationDetails('repeatDailyAtTime channel id', 'repeatDailyAtTime channel name', channelDescription: 'repeatDailyAtTime description');
// var iOSPlatformChannelSpecifics = DarwinNotificationDetails();
// var platformChannelSpecifics = NotificationDetails(
// androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
// await flutterLocalNotificationsPlugin.showDailyAtTime(
// 0,
// 'show daily title',
// 'Daily notification shown at approximately ${_toTwoDigitString(time.hour)}:${_toTwoDigitString(time.minute)}:${_toTwoDigitString(time.second)}',
// time,
// platformChannelSpecifics);
}
///Repeat notification weekly on Monday at approximately 10:00:00 am
Future showWeeklyAtDayAndTime() async {
// var time = Time(10, 0, 0);
// var androidPlatformChannelSpecifics = AndroidNotificationDetails('show weekly channel id', 'show weekly channel name', channelDescription: 'show weekly description');
// var iOSPlatformChannelSpecifics = DarwinNotificationDetails();
// var platformChannelSpecifics = NotificationDetails(
// androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
// await flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime(
// 0,
// 'show weekly title',
// 'Weekly notification shown on Monday at approximately ${_toTwoDigitString(time.hour)}:${_toTwoDigitString(time.minute)}:${_toTwoDigitString(time.second)}',
// Day.Monday,
// time,
// platformChannelSpecifics);
}
String _toTwoDigitString(int value) {
return value.toString().padLeft(2, '0');
}
Future cancelNotification() async {
await flutterLocalNotificationsPlugin.cancel(0);
}
Future cancelAllNotifications() async {
await flutterLocalNotificationsPlugin.cancelAll();
}
}

@ -0,0 +1,105 @@
import 'package:flutter/services.dart';
class PenguinMethodChannel {
static const MethodChannel _channel = MethodChannel('launch_penguin_ui');
Future<Uint8List> loadGif() async {
return await rootBundle.load("assets/images/progress-loading-red-crop-1.gif").then((data) => data.buffer.asUint8List());
}
Future<void> launch(String storyboardName, String languageCode, String username, {NavigationClinicDetails? details}) async {
// Uint8List image = await loadGif();
try {
await _channel.invokeMethod('launchPenguin', {
"storyboardName": storyboardName,
"baseURL": "https://penguinuat.hmg.com",
// "dataURL": "https://hmg.nav.penguinin.com",
// "positionURL": "https://hmg.nav.penguinin.com",
// "dataURL": "https://hmg-v33.local.penguinin.com",
// "positionURL": "https://hmg-v33.local.penguinin.com",
"dataURL": "https://penguinuat.hmg.com",
"positionURL": "https://penguinuat.hmg.com",
"dataServiceName": "api",
"positionServiceName": "pe",
"clientID": "HMG",
"clientKey": "UGVuZ3VpbklOX1Blbk5hdl9QSUY=",
"username": details?.patientId ?? "Haroon",
// "username": "Haroon",
"isSimulationModeEnabled": false,
"isShowUserName": false,
"isUpdateUserLocationSmoothly": true,
"isEnableReportIssue": true,
"languageCode": languageCode,
"mapBoxKey": "pk.eyJ1IjoicndhaWQiLCJhIjoiY2x5cGo4aHNjMGNsbTJyc2djempobGQxaSJ9.RCaC6WrUt4A4YnZNfxnONQ",
"clinicID": details?.clinicId ?? "",
// "clinicID": "108", // 46 ,49, 133
"patientID": details?.patientId ?? "",
"projectID": int.parse(details?.projectId ?? "-1"),
// "loaderImage": image,
});
} on PlatformException catch (e) {
print("Failed to launch PenguinIn: '${e.message}'.");
}
}
void setMethodCallHandler(){
_channel.setMethodCallHandler((MethodCall call) async {
try {
print(call.method);
switch (call.method) {
case PenguinMethodNames.onPenNavInitializationError:
_handleInitializationError(call.arguments); // Handle onPenNavInitializationError errors.
break;
case PenguinMethodNames.onPenNavUIDismiss:
//todo handle pen dismissable
// _handlePenNavUIDismiss(); // Handle UI dismissal event.
break;
case PenguinMethodNames.onReportIssue:
// Handle the report issue event.
_handleInitializationError(call.arguments);
break;
default:
_handleUnknownMethod(call.method); // Handle unknown method calls.
}
} catch (e) {
print("Error handling method call '${call.method}': $e");
// Optionally, log this error to an external service
}
});
}
static void _handleUnknownMethod(String method) {
print("Unknown method: $method");
// Optionally, handle this unknown method case, such as reporting or ignoring it
}
static void _handleInitializationError(Map<dynamic, dynamic> error) {
final type = error['type'] as String?;
final description = error['description'] as String?;
print("Initialization Error: ${type ?? 'Unknown Type'}, ${description ?? 'No Description'}");
}
}
// Define constants for method names
class PenguinMethodNames {
static const String showPenguinUI = 'showPenguinUI';
static const String openSharedLocation = 'openSharedLocation';
// ---- Handler Method
static const String onPenNavSuccess = 'onPenNavSuccess'; // Tested Android,iOS
static const String onPenNavInitializationError = 'onPenNavInitializationError'; // Tested Android,iOS
static const String onPenNavUIDismiss = 'onPenNavUIDismiss'; //Tested Android,iOS
static const String onReportIssue = 'onReportIssue'; // Tested Android,iOS
static const String onLocationOffCampus = 'onLocationOffCampus'; // Tested iOS,Android
static const String navigateToPOI = 'navigateToPOI'; // Tested Android,iOS
}
class NavigationClinicDetails {
String? clinicId;
String? patientId;
String? projectId;
}

@ -15,16 +15,11 @@ import 'package:flutter_callkit_incoming/entities/notification_params.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_ios_voip_kit_karmm/call_state_type.dart';
import 'package:flutter_ios_voip_kit_karmm/flutter_ios_voip_kit.dart';
// import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:hmg_patient_app_new/core/utils/local_notifications.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:uuid/uuid.dart';
import '../cache_consts.dart';
// |--> Push Notification Background
@pragma('vm:entry-point')
Future<dynamic> backgroundMessageHandler(dynamic message) async {
@ -36,7 +31,7 @@ Future<dynamic> backgroundMessageHandler(dynamic message) async {
// showCallkitIncoming(message);
_incomingCall(message.data);
return;
} else {}
}
}
callPage(String sessionID, String token) async {}
@ -323,7 +318,7 @@ class PushNotificationHandler {
if (fcmToken != null) onToken(fcmToken);
// }
} catch (ex) {
print("Notification Exception: " + ex.toString());
print("Notification Exception: $ex");
}
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
}
@ -331,7 +326,7 @@ class PushNotificationHandler {
if (Platform.isIOS) {
final permission = await FirebaseMessaging.instance.requestPermission();
await FirebaseMessaging.instance.getAPNSToken().then((value) async {
log("APNS token: " + value.toString());
log("APNS token: $value");
await Utils.saveStringFromPrefs(CacheConst.apnsToken, value.toString());
});
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
@ -378,14 +373,14 @@ class PushNotificationHandler {
});
FirebaseMessaging.instance.getToken().then((String? token) {
print("Push Notification getToken: " + token!);
print("Push Notification getToken: ${token!}");
onToken(token!);
}).catchError((err) {
print(err);
});
FirebaseMessaging.instance.onTokenRefresh.listen((fcm_token) {
print("Push Notification onTokenRefresh: " + fcm_token);
print("Push Notification onTokenRefresh: $fcm_token");
onToken(fcm_token);
});
@ -401,7 +396,7 @@ class PushNotificationHandler {
}
newMessage(RemoteMessage remoteMessage) async {
print("Remote Message: " + remoteMessage.data.toString());
print("Remote Message: ${remoteMessage.data}");
if (remoteMessage.data.isEmpty) {
return;
}
@ -427,7 +422,7 @@ class PushNotificationHandler {
}
onToken(String token) async {
print("Push Notification Token: " + token);
print("Push Notification Token: $token");
await Utils.saveStringFromPrefs(CacheConst.pushToken, token);
}
@ -441,9 +436,7 @@ class PushNotificationHandler {
Future<void> requestPermissions() async {
try {
if (Platform.isIOS) {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(alert: true, badge: true, sound: true);
await FirebaseMessaging.instance.requestPermission(alert: true, badge: true, sound: true);
} else if (Platform.isAndroid) {
Map<Permission, PermissionStatus> statuses = await [
Permission.notification,

@ -1,4 +1,5 @@
import 'dart:developer';
import 'dart:math' as math;
import 'package:flutter/material.dart'; // These are the Viewport values of your Figma Design.
@ -6,6 +7,16 @@ import 'package:flutter/material.dart'; // These are the Viewport values of your
const num figmaDesignWidth = 375; // iPhone X / 12 base width
const num figmaDesignHeight = 812; // iPhone X / 12 base height
extension ConstrainedResponsive on num {
/// Width with max cap for tablets
double get wCapped => isTablet ? math.min( w, this * 1.3) : w;
/// Height with max cap for tablets
double get hCapped => isTablet ? math.min(h, this * 1.3) : h;
}
extension ResponsiveExtension on num {
double get _screenWidth => SizeUtils.width;

@ -39,6 +39,50 @@ class Utils {
static bool get isLoading => _isLoadingVisible;
static var navigationProjectsList = [
{
"Desciption": "Sahafa Hospital",
"DesciptionN": "مستشفى الصحافة",
"ID": 1,
"LegalName": "Sahafa Hospital",
"LegalNameN": "مستشفى الصحافة",
"Name": "Sahafa Hospital",
"NameN": "مستشفى الصحافة",
"PhoneNumber": "+966115222222",
"SetupID": "013311",
"DistanceInKilometers": 0,
"HasVida3": false,
"IsActive": true,
"IsHmg": true,
"IsVidaPlus": false,
"Latitude": "24.8113774",
"Longitude": "46.6239813",
"MainProjectID": 130,
"ProjectOutSA": false,
"UsingInDoctorApp": false
},{
"Desciption": "Jeddah Hospital",
"DesciptionN": "مستشفى الصحافة",
"ID": 3,
"LegalName": "Jeddah Hospital",
"LegalNameN": "مستشفى الصحافة",
"Name": "Jeddah Hospital",
"NameN": "مستشفى الصحافة",
"PhoneNumber": "+966115222222",
"SetupID": "013311",
"DistanceInKilometers": 0,
"HasVida3": false,
"IsActive": true,
"IsHmg": true,
"IsVidaPlus": false,
"Latitude": "24.8113774",
"Longitude": "46.6239813",
"MainProjectID": 130,
"ProjectOutSA": false,
"UsingInDoctorApp": false
}
];
static void showToast(String message, {bool longDuration = true}) {
Fluttertoast.showToast(
msg: message,
@ -326,7 +370,7 @@ class Utils {
children: [
SizedBox(height: isSmallWidget ? 0.h : 48.h),
Lottie.asset(AppAnimations.noData,
repeat: false, reverse: false, frameRate: FrameRate(60), width: width.h, height: height.h, fit: BoxFit.fill),
repeat: false, reverse: false, frameRate: FrameRate(60), width: width.w, height: height.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
(noDataText ?? LocaleKeys.noDataAvailable.tr())
.toText14(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true)
@ -351,10 +395,10 @@ class Utils {
).center;
}
static Widget getSuccessWidget({String? loadingText}) {
static Widget getSuccessWidget({String? loadingText, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
crossAxisAlignment: crossAxisAlignment,
children: [
Lottie.asset(AppAnimations.checkmark, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
@ -722,7 +766,16 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset(AppAssets.mada, width: 25.h, height: 25.h),
Image.asset(AppAssets.tamaraEng, width: 25.h, height: 25.h),
Image.asset(
AppAssets.tamaraEng,
width: 25.h,
height: 25.h,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
debugPrint('Failed to load Tamara PNG in payment methods: $error');
return Utils.buildSvgWithAssets(icon: AppAssets.tamara, width: 25.h, height: 25.h, fit: BoxFit.contain);
},
),
Image.asset(AppAssets.visa, width: 25.h, height: 25.h),
Image.asset(AppAssets.mastercard, width: 25.h, height: 25.h),
Image.asset(AppAssets.applePay, width: 25.h, height: 25.h),
@ -859,6 +912,17 @@ class Utils {
isHMC: hospital.isHMC);
}
static HospitalsModel? convertToHospitalsModel(PatientDoctorAppointmentList? item) {
if (item == null) return null;
return HospitalsModel(
name: item.filterName,
nameN: item.filterName,
distanceInKilometers: item.distanceInKMs,
isHMC: item.isHMC,
);
}
static bool havePrivilege(int id) {
bool isHavePrivilege = false;
try {
@ -876,7 +940,6 @@ class Utils {
launchUrl(uri, mode: LaunchMode.inAppBrowserView);
}
static Color getCardBorderColor(int currentQueueStatus) {
switch (currentQueueStatus) {
case 0:

@ -23,14 +23,15 @@ extension CapExtension on String {
extension EmailValidator on String {
Widget get toWidget => Text(this);
Widget toText8({Color? color, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text(
Widget toText8({Color? color, FontWeight? fontWeight, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) =>
Text(
this,
maxLines: maxlines,
overflow: textOverflow,
style: TextStyle(
fontSize: 8.f,
fontStyle: fontStyle ?? FontStyle.normal,
fontWeight: isBold ? FontWeight.bold : FontWeight.normal,
fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,
letterSpacing: 0,
),
@ -41,7 +42,7 @@ extension EmailValidator on String {
FontWeight? weight,
bool isBold = false,
bool isUnderLine = false,
bool isCenter = false,
bool isCenter = false,
int? maxlines,
FontStyle? fontStyle,
TextOverflow? textOverflow,
@ -191,7 +192,8 @@ extension EmailValidator on String {
letterSpacing: letterSpacing,
height: height,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
decoration: isUnderLine ? TextDecoration.underline : null),
decoration: isUnderLine ? TextDecoration.underline : null,
decorationColor: color ?? AppColors.blackColor),
);
Widget toText15(
@ -214,39 +216,38 @@ extension EmailValidator on String {
decoration: isUnderLine ? TextDecoration.underline : null),
);
Widget toText16({
Color? color,
bool isUnderLine = false,
bool isBold = false,
bool isCenter = false,
int? maxlines,
double? height,
TextAlign? textAlign,
FontWeight? weight,
TextOverflow? textOverflow,
double? letterSpacing = -0.4,
Color decorationColor =AppColors.errorColor
}) =>
Widget toText16(
{Color? color,
bool isUnderLine = false,
bool isBold = false,
bool isCenter = false,
int? maxlines,
double? height,
TextAlign? textAlign,
FontWeight? weight,
TextOverflow? textOverflow,
double? letterSpacing = -0.4,
Color decorationColor = AppColors.errorColor}) =>
Text(
this,
maxLines: maxlines,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
color: color ?? AppColors.blackColor,
fontSize: 16.f,
letterSpacing: letterSpacing,
height: height,
overflow: textOverflow,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
decoration: isUnderLine ? TextDecoration.underline : null,
decorationColor: decorationColor
),
color: color ?? AppColors.blackColor,
fontSize: 16.f,
letterSpacing: letterSpacing,
height: height,
overflow: textOverflow,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
decoration: isUnderLine ? TextDecoration.underline : null,
decorationColor: decorationColor),
);
Widget toText17({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 17.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
style: TextStyle(
color: color ?? AppColors.blackColor, fontSize: 17.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toText18({Color? color, FontWeight? weight, bool isBold = false, bool isCenter = false, int? maxlines, TextOverflow? textOverflow}) => Text(
@ -255,39 +256,62 @@ extension EmailValidator on String {
this,
overflow: textOverflow,
style: TextStyle(
fontSize: 18.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4),
fontSize: 18.f,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,
letterSpacing: -0.4),
);
Widget toText19({Color? color, bool isBold = false}) => Text(
this,
style: TextStyle(fontSize: 19.f, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
style: TextStyle(
fontSize: 19.f, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
);
Widget toText20({Color? color, FontWeight? weight, bool isBold = false, }) => Text(
Widget toText20({
Color? color,
FontWeight? weight,
bool isBold = false,
}) =>
Text(
this,
style: TextStyle(
fontSize: 20.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4),
fontSize: 20.f,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,
letterSpacing: -0.4),
);
Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text(
this,
maxLines: maxlines,
style: TextStyle(
color: color ?? AppColors.blackColor, fontSize: 21.f, letterSpacing: -1, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
color: color ?? AppColors.blackColor,
fontSize: 21.f,
letterSpacing: -1,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
);
Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
height: 1, color: color ?? AppColors.blackColor, fontSize: 22.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
height: 1,
color: color ?? AppColors.blackColor,
fontSize: 22.f,
letterSpacing: -1,
fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight, double? letterSpacing}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: letterSpacing??-1, fontWeight: isBold ? FontWeight.bold : fontWeight??FontWeight.normal),
height: 23 / 24,
color: color ?? AppColors.blackColor,
fontSize: 24.f,
letterSpacing: letterSpacing ?? -1,
fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.normal),
);
Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing}) => Text(
@ -312,17 +336,25 @@ extension EmailValidator on String {
fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text(
Widget toText32({FontWeight? weight, Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
height: 32 / 32,
color: color ?? AppColors.blackColor,
fontSize: 32.f,
letterSpacing: -1,
fontWeight: isBold ? FontWeight.bold : weight ?? FontWeight.normal),
);
Widget toText44({Color? color, bool isBold = false}) => Text(
this,
style: TextStyle(
height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
height: 32 / 32,
color: color ?? AppColors.blackColor,
fontSize: 44.f,
letterSpacing: -1,
fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) {

@ -260,10 +260,10 @@ class AuthenticationRepoImp implements AuthenticationRepo {
newRequest.forRegisteration = newRequest.isRegister ?? false;
newRequest.isRegister = false;
//silent login case removed token and login token
// if(newRequest.logInTokenID.isEmpty && newRequest.isSilentLogin == true && (newRequest.loginType==1 || newRequest.loginType==4)) {
// newRequest.logInTokenID = null;
// newRequest.deviceToken = null;
// }
if(newRequest.logInTokenID.isEmpty && newRequest.isSilentLogin == true && (newRequest.loginType==1 || newRequest.loginType==4)) {
newRequest.logInTokenID = null;
newRequest.deviceToken = null;
}
}

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

Loading…
Cancel
Save