diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 0ffb97d..2987d3b 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -156,16 +156,16 @@ dependencies {
implementation("com.intuit.ssp:ssp-android:1.1.0")
implementation("com.intuit.sdp:sdp-android:1.1.0")
-// implementation("com.github.bumptech.glide:glide:4.16.0")
-// annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
+ implementation("com.github.bumptech.glide:glide:4.16.0")
+ annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.mapbox.maps:android:11.5.0")
// implementation("com.mapbox.maps:android:11.4.0")
// AARs
-// implementation(files("libs/PenNavUI.aar"))
-// implementation(files("libs/Penguin.aar"))
-// implementation(files("libs/PenguinRenderer.aar"))
+ implementation(files("libs/PenNavUI.aar"))
+ implementation(files("libs/Penguin.aar"))
+ implementation(files("libs/PenguinRenderer.aar"))
implementation("com.github.kittinunf.fuel:fuel:2.3.1")
implementation("com.github.kittinunf.fuel:fuel-android:2.3.1")
@@ -180,9 +180,11 @@ dependencies {
implementation("com.google.android.material:material:1.12.0")
implementation("pl.droidsonroids.gif:android-gif-drawable:1.2.25")
+ implementation("com.mapbox.mapboxsdk:mapbox-sdk-turf:7.3.1")
androidTestImplementation("androidx.test:core:1.6.1")
implementation("com.whatsapp.otp:whatsapp-otp-android-sdk:0.1.0")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
// implementation(project(":vitalSignEngine"))
+
}
\ No newline at end of file
diff --git a/android/app/libs/PenNavUI.aar b/android/app/libs/PenNavUI.aar
index d423bc1..7832df8 100644
Binary files a/android/app/libs/PenNavUI.aar and b/android/app/libs/PenNavUI.aar differ
diff --git a/android/app/libs/Penguin.aar b/android/app/libs/Penguin.aar
index 5c789c6..a769c7a 100644
Binary files a/android/app/libs/Penguin.aar and b/android/app/libs/Penguin.aar differ
diff --git a/android/app/libs/PenguinRenderer.aar b/android/app/libs/PenguinRenderer.aar
index b657ac6..2926e9a 100644
Binary files a/android/app/libs/PenguinRenderer.aar and b/android/app/libs/PenguinRenderer.aar differ
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 4f7ef74..8c1388b 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -49,7 +49,7 @@
-
+
@@ -58,6 +58,13 @@
+
+
+
+
+
+
+
,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+
+ val granted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
+ val intent = Intent("PERMISSION_RESULT_ACTION").apply {
+ putExtra("PERMISSION_GRANTED", granted)
+ }
+ sendBroadcast(intent)
+
+ // Log the request code and permission results
+ Log.d("PermissionsResult", "Request Code: $requestCode")
+ Log.d("PermissionsResult", "Permissions: ${permissions.joinToString()}")
+ Log.d("PermissionsResult", "Grant Results: ${grantResults.joinToString()}")
+
+ }
+
+ override fun onResume() {
+ super.onResume()
+ }
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PenguinInPlatformBridge.kt b/android/app/src/main/kotlin/com/ejada/hmg/PenguinInPlatformBridge.kt
new file mode 100644
index 0000000..4df25bc
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PenguinInPlatformBridge.kt
@@ -0,0 +1,61 @@
+package com.ejada.hmg.penguin
+
+import com.ejada.hmg.MainActivity
+import android.os.Build
+import android.util.Log
+import androidx.annotation.RequiresApi
+import com.ejada.hmg.penguin.PenguinView
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodCall
+import com.ejada.hmg.PermissionManager.HostNotificationPermissionManager
+import com.ejada.hmg.PermissionManager.HostBgLocationManager
+import com.ejada.hmg.PermissionManager.HostGpsStateManager
+import io.flutter.plugin.common.MethodChannel
+
+class PenguinInPlatformBridge(
+ private var flutterEngine: FlutterEngine,
+ private var mainActivity: MainActivity
+) {
+
+ private lateinit var channel: MethodChannel
+
+ companion object {
+ private const val CHANNEL = "launch_penguin_ui"
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ fun create() {
+// openTok = OpenTok(mainActivity, flutterEngine)
+ channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
+ channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
+ when (call.method) {
+ "launchPenguin" -> {
+ print("the platform channel is being called")
+
+ if (HostNotificationPermissionManager.isNotificationPermissionGranted(mainActivity))
+ else HostNotificationPermissionManager.requestNotificationPermission(mainActivity)
+ HostBgLocationManager.requestLocationBackgroundPermission(mainActivity)
+ HostGpsStateManager.requestLocationPermission(mainActivity)
+ val args = call.arguments as Map?
+ Log.d("TAG", "configureFlutterEngine: $args")
+ println("args")
+ args?.let {
+ PenguinView(
+ mainActivity,
+ 100,
+ args,
+ flutterEngine.dartExecutor.binaryMessenger,
+ activity = mainActivity,
+ channel
+ )
+ }
+ }
+
+ else -> {
+ result.notImplemented()
+ }
+ }
+ }
+ }
+
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/AppPreferences.java b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/AppPreferences.java
new file mode 100644
index 0000000..d012799
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/AppPreferences.java
@@ -0,0 +1,139 @@
+package com.ejada.hmg.PermissionManager;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+
+
+/**
+ * This preferences for app level
+ */
+
+public class AppPreferences {
+
+ public static final String PREF_NAME = "PenguinINUI_AppPreferences";
+ public static final int MODE = Context.MODE_PRIVATE;
+
+ public static final String campusIdKey = "campusId";
+
+ public static final String LANG = "Lang";
+
+ public static final String settingINFO = "SETTING-INFO";
+
+ public static final String userName = "userName";
+ public static final String passWord = "passWord";
+
+ private static HandlerThread handlerThread;
+ private static Handler handler;
+
+ static {
+ handlerThread = new HandlerThread("PreferencesHandlerThread");
+ handlerThread.start();
+ handler = new Handler(handlerThread.getLooper());
+ }
+
+
+
+ public static SharedPreferences getPreferences(final Context context) {
+ return context.getSharedPreferences(AppPreferences.PREF_NAME, AppPreferences.MODE);
+ }
+
+ public static SharedPreferences.Editor getEditor(final Context context) {
+ return getPreferences(context).edit();
+ }
+
+
+ public static void writeInt(final Context context, final String key, final int value) {
+ handler.post(() -> {
+ SharedPreferences.Editor editor = getEditor(context);
+ editor.putInt(key, value);
+ editor.apply();
+ });
+ }
+
+
+ public static int readInt(final Context context, final String key, final int defValue) {
+ Callable callable = () -> {
+ SharedPreferences preferences = getPreferences(context);
+ return preferences.getInt(key, -1);
+ };
+
+ Future future = new FutureTask<>(callable);
+ handler.post((Runnable) future);
+
+ try {
+ return future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace(); // Handle the exception appropriately
+ }
+
+ return -1; // Return the default value in case of an error
+ }
+
+ public static int getCampusId(final Context context) {
+ return readInt(context,campusIdKey,-1);
+ }
+
+
+
+ public static void writeString(final Context context, final String key, final String value) {
+ handler.post(() -> {
+ SharedPreferences.Editor editor = getEditor(context);
+ editor.putString(key, value);
+ editor.apply();
+ });
+ }
+
+
+ public static String readString(final Context context, final String key, final String defValue) {
+ Callable callable = () -> {
+ SharedPreferences preferences = getPreferences(context);
+ return preferences.getString(key, defValue);
+ };
+
+ Future future = new FutureTask<>(callable);
+ handler.post((Runnable) future);
+
+ try {
+ return future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace(); // Handle the exception appropriately
+ }
+
+ return defValue; // Return the default value in case of an error
+ }
+
+
+ public static void writeBoolean(final Context context, final String key, final boolean value) {
+ handler.post(() -> {
+ SharedPreferences.Editor editor = getEditor(context);
+ editor.putBoolean(key, value);
+ editor.apply();
+ });
+ }
+
+ public static boolean readBoolean(final Context context, final String key, final boolean defValue) {
+ Callable callable = () -> {
+ SharedPreferences preferences = getPreferences(context);
+ return preferences.getBoolean(key, defValue);
+ };
+
+ Future future = new FutureTask<>(callable);
+ handler.post((Runnable) future);
+
+ try {
+ return future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace(); // Handle the exception appropriately
+ }
+
+ return defValue; // Return the default value in case of an error
+ }
+
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostBgLocationManager.java b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostBgLocationManager.java
new file mode 100644
index 0000000..5bc332d
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostBgLocationManager.java
@@ -0,0 +1,136 @@
+package com.ejada.hmg.PermissionManager;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.provider.Settings;
+
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import com.peng.pennavmap.PlugAndPlaySDK;
+import com.peng.pennavmap.R;
+import com.peng.pennavmap.enums.InitializationErrorType;
+
+/**
+ * Manages background location permission requests and handling for the application.
+ */
+public class HostBgLocationManager {
+ /**
+ * Request code for background location permission
+ */
+ public static final int REQUEST_ACCESS_BACKGROUND_LOCATION_CODE = 301;
+
+ /**
+ * Request code for navigating to app settings
+ */
+ private static final int REQUEST_CODE_SETTINGS = 11234;
+
+ /**
+ * Alert dialog for denied permissions
+ */
+ private static AlertDialog deniedAlertDialog;
+
+ /**
+ * Checks if the background location permission has been granted.
+ *
+ * @param context the context of the application or activity
+ * @return true if the permission is granted, false otherwise
+ */
+
+ public static boolean isLocationBackgroundGranted(Context context) {
+ return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Requests the background location permission from the user.
+ *
+ * @param activity the activity from which the request is made
+ */
+ public static void requestLocationBackgroundPermission(Activity activity) {
+ // Check if the ACCESS_BACKGROUND_LOCATION permission is already granted
+ if (!isLocationBackgroundGranted(activity)) {
+ // Permission is not granted, so request it
+ ActivityCompat.requestPermissions(activity,
+ new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
+ REQUEST_ACCESS_BACKGROUND_LOCATION_CODE);
+ }
+ }
+
+ /**
+ * Displays a dialog prompting the user to grant the background location permission.
+ *
+ * @param activity the activity where the dialog is displayed
+ */
+ public static void showLocationBackgroundPermission(Activity activity) {
+ AlertDialog alertDialog = new AlertDialog.Builder(activity)
+ .setCancelable(false)
+ .setMessage(activity.getString(R.string.com_penguin_nav_ui_geofence_alert_msg))
+ .setPositiveButton(activity.getString(R.string.com_penguin_nav_ui_go_to_settings), (dialog, which) -> {
+ if (activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
+ HostBgLocationManager.requestLocationBackgroundPermission(activity);
+ } else {
+ openAppSettings(activity);
+ }
+ if (dialog != null) {
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(activity.getString(R.string.com_penguin_nav_ui_later), (dialog, which) -> {
+ dialog.cancel();
+ })
+ .create();
+
+ alertDialog.show();
+ }
+
+ /**
+ * Handles the scenario where permissions are denied by the user.
+ * Displays a dialog to guide the user to app settings or exit the activity.
+ *
+ * @param activity the activity where the dialog is displayed
+ */
+ public static synchronized void handlePermissionsDenied(Activity activity) {
+ if (deniedAlertDialog != null && deniedAlertDialog.isShowing()) {
+ deniedAlertDialog.dismiss();
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setCancelable(false)
+ .setMessage(activity.getString(R.string.com_penguin_nav_ui_permission_denied_dialog_msg))
+ .setNegativeButton(activity.getString(R.string.com_penguin_nav_ui_cancel), (dialogInterface, i) -> {
+ if (PlugAndPlaySDK.externalPenNavUIDelegate != null) {
+ PlugAndPlaySDK.externalPenNavUIDelegate.onPenNavInitializationError(
+ InitializationErrorType.permissions.getTypeKey(),
+ InitializationErrorType.permissions);
+ }
+ activity.finish();
+ })
+ .setPositiveButton(activity.getString(R.string.com_penguin_nav_ui_go_settings), (dialogInterface, i) -> {
+ dialogInterface.dismiss();
+ openAppSettings(activity);
+ });
+ deniedAlertDialog = builder.create();
+ deniedAlertDialog.show();
+ }
+
+ /**
+ * Opens the application's settings screen to allow the user to modify permissions.
+ *
+ * @param activity the activity from which the settings screen is launched
+ */
+ private static void openAppSettings(Activity activity) {
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
+ intent.setData(uri);
+
+ if (intent.resolveActivity(activity.getPackageManager()) != null) {
+ activity.startActivityForResult(intent, REQUEST_CODE_SETTINGS);
+ }
+ }
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostGpsStateManager.java b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostGpsStateManager.java
new file mode 100644
index 0000000..adde120
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostGpsStateManager.java
@@ -0,0 +1,68 @@
+package com.ejada.hmg.PermissionManager;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import com.peng.pennavmap.managers.permissions.managers.BgLocationManager;
+
+public class HostGpsStateManager {
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
+
+
+ public boolean checkGPSEnabled(Activity activity) {
+ LocationManager gpsStateManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
+ return gpsStateManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
+ }
+
+ public static boolean isGpsGranted(Activity activity) {
+ return BgLocationManager.isLocationBackgroundGranted(activity)
+ || ContextCompat.checkSelfPermission(
+ activity,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED
+ && ContextCompat.checkSelfPermission(
+ activity,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED;
+ }
+
+
+ /**
+ * Checks if the location permission is granted.
+ *
+ * @param activity the Activity context
+ * @return true if permission is granted, false otherwise
+ */
+ public static boolean isLocationPermissionGranted(Activity activity) {
+ return ContextCompat.checkSelfPermission(
+ activity,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED &&
+ ContextCompat.checkSelfPermission(
+ activity,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Requests the location permission.
+ *
+ * @param activity the Activity context
+ */
+ public static void requestLocationPermission(Activity activity) {
+ ActivityCompat.requestPermissions(
+ activity,
+ new String[]{
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ },
+ LOCATION_PERMISSION_REQUEST_CODE
+ );
+ }
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostNotificationPermissionManager.java b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostNotificationPermissionManager.java
new file mode 100644
index 0000000..5b9f19e
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/HostNotificationPermissionManager.java
@@ -0,0 +1,73 @@
+package com.ejada.hmg.PermissionManager;
+
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.ActivityCompat;
+import androidx.core.app.NotificationManagerCompat;
+
+public class HostNotificationPermissionManager {
+ private static final int REQUEST_NOTIFICATION_PERMISSION = 100;
+
+
+ /**
+ * Checks if the notification permission is granted.
+ *
+ * @return true if the notification permission is granted, false otherwise.
+ */
+ public static boolean isNotificationPermissionGranted(Activity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ try {
+ return ActivityCompat.checkSelfPermission(activity, android.Manifest.permission.POST_NOTIFICATIONS)
+ == PackageManager.PERMISSION_GRANTED;
+ } catch (Exception e) {
+ // Handle cases where the API is unavailable
+ e.printStackTrace();
+ return NotificationManagerCompat.from(activity).areNotificationsEnabled();
+ }
+ } else {
+ // Permissions were not required below Android 13 for notifications
+ return NotificationManagerCompat.from(activity).areNotificationsEnabled();
+ }
+ }
+
+ /**
+ * Requests the notification permission.
+ */
+ public static void requestNotificationPermission(Activity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (!isNotificationPermissionGranted(activity)) {
+ ActivityCompat.requestPermissions(activity,
+ new String[]{android.Manifest.permission.POST_NOTIFICATIONS},
+ REQUEST_NOTIFICATION_PERMISSION);
+ }
+ }
+ }
+
+ /**
+ * Handles the result of the permission request.
+ *
+ * @param requestCode The request code passed in requestPermissions().
+ * @param permissions The requested permissions.
+ * @param grantResults The grant results for the corresponding permissions.
+ */
+ public static boolean handlePermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (permissions.length > 0 &&
+ permissions[0].equals(android.Manifest.permission.POST_NOTIFICATIONS) &&
+ grantResults.length > 0 &&
+ grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // Permission granted
+ System.out.println("Notification permission granted.");
+ return true;
+ } else {
+ // Permission denied
+ System.out.println("Notification permission denied.");
+ return false;
+ }
+
+ }
+
+
+}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionHelper.kt b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionHelper.kt
new file mode 100644
index 0000000..9856a49
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionHelper.kt
@@ -0,0 +1,28 @@
+package com.ejada.hmg.PermissionManager
+
+import android.Manifest
+import android.os.Build
+
+object PermissionHelper {
+
+ fun getRequiredPermissions(): Array {
+ val permissions = mutableListOf(
+ Manifest.permission.INTERNET,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_NETWORK_STATE,
+ Manifest.permission.BLUETOOTH,
+ Manifest.permission.BLUETOOTH_ADMIN,
+// Manifest.permission.ACTIVITY_RECOGNITION
+ )
+
+ // For Android 12 (API level 31) and above, add specific permissions
+// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12 (API 31) and above
+ permissions.add(Manifest.permission.BLUETOOTH_SCAN)
+ permissions.add(Manifest.permission.BLUETOOTH_CONNECT)
+ permissions.add(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS)
+// }
+
+ return permissions.toTypedArray()
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionManager.kt b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionManager.kt
new file mode 100644
index 0000000..d8aea7b
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionManager.kt
@@ -0,0 +1,50 @@
+package com.ejada.hmg.PermissionManager
+
+import android.app.Activity
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+
+class PermissionManager(
+ private val context: Context,
+ val listener: PermissionListener,
+ private val requestCode: Int,
+ vararg permissions: String
+) {
+
+ private val permissionsArray = permissions
+
+ interface PermissionListener {
+ fun onPermissionGranted()
+ fun onPermissionDenied()
+ }
+
+ fun arePermissionsGranted(): Boolean {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ permissionsArray.all {
+ ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
+ }
+ } else {
+ true
+ }
+ }
+
+ fun requestPermissions(activity: Activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ ActivityCompat.requestPermissions(activity, permissionsArray, requestCode)
+ }
+ }
+
+ fun handlePermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
+ if (this.requestCode == requestCode) {
+ val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
+ if (allGranted) {
+ listener.onPermissionGranted()
+ } else {
+ listener.onPermissionDenied()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionResultReceiver.kt b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionResultReceiver.kt
new file mode 100644
index 0000000..c07d1de
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/PermissionManager/PermissionResultReceiver.kt
@@ -0,0 +1,15 @@
+package com.ejada.hmg.PermissionManager
+
+// PermissionResultReceiver.kt
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+
+class PermissionResultReceiver(
+ private val callback: (Boolean) -> Unit
+) : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ val granted = intent?.getBooleanExtra("PERMISSION_GRANTED", false) ?: false
+ callback(granted)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinMethod.kt b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinMethod.kt
new file mode 100644
index 0000000..18463d2
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinMethod.kt
@@ -0,0 +1,13 @@
+package com.ejada.hmg.penguin
+
+enum class PenguinMethod {
+ // initializePenguin("initializePenguin"),
+ // configurePenguin("configurePenguin"),
+ // showPenguinUI("showPenguinUI"),
+ // onPenNavUIDismiss("onPenNavUIDismiss"),
+ // onReportIssue("onReportIssue"),
+ // onPenNavSuccess("onPenNavSuccess"),
+ onPenNavInitializationError // onLocationOffCampus("onLocationOffCampus"),
+ // navigateToPOI("navigateToPOI"),
+ // openSharedLocation("openSharedLocation");
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinNavigator.kt b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinNavigator.kt
new file mode 100644
index 0000000..b822d67
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinNavigator.kt
@@ -0,0 +1,97 @@
+package com.ejada.hmg.penguin
+
+import android.content.Context
+import com.google.gson.Gson
+import com.peng.pennavmap.PlugAndPlaySDK
+import com.peng.pennavmap.connections.ApiController
+import com.peng.pennavmap.interfaces.RefIdDelegate
+import com.peng.pennavmap.models.TokenModel
+import com.peng.pennavmap.models.postmodels.PostToken
+import com.peng.pennavmap.utils.AppSharedData
+import okhttp3.ResponseBody
+import retrofit2.Call
+import retrofit2.Callback
+import retrofit2.Response
+import android.util.Log
+
+
+class PenguinNavigator() {
+
+ fun navigateTo(mContext: Context, refID: String, delegate: RefIdDelegate,clientID : String,clientKey : String ) {
+ val postToken = PostToken(clientID, clientKey)
+ getToken(mContext, postToken, object : RefIdDelegate {
+ override fun onRefByIDSuccess(PoiId: String?) {
+ Log.e("navigateTo", "PoiId is+++++++ $PoiId")
+
+ PlugAndPlaySDK.navigateTo(mContext, refID, object : RefIdDelegate {
+ override fun onRefByIDSuccess(PoiId: String?) {
+ Log.e("navigateTo", "PoiId 2is+++++++ $PoiId")
+
+ delegate.onRefByIDSuccess(refID)
+
+ }
+
+ override fun onGetByRefIDError(error: String?) {
+ delegate.onRefByIDSuccess(error)
+ }
+
+ })
+
+
+ }
+
+ override fun onGetByRefIDError(error: String?) {
+ delegate.onRefByIDSuccess(error)
+ }
+
+ })
+
+ }
+
+ fun getToken(mContext: Context, postToken: PostToken?, apiTokenCallBack: RefIdDelegate) {
+ try {
+ // Create the API call
+ val purposesCall: Call = ApiController.getInstance(mContext)
+ .apiMethods
+ .getToken(postToken)
+
+ // Enqueue the call for asynchronous execution
+ purposesCall.enqueue(object : Callback {
+ override fun onResponse(
+ call: Call,
+ response: Response
+ ) {
+ if (response.isSuccessful() && response.body() != null) {
+ try {
+ response.body()?.use { responseBody ->
+ val responseBodyString: String = responseBody.string() // Use `string()` to get the actual response content
+ if (responseBodyString.isNotEmpty()) {
+ val tokenModel = Gson().fromJson(responseBodyString, TokenModel::class.java)
+ if (tokenModel != null && tokenModel.token != null) {
+ AppSharedData.apiToken = tokenModel.token
+ apiTokenCallBack.onRefByIDSuccess(tokenModel.token)
+ } else {
+ apiTokenCallBack.onGetByRefIDError("Failed to parse token model")
+ }
+ } else {
+ apiTokenCallBack.onGetByRefIDError("Response body is empty")
+ }
+ }
+ } catch (e: Exception) {
+ apiTokenCallBack.onGetByRefIDError("An error occurred: ${e.message}")
+ }
+ } else {
+ apiTokenCallBack.onGetByRefIDError("Unsuccessful response: " + response.code())
+ }
+ }
+
+ override fun onFailure(call: Call, t: Throwable) {
+ apiTokenCallBack.onGetByRefIDError(t.message)
+ }
+ })
+ } catch (error: Exception) {
+ apiTokenCallBack.onGetByRefIDError("Exception during API call: $error")
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinView.kt b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinView.kt
new file mode 100644
index 0000000..6c7306d
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinView.kt
@@ -0,0 +1,376 @@
+package com.ejada.hmg.penguin
+
+import android.app.Activity
+import android.content.Context
+import android.content.Context.RECEIVER_EXPORTED
+import android.content.IntentFilter
+import android.graphics.Color
+import android.os.Build
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import android.widget.RelativeLayout
+import android.widget.Toast
+import androidx.annotation.RequiresApi
+import com.ejada.hmg.PermissionManager.PermissionManager
+import com.ejada.hmg.PermissionManager.PermissionResultReceiver
+import com.ejada.hmg.MainActivity
+import com.ejada.hmg.PermissionManager.PermissionHelper
+import com.peng.pennavmap.PlugAndPlayConfiguration
+import com.peng.pennavmap.PlugAndPlaySDK
+import com.peng.pennavmap.enums.InitializationErrorType
+import com.peng.pennavmap.interfaces.PenNavUIDelegate
+import com.peng.pennavmap.utils.Languages
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.platform.PlatformView
+import com.ejada.hmg.penguin.PenguinNavigator
+import com.peng.pennavmap.interfaces.PIEventsDelegate
+import com.peng.pennavmap.interfaces.PILocationDelegate
+import com.peng.pennavmap.interfaces.RefIdDelegate
+import com.peng.pennavmap.models.LocationMessage
+import com.peng.pennavmap.models.PIReportIssue
+import java.util.ArrayList
+import penguin.com.pennav.renderer.PIRendererSettings
+
+/**
+ * Custom PlatformView for displaying Penguin UI components within a Flutter app.
+ * Implements `PlatformView` for rendering the view, `MethodChannel.MethodCallHandler` for handling method calls,
+ * and `PenNavUIDelegate` for handling SDK events.
+ */
+@RequiresApi(Build.VERSION_CODES.O)
+internal class PenguinView(
+ context: Context,
+ id: Int,
+ val creationParams: Map,
+ messenger: BinaryMessenger,
+ activity: MainActivity,
+ val channel: MethodChannel
+) : PlatformView, MethodChannel.MethodCallHandler, PenNavUIDelegate, PIEventsDelegate,
+ PILocationDelegate {
+ // The layout for displaying the Penguin UI
+ private val mapLayout: RelativeLayout = RelativeLayout(context)
+ private val _context: Context = context
+
+ private val permissionResultReceiver: PermissionResultReceiver
+ private val permissionIntentFilter = IntentFilter("PERMISSION_RESULT_ACTION")
+
+ private companion object {
+ const val PERMISSIONS_REQUEST_CODE = 1
+ }
+
+ private lateinit var permissionManager: PermissionManager
+
+ // Reference to the main activity
+ private var _activity: Activity = activity
+
+ private lateinit var mContext: Context
+
+ lateinit var navigator: PenguinNavigator
+
+ init {
+ // Set layout parameters for the mapLayout
+ mapLayout.layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
+ )
+
+ mContext = context
+
+
+ permissionResultReceiver = PermissionResultReceiver { granted ->
+ if (granted) {
+ onPermissionsGranted()
+ } else {
+ onPermissionsDenied()
+ }
+ }
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ mContext.registerReceiver(
+ permissionResultReceiver,
+ permissionIntentFilter,
+ RECEIVER_EXPORTED
+ )
+ } else {
+ mContext.registerReceiver(
+ permissionResultReceiver,
+ permissionIntentFilter,
+ )
+ }
+
+ // Set the background color of the layout
+ mapLayout.setBackgroundColor(Color.RED)
+
+ permissionManager = PermissionManager(
+ context = mContext,
+ listener = object : PermissionManager.PermissionListener {
+ override fun onPermissionGranted() {
+ // Handle permissions granted
+ onPermissionsGranted()
+ }
+
+ override fun onPermissionDenied() {
+ // Handle permissions denied
+ onPermissionsDenied()
+ }
+ },
+ requestCode = PERMISSIONS_REQUEST_CODE,
+ PermissionHelper.getRequiredPermissions().get(0)
+ )
+
+ if (!permissionManager.arePermissionsGranted()) {
+ permissionManager.requestPermissions(_activity)
+ } else {
+ // Permissions already granted
+ permissionManager.listener.onPermissionGranted()
+ }
+
+
+ }
+
+ private fun onPermissionsGranted() {
+ // Handle the actions when permissions are granted
+ Log.d("PermissionsResult", "onPermissionsGranted")
+ // Register the platform view factory for creating custom views
+
+ // Initialize the Penguin SDK
+ initPenguin()
+
+
+ }
+
+ private fun onPermissionsDenied() {
+ // Handle the actions when permissions are denied
+ Log.d("PermissionsResult", "onPermissionsDenied")
+
+ }
+
+ /**
+ * Returns the view associated with this PlatformView.
+ *
+ * @return The main view for this PlatformView.
+ */
+ override fun getView(): View {
+ return mapLayout
+ }
+
+ /**
+ * Cleans up resources associated with this PlatformView.
+ */
+ override fun dispose() {
+ // Cleanup code if needed
+ }
+
+ /**
+ * Handles method calls from Dart code.
+ *
+ * @param call The method call from Dart.
+ * @param result The result callback to send responses back to Dart.
+ */
+ override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
+ // Handle method calls from Dart code here
+ }
+
+ /**
+ * Initializes the Penguin SDK with custom configuration and delegates.
+ */
+ private fun initPenguin() {
+ navigator = PenguinNavigator()
+ // Configure the PlugAndPlaySDK
+ val language = when (creationParams["languageCode"] as String) {
+ "ar" -> Languages.ar
+ "en" -> Languages.en
+ else -> {
+ Languages.en
+ }
+ }
+
+
+// PlugAndPlaySDK.configuration = Builder()
+// .setClientData(MConstantsDemo.CLIENT_ID, MConstantsDemo.CLIENT_KEY)
+// .setLanguageID(selectedLanguage)
+// .setBaseUrl(MConstantsDemo.DATA_URL, MConstantsDemo.POSITION_URL)
+// .setServiceName(MConstantsDemo.DATA_SERVICE_NAME, MConstantsDemo.POSITION_SERVICE_NAME)
+// .setUserName(name)
+// .setSimulationModeEnabled(isSimulation)
+// .setCustomizeColor(if (MConstantsDemo.APP_COLOR != null) MConstantsDemo.APP_COLOR else "#2CA0AF")
+// .setEnableBackButton(MConstantsDemo.SHOW_BACK_BUTTON)
+// .setCampusId(MConstantsDemo.selectedCampusId)
+//
+// .setShowUILoader(true)
+// .build()
+
+ PIRendererSettings.styleUri = "mapbox://styles/rwaid/cm3h30b36007v01qz7ik8a0sk"
+
+ PlugAndPlaySDK.configuration = PlugAndPlayConfiguration.Builder()
+ .setBaseUrl(
+ creationParams["dataURL"] as String,
+ creationParams["positionURL"] as String
+ )
+ .setServiceName(
+ creationParams["dataServiceName"] as String,
+ creationParams["positionServiceName"] as String
+ )
+ .setClientData(
+ creationParams["clientID"] as String,
+ creationParams["clientKey"] as String
+ )
+ .setUserName(creationParams["username"] as String)
+// .setLanguageID(Languages.en)
+ .setLanguageID(language)
+ .setSimulationModeEnabled(creationParams["isSimulationModeEnabled"] as Boolean)
+ .setEnableBackButton(true)
+// .setDeepLinkData("deeplink")
+ .setCustomizeColor("#2CA0AF")
+ .setDeepLinkSchema("", "")
+ .setIsEnableReportIssue(true)
+ .setDeepLinkData("")
+ .setEnableSharedLocationCallBack(false)
+ .setShowUILoader(true)
+ .setCampusId(creationParams["projectID"] as Int)
+ .build()
+
+
+ Log.d(
+ "TAG",
+ "initPenguin: ${creationParams["projectID"]}"
+ )
+
+ Log.d(
+ "TAG",
+ "initPenguin: creation param are ${creationParams}"
+ )
+
+ // Set location delegate to handle location updates
+// PlugAndPlaySDK.setPiLocationDelegate {
+ // Example code to handle location updates
+ // Uncomment and modify as needed
+ // if (location.size() > 0)
+ // Toast.makeText(_context, "Location Info Latitude: ${location[0]}, Longitude: ${location[1]}", Toast.LENGTH_SHORT).show()
+// }
+
+ // Set events delegate for reporting issues
+// PlugAndPlaySDK.setPiEventsDelegate(new PIEventsDelegate() {
+// @Override
+// public void onReportIssue(PIReportIssue issue) {
+// Log.e("Issue Reported: ", issue.getReportType());
+// }
+// // Implement issue reporting logic here }
+// @Override
+// public void onSharedLocation(String link) {
+// // Implement Shared location logic here
+// }
+// })
+
+ // Start the Penguin SDK
+ PlugAndPlaySDK.setPiEventsDelegate(this)
+ PlugAndPlaySDK.setPiLocationDelegate(this)
+ PlugAndPlaySDK.start(mContext, this)
+ }
+
+
+ /**
+ * Navigates to the specified reference ID.
+ *
+ * @param refID The reference ID to navigate to.
+ */
+ fun navigateTo(refID: String) {
+ try {
+ if (refID.isBlank()) {
+ Log.e("navigateTo", "Invalid refID: The reference ID is blank.")
+ }
+// referenceId = refID
+ navigator.navigateTo(mContext, refID,object : RefIdDelegate {
+ override fun onRefByIDSuccess(PoiId: String?) {
+ Log.e("navigateTo", "PoiId is penguin view+++++++ $PoiId")
+
+// channelFlutter.invokeMethod(
+// PenguinMethod.navigateToPOI.name,
+// "navigateTo Success"
+// )
+ }
+
+ override fun onGetByRefIDError(error: String?) {
+ Log.e("navigateTo", "error is penguin view+++++++ $error")
+
+// channelFlutter.invokeMethod(
+// PenguinMethod.navigateToPOI.name,
+// "navigateTo Failed: Invalid refID"
+// )
+ }
+ } , creationParams["clientID"] as String, creationParams["clientKey"] as String )
+
+ } catch (e: Exception) {
+ Log.e("navigateTo", "Exception occurred during navigation: ${e.message}", e)
+// channelFlutter.invokeMethod(
+// PenguinMethod.navigateToPOI.name,
+// "Failed: Exception - ${e.message}"
+// )
+ }
+ }
+
+ /**
+ * Called when Penguin UI setup is successful.
+ *
+ * @param warningCode Optional warning code received from the SDK.
+ */
+ override fun onPenNavSuccess(warningCode: String?) {
+ val clinicId = creationParams["clinicID"] as String
+
+ if(clinicId.isEmpty()) return
+
+ navigateTo(clinicId)
+ }
+
+ /**
+ * Called when there is an initialization error with Penguin UI.
+ *
+ * @param description Description of the error.
+ * @param errorType Type of initialization error.
+ */
+ override fun onPenNavInitializationError(
+ description: String?,
+ errorType: InitializationErrorType?
+ ) {
+ val arguments: Map = mapOf(
+ "description" to description,
+ "type" to errorType?.name
+ )
+ Log.d(
+ "description",
+ "description : ${description}"
+ )
+
+ channel.invokeMethod(PenguinMethod.onPenNavInitializationError.name, arguments)
+ Toast.makeText(mContext, "Navigation Error: $description", Toast.LENGTH_SHORT).show()
+ }
+
+ /**
+ * Called when Penguin UI is dismissed.
+ */
+ override fun onPenNavUIDismiss() {
+ // Handle UI dismissal if needed
+ try {
+ mContext.unregisterReceiver(permissionResultReceiver)
+ dispose();
+ } catch (e: IllegalArgumentException) {
+ Log.e("PenguinView", "Receiver not registered: $e")
+ }
+ }
+
+ override fun onReportIssue(issue: PIReportIssue?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onSharedLocation(link: String?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onLocationOffCampus(location: ArrayList?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onLocationMessage(locationMessage: LocationMessage?) {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/android/app/src/main/res/values/mapbox_access_token.xml b/android/app/src/main/res/values/mapbox_access_token.xml
index f1daf69..65bc4b3 100644
--- a/android/app/src/main/res/values/mapbox_access_token.xml
+++ b/android/app/src/main/res/values/mapbox_access_token.xml
@@ -1,3 +1,3 @@
- sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 328e8fc..2d10333 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -19,5 +19,5 @@
Geofence requests happened too frequently.
-
+ pk.eyJ1IjoicndhaWQiLCJhIjoiY2x5cGo4aHNjMGNsbTJyc2djempobGQxaSJ9.RCaC6WrUt4A4YnZNfxnONQ
diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html
index 866b270..9b679bc 100644
--- a/android/build/reports/problems/problems-report.html
+++ b/android/build/reports/problems/problems-report.html
@@ -650,7 +650,7 @@ code + .copy-button {
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index 3e6502f..6d0842d 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -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
}
diff --git a/assets/images/svg/blood_type.svg b/assets/images/svg/blood_type.svg
new file mode 100644
index 0000000..5aded31
--- /dev/null
+++ b/assets/images/svg/blood_type.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/confirm.svg b/assets/images/svg/confirm.svg
index 5ed3693..62cfa01 100755
--- a/assets/images/svg/confirm.svg
+++ b/assets/images/svg/confirm.svg
@@ -1,3 +1,3 @@
-add_