diff --git a/.gitignore b/.gitignore index cff0e374..55ad4693 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ pubspec.lock .pub-cache/ .pub/ /build/ +/ios/Frameworks/ # Web related lib/generated_plugin_registrant.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index f1799225..b5f2d00f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -56,6 +56,10 @@ android { compileSdkVersion 34 // ndkVersion "24.0.8215888" + buildFeatures { + viewBinding true + } + sourceSets { main.java.srcDirs += 'src/main/kotlin' } @@ -69,7 +73,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.ejada.hmg" - minSdkVersion 24 + minSdkVersion 26 targetSdkVersion 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName @@ -117,15 +121,16 @@ android { packagingOptions { exclude 'META-INF/proguard/androidx-annotations.pro' -// pickFirst 'lib/x86/libc++_shared.so' -// pickFirst 'lib/x86_64/libc++_shared.so' -// pickFirst 'lib/armeabi-v7a/libc++_shared.so' -// pickFirst 'lib/arm64-v8a/libc++_shared.so' -// pickFirst '**/*.so' + pickFirst 'lib/x86/libc++_shared.so' + pickFirst 'lib/x86_64/libc++_shared.so' + pickFirst 'lib/armeabi-v7a/libc++_shared.so' + pickFirst 'lib/arm64-v8a/libc++_shared.so' + pickFirst '**/*.so' } compileOptions { +// coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -136,13 +141,15 @@ flutter { } dependencies { +// coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "com.google.firebase:firebase-messaging:21.0.0" // implementation ('com.google.firebase:firebase-inappmessaging-display:19.1.2',{ // exclude group: 'com.google.protobuf',module: 'protobuf-javalite' // exclude group: 'com.google.protobuf',module: 'protobuf-lite' // }) - implementation 'pub.devrel:easypermissions:0.4.0' + implementation 'pub.devrel:easypermissions:3.0.0' // implementation 'com.google.firebase:firebase-inappmessaging-display:17.2.0' // implementation 'com.google.firebase:firebase-inappmessaging-display:17.2.0' implementation 'com.google.guava:guava:27.0.1-android' @@ -163,9 +170,34 @@ dependencies { // implementation "us.zoom.videosdk:ZoomVideoSDK:1.10.11" // implementation group: 'us.zoom.videosdk', name: 'zoomvideosdk-core', version: '1.10.11' - implementation "us.zoom.videosdk:zoomvideosdk-core:1.10.1" - implementation "us.zoom.videosdk:zoomvideosdk-annotation:1.10.1" - implementation "us.zoom.videosdk:zoomvideosdk-videoeffects:1.10.1" +// implementation "us.zoom.videosdk:zoomvideosdk-core:1.10.1" +// implementation "us.zoom.videosdk:zoomvideosdk-annotation:1.10.1" +// implementation "us.zoom.videosdk:zoomvideosdk-videoeffects:1.10.1" + + implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' + implementation 'com.squareup.retrofit2:adapter-java8:2.4.0' + implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.android.gms:play-services-location:21.3.0' + implementation 'com.squareup.okhttp3:okhttp:4.10.0' + implementation 'androidx.test.ext:junit:1.1.5' + implementation 'com.android.volley:volley:1.2.1' + def room_version = "2.4.0-alpha04" + implementation "androidx.room:room-runtime:$room_version" + annotationProcessor "androidx.room:room-compiler:$room_version" + implementation 'net.zetetic:android-database-sqlcipher:4.5.2' + 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.12.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' + +// implementation "com.mapbox.maps:android:10.16.6" + implementation 'com.mapbox.maps:android:11.3.1' + + implementation files('libs/PenNavUI.aar') + implementation files('libs/Penguin.aar') + implementation files('libs/PenguinRenderer.aar') implementation "org.jetbrains.anko:anko-commons:0.10.4" implementation 'com.github.kittinunf.fuel:fuel:2.3.0' //for JVM @@ -177,6 +209,7 @@ dependencies { implementation 'com.facebook.stetho:stetho-urlconnection:1.5.1' implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'com.google.android.material:material:1.9.0' androidTestImplementation "androidx.test:core:1.4.0" } diff --git a/android/app/libs/PenNavUI.aar b/android/app/libs/PenNavUI.aar new file mode 100644 index 00000000..6291c4ec 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..7cdc0b71 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..c60248c9 Binary files /dev/null and b/android/app/libs/PenguinRenderer.aar differ diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 69cd8026..ab7b2ca4 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -53,4 +53,20 @@ } -dontwarn com.opentok.android.** --dontwarn com.opentok.otc.** \ No newline at end of file +-dontwarn com.opentok.otc.** + +-dontwarn penguin.com.pennav.Model.Navigation.NearLandmark + +-keep,includedescriptorclasses class net.sqlcipher.** { *; } +-keep,includedescriptorclasses interface net.sqlcipher.** { *; } + +-keep class retrofit2.** { *; } +-keep class okhttp3.** { *; } +-dontwarn retrofit2.** + +-keep class com.google.gson.** { *; } +-dontwarn com.google.gson.** + +# Penguin classes +-keep class com.peng.pennavmap.models.** { *; } +-keep class com.peng.pennavmap.db.** { *; } \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b1540644..58ac88b7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,21 +8,36 @@ FlutterApplication and put your custom class here. --> - - - - + + + + - + - + + + + + + + + + + + + + + + + @@ -42,6 +57,7 @@ + @@ -59,13 +75,13 @@ 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 8aa1ef85..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,21 +1,19 @@ 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() { + @RequiresApi(Build.VERSION_CODES.O) override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); // Create Flutter Platform Bridge @@ -23,6 +21,7 @@ class MainActivity: FlutterFragmentActivity() { PlatformBridge(flutterEngine, this).create() OpenTokPlatformBridge(flutterEngine, this).create() + PenguinInPlatformBridge(flutterEngine, this).create() // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // val mChannel = NotificationChannel("video_call_noti", "video call", NotificationManager.IMPORTANCE_HIGH) @@ -43,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..93affaef --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PenguinInPlatformBridge.kt @@ -0,0 +1,52 @@ +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, + channel + ) + } + } + + 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..b308af05 --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/PermissionManager/PermissionHelper.kt @@ -0,0 +1,28 @@ +package com.cloud.diplomaticquarterapp.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/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/PenguinMethod.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/penguin/PenguinMethod.kt new file mode 100644 index 00000000..c76f9784 --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/penguin/PenguinMethod.kt @@ -0,0 +1,13 @@ +package com.cloud.diplomaticquarterapp.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/cloud/diplomaticquarterapp/penguin/PenguinNavigator.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/penguin/PenguinNavigator.kt new file mode 100644 index 00000000..98245cc9 --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/penguin/PenguinNavigator.kt @@ -0,0 +1,97 @@ +package com.cloud.diplomaticquarterapp.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/cloud/diplomaticquarterapp/penguin/PenguinView.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/penguin/PenguinView.kt new file mode 100644 index 00000000..743ce93b --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/penguin/PenguinView.kt @@ -0,0 +1,316 @@ +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 +import com.cloud.diplomaticquarterapp.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.PIReportIssue +/** + * 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 { + // 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() + ) + + 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 + } + } + Log.d( + "TAG", + "initPenguin: ${Languages.getLanguageEnum(creationParams["languageCode"] as String)}" + ) + 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("") + .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) + } + + + /** + * 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 + ) + + 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") + } + } +} + diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt index 7fbf859a..57da2eb8 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt @@ -22,7 +22,7 @@ import com.github.kittinunf.fuel.httpPost import com.google.gson.Gson import com.google.gson.reflect.TypeToken import io.flutter.plugin.common.MethodChannel -import org.jetbrains.anko.doAsyncResult +//import org.jetbrains.anko.doAsyncResult import org.json.JSONArray import org.json.JSONException import org.json.JSONObject @@ -149,7 +149,7 @@ fun sendNotification(context: Context, title: String, @Nullable subtitle: String val notificationPendingIntent = stackBuilder.getPendingIntent(getUniqueId(), PendingIntent.FLAG_UPDATE_CURRENT) val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) - .setSmallIcon(R.mipmap.ic_launcher) + .setSmallIcon(R.mipmap.ic_launcher_local) .setContentIntent(notificationPendingIntent) .setAutoCancel(true) .setContentTitle(title) @@ -208,7 +208,6 @@ fun httpPost(url: String, body: Map, onSuccess: (response: HTTP .header("Content-Type", "application/json") .header("Allow", "*/*") .response { request, response, result -> - result.doAsyncResult { } result.fold({ data -> val dataString = String(data) if (isJSONValid(dataString)) { diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_local.png similarity index 100% rename from android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-hdpi/ic_launcher_local.png diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_local.png similarity index 100% rename from android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-mdpi/ic_launcher_local.png diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_local.png similarity index 100% rename from android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xhdpi/ic_launcher_local.png diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_local.png similarity index 100% rename from android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xxhdpi/ic_launcher_local.png diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_local.png similarity index 100% rename from android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_local.png diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 4e107030..1093435a 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -19,4 +19,5 @@ Geofence requests happened too frequently. + sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg diff --git a/android/build.gradle b/android/build.gradle index b5185cca..c5f1baa2 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -13,6 +13,20 @@ buildscript { google() jcenter() maven { url 'https://developer.huawei.com/repo/' } + maven { + url 'https://api.mapbox.com/downloads/v2/releases/maven' + + credentials { + username = 'mapbox' + password = "sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg" + if (password == null || password == "") { + throw new GradleException("MAPBOX_DOWNLOADS_TOKEN isn't set. Set it to the project properties or to the enviroment variables.") + } + } + authentication { + basic(BasicAuthentication) + } + } // maven { // url "https://dl.bintray.com/kotlin/kotlin-eap/" @@ -26,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" } } @@ -34,12 +49,29 @@ allprojects { google() // jcenter() mavenCentral() + flatDir { + dirs 'libs' + } maven { url 'https://developer.huawei.com/repo/' } maven { url "https://artifactory.ess-dev.com/artifactory/gradle-dev-local" } + maven { + url 'https://api.mapbox.com/downloads/v2/releases/maven' + + credentials { + username = 'mapbox' + password = "sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg" + if (password == null || password == "") { + throw new GradleException("MAPBOX_DOWNLOADS_TOKEN isn't set. Set it to the project properties or to the enviroment variables.") + } + } + authentication { + basic(BasicAuthentication) + } + } } } diff --git a/android/gradle.properties b/android/gradle.properties index 06a44c05..cf554544 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -3,3 +3,5 @@ org.gradle.jvmargs=-Xmx4096m android.useAndroidX=true android.enableJetifier=true android.suppressUnsupportedCompileSdk=33 +MAPBOX_USER_NAME = "mapbox" +MAPBOX_DOWNLOADS_TOKEN="sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg" \ No newline at end of file diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index cb6be309..c5ff5d37 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 13.0 diff --git a/ios/Podfile b/ios/Podfile index 5495f0ca..5564c00b 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -33,6 +33,7 @@ target 'Runner' do pod 'OpenTok', '~> 2.22.0' pod 'VTO2Lib' + pod 'MapboxMaps', '10.18.2' flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end @@ -54,6 +55,7 @@ post_install do |installer| ] build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' + build_configuration.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES' xcconfig_path = build_configuration.base_configuration_reference.real_path xcconfig = File.read(xcconfig_path) xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, "TOOLCHAIN_DIR") @@ -62,6 +64,17 @@ post_install do |installer| if build_configuration.build_settings['WRAPPER_EXTENSION'] == 'bundle' build_configuration.build_settings['DEVELOPMENT_TEAM'] = '3A359E86ZF' end + if target.name == 'MapboxMobileEvents' + `xcrun -sdk iphoneos bitcode_strip -r Pods/MapboxMobileEvents/MapboxMobileEvents.xcframework/ios-arm64_armv7/MapboxMobileEvents.framework/MapboxMobileEvents -o Pods/MapboxMobileEvents/MapboxMobileEvents.xcframework/ios-arm64_armv7/MapboxMobileEvents.framework/MapboxMobileEvents` + end + + if target.name == 'MapboxCommon' + `xcrun -sdk iphoneos bitcode_strip -r Pods/MapboxCommon/MapboxCommon.xcframework/ios-arm64/MapboxCommon.framework/MapboxCommon -o Pods/MapboxCommon/MapboxCommon.xcframework/ios-arm64/MapboxCommon.framework/MapboxCommon` + end + + if target.name == 'MapboxCoreMaps' + `xcrun -sdk iphoneos bitcode_strip -r Pods/MapboxCoreMaps/MapboxCoreMaps.xcframework/ios-arm64/MapboxCoreMaps.framework/MapboxCoreMaps -o Pods/MapboxCoreMaps/MapboxCoreMaps.xcframework/ios-arm64/MapboxCoreMaps.framework/MapboxCoreMaps` + end end end end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index e7357518..5dad833f 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,18 +8,30 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 29631B9E2C96C7F600DF5916 /* PenguinNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29631B9D2C96C7F600DF5916 /* PenguinNavigator.swift */; }; 301C79AE27200D9F0016307B /* OpenTokRemoteVideoFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 301C79AD27200D9F0016307B /* OpenTokRemoteVideoFactory.swift */; }; 301C79B027200DED0016307B /* OpenTokLocalVideoFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 301C79AF27200DED0016307B /* OpenTokLocalVideoFactory.swift */; }; 306FE6C8271D790C002D6EFC /* OpenTokPlatformBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 306FE6C7271D790C002D6EFC /* OpenTokPlatformBridge.swift */; }; 306FE6CB271D8B73002D6EFC /* OpenTok.swift in Sources */ = {isa = PBXBuildFile; fileRef = 306FE6CA271D8B73002D6EFC /* OpenTok.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 4EE411B2E3CB5D4F81AE5078 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D1513E0724DAFB89C198BDD /* Pods_Runner.framework */; }; + 47C935002C91766800981BA7 /* Penguin.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47C934FC2C91766400981BA7 /* Penguin.xcframework */; }; + 47C935012C91766800981BA7 /* Penguin.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 47C934FC2C91766400981BA7 /* Penguin.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 47C935022C91766900981BA7 /* PenguinINRenderer.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47C934FA2C91766400981BA7 /* PenguinINRenderer.xcframework */; }; + 47C935032C91766900981BA7 /* PenguinINRenderer.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 47C934FA2C91766400981BA7 /* PenguinINRenderer.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 47C935042C91766A00981BA7 /* PenNavUI.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47C934FB2C91766400981BA7 /* PenNavUI.xcframework */; }; + 47C935052C91766A00981BA7 /* PenNavUI.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 47C934FB2C91766400981BA7 /* PenNavUI.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 762D738E274E42650063CE73 /* ring_30Sec.caf in Resources */ = {isa = PBXBuildFile; fileRef = 762D738C274E42650063CE73 /* ring_30Sec.caf */; }; 762D738F274E42650063CE73 /* ring_30Sec.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 762D738D274E42650063CE73 /* ring_30Sec.mp3 */; }; 76815B27275F381C00E66E94 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76815B26275F381C00E66E94 /* HealthKit.framework */; }; 76962ECE28AE5C10004EAE09 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 76962ECD28AE5C10004EAE09 /* GoogleService-Info.plist */; }; + 76D71B672C6B7F9C00DAFB84 /* HMGPenguinInPlatformBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D71B662C6B7F9C00DAFB84 /* HMGPenguinInPlatformBridge.swift */; }; + 76D71B6A2C6B819000DAFB84 /* PenguinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D71B692C6B819000DAFB84 /* PenguinModel.swift */; }; + 76D71B6C2C6B81B300DAFB84 /* PenguinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D71B6B2C6B81B300DAFB84 /* PenguinView.swift */; }; + 76D71B6E2C6B81CC00DAFB84 /* PenguinPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D71B6D2C6B81CC00DAFB84 /* PenguinPlugin.swift */; }; + 76D71B702C6B81EA00DAFB84 /* PenguinViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D71B6F2C6B81EA00DAFB84 /* PenguinViewFactory.swift */; }; 76F2556127F1FFED0062C1CD /* PassKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76F2556027F1FFED0062C1CD /* PassKit.framework */; }; + 76F9B0E22C6DED310064C51B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13E5B711BC29EB6ECCE1964C /* Pods_Runner.framework */; }; 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 */; }; @@ -43,12 +55,15 @@ /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + 768D81742C6DF6E4005C655F /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( + 47C935032C91766900981BA7 /* PenguinINRenderer.xcframework in Embed Frameworks */, + 47C935012C91766800981BA7 /* Penguin.xcframework in Embed Frameworks */, + 47C935052C91766A00981BA7 /* PenNavUI.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -56,14 +71,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 13E5B711BC29EB6ECCE1964C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 29631B9D2C96C7F600DF5916 /* PenguinNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinNavigator.swift; sourceTree = ""; }; 301C79AD27200D9F0016307B /* OpenTokRemoteVideoFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenTokRemoteVideoFactory.swift; sourceTree = ""; }; 301C79AF27200DED0016307B /* OpenTokLocalVideoFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenTokLocalVideoFactory.swift; sourceTree = ""; }; 306FE6C7271D790C002D6EFC /* OpenTokPlatformBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenTokPlatformBridge.swift; sourceTree = ""; }; 306FE6CA271D8B73002D6EFC /* OpenTok.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenTok.swift; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3D1513E0724DAFB89C198BDD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 47C934FA2C91766400981BA7 /* PenguinINRenderer.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PenguinINRenderer.xcframework; path = Frameworks/PenguinINRenderer.xcframework; sourceTree = ""; }; + 47C934FB2C91766400981BA7 /* PenNavUI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PenNavUI.xcframework; path = Frameworks/PenNavUI.xcframework; sourceTree = ""; }; + 47C934FC2C91766400981BA7 /* Penguin.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Penguin.xcframework; path = Frameworks/Penguin.xcframework; sourceTree = ""; }; 6EE8819867EC2775AB578377 /* 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 = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -74,6 +93,11 @@ 7643E4062BE0D0B400BD2F25 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/LaunchScreen.strings; sourceTree = ""; }; 76815B26275F381C00E66E94 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; 76962ECD28AE5C10004EAE09 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 76D71B662C6B7F9C00DAFB84 /* HMGPenguinInPlatformBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HMGPenguinInPlatformBridge.swift; sourceTree = ""; }; + 76D71B692C6B819000DAFB84 /* PenguinModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinModel.swift; sourceTree = ""; }; + 76D71B6B2C6B81B300DAFB84 /* PenguinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinView.swift; sourceTree = ""; }; + 76D71B6D2C6B81CC00DAFB84 /* PenguinPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinPlugin.swift; sourceTree = ""; }; + 76D71B6F2C6B81EA00DAFB84 /* PenguinViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinViewFactory.swift; sourceTree = ""; }; 76F2556027F1FFED0062C1CD /* PassKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PassKit.framework; path = System/Library/Frameworks/PassKit.framework; sourceTree = SDKROOT; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 838788A2BEDC4910F4B029A6 /* 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 = ""; }; @@ -110,10 +134,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 76F9B0E22C6DED310064C51B /* Pods_Runner.framework in Frameworks */, 76F2556127F1FFED0062C1CD /* PassKit.framework in Frameworks */, 76815B27275F381C00E66E94 /* HealthKit.framework in Frameworks */, E9620805255C2ED100D3A35D /* NetworkExtension.framework in Frameworks */, - 4EE411B2E3CB5D4F81AE5078 /* Pods_Runner.framework in Frameworks */, + 47C935042C91766A00981BA7 /* PenNavUI.xcframework in Frameworks */, + 47C935002C91766800981BA7 /* Penguin.xcframework in Frameworks */, + 47C935022C91766900981BA7 /* PenguinINRenderer.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -133,10 +160,13 @@ 555EAAA626EFB641859EF0BE /* Frameworks */ = { isa = PBXGroup; children = ( + 47C934FC2C91766400981BA7 /* Penguin.xcframework */, + 47C934FA2C91766400981BA7 /* PenguinINRenderer.xcframework */, + 47C934FB2C91766400981BA7 /* PenNavUI.xcframework */, 76F2556027F1FFED0062C1CD /* PassKit.framework */, 76815B26275F381C00E66E94 /* HealthKit.framework */, E9620804255C2ED100D3A35D /* NetworkExtension.framework */, - 3D1513E0724DAFB89C198BDD /* Pods_Runner.framework */, + 13E5B711BC29EB6ECCE1964C /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -151,6 +181,18 @@ path = Pods; sourceTree = ""; }; + 76D71B682C6B817500DAFB84 /* Penguin */ = { + isa = PBXGroup; + children = ( + 76D71B692C6B819000DAFB84 /* PenguinModel.swift */, + 76D71B6B2C6B81B300DAFB84 /* PenguinView.swift */, + 76D71B6D2C6B81CC00DAFB84 /* PenguinPlugin.swift */, + 76D71B6F2C6B81EA00DAFB84 /* PenguinViewFactory.swift */, + 29631B9D2C96C7F600DF5916 /* PenguinNavigator.swift */, + ); + path = Penguin; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -186,6 +228,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 76D71B682C6B817500DAFB84 /* Penguin */, 762D738C274E42650063CE73 /* ring_30Sec.caf */, 762D738D274E42650063CE73 /* ring_30Sec.mp3 */, 306FE6C9271D8B54002D6EFC /* OpenTok */, @@ -220,6 +263,7 @@ E923EFD52587443800E3E751 /* HMGPlatformBridge.swift */, 306FE6C7271D790C002D6EFC /* OpenTokPlatformBridge.swift */, E9F7623A25922BCE00FB5CCF /* FlutterConstants.swift */, + 76D71B662C6B7F9C00DAFB84 /* HMGPenguinInPlatformBridge.swift */, ); path = Helper; sourceTree = ""; @@ -254,7 +298,7 @@ 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, + 768D81742C6DF6E4005C655F /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 487FDD6493EB9AE7D8E39485 /* [CP] Embed Pods Frameworks */, 4671D3CD126F6635F4B75A6E /* [CP] Copy Pods Resources */, @@ -274,7 +318,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -419,12 +463,18 @@ buildActionMask = 2147483647; files = ( 306FE6C8271D790C002D6EFC /* OpenTokPlatformBridge.swift in Sources */, + 76D71B6E2C6B81CC00DAFB84 /* PenguinPlugin.swift in Sources */, E923EFD225863FDF00E3E751 /* GeoZoneModel.swift in Sources */, E91B539A256AAA6500E96549 /* MainFlutterVC.swift in Sources */, + 76D71B672C6B7F9C00DAFB84 /* HMGPenguinInPlatformBridge.swift in Sources */, E91B539C256AAA6500E96549 /* HMG_GUEST_bkp.swift in Sources */, E91B5396256AAA6500E96549 /* GlobalHelper.swift in Sources */, E923EFD4258645C100E3E751 /* HMG_Geofence.swift in Sources */, E923EFD62587443800E3E751 /* HMGPlatformBridge.swift in Sources */, + 76D71B6C2C6B81B300DAFB84 /* PenguinView.swift in Sources */, + 29631B9E2C96C7F600DF5916 /* PenguinNavigator.swift in Sources */, + 76D71B6A2C6B819000DAFB84 /* PenguinModel.swift in Sources */, + 76D71B702C6B81EA00DAFB84 /* PenguinViewFactory.swift in Sources */, 301C79AE27200D9F0016307B /* OpenTokRemoteVideoFactory.swift in Sources */, E9F7623B25922BCE00FB5CCF /* FlutterConstants.swift in Sources */, 306FE6CB271D8B73002D6EFC /* OpenTok.swift in Sources */, @@ -528,6 +578,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; @@ -553,10 +604,13 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = singlefile; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -675,6 +729,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; @@ -700,10 +755,14 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = singlefile; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -713,6 +772,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; @@ -738,11 +798,14 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = singlefile; "SWIFT_COMPILATION_MODE[arch=*]" = singlefile; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index fab8f735..b2d2f7e5 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ 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"{ + 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) + + result(nil) // Call result to indicate the method was successfully handled + } + +} diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 43474d39..13782c80 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -39,7 +39,7 @@ LSRequiresIPhoneOS MinimumOSVersion - 11.0 + 13.0 NFCReaderUsageDescription This app requires NFC Usage access to allow for Online CheckIn for appointments. NSAppTransportSecurity @@ -60,7 +60,7 @@ NSCalendarsUsageDescription This app requires calendar access to set reminders for Virtual & Normal Appointments. NSCalendarsWriteOnlyAccessUsageDescription - This app requires calendar access to set reminders for Virtual & Normal Appointments. + This app requires calendar access to set reminders for Virtual & Normal Appointments. NSCalendarsFullAccessUsageDescription This app requires calendar access to set reminders for Virtual & Normal Appointments. NSCameraUsageDescription diff --git a/ios/Runner/Penguin/PenguinModel.swift b/ios/Runner/Penguin/PenguinModel.swift new file mode 100644 index 00000000..e41979d6 --- /dev/null +++ b/ios/Runner/Penguin/PenguinModel.swift @@ -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 + } +} diff --git a/ios/Runner/Penguin/PenguinNavigator.swift b/ios/Runner/Penguin/PenguinNavigator.swift new file mode 100644 index 00000000..e7ce55b4 --- /dev/null +++ b/ios/Runner/Penguin/PenguinNavigator.swift @@ -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) + } + } + } +} diff --git a/ios/Runner/Penguin/PenguinPlugin.swift b/ios/Runner/Penguin/PenguinPlugin.swift new file mode 100644 index 00000000..029bec35 --- /dev/null +++ b/ios/Runner/Penguin/PenguinPlugin.swift @@ -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") +// } +//} diff --git a/ios/Runner/Penguin/PenguinView.swift b/ios/Runner/Penguin/PenguinView.swift new file mode 100644 index 00000000..81377f7b --- /dev/null +++ b/ios/Runner/Penguin/PenguinView.swift @@ -0,0 +1,224 @@ +// +// 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? + + + /** + * 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() + 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 =========") + } + + /** + * Called when the Penguin UI setup is successful. + */ + func onPenNavSuccess() { + print("====== onPenNavSuccess =========") + // Obtain the FlutterViewController instance + let controller: FlutterViewController = UIApplication.shared.windows.first?.rootViewController as! FlutterViewController + + print("====== after contoller 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)") + + } + + + + +} diff --git a/ios/Runner/Penguin/PenguinViewFactory.swift b/ios/Runner/Penguin/PenguinViewFactory.swift new file mode 100644 index 00000000..a88bb5d0 --- /dev/null +++ b/ios/Runner/Penguin/PenguinViewFactory.swift @@ -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() + } +} diff --git a/lib/config/config.dart b/lib/config/config.dart index 75eac393..16a900ec 100644 --- a/lib/config/config.dart +++ b/lib/config/config.dart @@ -22,8 +22,8 @@ var PACKAGES_ORDER_HISTORY = '/api/orders/items'; var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara'; // var BASE_URL = 'http://10.50.100.198:2018/'; // var BASE_URL = 'http://10.50.100.198:4422/'; -// var BASE_URL = 'https://uat.hmgwebservices.com/'; -var BASE_URL = 'https://hmgwebservices.com/'; +var BASE_URL = 'https://uat.hmgwebservices.com/'; +// var BASE_URL = 'https://hmgwebservices.com/'; // var BASE_URL = 'http://10.20.200.111:1010/'; // var BASE_URL = 'https://orash.cloudsolutions.com.sa/'; // var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/'; diff --git a/lib/config/localized_values.dart b/lib/config/localized_values.dart index a2165f33..7107d357 100644 --- a/lib/config/localized_values.dart +++ b/lib/config/localized_values.dart @@ -1976,5 +1976,8 @@ const Map localizedValues = { "selectHospitalBloodDonation": {"en": "Please select the hospital you want to book an appointment with: ", "ar": "يرجى اختيار المستشفى الذي تريد حجز موعد معه:"}, "wecare": {"en": "We Care", "ar": "نحن نهتم"}, "myinstructions": {"en": "My Instructions", "ar": "تعليماتي"}, + "clinicLocation": {"en": "Clinic Location", "ar": "موقع العيادة"}, "searchClinic": {"en": "Search Clinic", "ar": "بحث العيادة"}, + "selectBranch":{"en":"Select Branch", "ar":"اختر الفرع"}, + "searchByBranch":{"en":"Search By Branch Name", "ar":"البحث عن طريق اسم الفرع"} }; diff --git a/lib/core/service/client/base_app_client.dart b/lib/core/service/client/base_app_client.dart index 98a884f4..3bd1cf62 100644 --- a/lib/core/service/client/base_app_client.dart +++ b/lib/core/service/client/base_app_client.dart @@ -199,9 +199,9 @@ class BaseAppClient { // } // if (AppGlobal.isNetworkDebugEnabled) { - // print("URL : $url"); - // final jsonBody = json.encode(body); - // print(jsonBody); + print("URL : $url"); + final jsonBody = json.encode(body); + print(jsonBody); // } if (await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck)) { diff --git a/lib/core/viewModels/project_view_model.dart b/lib/core/viewModels/project_view_model.dart index 7216da7b..61b453a4 100644 --- a/lib/core/viewModels/project_view_model.dart +++ b/lib/core/viewModels/project_view_model.dart @@ -19,6 +19,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_datetime_picker_plus/flutter_datetime_picker_plus.dart'; bool isAppArabic = false; + class ProjectViewModel extends BaseViewModel { GAnalytics get analytics => locator(); @@ -38,6 +39,7 @@ class ProjectViewModel extends BaseViewModel { dynamic searchvalue; bool isLogin = false; bool _isAllAppointmentsLoaded = false; + bool get isAllAppointmentsLoaded => _isAllAppointmentsLoaded; bool isPatientAdmitted = false; bool patientHasAdmissionRequest = false; @@ -45,15 +47,17 @@ class ProjectViewModel extends BaseViewModel { GetAdmissionInfoResponseModel getAdmissionInfoResponseModel = GetAdmissionInfoResponseModel(); GetAdmissionRequestInfoResponseModel getAdmissionRequestInfoResponseModel = GetAdmissionRequestInfoResponseModel(); + bool isIndoorNavigationEnabled = false; + void setIsAllAppointmentsLoaded(bool value) { _isAllAppointmentsLoaded = value; notifyListeners(); } - RegisterInfoResponse _registerInfo = RegisterInfoResponse(); RegisterInfoResponse get registerInfo => _registerInfo; + dynamic get searchValue => searchvalue; Locale get appLocal => _appLocale; @@ -70,7 +74,9 @@ class ProjectViewModel extends BaseViewModel { List _projectDetailListModel = []; List get privileges => isLoginChild ? privilegeChildUser : privilegeChildUser; + List get vidaPlusProjectList => _vidaPlusProjectListModel; + List get hMCProjectListModel => _hMCProjectListModel; List get projectDetailListModel => _projectDetailListModel; @@ -137,6 +143,11 @@ class ProjectViewModel extends BaseViewModel { notifyListeners(); } + setIsIndoorNavigationEnabled(bool isEnabled) { + this.isIndoorNavigationEnabled = isEnabled; + notifyListeners(); + } + setPatientHasAdmissionRequest(bool hasAdmissionRequest) { this.patientHasAdmissionRequest = hasAdmissionRequest; notifyListeners(); @@ -200,8 +211,6 @@ class ProjectViewModel extends BaseViewModel { notifyListeners(); } - - bool havePrivilege(int id) { bool isHavePrivilege = false; try { @@ -222,7 +231,7 @@ class ProjectViewModel extends BaseViewModel { @override void dispose() { - if (subscription != null) subscription.cancel(); + if (subscription != null) subscription.cancel(); super.dispose(); } diff --git a/lib/pages/AlHabibMedicalService/all_habib_medical_service_page2.dart b/lib/pages/AlHabibMedicalService/all_habib_medical_service_page2.dart index f23d175d..420f08f1 100644 --- a/lib/pages/AlHabibMedicalService/all_habib_medical_service_page2.dart +++ b/lib/pages/AlHabibMedicalService/all_habib_medical_service_page2.dart @@ -37,6 +37,7 @@ class _AllHabibMedicalSevicePage2State extends State AuthProvider authProvider = new AuthProvider(); WeatherService _weatherService = WeatherService(); double weatherNum = 30; + late ProjectViewModel projectViewModel; @override void initState() { @@ -235,7 +236,7 @@ class _AllHabibMedicalSevicePage2State extends State itemCount: hmgServices.length, padding: EdgeInsets.zero, itemBuilder: (BuildContext context, int index) { - return ServicesView(hmgServices[index], index, false); + return ServicesView(hmgServices[index], index, false, projectViewModel); }, ), ), diff --git a/lib/pages/InPatientServices/components/inpatient_pending_advance_payment.dart b/lib/pages/InPatientServices/components/inpatient_pending_advance_payment.dart index 82e6ab96..8281450b 100644 --- a/lib/pages/InPatientServices/components/inpatient_pending_advance_payment.dart +++ b/lib/pages/InPatientServices/components/inpatient_pending_advance_payment.dart @@ -272,7 +272,7 @@ class _InPatientPendingAdvancePaymentState extends State { navigateToInsertComplaint(); locator().appointment.appointment_detail_action(appointment: widget.appo, action: 'raise complaint'); break; + + case "goToNavigation": + initPenguinSDK(); + locator().appointment.appointment_detail_action(appointment: widget.appo, action: 'navigate to clinic'); + break; + } + } + + initPenguinSDK() async { + NavigationClinicDetails data = NavigationClinicDetails(); + // data.clinicId = widget.appo.clinicID.toString(); + data.clinicId = "49"; + data.patientId = widget.appo.patientID.toString(); + data.projectId = widget.appo.projectID.toString(); + 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", widget.projectViewModel!.isArabic ? "ar" : "en", widget.projectViewModel!.authenticatedUserObject.user.patientID.toString(), details: data); + }); } } diff --git a/lib/pages/ToDoList/ToDo.dart b/lib/pages/ToDoList/ToDo.dart index bb1c3f93..b9ede8f8 100644 --- a/lib/pages/ToDoList/ToDo.dart +++ b/lib/pages/ToDoList/ToDo.dart @@ -29,10 +29,12 @@ import 'package:diplomaticquarterapp/services/livecare_services/livecare_provide import 'package:diplomaticquarterapp/services/payfort_services/payfort_project_details_resp_model.dart'; import 'package:diplomaticquarterapp/services/payfort_services/payfort_view_model.dart'; import 'package:diplomaticquarterapp/theme/colors.dart'; +import 'package:diplomaticquarterapp/uitl/app-permissions.dart'; import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart'; import 'package:diplomaticquarterapp/uitl/app_toast.dart'; import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; +import 'package:diplomaticquarterapp/uitl/penguin_method_channel.dart'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/uitl/utils.dart'; import 'package:diplomaticquarterapp/uitl/utils_new.dart'; @@ -50,6 +52,7 @@ import 'package:flutter_countdown_timer/current_remaining_time.dart'; import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; class ToDo extends StatefulWidget { @@ -354,23 +357,56 @@ class _ToDoState extends State with SingleTickerProviderStateMixin { style: TextStyle(fontSize: 10, fontWeight: FontWeight.w600, color: Color(0xff2E303A), letterSpacing: -0.48, height: 25 / 16), ), ), - InkWell( - onTap: () { - navigateToAppointmentDetails(context, appoList[index]); - }, - child: Padding( - padding: const EdgeInsets.only(top: 0.0), - child: Text( - TranslationBase.of(context).moreDetails, - style: TextStyle( - fontSize: 10, - fontWeight: FontWeight.w600, - color: CustomColors.accentColor, - letterSpacing: -0.48, - height: 25 / 16, - decoration: TextDecoration.underline), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () { + navigateToAppointmentDetails(context, appoList[index]); + }, + child: Padding( + padding: const EdgeInsets.only(top: 0.0), + child: Text( + TranslationBase.of(context).moreDetails, + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.w600, + color: CustomColors.accentColor, + letterSpacing: -0.48, + height: 25 / 16, + decoration: TextDecoration.underline), + ), + ), ), - ), + if (projectViewModel.isIndoorNavigationEnabled) + InkWell( + onTap: () { + NavigationClinicDetails data = NavigationClinicDetails(); + // data.clinicId = appoList[index].clinicID.toString(); + data.clinicId = "46"; + data.patientId = appoList[index].patientID.toString(); + data.projectId = appoList[index].projectID.toString(); + initPenguinSDK(data); + }, + child: Column( + children: [ + SvgPicture.asset( + "assets/images/new/Indoor_nav_appo.svg", + ), + Text( + TranslationBase.of(context).clinicLocation, + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.w600, + color: CustomColors.accentColor, + letterSpacing: -0.48, + height: 25 / 16, + decoration: TextDecoration.underline), + ), + ], + ), + ), + ], ), ], ), @@ -530,6 +566,21 @@ class _ToDoState extends State with SingleTickerProviderStateMixin { ); } + initPenguinSDK(NavigationClinicDetails data) 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", projectViewModel.authenticatedUserObject.user.patientID.toString(), details: data); + }); + } + } + String getNextActionImage(nextAction) { switch (nextAction) { case 0: diff --git a/lib/pages/landing/fragments/home_page_fragment2.dart b/lib/pages/landing/fragments/home_page_fragment2.dart index c1c922ac..7711b4ff 100644 --- a/lib/pages/landing/fragments/home_page_fragment2.dart +++ b/lib/pages/landing/fragments/home_page_fragment2.dart @@ -52,7 +52,11 @@ class _HomePageFragment2State extends State { hmgServices.add(HmgServices(0, TranslationBase.of(context).book, TranslationBase.of(context).appointmentLabel, "assets/images/new/book appointment.svg", isLogin)); hmgServices.add(HmgServices(1, TranslationBase.of(context).liveCare, TranslationBase.of(context).onlineConsulting, "assets/images/new/Live_Care.svg", isLogin)); - hmgServices.add(HmgServices(2, TranslationBase.of(context).emergencyTitle, TranslationBase.of(context).emergencySubtitle, "assets/images/new/emergency.svg", isLogin)); + + projectViewModel.isIndoorNavigationEnabled + ? hmgServices.add(HmgServices(2, "Hospital", "Navigation", "assets/images/new/indoor_nav_home.svg", isLogin)) + : hmgServices.add(HmgServices(2, TranslationBase.of(context).emergencyTitle, TranslationBase.of(context).emergencySubtitle, "assets/images/new/emergency.svg", isLogin)); + hmgServices.add(HmgServices(3, TranslationBase.of(context).hhcHome, TranslationBase.of(context).healthCare, "assets/images/new/HHC.svg", isLogin)); hmgServices.add(HmgServices(4, TranslationBase.of(context).checkup, TranslationBase.of(context).comprehensive, "assets/images/new/comprehensive_checkup.svg", isLogin)); hmgServices.add(HmgServices(5, TranslationBase.of(context).online, TranslationBase.of(context).payment, "assets/images/new/paymentMethods.png", isLogin)); @@ -296,15 +300,16 @@ class _HomePageFragment2State extends State { child: ServicesView( new HmgServices(23, TranslationBase.of(context).InPatient, TranslationBase.of(context).inPatientServices, "assets/images/new/InPatient.svg", false), 23, - true)), + true, + projectViewModel)), ), ), Expanded( flex: 4, child: AspectRatio( aspectRatio: 1.0, - child: ServicesView( - new HmgServices(5, TranslationBase.of(context).online, TranslationBase.of(context).payment, "assets/images/new/paymentMethods.png", false), 2, true)), + child: ServicesView(new HmgServices(5, TranslationBase.of(context).online, TranslationBase.of(context).payment, "assets/images/new/paymentMethods.png", false), 2, + true, projectViewModel)), ), ], ), @@ -322,7 +327,7 @@ class _HomePageFragment2State extends State { itemCount: hmgServices.length, padding: EdgeInsets.zero, itemBuilder: (BuildContext context, int index) { - return ServicesView(hmgServices[index], index, true); + return ServicesView(hmgServices[index], index, true, projectViewModel); }, ), ), @@ -341,7 +346,7 @@ class _HomePageFragment2State extends State { itemCount: hmgServices.length, padding: EdgeInsets.zero, itemBuilder: (BuildContext context, int index) { - return ServicesView(hmgServices[index], index, true); + return ServicesView(hmgServices[index], index, true, projectViewModel, isLocked: hmgServices[index].action == 2 ? !projectViewModel.havePrivilege(107) : false); }, ), ), @@ -489,8 +494,7 @@ class _HomePageFragment2State extends State { flex: 1, child: InkWell( onTap: () { - if (projectViewModel.havePrivilege(100)) - widget.onPharmacyClick!(); + if (projectViewModel.havePrivilege(100)) widget.onPharmacyClick!(); }, child: Stack(children: [ Container( diff --git a/lib/pages/landing/landing_page.dart b/lib/pages/landing/landing_page.dart index 6fe004fc..0cf6fd2c 100644 --- a/lib/pages/landing/landing_page.dart +++ b/lib/pages/landing/landing_page.dart @@ -43,7 +43,7 @@ import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_app_icon_badge/flutter_app_icon_badge.dart'; +// import 'package:flutter_app_icon_badge/flutter_app_icon_badge.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_zoom_videosdk/native/zoom_videosdk.dart'; @@ -664,7 +664,7 @@ class _LandingPageState extends State with WidgetsBindingObserver { notificationCount = value['List_PatientDashboard'][0]['UnreadPatientNotificationCount'] > 99 ? '99+' : value['List_PatientDashboard'][0]['UnreadPatientNotificationCount'].toString(); model.setState(model.count, 0, true, notificationCount); sharedPref.setString(NOTIFICATION_COUNT, notificationCount); - FlutterAppIconBadge.updateBadge(int.parse(notificationCount)); + // FlutterAppIconBadge.updateBadge(int.parse(notificationCount)); } }); // if (await AppSharedPreferences().getBool(IS_LAST_APPOINTMENT_RATE_SHOWN) == null || !await AppSharedPreferences().getBool(IS_LAST_APPOINTMENT_RATE_SHOWN)) { diff --git a/lib/pages/landing/widgets/services_view.dart b/lib/pages/landing/widgets/services_view.dart index 5635a4a6..c69f772c 100644 --- a/lib/pages/landing/widgets/services_view.dart +++ b/lib/pages/landing/widgets/services_view.dart @@ -4,7 +4,9 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:diplomaticquarterapp/analytics/google-analytics.dart'; import 'package:diplomaticquarterapp/config/config.dart'; import 'package:diplomaticquarterapp/config/size_config.dart'; +import 'package:diplomaticquarterapp/core/model/hospitals/hospitals_model.dart'; import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/pharmacy_module_view_model.dart'; +import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart'; import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart'; import 'package:diplomaticquarterapp/models/hmg_services.dart'; import 'package:diplomaticquarterapp/pages/AlHabibMedicalService/%E2%80%8B%20health_calculators.dart'; @@ -31,14 +33,20 @@ 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'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart'; import 'package:diplomaticquarterapp/uitl/utils_new.dart'; import 'package:diplomaticquarterapp/widgets/dialogs/covid_consent_dialog.dart'; +import 'package:diplomaticquarterapp/widgets/dialogs/location_selection_dialog.dart'; +import 'package:diplomaticquarterapp/widgets/dialogs/radio_selection_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'; @@ -52,8 +60,10 @@ class ServicesView extends StatelessWidget { PharmacyModuleViewModel pharmacyModuleViewModel = locator(); late LocationUtils locationUtils; bool isHomePage; + late ProjectViewModel projectViewModel; + bool isLocked; - ServicesView(this.hmgServices, this.index, this.isHomePage); + ServicesView(this.hmgServices, this.index, this.isHomePage, this.projectViewModel, {this.isLocked = false}); @override Widget build(BuildContext context) { @@ -159,12 +169,57 @@ class ServicesView extends StatelessWidget { ], ), ), + isLocked + ? Container( + width: double.infinity, + height: double.infinity, + decoration: containerRadiusWithGradientServices(20, lightColor: Colors.grey.withOpacity(0.6), darkColor: Colors.grey.withOpacity(0.6)), + child: Icon( + Icons.lock_outline, + size: 40, + ), + ) + : Container() ], ), ), ); } + openNavigationProjectSelection(BuildContext context) { + int _selectedHospitalIndex = 0; + List projectsListLocal = []; + Utils.navigationProjectsList.forEach((v) { + projectsListLocal.add(new HospitalsModel.fromJson(v)); + }); + showDialog( + context: context, + builder: (cxt) => LocationSelectionDialog( + data: projectsListLocal, + selectedIndex: _selectedHospitalIndex, + onValueSelected: (index) { + _selectedHospitalIndex = index; + initPenguinSDK(projectsListLocal[index].iD); + }, + ), + ); + } + + initPenguinSDK(int projectID) 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) { if (hmgServices.action == 0) { Navigator.push(context, FadePage(page: Search())); @@ -172,8 +227,13 @@ class ServicesView extends StatelessWidget { } else if (hmgServices.action == 1) { openLiveCare(context); } else if (hmgServices.action == 2) { - Navigator.push(context, FadePage(page: ErOptions(isAppbar: true))); - locator().hmgServices.logServiceName('emergency service'); + // initPenguinSDK(); + if (projectViewModel.isIndoorNavigationEnabled) { + if (!isLocked) openNavigationProjectSelection(context); + } else { + Navigator.push(context, FadePage(page: ErOptions(isAppbar: true))); + locator().hmgServices.logServiceName('emergency service'); + } } else if (hmgServices.action == 3) { Navigator.push(context, FadePage(page: HomeHealthCarePage())); locator().hmgServices.logServiceName('home health care'); @@ -266,10 +326,8 @@ class ServicesView extends StatelessWidget { } else if (hmgServices.action == 21) { Navigator.of(context).push( MaterialPageRoute( - builder: (BuildContext context) => MyWebView( - title: "HMG News", - selectedUrl:"https://twitter.com/hmg" //"https://twitter.com/hashtag/مجموعة_د_سليمان_الحبيب_الطبية?src=hashtag_click&f=live", - ), + builder: (BuildContext context) => MyWebView(title: "HMG News", selectedUrl: "https://twitter.com/hmg" //"https://twitter.com/hashtag/مجموعة_د_سليمان_الحبيب_الطبية?src=hashtag_click&f=live", + ), ), ); locator().hmgServices.logServiceName('latest news'); diff --git a/lib/pages/livecare/video-call-web-page.dart b/lib/pages/livecare/video-call-web-page.dart index 46a29924..f755d1b0 100644 --- a/lib/pages/livecare/video-call-web-page.dart +++ b/lib/pages/livecare/video-call-web-page.dart @@ -14,7 +14,7 @@ class VideoCallWebPage extends StatelessWidget{ final String receiverId; final String callerId; VideoCallWebPage({required this.receiverId, required this.callerId}){ - request = URLRequest(url: Uri.parse("https://vcallapi.hmg.com/Mobileindex.html?username=$receiverId&doctorid=$callerId")); + request = URLRequest(url: WebUri.uri(Uri.parse("https://vcallapi.hmg.com/Mobileindex.html?username=$receiverId&doctorid=$callerId"))); } InAppWebViewGroupOptions options = InAppWebViewGroupOptions( diff --git a/lib/pages/medical/smart_watch_health_data/syncHealthData.dart b/lib/pages/medical/smart_watch_health_data/syncHealthData.dart index e6ec5c9a..6d6fdf4f 100644 --- a/lib/pages/medical/smart_watch_health_data/syncHealthData.dart +++ b/lib/pages/medical/smart_watch_health_data/syncHealthData.dart @@ -34,7 +34,7 @@ class _syncHealthDataButtonState extends State { int MedCategoryID = 0; - HealthFactory health = HealthFactory(); + // HealthFactory health = HealthFactory(); List types = [HealthDataType.STEPS, HealthDataType.HEART_RATE, Platform.isAndroid ? HealthDataType.DISTANCE_DELTA : HealthDataType.DISTANCE_WALKING_RUNNING]; @override @@ -48,29 +48,29 @@ class _syncHealthDataButtonState extends State { } checkPermissions() async { - if (Platform.isAndroid) { - if (await PermissionService.isHealthDataPermissionEnabled()) { - await health.requestAuthorization(types).then((value) { - if (value) { - readAll(); - } - }); - } else { - Utils.showPermissionConsentDialog(context, TranslationBase.of(context).physicalActivityPermission, () async { - await health.requestAuthorization(types).then((value) { - if (value) { - readAll(); - } - }); - }); - } - } else { - await health.requestAuthorization(types).then((value) { - if (value) { - readAll(); - } - }); - } + // if (Platform.isAndroid) { + // if (await PermissionService.isHealthDataPermissionEnabled()) { + // await health.requestAuthorization(types).then((value) { + // if (value) { + // readAll(); + // } + // }); + // } else { + // Utils.showPermissionConsentDialog(context, TranslationBase.of(context).physicalActivityPermission, () async { + // await health.requestAuthorization(types).then((value) { + // if (value) { + // readAll(); + // } + // }); + // }); + // } + // } else { + // await health.requestAuthorization(types).then((value) { + // if (value) { + // readAll(); + // } + // }); + // } } void readAll() async { @@ -83,13 +83,13 @@ class _syncHealthDataButtonState extends State { DateTime startDate = DateTime.now().subtract(new Duration(days: 30)); try { - List healthData = await health.getHealthDataFromTypes(startDate, DateTime.now(), types); - _healthDataList.addAll(healthData); + // List healthData = await health.getHealthDataFromTypes(startDate, DateTime.now(), types); + // _healthDataList.addAll(healthData); } catch (e) { print("Caught exception in getHealthDataFromTypes: $e"); } - _healthDataList = HealthFactory.removeDuplicates(_healthDataList); + // _healthDataList = HealthFactory.removeDuplicates(_healthDataList); _healthDataList.forEach((x) { if (x.type == HealthDataType.STEPS) { diff --git a/lib/services/payfort_services/payfort_service.dart b/lib/services/payfort_services/payfort_service.dart index 755ec7b7..64a015cd 100644 --- a/lib/services/payfort_services/payfort_service.dart +++ b/lib/services/payfort_services/payfort_service.dart @@ -54,7 +54,7 @@ class PayfortService extends BaseService { "Pat_Token": result.tokenName, "IsRefund": false, "RemmeberMe": false, - "Reconciliation_Reference": result.reconciliationReference, + // "Reconciliation_Reference": result.reconciliationReference, "LanguageID": 1, "PatientID": patientID }; diff --git a/lib/services/permission/permission_service.dart b/lib/services/permission/permission_service.dart index 641d6444..cc73c303 100644 --- a/lib/services/permission/permission_service.dart +++ b/lib/services/permission/permission_service.dart @@ -3,7 +3,7 @@ import 'package:diplomaticquarterapp/core/service/base_service.dart'; import 'package:flutter/material.dart'; import 'package:localstorage/localstorage.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:vibration/vibration.dart'; +// import 'package:vibration/vibration.dart'; import 'package:geolocator/geolocator.dart' as geo; class PermissionService extends BaseService { @@ -25,10 +25,10 @@ class PermissionService extends BaseService { vibrate(callback, context) async { if (callback == null) return null; if (isVibrationEnabled() == true) { - if (await Vibration.hasVibrator() !=null) { - Vibration.vibrate(duration: 100); - callback(); - } + // if (await Vibration.hasVibrator() !=null) { + // Vibration.vibrate(duration: 100); + // callback(); + // } } else { callback(); } 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 +} diff --git a/lib/uitl/penguin_method_channel.dart b/lib/uitl/penguin_method_channel.dart new file mode 100644 index 00000000..12421f5e --- /dev/null +++ b/lib/uitl/penguin_method_channel.dart @@ -0,0 +1,98 @@ +import 'package:flutter/services.dart'; + +class PenguinMethodChannel { + static const MethodChannel _channel = MethodChannel('launch_penguin_ui'); + + static Future launch(String storyboardName, String languageCode, String username, {NavigationClinicDetails? details}) async { + try { + await _channel.invokeMethod('launchPenguin', { + "storyboardName": storyboardName, + "baseURL": "https://prod.hmg.nav.penguinin.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://prod.hmg.nav.penguinin.com", + "positionURL": "https://prod.hmg.nav.penguinin.com", + "dataServiceName": "api", + "positionServiceName": "pe", + "clientID": "HMG", + "username": "test", + "isSimulationModeEnabled": false, + "isShowUserName": false, + "isUpdateUserLocationSmoothly": true, + "isEnableReportIssue": true, + "languageCode": languageCode, + "clientKey": "UGVuZ3VpbklOX1Blbk5hdl9QSUY=", + "mapBoxKey": "sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg", + // "clinicID": details?.clinicId ?? "", + "clinicID": details?.clinicId ?? "", // 46 ,49, 133 + "patientID": details?.patientId ?? "", + "projectID": details?.projectId ?? "", + }); + } 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 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; +} diff --git a/lib/uitl/push-notification-handler.dart b/lib/uitl/push-notification-handler.dart index f1c53bbe..5bf073b3 100644 --- a/lib/uitl/push-notification-handler.dart +++ b/lib/uitl/push-notification-handler.dart @@ -15,6 +15,7 @@ import 'package:diplomaticquarterapp/pages/webRTC/OpenTok/OpenTok.dart'; import 'package:diplomaticquarterapp/uitl/LocalNotification.dart'; import 'package:diplomaticquarterapp/uitl/app-permissions.dart'; import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; +import 'package:diplomaticquarterapp/uitl/penguin_method_channel.dart'; import 'package:diplomaticquarterapp/uitl/utils.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart' as fir; @@ -388,9 +389,16 @@ class PushNotificationHandler { if (remoteMessage.data.isEmpty) { return; } + debugPrint('the value of the remote message is ${remoteMessage.data}'); if (remoteMessage.data['is_call'] == 'true' || remoteMessage.data['is_call'] == true) { _incomingCall(remoteMessage.data); // showCallkitIncoming(remoteMessage.data); + }if(remoteMessage.data['is_navigation'] == 'true' || remoteMessage.data['is_navigation'] == true ){ + NavigationClinicDetails data = NavigationClinicDetails(); + data.clinicId = remoteMessage.data['clinic_Id']; + data.patientId = remoteMessage.data['patient_Id']; + data.projectId = remoteMessage.data['project_Id']; + initPenguinSDK(data); } else { GetNotificationsResponseModel notification = new GetNotificationsResponseModel(); @@ -408,6 +416,21 @@ class PushNotificationHandler { } } + initPenguinSDK(NavigationClinicDetails data) 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", "en", "1231755", details: data); + }); + } + } + onToken(String token) async { // print("Push Notification Token: " + token); await AppSharedPreferences().setString(PUSH_TOKEN, token); diff --git a/lib/uitl/translations_delegate_base.dart b/lib/uitl/translations_delegate_base.dart index 9aa15082..01e737a8 100644 --- a/lib/uitl/translations_delegate_base.dart +++ b/lib/uitl/translations_delegate_base.dart @@ -2963,7 +2963,10 @@ class TranslationBase { String get selectHospitalBloodDonation => localizedValues["selectHospitalBloodDonation"][locale.languageCode]; String get wecare => localizedValues["wecare"][locale.languageCode]; String get myinstructions => localizedValues["myinstructions"][locale.languageCode]; + String get clinicLocation => localizedValues["clinicLocation"][locale.languageCode]; String get searchClinic => localizedValues["searchClinic"][locale.languageCode]; + String get selectBranch => localizedValues["selectBranch"][locale.languageCode]; + String get searchByBranch => localizedValues["searchByBranch"][locale.languageCode]; } diff --git a/lib/uitl/utils.dart b/lib/uitl/utils.dart index d923dfed..01779851 100644 --- a/lib/uitl/utils.dart +++ b/lib/uitl/utils.dart @@ -65,6 +65,30 @@ AppSharedPreferences sharedPref = new AppSharedPreferences(); class Utils { // static ProgressDialog pr; + static var navigationProjectsList = [ + { + "Desciption": "Sahafa Hospital", + "DesciptionN": null, + "ID": 130, + "LegalName": "Sahafa Hospital", + "LegalNameN": "مستشفى الصحافة", + "Name": "Sahafa Hospital", + "NameN": null, + "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 + } + ]; + ///show custom Error Toast /// [message] to show for user static showErrorToast([String? message]) { @@ -909,7 +933,7 @@ class Utils { return projectDetailListModel; } - //static String generateSignature() {} +//static String generateSignature() {} } Widget applyShadow({Color color = Colors.grey, double shadowOpacity = 0.5, double spreadRadius = 2, double blurRadius = 7, Offset offset = const Offset(2, 2), required Widget child}) { diff --git a/lib/widgets/dialogs/location_selection_dialog.dart b/lib/widgets/dialogs/location_selection_dialog.dart new file mode 100644 index 00000000..9181615f --- /dev/null +++ b/lib/widgets/dialogs/location_selection_dialog.dart @@ -0,0 +1,290 @@ +import 'package:diplomaticquarterapp/core/model/hospitals/hospitals_model.dart'; +import 'package:diplomaticquarterapp/core/viewModels/medical/radiology_view_model.dart'; +import 'package:diplomaticquarterapp/theme/colors.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:flutter/material.dart'; + +import '../../uitl/translations_delegate_base.dart'; + +class LocationSelectionDialog extends StatelessWidget { + final List data; + final Function(int)? onValueSelected; + final int? selectedIndex; + + const LocationSelectionDialog( + {super.key, + required this.data, + this.onValueSelected, + this.selectedIndex}); + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: LocationDialogBody( + data: data, + onValueSelected: onValueSelected, + selectedIndex: selectedIndex, + )); + } +} + +class LocationDialogBody extends StatefulWidget { + final List data; + final Function(int)? onValueSelected; + final int? selectedIndex; + + const LocationDialogBody( + {super.key, + required this.data, + this.onValueSelected, + this.selectedIndex}); + + @override + State createState() => _LocationDialogBodyState(); +} + +class _LocationDialogBodyState extends State { + bool isListVisible = false; + int currentlySelected = -1; + + @override + Widget build(BuildContext context) { + return isListVisible + ? LocationListExpandedBody( + data: widget.data, + onItemClick: (data) { + if (data.isEmpty) return; + var selected = widget.data.indexWhere( + (item) => item.name == data, + ); + + if (selected == -1) return; + setState(() { + currentlySelected = selected; + isListVisible = false; + }); + }, + ) + : LocationListWrapBody( + onConfirmClicked: () { + widget.onValueSelected?.call(currentlySelected); + }, + onTextBoxClicked: () { + setState(() { + isListVisible = true; + }); + }, + selectedText: currentlySelected == -1 + ? "" + : widget.data[currentlySelected].name ?? ""); + } +} + +class LocationListExpandedBody extends StatefulWidget { + final List data; + final Function(String) onItemClick; + + const LocationListExpandedBody( + {super.key, required this.data, required this.onItemClick}); + + @override + State createState() => + _LocationListExpandedBodyState(); +} + +class _LocationListExpandedBodyState extends State { + List tempListData = []; + TextEditingController controller = new TextEditingController(); + + @override + void initState() { + super.initState(); + setState(() { + tempListData.addAll(widget.data); + }); + } + + @override + Widget build(BuildContext context) { + return Container( + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SelectBranchHeader(), + SizedBox(height: 8,), + TextField( + controller: controller, + onChanged: (v) { + tempListData.clear(); + if (v.length > 0) { + for (int i = 0; i < widget.data.length; i++) { + if (widget.data[i].name + ?.toLowerCase() + .contains(v.toLowerCase()) == + true) { + tempListData.add(widget.data[i]); + } + } + } else { + tempListData.addAll(widget.data); + } + setState(() {}); + }, + decoration: InputDecoration( + hintStyle: TextStyle(fontSize: 12), + hintText: TranslationBase.of(context).searchByBranch, + suffixIcon: Icon(Icons.search), + contentPadding: EdgeInsets.symmetric(vertical: 9,horizontal: 14), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8.0), + borderSide: BorderSide( + color: Colors.grey, // Normal border color + width: 1.0, + ), + ), + ), + ), + SizedBox( + height: 16, + ), + ListView.builder( + itemCount: tempListData.length, + shrinkWrap: true, + itemBuilder: (context, index) { + return Padding( + padding: EdgeInsets.only(bottom: 8), + child: SizedBox( + height: 24, + child: InkWell( + onTap: () { + widget + .onItemClick(tempListData[index].name ?? ""); + }, + child: Text( + "${tempListData[index].name ?? ""} ( ${tempListData[index].distanceInKilometers} km )", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Colors.black, + letterSpacing: -0.96), + ), + ), + ), + ); + }) + ], + ), + ), + ); + } +} + +class LocationListWrapBody extends StatelessWidget { + final VoidCallback? onConfirmClicked; + final VoidCallback? onTextBoxClicked; + final String selectedText; + + const LocationListWrapBody( + {super.key, + this.onConfirmClicked, + this.onTextBoxClicked, + required this.selectedText}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SelectBranchHeader(), + SizedBox( + height: 24, + ), + ListTile( + onTap: () { + onTextBoxClicked?.call(); + }, + title: Text( + TranslationBase.of(context).selectBranch, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Colors.black, + letterSpacing: -0.96), + ), + subtitle: selectedText.isEmpty + ? null + : Text( + selectedText, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: Colors.black, + letterSpacing: -0.96), + ), + trailing: Icon( + Icons.arrow_drop_down_outlined, + color: Colors.black, + ), + shape: RoundedRectangleBorder( + side: BorderSide(color: Colors.grey, width: 1), + borderRadius: BorderRadius.circular(8), + ), + ), + SizedBox( + height: 24, + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: DefaultButton( + TranslationBase.of(context).confirm, + () { + Navigator.pop(context); + onConfirmClicked?.call(); + }, + color: CustomColors.accentColor, + ), + ), + ], + ), + ], + ), + ); + } +} + +class SelectBranchHeader extends StatelessWidget{ + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + TranslationBase.of(context).selectBranch, + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w600, + color: Colors.black, + letterSpacing: -0.96), + ), + InkWell( + onTap: () { + Navigator.of(context).pop(); + }, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Icon(Icons.close), + )) + ], + ); + } +} diff --git a/lib/widgets/in_app_browser/InAppBrowser.dart b/lib/widgets/in_app_browser/InAppBrowser.dart index f353eec2..93a4a0e0 100644 --- a/lib/widgets/in_app_browser/InAppBrowser.dart +++ b/lib/widgets/in_app_browser/InAppBrowser.dart @@ -38,9 +38,9 @@ class MyInAppBrowser extends InAppBrowser { static String APPLE_PAY_PAYFORT_URL = 'https://hmgwebservices.com/PayFortWebLive/PayFortApi/MakeApplePayRequest'; // Payfort Payment Gateway URL LIVE // static String APPLE_PAY_PAYFORT_URL = 'https://hmgwebservices.com/PayFortWebLive/PayFortApi/MakeApplePayRequest'; // Payfort Payment Gateway URL UAT - // static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT + static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT - static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL LIVE + // static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL LIVE // static String SERVICE_URL = 'https://uat.hmgwebservices.com/payfortforvidaplus/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL UAT VIDA PLUS @@ -138,7 +138,7 @@ class MyInAppBrowser extends InAppBrowser { openPackagesPaymentBrowser({required int customer_id, required int order_id}) { paymentType = _PAYMENT_TYPE.PACKAGES; var full_url = '$PACKAGES_REQUEST_PAYMENT_URL?customer_id=$customer_id&order_id=$order_id'; - this.openUrlRequest(urlRequest: URLRequest(url: Uri.parse(full_url)), options: _InAppBrowserOptions); + this.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(full_url))), options: _InAppBrowserOptions); } openPaymentBrowser(num amount, String orderDesc, String transactionID, String projId, String emailId, String paymentMethod, dynamic patientType, String patientName, dynamic patientID, @@ -190,7 +190,7 @@ class MyInAppBrowser extends InAppBrowser { String url = "https://hmgwebservices.com/HMGApplePayLive/applepay/pay?apq=" + res['result']; // Prod // String url = "https://uat.hmgwebservices.com/HMGApplePayLive/applepay/pay?apq=" + res['result']; // UAT // safariBrowser.open(url: Uri.parse(url)); - this.browser.openUrlRequest(urlRequest: URLRequest(url: Uri.parse(url)), options: _InAppBrowserOptions); + this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(url))), options: _InAppBrowserOptions); }).catchError((err) { print(err); if (context != null) GifLoaderDialogUtils.hideDialog(context); @@ -233,7 +233,7 @@ class MyInAppBrowser extends InAppBrowser { appoNo, clinicID, doctorID, "", installments) .then((value) { paymentType = _PAYMENT_TYPE.PATIENT; - this.browser.openUrlRequest(urlRequest: URLRequest(url: Uri.parse(value)), options: _InAppBrowserOptions); + this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(value))), options: _InAppBrowserOptions); }); }).catchError((err) { print(err); @@ -245,7 +245,7 @@ class MyInAppBrowser extends InAppBrowser { clinicID, doctorID) .then((value) { paymentType = _PAYMENT_TYPE.PATIENT; - this.browser.openUrlRequest(urlRequest: URLRequest(url: Uri.parse(value)), options: _InAppBrowserOptions); + this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(value))), options: _InAppBrowserOptions); }); } } @@ -258,16 +258,16 @@ class MyInAppBrowser extends InAppBrowser { // getPatientData(); generatePharmacyURL(order, amount, orderDesc, transactionID, emailId, paymentMethod, patientName, patientID, authenticatedUser).then((value) { if (order.customValuesXml!.contains("ApplePay")) { - safariBrowser.open(url: Uri.parse(value)); + safariBrowser.open(url: WebUri.uri(Uri.parse(value))); } else { - this.browser.openUrlRequest(urlRequest: URLRequest(url: Uri.parse(value)), options: _InAppBrowserOptions); + this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(value))), options: _InAppBrowserOptions); } }); } openBrowser(String url) { this.browser = browser; - this.browser.openUrlRequest(urlRequest: URLRequest(url: Uri.parse(url)), options: _InAppBrowserOptions); + this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(url))), options: _InAppBrowserOptions); } Future generateURL(num amount, String orderDesc, String transactionID, String projId, String emailId, String paymentMethod, dynamic patientType, String patientName, dynamic patientID, @@ -489,7 +489,7 @@ class MyChromeSafariBrowser extends ChromeSafariBrowser { } @override - void onCompletedInitialLoad() { + void onCompletedInitialLoad(bool? didLoadSuccessfully) { print("ChromeSafari browser initial load completed"); onLoadStartCallback!("ApplePay"); } diff --git a/pubspec.yaml b/pubspec.yaml index 8085ed80..02c894e1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: diplomaticquarterapp description: A new Flutter application. -version: 4.5.063+4050063 +version: 4.5.065+4050065 environment: sdk: ">=3.0.0 <3.13.0" @@ -34,7 +34,8 @@ dependencies: get_it: ^7.2.0 #Google Fit & Apple HealthKit - health: ^3.0.3 +# health: ^3.0.3 + health: ^11.1.0 #chart fl_chart: ^0.64.0 @@ -99,7 +100,7 @@ dependencies: flutter_rating_bar: ^4.0.1 # Calendar -# syncfusion_flutter_calendar: ^23.1.42 + # syncfusion_flutter_calendar: ^23.1.42 syncfusion_flutter_calendar: ^24.1.47 # SVG Images @@ -109,7 +110,7 @@ dependencies: manage_calendar_events: ^2.0.3 #InAppBrowser - flutter_inappwebview: ^5.8.0 + flutter_inappwebview: ^6.1.5 #Circular progress bar for reverse timer circular_countdown_timer: ^0.2.0 @@ -124,7 +125,7 @@ dependencies: flutter_datetime_picker_plus: ^2.1.0 carousel_pro_nullsafety: ^2.0.0 flutter_local_notifications: any - device_calendar: ^4.3.2 + device_calendar: ^4.3.3 geolocator: ^9.0.2 geocoding: ^2.0.1 jiffy: ^6.2.1 @@ -135,13 +136,15 @@ dependencies: google_maps_place_picker_mb: ^3.0.0 map_launcher: ^3.0.1 flutter_countdown_timer: ^4.1.0 + carousel_slider: ^5.0.0 #Dependencies for video call implementation native_device_orientation: ^1.0.0 - wakelock: ^0.6.2 +# wakelock: ^0.6.2 + wakelock_plus: ^1.1.4 after_layout: ^1.1.0 cached_network_image: ^3.3.0 flutter_tts: ^3.6.1 - vibration: ^1.7.3 +# vibration: ^1.7.3 flutter_nfc_kit: ^3.3.1 #geofencing: any speech_to_text: ^6.1.1 @@ -151,16 +154,16 @@ dependencies: in_app_review: ^2.0.3 badges: ^3.1.2 - flutter_app_icon_badge: ^2.0.0 +# flutter_app_icon_badge: ^2.0.0 # dropdown_search: 5.0.6 - youtube_player_flutter: ^8.1.2 + youtube_player_flutter: ^9.1.0 # shimmer: ^3.0.0 - carousel_slider: ^5.0.0 +# carousel_slider: ^4.0.0 # flutter_staggered_grid_view: ^0.7.0 huawei_hmsavailability: ^6.11.0+301 huawei_location: ^6.11.0+301 - share_plus: ^6.3.4 + share_plus: ^10.0.2 # Marker Animation auto_size_text: ^3.0.0 equatable: ^2.0.3 @@ -170,17 +173,17 @@ dependencies: google_api_availability: ^5.0.0 open_filex: ^4.3.2 path_provider: ^2.0.8 - amazon_payfort: ^1.1.0 + amazon_payfort: ^1.1.3 logger: ^2.0.2+1 network_info_plus: any - flutter_zoom_videosdk: ^1.10.11 + flutter_zoom_videosdk: ^1.11.2 dart_jsonwebtoken: ^2.14.0 + win32: ^5.5.4 cloudflare_turnstile: ^2.0.1 dependency_overrides: - win32: ^5.5.4 dev_dependencies: flutter_test: