diff --git a/android/app/libs/PenNavUI.aar b/android/app/libs/PenNavUI.aar
new file mode 100644
index 00000000..d1381b00
Binary files /dev/null and b/android/app/libs/PenNavUI.aar differ
diff --git a/android/app/libs/Penguin.aar b/android/app/libs/Penguin.aar
new file mode 100644
index 00000000..a5319586
Binary files /dev/null and b/android/app/libs/Penguin.aar differ
diff --git a/android/app/libs/PenguinRenderer.aar b/android/app/libs/PenguinRenderer.aar
new file mode 100644
index 00000000..5e6da32f
Binary files /dev/null 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 67bc20bf..620ad178 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -8,13 +8,13 @@
FlutterApplication and put your custom class here. -->
-
-
-
-
+
+
+
+
-
+
@@ -23,6 +23,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -42,7 +57,7 @@
-
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt
index c6da60fa..df41e342 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt
@@ -1,19 +1,15 @@
package com.ejada.hmg
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.content.ContentResolver
-import android.media.AudioAttributes
-import android.net.Uri
-import android.os.Bundle
-import android.util.Log
+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.cloud.diplomaticquarterapp.PenguinInPlatformBridge
import com.ejada.hmg.utils.*
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
-import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterFragmentActivity() {
@@ -46,6 +42,25 @@ class MainActivity: FlutterFragmentActivity() {
// val time = timeToMillis("04:00:00", "HH:mm:ss")
+ }
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ 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() {
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PenguinInPlatformBridge.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PenguinInPlatformBridge.kt
new file mode 100644
index 00000000..b6b4d7ad
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PenguinInPlatformBridge.kt
@@ -0,0 +1,51 @@
+package com.cloud.diplomaticquarterapp
+import com.ejada.hmg.MainActivity
+import android.os.Build
+import android.util.Log
+import androidx.annotation.RequiresApi
+import com.cloud.diplomaticquarterapp.penguin.PenguinView
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodCall
+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")
+ val args = call.arguments as Map?
+ Log.d("TAG", "configureFlutterEngine: $args")
+ println("args")
+ args?.let {
+ PenguinView(
+ mainActivity,
+ 100,
+ args,
+ flutterEngine.dartExecutor.binaryMessenger,
+ activity = mainActivity,
+ )
+ }
+ }
+
+ else -> {
+ result.notImplemented()
+ }
+ }
+ }
+ }
+
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PermissionManager/PermissionHelper.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PermissionManager/PermissionHelper.kt
new file mode 100644
index 00000000..ccc7e842
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PermissionManager/PermissionHelper.kt
@@ -0,0 +1,21 @@
+package com.cloud.diplomaticquarterapp.PermissionManager
+
+import android.Manifest
+
+object PermissionHelper {
+
+ fun getRequiredPermissions(): Array {
+ return arrayOf(
+ 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.BLUETOOTH_SCAN,
+ Manifest.permission.BLUETOOTH_CONNECT,
+ Manifest.permission.HIGH_SAMPLING_RATE_SENSORS,
+// Manifest.permission.ACTIVITY_RECOGNITION
+ )
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PermissionManager/PermissionManager.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PermissionManager/PermissionManager.kt
new file mode 100644
index 00000000..81f2a6f0
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PermissionManager/PermissionManager.kt
@@ -0,0 +1,50 @@
+package com.cloud.diplomaticquarterapp.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/cloud/diplomaticquarterapp/PermissionManager/PermissionResultReceiver.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PermissionManager/PermissionResultReceiver.kt
new file mode 100644
index 00000000..33765192
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PermissionManager/PermissionResultReceiver.kt
@@ -0,0 +1,15 @@
+package com.cloud.diplomaticquarterapp.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/cloud/diplomaticquarterapp/penguin/PenguinView.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/penguin/PenguinView.kt
new file mode 100644
index 00000000..2592e801
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/penguin/PenguinView.kt
@@ -0,0 +1,261 @@
+package com.cloud.diplomaticquarterapp.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.cloud.diplomaticquarterapp.PermissionManager.PermissionHelper
+import com.cloud.diplomaticquarterapp.PermissionManager.PermissionManager
+import com.cloud.diplomaticquarterapp.PermissionManager.PermissionResultReceiver
+import com.ejada.hmg.MainActivity
+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
+
+/**
+ * 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
+) : PlatformView, MethodChannel.MethodCallHandler, PenNavUIDelegate {
+ // 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
+
+ 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()
+ )
+
+ 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() {
+ // Configure the PlugAndPlaySDK
+ val language = when (creationParams["languageCode"] as String) {
+ "ar" -> Languages.ar
+ "en" -> Languages.en
+ else -> {
+ Languages.en
+ }
+ }
+ Log.d(
+ "TAG",
+ "initPenguin: ${Languages.getLanguageEnum(creationParams["languageCode"] as String)}"
+ )
+ PlugAndPlaySDK.configuration = PlugAndPlayConfiguration.Builder()
+ .setBaseUrl(
+ creationParams["baseURL"] 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("")
+ .build()
+
+ // 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 { }
+
+ // Start the Penguin SDK
+ PlugAndPlaySDK.start(mContext, this)
+ }
+
+ /**
+ * Called when Penguin UI setup is successful.
+ *
+ * @param warningCode Optional warning code received from the SDK.
+ */
+ override fun onPenNavSuccess(warningCode: String?) {
+// if (_context is Activity) {
+// _context.runOnUiThread {
+// Toast.makeText(_context, "Success Info: $warningCode", Toast.LENGTH_SHORT).show()
+// }
+// } else {
+// println("the warming is presented $$warningCode")
+//// val handler = Handler(Looper.getMainLooper())
+//// handler.post {
+//// Toast.makeText(_context, "Success Info: $warningCode", Toast.LENGTH_SHORT).show()
+//// }
+// }
+ }
+
+ /**
+ * 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?
+ ) {
+ 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")
+ }
+ }
+}
+
diff --git a/android/build.gradle b/android/build.gradle
index dfa013d5..c5f1baa2 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -40,6 +40,7 @@ buildscript {
// classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath 'com.huawei.agconnect:agcp:1.5.2.300'
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12'
+ classpath "com.mapbox.gradle.plugins:access-token:0.4.0"
}
}
diff --git a/lib/pages/landing/widgets/services_view.dart b/lib/pages/landing/widgets/services_view.dart
index fbc3f19c..7109ad9a 100644
--- a/lib/pages/landing/widgets/services_view.dart
+++ b/lib/pages/landing/widgets/services_view.dart
@@ -32,6 +32,7 @@ import 'package:diplomaticquarterapp/pages/medical/smart_watch_health_data/smart
import 'package:diplomaticquarterapp/pages/paymentService/payment_service.dart';
import 'package:diplomaticquarterapp/services/authentication/auth_provider.dart';
import 'package:diplomaticquarterapp/theme/colors.dart';
+import 'package:diplomaticquarterapp/uitl/app-permissions.dart';
import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart';
import 'package:diplomaticquarterapp/uitl/location_util.dart';
import 'package:diplomaticquarterapp/uitl/penguin_method_channel.dart';
@@ -41,6 +42,7 @@ import 'package:diplomaticquarterapp/widgets/dialogs/covid_consent_dialog.dart';
import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:permission_handler/permission_handler.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../locator.dart';
@@ -168,8 +170,19 @@ class ServicesView extends StatelessWidget {
);
}
- initPenguinSDK() {
- PenguinMethodChannel.launch("penguin", projectViewModel.isArabic ? "ar" : "en", "1231755");
+ initPenguinSDK() async {
+ final bool permited = await AppPermission.askPenguinPermissions();
+ if (!permited) {
+ Map statuses = await [
+ Permission.location,
+ Permission.bluetooth,
+ Permission.bluetoothConnect,
+ Permission.bluetoothScan,
+ Permission.activityRecognition,
+ ].request().whenComplete(() {
+ PenguinMethodChannel.launch("penguin", projectViewModel.isArabic ? "ar" : "en", "1231755");
+ });
+ }
}
handleHomePageServices(HmgServices hmgServices, BuildContext context) {
diff --git a/lib/uitl/app-permissions.dart b/lib/uitl/app-permissions.dart
index 62fd484a..94697e4f 100644
--- a/lib/uitl/app-permissions.dart
+++ b/lib/uitl/app-permissions.dart
@@ -7,9 +7,7 @@ import 'package:permission_handler/permission_handler.dart';
import 'PlatformBridge.dart';
-class AppPermission{
-
-
+class AppPermission {
static Future askVideoCallPermission(BuildContext context) async {
if (!(await Permission.camera.request().isGranted) || !(await Permission.microphone.request().isGranted)) {
return false;
@@ -21,6 +19,30 @@ class AppPermission{
return true;
}
+//
+
+ // 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.BLUETOOTH_SCAN,
+ // Manifest.permission.BLUETOOTH_CONNECT,
+ // Manifest.permission.HIGH_SAMPLING_RATE_SENSORS,
+ // Manifest.permission.ACTIVITY_RECOGNITION
+
+ static Future askPenguinPermissions() async {
+ if (!(await Permission.location.request().isGranted) ||
+ !(await Permission.bluetooth.request().isGranted) ||
+ !(await Permission.bluetoothScan.request().isGranted) ||
+ !(await Permission.bluetoothConnect.request().isGranted) ||
+ !(await Permission.activityRecognition.request().isGranted)) {
+ return false;
+ }
+ return true;
+ }
+
static Future _drawOverAppsMessageDialog(BuildContext context) async {
ConfirmDialog dialog = new ConfirmDialog(
context: context,
@@ -34,4 +56,4 @@ class AppPermission{
cancelFunction: () => {});
dialog.showAlertDialog(context);
}
-}
\ No newline at end of file
+}