diff --git a/android/app/agconnect-services-new.json b/android/app/agconnect-services-new.json new file mode 100644 index 00000000..fce77eec --- /dev/null +++ b/android/app/agconnect-services-new.json @@ -0,0 +1,74 @@ +{ + "agcgw":{ + "backurl":"connect-drcn.hispace.hicloud.com", + "url":"connect-drcn.dbankcloud.cn", + "websocketbackurl":"connect-ws-drcn.hispace.dbankcloud.com", + "websocketurl":"connect-ws-drcn.hispace.dbankcloud.cn" + }, + "agcgw_all":{ + "CN":"connect-drcn.dbankcloud.cn", + "CN_back":"connect-drcn.hispace.hicloud.com", + "DE":"connect-dre.dbankcloud.cn", + "DE_back":"connect-dre.hispace.hicloud.com", + "RU":"connect-drru.dbankcloud.cn", + "RU_back":"connect-drru.hispace.hicloud.com", + "SG":"connect-dra.dbankcloud.cn", + "SG_back":"connect-dra.hispace.hicloud.com" + }, + "client":{ + "cp_id":"2640966000002322881", + "product_id":"736430079244816567", + "client_id":"563735388191982656", + "client_secret":"650C7C799812AFFD53A10C7756CF05FB9F215A7E49032ABA8EBF3E14B77535CF", + "project_id":"736430079244816567", + "app_id":"102857389", + "api_key":"CgB6e3x9DJzMgRCmnT6dyUEkp6UsIfddb6l3w0ZEXzeiRMHEFi3400Z5fJ5qaHneU0OrAI/JRpk+DMGVs3QpUxlI", + "package_name":"com.ejada.hmg" + }, + "oauth_client":{ + "client_id":"102857389", + "client_type":1 + }, + "app_info":{ + "app_id":"102857389", + "package_name":"com.ejada.hmg" + }, + "service":{ + "analytics":{ + "collector_url":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn", + "collector_url_ru":"datacollector-drru.dt.hicloud.com,datacollector-drru.dt.dbankcloud.cn", + "collector_url_sg":"datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn", + "collector_url_de":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", + "collector_url_cn":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn", + "resource_id":"p1", + "channel_id":"" + }, + "search":{ + "url":"https://search-drcn.cloud.huawei.com" + }, + "cloudstorage":{ + "storage_url":"https://agc-storage-drcn.platform.dbankcloud.cn" + }, + "ml":{ + "mlservice_url":"ml-api-drcn.ai.dbankcloud.com,ml-api-drcn.ai.dbankcloud.cn" + } + }, + "region":"CN", + "configuration_version":"3.0", + "appInfos":[ + { + "package_name":"com.ejada.hmg", + "client":{ + "app_id":"102857389" + }, + "app_info":{ + "package_name":"com.ejada.hmg", + "app_id":"102857389" + }, + "oauth_client":{ + "client_type":1, + "client_id":"102857389" + } + } + ] +} \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index bfe7fde4..f13bce1b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -52,12 +52,21 @@ android { } + signingConfigs { + config{ + storeFile file('key') + keyAlias 'HMG' + storePassword 'HmGsa123' + keyPassword 'HmGsa123' + } + } + buildTypes { - debug {} + debug { + signingConfig signingConfigs.config + } release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig signingConfigs.config } staging { // Specifies a sorted list of fallback build types that the @@ -98,6 +107,10 @@ dependencies { implementation 'com.github.kittinunf.fuel:fuel:2.3.0' //for JVM implementation 'com.github.kittinunf.fuel:fuel-android:2.3.0' implementation 'com.google.android.gms:play-services-location:17.1.0'//for Android - implementation 'com.google.android.gms:play-services-basement:17.5.0' + implementation 'com.google.android.gms:play-services-basement:17.5.0' + + + implementation "com.opentok.android:opentok-android-sdk:2.19.1" + } diff --git a/android/app/key b/android/app/key new file mode 100644 index 00000000..15b9f5d1 Binary files /dev/null and b/android/app/key differ diff --git a/android/app/keystores/keystore b/android/app/keystores/keystore new file mode 100644 index 00000000..e69de29b 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 26484b5c..e754e44c 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt @@ -4,15 +4,16 @@ import android.util.Log import androidx.annotation.NonNull; import com.ejada.hmg.utils.* import io.flutter.embedding.android.FlutterFragmentActivity - import io.flutter.embedding.engine.FlutterEngine +import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel import io.flutter.plugins.GeneratedPluginRegistrant class MainActivity: FlutterFragmentActivity() { override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { - GeneratedPluginRegistrant.registerWith(flutterEngine); - // Create Flutter Platform Bridge - PlatformBridge(flutterEngine.dartExecutor.binaryMessenger, this).create() + GeneratedPluginRegistrant.registerWith(flutterEngine); + // Create Flutter Platform Bridge + PlatformBridge(flutterEngine, this).create() + OpenTokPlatformBridge(flutterEngine, this).create() val time = timeToMillis("04:00:00", "HH:mm:ss") @@ -24,7 +25,7 @@ class MainActivity: FlutterFragmentActivity() { // val d5 = Logs.GeofenceEvent.list(this) // val d6 = Logs.GeofenceEvent.raw(this) - } + } override fun onResume() { super.onResume() diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/LocalVideo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/LocalVideo.kt new file mode 100644 index 00000000..cfebfe65 --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/LocalVideo.kt @@ -0,0 +1,58 @@ +package com.ejada.hmg.opentok + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.ejada.hmg.R +import io.flutter.plugin.common.StandardMessageCodec +import io.flutter.plugin.platform.PlatformView +import io.flutter.plugin.platform.PlatformViewFactory + +class LocalVideoFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) { + + companion object { + private lateinit var view: LocalVideoPlatformView + + fun getViewInstance(context: Context): LocalVideoPlatformView { + if(!this::view.isInitialized) { + view = LocalVideoPlatformView(context) + } + + return view + } + } + + override fun create(context: Context, viewId: Int, args: Any?): PlatformView { + return getViewInstance(context) + } +} + +class LocalVideoPlatformView(context: Context) : PlatformView { + private val videoContainer: LocalVideoContainer = LocalVideoContainer(context) + + val container get() = videoContainer.publisherContainer + + override fun getView(): View { + return videoContainer + } + + override fun dispose() {} +} + +class LocalVideoContainer @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = 0, + defStyleRes: Int = 0 +) : LinearLayout(context, attrs, defStyle, defStyleRes) { + + var publisherContainer: FrameLayout private set + + init { + val view = LayoutInflater.from(context).inflate(R.layout.local_video, this, true) + publisherContainer = view.findViewById(R.id.publisher_container) + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/OpenTok.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/OpenTok.kt new file mode 100644 index 00000000..0cdc3751 --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/OpenTok.kt @@ -0,0 +1,168 @@ +package com.ejada.hmg.opentok + +import android.content.Context +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.view.ViewGroup +import com.opentok.android.* +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel + + +enum class OpenTokSDKState { + LOGGED_OUT, + LOGGED_IN, + WAIT, + ERROR +} + +class OpenTok(private var context: Context, private var flutterEngine: FlutterEngine){ + private lateinit var remoteVideoPlatformView: RemoteVideoPlatformView + private lateinit var localVideoPlatformView: LocalVideoPlatformView + + init { + remoteVideoPlatformView = RemoteVideoFactory.getViewInstance(context) + flutterEngine + .platformViewsController + .registry + .registerViewFactory("remote-video-container", RemoteVideoFactory()) + + localVideoPlatformView = LocalVideoFactory.getViewInstance(context) + flutterEngine + .platformViewsController + .registry + .registerViewFactory("local-video-container", LocalVideoFactory()) + } + + private var session: Session? = null + private var publisher: Publisher? = null + private var subscriber: Subscriber? = null + + + + private val sessionListener: Session.SessionListener = object: Session.SessionListener { + override fun onConnected(session: Session) { + // Connected to session + Log.d("MainActivity", "Connected to session ${session.sessionId}") + + publisher = Publisher.Builder(context).build().apply { + setPublisherListener(publisherListener) + renderer?.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL) + + view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + localVideoPlatformView.container.addView(view) + } + + notifyFlutter(OpenTokSDKState.LOGGED_IN) + session.publish(publisher) + } + + override fun onDisconnected(session: Session) { + notifyFlutter(OpenTokSDKState.LOGGED_OUT) + } + + override fun onStreamReceived(session: Session, stream: Stream) { + Log.d( + "MainActivity", + "onStreamReceived: New Stream Received " + stream.streamId + " in session: " + session.sessionId + ) + if (subscriber == null) { + subscriber = Subscriber.Builder(context, stream).build().apply { + renderer?.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL) + setSubscriberListener(subscriberListener) + session.subscribe(this) + + remoteVideoPlatformView.container.addView(view) + } + } + } + + override fun onStreamDropped(session: Session, stream: Stream) { + Log.d( + "MainActivity", + "onStreamDropped: Stream Dropped: " + stream.streamId + " in session: " + session.sessionId + ) + + if (subscriber != null) { + subscriber = null + + remoteVideoPlatformView.container.removeAllViews() + } + } + + override fun onError(session: Session, opentokError: OpentokError) { + Log.d("MainActivity", "Session error: " + opentokError.message) + notifyFlutter(OpenTokSDKState.ERROR) + } + } + + private val publisherListener: PublisherKit.PublisherListener = object : PublisherKit.PublisherListener { + override fun onStreamCreated(publisherKit: PublisherKit, stream: Stream) { + Log.d("MainActivity", "onStreamCreated: Publisher Stream Created. Own stream " + stream.streamId) + } + + override fun onStreamDestroyed(publisherKit: PublisherKit, stream: Stream) { + Log.d("MainActivity", "onStreamDestroyed: Publisher Stream Destroyed. Own stream " + stream.streamId) + } + + override fun onError(publisherKit: PublisherKit, opentokError: OpentokError) { + Log.d("MainActivity", "PublisherKit onError: " + opentokError.message) + notifyFlutter(OpenTokSDKState.ERROR) + } + } + + var subscriberListener: SubscriberKit.SubscriberListener = object : SubscriberKit.SubscriberListener { + override fun onConnected(subscriberKit: SubscriberKit) { + Log.d("MainActivity", "onConnected: Subscriber connected. Stream: " + subscriberKit.stream.streamId) + } + + override fun onDisconnected(subscriberKit: SubscriberKit) { + Log.d("MainActivity", "onDisconnected: Subscriber disconnected. Stream: " + subscriberKit.stream.streamId) + notifyFlutter(OpenTokSDKState.LOGGED_OUT) + } + + override fun onError(subscriberKit: SubscriberKit, opentokError: OpentokError) { + Log.d("MainActivity", "SubscriberKit onError: " + opentokError.message) + notifyFlutter(OpenTokSDKState.ERROR) + } + } + + fun initSession(call: MethodCall, result: MethodChannel.Result) { + + val apiKey = requireNotNull(call.argument("apiKey")) + val sessionId = requireNotNull(call.argument("sessionId")) + val token = requireNotNull(call.argument("token")) + + notifyFlutter(OpenTokSDKState.WAIT) + session = Session.Builder(context, apiKey, sessionId).build() + session?.setSessionListener(sessionListener) + session?.connect(token) + result.success("") + } + + fun swapCamera(call: MethodCall, result: MethodChannel.Result) { + publisher?.cycleCamera() + result.success("") + } + + fun toggleAudio(call: MethodCall, result: MethodChannel.Result) { + val publishAudio = requireNotNull(call.argument("publishAudio")) + publisher?.publishAudio = publishAudio + result.success("") + } + + fun toggleVideo(call: MethodCall, result: MethodChannel.Result) { + val publishVideo = requireNotNull(call.argument("publishVideo")) + publisher?.publishVideo = publishVideo + result.success("") + } + + private fun notifyFlutter(state: OpenTokSDKState) { + Handler(Looper.getMainLooper()).post { + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "OpenTok-Platform-Bridge") + .invokeMethod("updateState", state.toString()) + } + } +} diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/RemoteVideo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/RemoteVideo.kt new file mode 100644 index 00000000..9116a1e8 --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/RemoteVideo.kt @@ -0,0 +1,58 @@ +package com.ejada.hmg.opentok + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.ejada.hmg.R +import io.flutter.plugin.common.StandardMessageCodec +import io.flutter.plugin.platform.PlatformView +import io.flutter.plugin.platform.PlatformViewFactory + +class RemoteVideoFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) { + + companion object { + private lateinit var view: RemoteVideoPlatformView + + fun getViewInstance(context: Context): RemoteVideoPlatformView { + if(!this::view.isInitialized) { + view = RemoteVideoPlatformView(context) + } + + return view + } + } + + override fun create(context: Context, viewId: Int, args: Any?): PlatformView { + return getViewInstance(context) + } +} + +class RemoteVideoPlatformView(context: Context) : PlatformView { + private val videoContainer: RemoteVideoContainer = RemoteVideoContainer(context) + + val container get() = videoContainer.subscriberContainer + + override fun getView(): View { + return videoContainer + } + + override fun dispose() {} +} + +class RemoteVideoContainer @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = 0, + defStyleRes: Int = 0 +) : LinearLayout(context, attrs, defStyle, defStyleRes) { + + var subscriberContainer: FrameLayout private set + + init { + val view = LayoutInflater.from(context).inflate(R.layout.remote_video, this, true) + subscriberContainer = view.findViewById(R.id.subscriber_container) + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/OpenTokPlatformBridge.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/OpenTokPlatformBridge.kt new file mode 100644 index 00000000..6ac62546 --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/OpenTokPlatformBridge.kt @@ -0,0 +1,42 @@ +package com.ejada.hmg.utils + +import com.ejada.hmg.MainActivity +import com.ejada.hmg.opentok.OpenTok +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel + +class OpenTokPlatformBridge(private var flutterEngine: FlutterEngine, private var mainActivity: MainActivity) { + + private lateinit var channel: MethodChannel + private lateinit var openTok: OpenTok + + companion object { + private const val CHANNEL = "OpenTok-Platform-Bridge" + } + + fun create(){ + openTok = OpenTok(mainActivity, flutterEngine) + channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) + channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result -> + when (call.method) { + "initSession" -> { + openTok.initSession(call, result) + } + "swapCamera" -> { + openTok.swapCamera(call, result) + } + "toggleAudio" -> { + openTok.toggleAudio(call, result) + } + "toggleVideo" -> { + openTok.toggleVideo(call, result) + } + else -> { + result.notImplemented() + } + } + } + } + +} diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/PlatformBridge.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/PlatformBridge.kt index cb8c2c3d..003b9e24 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/PlatformBridge.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/PlatformBridge.kt @@ -8,13 +8,12 @@ import com.ejada.hmg.hmgwifi.HMG_Guest import com.ejada.hmg.hmgwifi.HMG_Internet import com.ejada.hmg.geofence.GeoZoneModel import com.ejada.hmg.geofence.HMG_Geofence +import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel -class PlatformBridge(binaryMessenger: BinaryMessenger, flutterMainActivity: MainActivity) { - private var binaryMessenger = binaryMessenger - private var mainActivity = flutterMainActivity +class PlatformBridge(private var flutterEngine: FlutterEngine, private var mainActivity: MainActivity) { private lateinit var channel: MethodChannel @@ -28,7 +27,7 @@ class PlatformBridge(binaryMessenger: BinaryMessenger, flutterMainActivity: Main } fun create(){ - channel = MethodChannel(binaryMessenger, CHANNEL) + channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) HMGUtils.setPlatformChannel(channel) channel.setMethodCallHandler { methodCall: MethodCall, result: MethodChannel.Result -> diff --git a/android/build.gradle b/android/build.gradle index 4ef0d731..ecb1ed70 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -23,7 +23,12 @@ allprojects { google() // jcenter() mavenCentral() - maven { url 'https://developer.huawei.com/repo/' } + maven { + url 'https://developer.huawei.com/repo/' + } + maven{ + url "https://artifactory.ess-dev.com/artifactory/gradle-dev-local" + } } } diff --git a/ios/Flutter/.last_build_id b/ios/Flutter/.last_build_id index d070415c..53427e0c 100644 --- a/ios/Flutter/.last_build_id +++ b/ios/Flutter/.last_build_id @@ -1 +1 @@ -e52eba3667a38bec777870899c15ae7d \ No newline at end of file +3f8c659591fcdd0e47e3895f74af395c \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index 9fbc2933..46c98373 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -42,6 +42,7 @@ target 'Runner' do # Native Pods pod 'NVActivityIndicatorView' + pod 'OpenTok' # Flutter Pod diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2ce396c4..549f5592 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -370,6 +370,10 @@ PODS: - Flutter - flutter_tts (0.0.1): - Flutter + - flutter_webrtc (0.2.2): + - Flutter + - GoogleWebRTC (= 1.1.31999) + - Libyuv (= 1703) - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) @@ -410,6 +414,7 @@ PODS: - GoogleUtilities/Logger - GoogleUtilities/UserDefaults (6.7.2): - GoogleUtilities/Logger + - GoogleWebRTC (1.1.31999) - "gRPC-C++ (1.28.2)": - "gRPC-C++/Implementation (= 1.28.2)" - "gRPC-C++/Interface (= 1.28.2)" @@ -438,6 +443,8 @@ PODS: - Flutter - "huawei_location (5.0.0+301)": - Flutter + - "huawei_map (5.0.3+303)": + - Flutter - image_cropper (0.0.3): - Flutter - TOCropViewController (~> 2.5.4) @@ -446,6 +453,7 @@ PODS: - just_audio (0.0.1): - Flutter - leveldb-library (1.22) + - Libyuv (1703) - local_auth (0.0.1): - Flutter - location (0.0.1): @@ -469,6 +477,7 @@ PODS: - NVActivityIndicatorView (5.1.1): - NVActivityIndicatorView/Base (= 5.1.1) - NVActivityIndicatorView/Base (5.1.1) + - OpenTok (2.20.0) - path_provider (0.0.1): - Flutter - path_provider_linux (0.0.1): @@ -562,10 +571,12 @@ DEPENDENCIES: - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`) - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`) + - flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`) - geolocator (from `.symlinks/plugins/geolocator/ios`) - google_maps_flutter (from `.symlinks/plugins/google_maps_flutter/ios`) - hexcolor (from `.symlinks/plugins/hexcolor/ios`) - huawei_location (from `.symlinks/plugins/huawei_location/ios`) + - huawei_map (from `.symlinks/plugins/huawei_map/ios`) - image_cropper (from `.symlinks/plugins/image_cropper/ios`) - image_picker (from `.symlinks/plugins/image_picker/ios`) - just_audio (from `.symlinks/plugins/just_audio/ios`) @@ -577,6 +588,7 @@ DEPENDENCIES: - native_device_orientation (from `.symlinks/plugins/native_device_orientation/ios`) - nfc_in_flutter (from `.symlinks/plugins/nfc_in_flutter/ios`) - NVActivityIndicatorView + - OpenTok - path_provider (from `.symlinks/plugins/path_provider/ios`) - path_provider_linux (from `.symlinks/plugins/path_provider_linux/ios`) - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`) @@ -624,12 +636,15 @@ SPEC REPOS: - GoogleDataTransport - GoogleMaps - GoogleUtilities + - GoogleWebRTC - "gRPC-C++" - gRPC-Core - leveldb-library + - Libyuv - MTBBarcodeScanner - nanopb - NVActivityIndicatorView + - OpenTok - PromisesObjC - Protobuf - Reachability @@ -688,6 +703,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios" flutter_tts: :path: ".symlinks/plugins/flutter_tts/ios" + flutter_webrtc: + :path: ".symlinks/plugins/flutter_webrtc/ios" geolocator: :path: ".symlinks/plugins/geolocator/ios" google_maps_flutter: @@ -696,6 +713,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/hexcolor/ios" huawei_location: :path: ".symlinks/plugins/huawei_location/ios" + huawei_map: + :path: ".symlinks/plugins/huawei_map/ios" image_cropper: :path: ".symlinks/plugins/image_cropper/ios" image_picker: @@ -808,6 +827,7 @@ SPEC CHECKSUMS: flutter_local_notifications: 9e4738ce2471c5af910d961a6b7eadcf57c50186 flutter_plugin_android_lifecycle: dc0b544e129eebb77a6bfb1239d4d1c673a60a35 flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d + flutter_webrtc: 39898454258b54ba51996850d5da8d5d53bf1524 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a geolocator: f5e3de65e241caba7ce3e8a618803387bda73384 google_maps_flutter: c7f9c73576de1fbe152a227bfd6e6c4ae8088619 @@ -815,14 +835,17 @@ SPEC CHECKSUMS: GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 GoogleMaps: 4b5346bddfe6911bb89155d43c903020170523ac GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 + GoogleWebRTC: b39a78c4f5cc6b0323415b9233db03a2faa7b0f0 "gRPC-C++": 13d8ccef97d5c3c441b7e3c529ef28ebee86fad2 gRPC-Core: 4afa11bfbedf7cdecd04de535a9e046893404ed5 hexcolor: fdfb9c4258ad96e949c2dbcdf790a62194b8aa89 huawei_location: a1c3d2a029138b3d0b9a72ce910ffae3ff23c1aa + huawei_map: 34fbad9c274ea78a4151487c9ebe27f1887777e7 image_cropper: c8f9b4157933c7bb965a66d1c5e6c8fd408c6eb4 image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09 just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 + Libyuv: 5f79ced0ee66e60a612ca97de1e6ccacd187a437 local_auth: 25938960984c3a7f6e3253e3f8d962fdd16852bd location: 3a2eed4dd2fab25e7b7baf2a9efefe82b512d740 manage_calendar_events: 0338d505ea26cdfd20cd883279bc28afa11eca34 @@ -833,6 +856,7 @@ SPEC CHECKSUMS: native_device_orientation: e24d00be281de72996640885d80e706142707660 nfc_in_flutter: c656fbfb1ec5b9d021da87b0c87629d62fd5264d NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667 + OpenTok: 414c2c1dc6486f1897015e4d1703558d5eed48c3 path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4 path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0 @@ -869,6 +893,6 @@ SPEC CHECKSUMS: webview_flutter: d2b4d6c66968ad042ad94cbb791f5b72b4678a96 wifi: d7d77c94109e36c4175d845f0a5964eadba71060 -PODFILE CHECKSUM: 900f7dc480de5b961c031ed418407e0e40ed455c +PODFILE CHECKSUM: df22e8ed6009dd48559053927dc365d6dad6c11f COCOAPODS: 1.10.1 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index e4453fa6..6c02c983 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,6 +8,10 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 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 */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 813F9CBA7DD5ED63B28B8BB6 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 767C165F7ABF3BF1F829B9BF /* Pods_Runner.framework */; }; @@ -50,6 +54,10 @@ /* Begin PBXFileReference section */ 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 = ""; }; + 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 = ""; }; 308FEC658188F7D588BE7580 /* 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 = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; @@ -99,6 +107,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 306FE6C9271D8B54002D6EFC /* OpenTok */ = { + isa = PBXGroup; + children = ( + 306FE6CA271D8B73002D6EFC /* OpenTok.swift */, + 301C79AF27200DED0016307B /* OpenTokLocalVideoFactory.swift */, + 301C79AD27200D9F0016307B /* OpenTokRemoteVideoFactory.swift */, + ); + path = OpenTok; + sourceTree = ""; + }; 555EAAA626EFB641859EF0BE /* Frameworks */ = { isa = PBXGroup; children = ( @@ -153,6 +171,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 306FE6C9271D8B54002D6EFC /* OpenTok */, E91B539E256AAC1400E96549 /* GuestPOC_Certificate.cer */, E91B539D256AAC1400E96549 /* GuestPOC_Certificate.p12 */, E91B5390256AAA6500E96549 /* Controllers */, @@ -182,6 +201,7 @@ E923EFD125863FDF00E3E751 /* GeoZoneModel.swift */, E923EFD3258645C100E3E751 /* HMG_Geofence.swift */, E923EFD52587443800E3E751 /* HMGPlatformBridge.swift */, + 306FE6C7271D790C002D6EFC /* OpenTokPlatformBridge.swift */, E9F7623A25922BCE00FB5CCF /* FlutterConstants.swift */, ); path = Helper; @@ -375,16 +395,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 306FE6C8271D790C002D6EFC /* OpenTokPlatformBridge.swift in Sources */, E923EFD225863FDF00E3E751 /* GeoZoneModel.swift in Sources */, E91B539A256AAA6500E96549 /* MainFlutterVC.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 */, + 301C79AE27200D9F0016307B /* OpenTokRemoteVideoFactory.swift in Sources */, E9F7623B25922BCE00FB5CCF /* FlutterConstants.swift in Sources */, + 306FE6CB271D8B73002D6EFC /* OpenTok.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, E9E27168256E3A4000F49B69 /* LocalizedFromFlutter.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 301C79B027200DED0016307B /* OpenTokLocalVideoFactory.swift in Sources */, E9C8C136256BACDA00EFFB62 /* HMG_Guest.swift in Sources */, E91B539B256AAA6500E96549 /* HMG_Internet.swift in Sources */, E91B5397256AAA6500E96549 /* Extensions.swift in Sources */, diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index c4a74c80..35a3c022 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -14,7 +14,7 @@ var userNotificationCenterDelegate:UNUserNotificationCenterDelegate? = nil GMSServices.provideAPIKey("AIzaSyCmevVlr2Bh-c8W1VUzo8gt8JRY7n5PANw") GeneratedPluginRegistrant.register(with: self) - initializePlatformChannel() + initializePlatformChannels() if let _ = launchOptions?[.location] { HMG_Geofence.initGeofencing() @@ -25,14 +25,16 @@ var userNotificationCenterDelegate:UNUserNotificationCenterDelegate? = nil } - func initializePlatformChannel(){ + func initializePlatformChannels(){ if let mainViewController = window.rootViewController as? MainFlutterVC{ // platform initialization suppose to be in foreground flutterViewController = mainViewController HMGPlatformBridge.initialize(flutterViewController: flutterViewController) + OpenTokPlatformBridge.initialize(flutterViewController: flutterViewController, registrar: self.registrar(forPlugin: "open-tok")) }else if let mainViewController = initialViewController(){ // platform initialization suppose to be in background flutterViewController = mainViewController HMGPlatformBridge.initialize(flutterViewController: flutterViewController) + OpenTokPlatformBridge.initialize(flutterViewController: flutterViewController, registrar: self.registrar(forPlugin: "open-tok")) } } diff --git a/ios/Runner/Helper/HMGPlatformBridge.swift b/ios/Runner/Helper/HMGPlatformBridge.swift index f94f9b34..fd9fb401 100644 --- a/ios/Runner/Helper/HMGPlatformBridge.swift +++ b/ios/Runner/Helper/HMGPlatformBridge.swift @@ -10,7 +10,7 @@ import NetworkExtension import SystemConfiguration.CaptiveNetwork var flutterMethodChannel:FlutterMethodChannel? = nil -var mainViewController:MainFlutterVC! +fileprivate var mainViewController:MainFlutterVC! class HMGPlatformBridge{ private let channelName = "HMG-Platform-Bridge" diff --git a/ios/Runner/Helper/OpenTokPlatformBridge.swift b/ios/Runner/Helper/OpenTokPlatformBridge.swift new file mode 100644 index 00000000..38e826e1 --- /dev/null +++ b/ios/Runner/Helper/OpenTokPlatformBridge.swift @@ -0,0 +1,59 @@ +// +// HMGPlatformBridge.swift +// Runner +// +// Created by ZiKambrani on 14/12/2020. +// + +import UIKit +import NetworkExtension +import SystemConfiguration.CaptiveNetwork +import OpenTok + + +fileprivate var openTok:OpenTok? + +class OpenTokPlatformBridge : NSObject{ + private var methodChannel:FlutterMethodChannel? = nil + private var mainViewController:MainFlutterVC! + private static var shared_:OpenTokPlatformBridge? + + class func initialize(flutterViewController:MainFlutterVC, registrar:FlutterPluginRegistrar?){ + shared_ = OpenTokPlatformBridge() + shared_?.mainViewController = flutterViewController + + shared_?.openChannel() + openTok = OpenTok(mainViewController: flutterViewController, registrar: registrar) + } + + func shared() -> OpenTokPlatformBridge{ + assert((OpenTokPlatformBridge.shared_ != nil), "OpenTokPlatformBridge is not initialized, call initialize(mainViewController:MainFlutterVC) function first.") + return OpenTokPlatformBridge.shared_! + } + + private func openChannel(){ + methodChannel = FlutterMethodChannel(name: "OpenTok-Platform-Bridge", binaryMessenger: mainViewController.binaryMessenger) + methodChannel?.setMethodCallHandler { (call, result) in + print("Called function \(call.method)") + + switch(call.method) { + case "initSession": + openTok?.initSession(call: call, result: result) + + case "swapCamera": + openTok?.swapCamera(call: call, result: result) + + case "toggleAudio": + openTok?.toggleAudio(call: call, result: result) + + case "toggleVideo": + openTok?.toggleVideo(call: call, result: result) + + default: + result(FlutterMethodNotImplemented) + } + + print("") + } + } +} diff --git a/ios/Runner/OpenTok/OpenTok.swift b/ios/Runner/OpenTok/OpenTok.swift new file mode 100644 index 00000000..9cc0b79a --- /dev/null +++ b/ios/Runner/OpenTok/OpenTok.swift @@ -0,0 +1,179 @@ +// +// OpenTok.swift +// Runner +// +// Created by Zohaib Iqbal Kambrani on 18/10/2021. +// + +import Foundation +import OpenTok +import UIKit + +enum SdkState: String { + case loggedOut = "LOGGED_OUT" + case loggedIn = "LOGGED_IN" + case wait = "WAIT" + case error = "ERROR" +} + +class OpenTok : NSObject{ + private var mainViewController:MainFlutterVC! + private var registrar:FlutterPluginRegistrar? + var methodChannel: FlutterMethodChannel? + + init(mainViewController:MainFlutterVC, registrar:FlutterPluginRegistrar?){ + self.mainViewController = mainViewController + self.methodChannel = FlutterMethodChannel(name: "OpenTok-Platform-Bridge", binaryMessenger: mainViewController.binaryMessenger) + self.registrar = registrar + + let remoteVDOFactory = OpenTokRemoteVideoFactory(messenger: registrar!.messenger()) + registrar?.register(remoteVDOFactory, withId: "remote-video-container") + + let localVDOFactory = OpenTokLocalVideoFactory(messenger: registrar!.messenger()) + registrar?.register(localVDOFactory, withId: "local-video-container") + } + + var otSession: OTSession? + + var subscriber: OTSubscriber? + lazy var publisher: OTPublisher = { + let settings = OTPublisherSettings() + settings.name = UIDevice.current.name + return OTPublisher(delegate: self, settings: settings)! + }() + + func initSession(call:FlutterMethodCall, result: @escaping FlutterResult){ + if let arguments = call.arguments as? [String: String], + let apiKey = arguments["apiKey"], + let sessionId = arguments["sessionId"], + let token = arguments["token"]{ + + var error: OTError? + defer { + // todo + } + + notifyFlutter(state: SdkState.wait) + otSession = OTSession(apiKey: apiKey, sessionId: sessionId, delegate: self)! + otSession?.connect(withToken: token, error: &error) + + result("") + }else{ + + } + + } + + + func swapCamera(call:FlutterMethodCall, result: @escaping FlutterResult) { + if publisher.cameraPosition == .front { + publisher.cameraPosition = .back + } else { + publisher.cameraPosition = .front + } + result("") + } + + func toggleAudio(call:FlutterMethodCall, result: @escaping FlutterResult) { + if let arguments = call.arguments as? [String: Bool], + let publishAudio = arguments["publishAudio"] { + publisher.publishAudio = !publisher.publishAudio + } + result("") + } + + func toggleVideo(call:FlutterMethodCall, result: @escaping FlutterResult) { + if let arguments = call.arguments as? [String: Bool], + let publishVideo = arguments["publishVideo"] { + publisher.publishVideo = !publisher.publishVideo + } + result("") + } + + + func notifyFlutter(state: SdkState) { + methodChannel?.invokeMethod("updateState", arguments: state.rawValue) + } + +} + +extension OpenTok: OTSessionDelegate { + func sessionDidConnect(_ sessionDelegate: OTSession) { + print("The client connected to the session.") + notifyFlutter(state: SdkState.loggedIn) + + var error: OTError? + defer { + // todo + } + + self.otSession?.publish(self.publisher, error: &error) + + if let pubView = self.publisher.view { + pubView.frame = CGRect(x: 0, y: 0, width: 200, height: 300) + + if OpenTokLocalVideoFactory.view == nil { + OpenTokLocalVideoFactory.viewToAddPub = pubView + } else { + OpenTokLocalVideoFactory.view?.addPublisherView(pubView) + } + } + } + + func sessionDidDisconnect(_ session: OTSession) { + print("The client disconnected from the session.") + notifyFlutter(state: SdkState.loggedOut) + } + + func session(_ session: OTSession, didFailWithError error: OTError) { + print("The client failed to connect to the session: \(error).") + } + + func session(_ session: OTSession, streamCreated stream: OTStream) { + print("A stream was created in the session.") + var error: OTError? + defer { + // todo + } + subscriber = OTSubscriber(stream: stream, delegate: self) + + session.subscribe(subscriber!, error: &error) + } + + func session(_ session: OTSession, streamDestroyed stream: OTStream) { + print("A stream was destroyed in the session.") + } +} + +extension OpenTok: OTPublisherDelegate { + func publisher(_ publisher: OTPublisherKit, streamCreated stream: OTStream) { + } + + func publisher(_ publisher: OTPublisherKit, streamDestroyed stream: OTStream) { + } + + func publisher(_ publisher: OTPublisherKit, didFailWithError error: OTError) { + print("Publisher failed: \(error.localizedDescription)") + } +} + +extension OpenTok: OTSubscriberDelegate { + func subscriberDidConnect(toStream subscriberKit: OTSubscriberKit) { + print("Subscriber connected") + + if let subView = self.subscriber?.view { + subView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) + + if OpenTokRemoteVideoFactory.view == nil { + OpenTokRemoteVideoFactory.viewToAddSub = subView + } else { + OpenTokRemoteVideoFactory.view?.addSubscriberView(subView) + } + } + } + + func subscriber(_ subscriber: OTSubscriberKit, didFailWithError error: OTError) { + print("Subscriber failed: \(error.localizedDescription)") + } + +} diff --git a/ios/Runner/OpenTok/OpenTokLocalVideoFactory.swift b/ios/Runner/OpenTok/OpenTokLocalVideoFactory.swift new file mode 100644 index 00000000..361b482d --- /dev/null +++ b/ios/Runner/OpenTok/OpenTokLocalVideoFactory.swift @@ -0,0 +1,91 @@ +// +// OpenTokLocalVideoFactory.swift +// Runner +// +// Created by Zohaib Iqbal Kambrani on 20/10/2021. +// + +import Foundation + +class OpenTokLocalVideoFactory: NSObject, FlutterPlatformViewFactory { + static var view: LocalVideoPlatformView? + + static var viewToAddPub: UIView? + + static func getViewInstance( + frame: CGRect, + viewId: Int64, + args: Any?, + messenger: FlutterBinaryMessenger? + ) -> LocalVideoPlatformView{ + if(view == nil) { + view = LocalVideoPlatformView() + if viewToAddPub != nil { + view?.addPublisherView(viewToAddPub!) + } + } + + return view! + } + + private var messenger: FlutterBinaryMessenger + + init(messenger: FlutterBinaryMessenger) { + self.messenger = messenger + super.init() + } + + func create( + withFrame frame: CGRect, + viewIdentifier viewId: Int64, + arguments args: Any? + ) -> FlutterPlatformView { + return OpenTokLocalVideoFactory.getViewInstance( + frame: frame, + viewId: viewId, + args: args, + messenger: messenger) + } +} + +class LocalVideoPlatformView: NSObject, FlutterPlatformView { + private let videoContainer: LocalVideoContainer + + override init() { + videoContainer = LocalVideoContainer() + super.init() + } + + public func addPublisherView(_ view: UIView) { + videoContainer.addPublisherView(view) + } + + func view() -> UIView { + return videoContainer + } +} + +final class LocalVideoContainer: UIView { + private let publisherContainer = UIView() + + init() { + super.init(frame: .zero) + addSubview(publisherContainer) + } + + public func addPublisherView(_ view: UIView) { + publisherContainer.addSubview(view) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + let width = frame.width + let height = frame.height + + publisherContainer.frame = CGRect(x: 0, y: 0, width: width, height: height) + } +} diff --git a/ios/Runner/OpenTok/OpenTokRemoteVideoFactory.swift b/ios/Runner/OpenTok/OpenTokRemoteVideoFactory.swift new file mode 100644 index 00000000..50dcf695 --- /dev/null +++ b/ios/Runner/OpenTok/OpenTokRemoteVideoFactory.swift @@ -0,0 +1,91 @@ +// +// OpenTokRemoteVideoFactory.swift +// Runner +// +// Created by Zohaib Iqbal Kambrani on 20/10/2021. +// + +import Foundation + +class OpenTokRemoteVideoFactory: NSObject, FlutterPlatformViewFactory { + static var view: RemoteVideoPlatformView? + + static var viewToAddSub: UIView? + + static func getViewInstance( + frame: CGRect, + viewId: Int64, + args: Any?, + messenger: FlutterBinaryMessenger? + ) -> RemoteVideoPlatformView{ + if(view == nil) { + view = RemoteVideoPlatformView() + if viewToAddSub != nil { + view?.addSubscriberView(viewToAddSub!) + } + } + + return view! + } + + private var messenger: FlutterBinaryMessenger + + init(messenger: FlutterBinaryMessenger) { + self.messenger = messenger + super.init() + } + + func create( + withFrame frame: CGRect, + viewIdentifier viewId: Int64, + arguments args: Any? + ) -> FlutterPlatformView { + return OpenTokRemoteVideoFactory.getViewInstance( + frame: frame, + viewId: viewId, + args: args, + messenger: messenger) + } +} + +class RemoteVideoPlatformView: NSObject, FlutterPlatformView { + private let videoContainer: RemoteVideoContainer + + override init() { + videoContainer = RemoteVideoContainer() + super.init() + } + + public func addSubscriberView(_ view: UIView) { + videoContainer.addSubscriberView(view) + } + + func view() -> UIView { + return videoContainer + } +} + +final class RemoteVideoContainer: UIView { + private let subscriberContainer = UIView() + + init() { + super.init(frame: .zero) + addSubview(subscriberContainer) + } + + + public func addSubscriberView(_ view: UIView) { + subscriberContainer.addSubview(view) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + let width = frame.width + let height = frame.height + subscriberContainer.frame = CGRect(x: 0, y: 0, width: width, height: height) + } +} diff --git a/lib/config/config.dart b/lib/config/config.dart index efac58e5..c1622769 100644 --- a/lib/config/config.dart +++ b/lib/config/config.dart @@ -6,8 +6,10 @@ import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart'; const MAX_SMALL_SCREEN = 660; // PACKAGES and OFFERS -const EXA_CART_API_BASE_URL = 'http://10.50.100.217:1010/offersdiscounts'; // http://10.200.101.75:9000 +const EXA_CART_API_BASE_URL = 'https://mdlaboratories.com/offersdiscounts'; +// const EXA_CART_API_BASE_URL = 'http://10.200.101.75:9000'; const PACKAGES_CATEGORIES = '/api/categories'; +const PACKAGES_TOKEN = '/api/token'; const PACKAGES_PRODUCTS = '/api/products'; const PACKAGES_CUSTOMER = '/api/customers'; const PACKAGES_SHOPPING_CART = '/api/shopping_cart_items'; @@ -19,7 +21,7 @@ const BASE_URL = 'https://uat.hmgwebservices.com/'; // const BASE_PHARMACY_URL = 'https://uat.hmgwebservices.com/epharmacy/api/'; // const PHARMACY_BASE_URL = 'https://uat.hmgwebservices.com/epharmacy/api/'; -// // Pharmacy Production URLs +// Pharmacy Production URLs const BASE_PHARMACY_URL = 'https://mdlaboratories.com/exacartapi/api/'; const PHARMACY_BASE_URL = 'https://mdlaboratories.com/exacartapi/api/'; @@ -420,8 +422,8 @@ const TRANSFER_YAHALA_LOYALITY_POINTS = "Services/Patients.svc/REST/TransferYaHa const LAKUM_GET_USER_TERMS_AND_CONDITIONS = "Services/ERP.svc/REST/GetUserTermsAndConditionsForEPharmcy"; const PRESCRIPTION = 'Services/Patients.svc/REST/GetPrescriptionApptList'; const GET_RECOMMENDED_PRODUCT = 'alsoProduct/'; -const GET_MOST_VIEWED_PRODUCTS = "mostview"; -const GET_NEW_PRODUCTS = "newproducts"; +const GET_MOST_VIEWED_PRODUCTS = "mostview?"; +const GET_NEW_PRODUCTS = "newproducts?"; // Home Health Care const HHC_GET_ALL_SERVICES = "Services/Patients.svc/REST/PatientER_HHC_GetAllServices"; @@ -438,20 +440,9 @@ const PHARMACY_MAKE_REVIEW = 'epharmacy/api/insertreviews'; const BLOOD_DONATION_REGISTER_BLOOD_TYPE = 'Services/PatientVarification.svc/REST/BloodDonation_RegisterBloodType'; const ADD_USER_AGREEMENT_FOR_BLOOD_DONATION = 'Services/PatientVarification.svc/REST/AddUserAgreementForBloodDonation'; -// HHC RC SERVICES -const HHC_GET_ALL_SERVICES_RC = "rc/api/HHC/getallhhc"; -const ADD_HHC_ORDER_RC = "rc/api/HHC/add"; -const GET_ALL_HHC_ORDERS_RC = 'rc/api/hhc/list'; -const UPDATE_HHC_ORDER_RC = 'rc/api/hhc/update'; - // CMC RC SERVICES const GET_ALL_CMC_SERVICES_RC = 'rc/api/cmc/getallcmc'; const ADD_CMC_ORDER_RC = 'rc/api/cmc/add'; -const GET_ALL_CMC_ORDERS_RC = 'rc/api/cmc/list'; -const UPDATE_CMC_ORDER_RC = 'rc/api/cmc/update'; - -// RRT RC SERVICES -const ADD_RRT_ORDER_RC = "rc/api/rrt/add"; //Pharmacy wishlist const GET_WISHLIST = "shopping_cart_items/"; diff --git a/lib/config/localized_values.dart b/lib/config/localized_values.dart index 93716c0c..30f3024b 100644 --- a/lib/config/localized_values.dart +++ b/lib/config/localized_values.dart @@ -830,6 +830,10 @@ const Map localizedValues = { }, "failedToAccessHmgServices": {"en": "Connected with HMG Network,\n\nBut failed to access HMG services", "ar": "Connected with HMG Network,\n\nBut failed to access HMG services"}, "offerAndPackages": {"en": "Offers and Packages", "ar": "العروض والباقات"}, + "offerAndPackagesDetails": { + "en": "This service allows you to view all HMG Offers:", + "ar": "This service allows you to view all HMG Offers:" + }, "InvoiceNo": {"en": "Invoice No", "ar": "رقم الفاتورة"}, "InvoiceDate": {"en": "Invoice Date", "ar": "تاريخ الفاتورة"}, "SpecialResult": {"en": " Special Result", "ar": "نتيجة خاصة"}, diff --git a/lib/core/model/packages_offers/requests/CreateCustomerRequestModel.dart b/lib/core/model/packages_offers/requests/CreateCustomerRequestModel.dart index 116545bd..9cc3d3b9 100644 --- a/lib/core/model/packages_offers/requests/CreateCustomerRequestModel.dart +++ b/lib/core/model/packages_offers/requests/CreateCustomerRequestModel.dart @@ -1,20 +1,41 @@ +import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart'; import 'package:flutter/cupertino.dart'; class PackagesCustomerRequestModel { + String username; + String first_name; + String last_name; String email; - String phoneNumber; + String phone; + String national_id; + String date_of_birth; - PackagesCustomerRequestModel({@required this.email, @required this.phoneNumber}); + + PackagesCustomerRequestModel.fromUser(AuthenticatedUser user){ + this.username = "${user.patientID}"; + this.first_name = user.firstName; + this.last_name = user.lastName; + this.email = user.emailAddress; + this.phone = user.mobileNumber; + this.national_id = user.patientIdentificationNo; + this.date_of_birth = user.dateofBirth; + } Map json() { return { "customer" : { - "email": email, - "addresses": [{ - "email": email, - "phone_number": phoneNumber - }] + "email": email ?? '', + "username": username ?? '', + "national_id": national_id ?? '', + "phone": phone ?? '', + "date_of_birth": date_of_birth ?? '', + "first_name": first_name ?? '', + "last_name": last_name ?? '', + // "addresses": [{ + // "email": email, + // "phone_number": phoneNumber + // }] } }; } diff --git a/lib/core/service/client/base_app_client.dart b/lib/core/service/client/base_app_client.dart index b1a84fb0..56d5f6d6 100644 --- a/lib/core/service/client/base_app_client.dart +++ b/lib/core/service/client/base_app_client.dart @@ -1,9 +1,11 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io' show Platform; import 'package:diplomaticquarterapp/config/config.dart'; import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; import 'package:diplomaticquarterapp/core/service/medical/vital_sign_service.dart'; +import 'package:diplomaticquarterapp/core/service/packages_offers/PackagesOffersServices.dart'; import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart'; import 'package:diplomaticquarterapp/models/Appointments/toDoCountProviderModel.dart'; import 'package:diplomaticquarterapp/pages/appUpdatePage/app_update_page.dart'; @@ -462,7 +464,7 @@ class BaseAppClient { simplePost( String fullUrl, { - Map body, + Map body, Map headers, Function(dynamic response, int statusCode) onSuccess, Function(String error, int statusCode) onFailure, }) async { @@ -470,14 +472,17 @@ class BaseAppClient { print("URL Query String: $url"); if (await Utils.checkConnection()) { + headers.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); final response = await http.post( url.trim(), body: json.encode(body), - headers: {'Content-Type': 'application/json', 'Accept': 'application/json'}, + headers: headers, ); final int statusCode = response.statusCode; print("statusCode :$statusCode"); + if(await handleUnauthorized(statusCode, forUrl: fullUrl)) + simplePost(fullUrl, onFailure: onFailure, onSuccess: onSuccess, body: body, headers: headers); if (statusCode < 200 || statusCode >= 400 || json == null) { onFailure('Error While Fetching data', statusCode); @@ -489,7 +494,7 @@ class BaseAppClient { } } - simpleGet(String fullUrl, {Function(dynamic response, int statusCode) onSuccess, Function(String error, int statusCode) onFailure, Map queryParams}) async { + simpleGet(String fullUrl, {Function(dynamic response, int statusCode) onSuccess, Function(String error, int statusCode) onFailure, Map queryParams, Map headers}) async { String url = fullUrl; print("URL Query String: $url"); @@ -501,13 +506,16 @@ class BaseAppClient { } if (await Utils.checkConnection()) { + headers.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); final response = await http.get( url.trim(), - headers: {'Content-Type': 'application/json', 'Accept': 'application/json'}, + headers: headers, ); final int statusCode = response.statusCode; print("statusCode :$statusCode"); + if(await handleUnauthorized(statusCode, forUrl: fullUrl)) + simpleGet(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, queryParams: queryParams); if (statusCode < 200 || statusCode >= 400 || json == null) { onFailure('Error While Fetching data', statusCode); @@ -519,19 +527,22 @@ class BaseAppClient { } } - simplePut(String fullUrl, {Map body, Function(dynamic response, int statusCode) onSuccess, Function(String error, int statusCode) onFailure}) async { + simplePut(String fullUrl, {Map body, Map headers, Function(dynamic response, int statusCode) onSuccess, Function(String error, int statusCode) onFailure}) async { String url = fullUrl; print("URL Query String: $url"); if (await Utils.checkConnection()) { + headers.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); final response = await http.put( url.trim(), body: json.encode(body), - headers: {'Content-Type': 'application/json', 'Accept': 'application/json'}, + headers: headers, ); final int statusCode = response.statusCode; print("statusCode :$statusCode"); + if(await handleUnauthorized(statusCode, forUrl: fullUrl)) + simplePut(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, body: body); if (statusCode < 200 || statusCode >= 400 || json == null) { onFailure('Error While Fetching data', statusCode); @@ -543,7 +554,7 @@ class BaseAppClient { } } - simpleDelete(String fullUrl, {Function(dynamic response, int statusCode) onSuccess, Function(String error, int statusCode) onFailure, Map queryParams}) async { + simpleDelete(String fullUrl, {Function(dynamic response, int statusCode) onSuccess, Function(String error, int statusCode) onFailure, Map queryParams, Map headers}) async { String url = fullUrl; print("URL Query String: $url"); @@ -555,13 +566,16 @@ class BaseAppClient { } if (await Utils.checkConnection()) { + headers.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); final response = await http.delete( url.trim(), - headers: {'Content-Type': 'application/json', 'Accept': 'application/json'}, + headers: headers, ); final int statusCode = response.statusCode; print("statusCode :$statusCode"); + if(await handleUnauthorized(statusCode, forUrl: fullUrl)) + simpleDelete(fullUrl, onFailure: onFailure, onSuccess: onSuccess, queryParams: queryParams, headers: headers); if (statusCode < 200 || statusCode >= 400 || json == null) { onFailure('Error While Fetching data', statusCode); @@ -573,6 +587,15 @@ class BaseAppClient { } } + Future handleUnauthorized(int statusCode, {String forUrl}) async{ + if(forUrl.startsWith(EXA_CART_API_BASE_URL) && statusCode == 401) { + final token = await generatePackagesToken(); + packagesAuthHeader['Authorization'] = 'Bearer $token'; + return token != null && (token is String); + } + return false; + } + logout() async { await sharedPref.remove(LOGIN_TOKEN_ID); await sharedPref.remove(PHARMACY_CUSTOMER_ID); @@ -765,4 +788,29 @@ class BaseAppClient { onFailure(e.toString(), -1); } } + + + + Future generatePackagesToken() async { + var url = EXA_CART_API_BASE_URL + PACKAGES_TOKEN; + var body = { + "api_client": { + "client_id": "a4ab6be4-424f-4836-b032-46caed88e184", + "client_secret": "3c1a3e07-4a40-4510-9fb0-ee5f0a72752c" + } + }; + String token; + final completer = Completer(); + simplePost(url, body: body, headers: {}, onSuccess: (dynamic stringResponse, int statusCode) { + if (statusCode == 200) { + var jsonResponse = json.decode(stringResponse); + token = jsonResponse['auth_token']; + completer.complete(); + } + }, onFailure: (String error, int statusCode) { + completer.complete(); + }); + await completer.future; + return token; + } } diff --git a/lib/core/service/packages_offers/PackagesOffersServices.dart b/lib/core/service/packages_offers/PackagesOffersServices.dart index 479f59d3..dbc32029 100644 --- a/lib/core/service/packages_offers/PackagesOffersServices.dart +++ b/lib/core/service/packages_offers/PackagesOffersServices.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:developer'; import 'package:diplomaticquarterapp/config/config.dart'; +import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; import 'package:diplomaticquarterapp/core/model/ResponseModel.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/requests/AddProductToCartRequestModel.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/requests/CreateCustomerRequestModel.dart'; @@ -13,10 +14,15 @@ import 'package:diplomaticquarterapp/core/model/packages_offers/responses/Packag import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/responses/order_response_model.dart'; import 'package:diplomaticquarterapp/core/service/base_service.dart'; +import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart'; +import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart'; import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart'; import 'package:flutter/cupertino.dart'; +var packagesAuthHeader = {'Authorization' : ''}; class OffersAndPackagesServices extends BaseService { + AuthenticatedUser patientUser; List categoryList = List(); List productList = List(); List latestOffersList = List(); @@ -25,13 +31,14 @@ class OffersAndPackagesServices extends BaseService { List cartItemList = List(); String cartItemCount = ""; + PackagesCustomerResponseModel customer; Future> getAllCategories(OffersCategoriesRequestModel request) async { Future errorThrow; var url = EXA_CART_API_BASE_URL + PACKAGES_CATEGORIES; - await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + await baseAppClient.simpleGet(url, headers: packagesAuthHeader, onSuccess: (dynamic stringResponse, int statusCode) { if (statusCode == 200) { var jsonResponse = json.decode(stringResponse); jsonResponse['categories'].forEach((json) { @@ -39,7 +46,6 @@ class OffersAndPackagesServices extends BaseService { }); } }, onFailure: (String error, int statusCode) { - log(error); }, queryParams: request.toFlatMap()); return categoryList; @@ -52,7 +58,7 @@ class OffersAndPackagesServices extends BaseService { productList = List(); var url = EXA_CART_API_BASE_URL + PACKAGES_PRODUCTS; - await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + await baseAppClient.simpleGet(url, headers: packagesAuthHeader, onSuccess: (dynamic stringResponse, int statusCode) { if (statusCode == 200) { var jsonResponse = json.decode(stringResponse); jsonResponse['products'].forEach((json) { @@ -60,7 +66,6 @@ class OffersAndPackagesServices extends BaseService { }); } }, onFailure: (String error, int statusCode) { - log(error); }, queryParams: request.toFlatMap()); return productList; @@ -69,7 +74,7 @@ class OffersAndPackagesServices extends BaseService { Future> getLatestOffers({@required OffersProductsRequestModel request, @required BuildContext context, @required bool showLoading = true}) async { var url = EXA_CART_API_BASE_URL + PACKAGES_PRODUCTS; - await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + await baseAppClient.simpleGet(url, headers: packagesAuthHeader, onSuccess: (dynamic stringResponse, int statusCode) { if (statusCode == 200) { var jsonResponse = json.decode(stringResponse); jsonResponse['products'].forEach((json) { @@ -86,7 +91,7 @@ class OffersAndPackagesServices extends BaseService { Future> getBestSellers({@required OffersProductsRequestModel request, @required BuildContext context, @required bool showLoading = true}) async { var url = EXA_CART_API_BASE_URL + PACKAGES_PRODUCTS; - await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + await baseAppClient.simpleGet(url, headers: packagesAuthHeader, onSuccess: (dynamic stringResponse, int statusCode) { if (statusCode == 200) { var jsonResponse = json.decode(stringResponse); jsonResponse['products'].forEach((json) { @@ -103,7 +108,7 @@ class OffersAndPackagesServices extends BaseService { Future> getBanners({@required OffersProductsRequestModel request, @required BuildContext context, @required bool showLoading = true}) async { var url = EXA_CART_API_BASE_URL + PACKAGES_PRODUCTS; - await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + await baseAppClient.simpleGet(url, headers: packagesAuthHeader, onSuccess: (dynamic stringResponse, int statusCode) { if (statusCode == 200) { var jsonResponse = json.decode(stringResponse); jsonResponse['products'].forEach((json) { @@ -121,6 +126,22 @@ class OffersAndPackagesServices extends BaseService { var finished = 0; var totalCalls = 3; + final auth_token = await baseAppClient.generatePackagesToken(); + if(auth_token == null){ + throw 'Something went wrong while authentication, Please try again letter'; + } + packagesAuthHeader["Authorization"] = 'Bearer $auth_token'; + + + // Check and Create Customer + if(patientUser != null){ + customer = await getCurrentCustomer(context: context, showLoading: showLoading); + if(customer == null){ + createCustomer(PackagesCustomerRequestModel.fromUser(patientUser), context: context); + } + } + + completedAll(){ finished++; @@ -162,24 +183,42 @@ class OffersAndPackagesServices extends BaseService { _showLoading(context, showLoading); var url = EXA_CART_API_BASE_URL + PACKAGES_CUSTOMER; - await baseAppClient.simplePost(url, body: request.json(), onSuccess: (dynamic stringResponse, int statusCode){ - _hideLoading(context, showLoading); + await baseAppClient.simplePost(url, headers: packagesAuthHeader, body: request.json(), onSuccess: (dynamic stringResponse, int statusCode){ var jsonResponse = json.decode(stringResponse); var customerJson = jsonResponse['customers'].first; customer = PackagesCustomerResponseModel.fromJson(customerJson); }, onFailure: (String error, int statusCode){ - _hideLoading(context, showLoading); errorThrow = Future.error(error); log(error); }); + _hideLoading(context, showLoading); await Future.delayed(Duration(seconds: 1)); return errorThrow ?? customer; } + Future getCurrentCustomer({@required BuildContext context, bool showLoading = true}) async{ + if(customer != null) + return Future.value(customer); + + _showLoading(context, showLoading); + var url = EXA_CART_API_BASE_URL + PACKAGES_CUSTOMER + "/username/${patientUser.patientID}"; + await baseAppClient.simpleGet(url, headers: packagesAuthHeader, onSuccess: (dynamic stringResponse, int statusCode){ + var jsonResponse = json.decode(stringResponse); + var customerJson = jsonResponse['customers'].first; + customer = PackagesCustomerResponseModel.fromJson(customerJson); + + }, onFailure: (String error, int statusCode){ + log(error); + }); + + _hideLoading(context, showLoading); + return customer; + } + // -------------------- @@ -191,7 +230,7 @@ class OffersAndPackagesServices extends BaseService { cartItemList.clear(); _showLoading(context, showLoading); var url = EXA_CART_API_BASE_URL + PACKAGES_SHOPPING_CART + '/${customer.id}'; - await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + await baseAppClient.simpleGet(url, headers: packagesAuthHeader, onSuccess: (dynamic stringResponse, int statusCode) { _hideLoading(context, showLoading); var jsonResponse = json.decode(stringResponse); @@ -216,7 +255,7 @@ class OffersAndPackagesServices extends BaseService { _showLoading(context, showLoading); var url = EXA_CART_API_BASE_URL + PACKAGES_SHOPPING_CART; - await baseAppClient.simplePost(url, body: request.json(), onSuccess: (dynamic stringResponse, int statusCode){ + await baseAppClient.simplePost(url, headers: packagesAuthHeader, body: request.json(), onSuccess: (dynamic stringResponse, int statusCode){ _hideLoading(context, showLoading); var jsonResponse = json.decode(stringResponse); @@ -237,7 +276,7 @@ class OffersAndPackagesServices extends BaseService { _showLoading(context, showLoading); var url = EXA_CART_API_BASE_URL + PACKAGES_SHOPPING_CART + '/$cartItemID'; - await baseAppClient.simplePut(url, body: request.json(), onSuccess: (dynamic stringResponse, int statusCode){ + await baseAppClient.simplePut(url, headers: packagesAuthHeader, body: request.json(), onSuccess: (dynamic stringResponse, int statusCode){ _hideLoading(context, showLoading); var jsonResponse = json.decode(stringResponse); @@ -257,7 +296,7 @@ class OffersAndPackagesServices extends BaseService { _showLoading(context, showLoading); var url = EXA_CART_API_BASE_URL + PACKAGES_SHOPPING_CART + '/$cartItemID'; - await baseAppClient.simpleDelete(url, onSuccess: (dynamic stringResponse, int statusCode){ + await baseAppClient.simpleDelete(url, headers: packagesAuthHeader, onSuccess: (dynamic stringResponse, int statusCode){ _hideLoading(context, showLoading); // var jsonResponse = json.decode(stringResponse); @@ -280,8 +319,8 @@ class OffersAndPackagesServices extends BaseService { "order": { "customer_id" : customer.id, "billing_address": { - "email": customer.email, - "phone_number": customer.addresses.first.phoneNumber + "email": patientUser.emailAddress, + "phone_number": patientUser.mobileNumber }, "payment_method_system_name": "Payments.PayFort", "payment_option": paymentOption @@ -291,7 +330,7 @@ class OffersAndPackagesServices extends BaseService { int order_id; _showLoading(context, showLoading); var url = EXA_CART_API_BASE_URL + PACKAGES_ORDERS; - await baseAppClient.simplePost(url, body: jsonBody, onSuccess: (dynamic stringResponse, int statusCode){ + await baseAppClient.simplePost(url, headers: packagesAuthHeader, body: jsonBody, onSuccess: (dynamic stringResponse, int statusCode){ _hideLoading(context, showLoading); var jsonResponse = json.decode(stringResponse); @@ -312,7 +351,7 @@ class OffersAndPackagesServices extends BaseService { _showLoading(context, showLoading); var url = EXA_CART_API_BASE_URL + PACKAGES_ORDERS + '/$id'; - await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + await baseAppClient.simpleGet(url, headers: packagesAuthHeader, onSuccess: (dynamic stringResponse, int statusCode) { _hideLoading(context, showLoading); var jsonResponse = json.decode(stringResponse); diff --git a/lib/core/viewModels/packages_offers/PackagesOffersViewModel.dart b/lib/core/viewModels/packages_offers/PackagesOffersViewModel.dart index ad950b5d..1b68e9d7 100644 --- a/lib/core/viewModels/packages_offers/PackagesOffersViewModel.dart +++ b/lib/core/viewModels/packages_offers/PackagesOffersViewModel.dart @@ -4,6 +4,7 @@ import 'package:diplomaticquarterapp/core/model/packages_offers/responses/Packag import 'package:diplomaticquarterapp/core/service/packages_offers/PackagesOffersServices.dart'; import 'package:diplomaticquarterapp/core/viewModels/base_view_model.dart'; import 'package:diplomaticquarterapp/locator.dart'; +import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart'; class OfferCategoriesViewModel extends BaseViewModel{ OffersAndPackagesServices service = locator(); @@ -14,6 +15,7 @@ class OfferCategoriesViewModel extends BaseViewModel{ class PackagesViewModel extends BaseViewModel { OffersAndPackagesServices service = locator(); + List get categoryList => service.categoryList; List get productList => service.productList; List get latestOffersList => service.latestOffersList; diff --git a/lib/main.dart b/lib/main.dart index 71420951..99b7cefe 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -167,6 +167,7 @@ class _MyApp extends State { // ), // ), initialRoute: SPLASH, + // initialRoute: OPENTOK_CALL_PAGE, // initialRoute: PACKAGES_OFFERS, // initialRoute: PACKAGES_ORDER_COMPLETED, routes: routes, diff --git a/lib/pages/landing/fragments/home_page_fragment2.dart b/lib/pages/landing/fragments/home_page_fragment2.dart index 21a82f6a..7fcfecae 100644 --- a/lib/pages/landing/fragments/home_page_fragment2.dart +++ b/lib/pages/landing/fragments/home_page_fragment2.dart @@ -1,5 +1,6 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:diplomaticquarterapp/config/size_config.dart'; +import 'package:diplomaticquarterapp/core/service/packages_offers/PackagesOffersServices.dart'; import 'package:diplomaticquarterapp/core/viewModels/dashboard_view_model.dart'; import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart'; import 'package:diplomaticquarterapp/models/Appointments/AppoimentAllHistoryResultList.dart'; @@ -8,14 +9,12 @@ import 'package:diplomaticquarterapp/models/gradient_color.dart'; import 'package:diplomaticquarterapp/models/hmg_services.dart'; import 'package:diplomaticquarterapp/models/slider_data.dart'; import 'package:diplomaticquarterapp/pages/AlHabibMedicalService/all_habib_medical_service_page.dart'; -import 'package:diplomaticquarterapp/pages/AlHabibMedicalService/all_habib_medical_service_page2.dart'; import 'package:diplomaticquarterapp/pages/landing/widgets/logged_slider_view.dart'; import 'package:diplomaticquarterapp/pages/landing/widgets/services_view.dart'; import 'package:diplomaticquarterapp/pages/landing/widgets/slider_view.dart'; import 'package:diplomaticquarterapp/pages/medical/medical_profile_page_new.dart'; import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackagesPage.dart'; import 'package:diplomaticquarterapp/theme/colors.dart'; -import 'package:diplomaticquarterapp/uitl/app_toast.dart'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/uitl/utils.dart'; import 'package:diplomaticquarterapp/uitl/utils_new.dart'; @@ -53,15 +52,15 @@ class _HomePageFragment2State extends State { initialiseHmgServices(bool isLogin) { hmgServices.clear(); - hmgServices.add(new HmgServices(0, TranslationBase.of(context).liveCareTitle, TranslationBase.of(context).liveCareSubtitle, "assets/images/new/Live_Care.svg", isLogin)); + hmgServices.add(new HmgServices(0, TranslationBase.of(context).liveCare, TranslationBase.of(context).onlineConsulting, "assets/images/new/Live_Care.svg", isLogin)); hmgServices.add(new HmgServices(1, TranslationBase.of(context).covidTest, TranslationBase.of(context).driveThru, "assets/images/new/covid_test_drive_thru.svg", isLogin)); - hmgServices.add(new HmgServices(2, TranslationBase.of(context).onlinePayment, TranslationBase.of(context).onlinePaymentSubtitle, "assets/images/new/paymentMethods.png", isLogin)); + hmgServices.add(new HmgServices(2, TranslationBase.of(context).online, TranslationBase.of(context).payment, "assets/images/new/paymentMethods.png", isLogin)); hmgServices.add(new HmgServices(3, TranslationBase.of(context).hhcHome, TranslationBase.of(context).healthCare, "assets/images/new/HHC.svg", isLogin)); - hmgServices.add(new HmgServices(4, TranslationBase.of(context).cmcTitle, TranslationBase.of(context).cmcSubtitle, "assets/images/new/comprehensive_checkup.svg", isLogin)); - hmgServices.add(new HmgServices(5, TranslationBase.of(context).emergencyTitle, TranslationBase.of(context).emergencySubtitle, "assets/images/new/emergency.svg", isLogin)); - hmgServices.add(new HmgServices(6, TranslationBase.of(context).ereferralTitle, TranslationBase.of(context).ereferralSubtitle, "assets/images/new/E_Referral.svg", isLogin)); - hmgServices.add(new HmgServices(7, TranslationBase.of(context).waterTitle, TranslationBase.of(context).waterSubtitle, "assets/images/new/h2o.svg", isLogin)); - hmgServices.add(new HmgServices(8, TranslationBase.of(context).findUs, TranslationBase.of(context).reachUs, "assets/images/new/reach_us.svg", isLogin)); + hmgServices.add(new HmgServices(4, TranslationBase.of(context).checkup, TranslationBase.of(context).comprehensive, "assets/images/new/comprehensive_checkup.svg", isLogin)); + hmgServices.add(new HmgServices(5, TranslationBase.of(context).emergency, TranslationBase.of(context).services2, "assets/images/new/emergency.svg", isLogin)); + hmgServices.add(new HmgServices(6, TranslationBase.of(context).refferal, TranslationBase.of(context).services2, "assets/images/new/E_Referral.svg", isLogin)); + hmgServices.add(new HmgServices(7, "H\u2082O", TranslationBase.of(context).dailyWater, "assets/images/new/h2o.svg", isLogin)); + hmgServices.add(new HmgServices(8, TranslationBase.of(context).reachUs, TranslationBase.of(context).findUs, "assets/images/new/reach_us.svg", isLogin)); } @override @@ -237,7 +236,7 @@ class _HomePageFragment2State extends State { ), FlatButton( onPressed: () { - Navigator.push(context, FadePage(page: AllHabibMedicalSevicePage2())); + Navigator.push(context, FadePage(page: AllHabibMedicalService())); }, child: Text( TranslationBase.of(context).viewAllServices, @@ -271,7 +270,7 @@ class _HomePageFragment2State extends State { ), ], ), - mHeight(60), + mHeight(140), ], ), ); @@ -282,8 +281,8 @@ class _HomePageFragment2State extends State { flex: 1, child: InkWell( onTap: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => PackagesHomePage())); - + final user = projectViewModel.user; + Navigator.of(context).push(MaterialPageRoute(builder: (context) => PackagesHomePage(user))); }, child: Container( width: double.infinity, @@ -496,7 +495,7 @@ class _HomePageFragment2State extends State { Container( width: double.infinity, height: double.infinity, - padding: EdgeInsets.all(SizeConfig.widthMultiplier * 3.3), + padding: EdgeInsets.all(SizeConfig.widthMultiplier * 3.4), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, @@ -515,7 +514,7 @@ class _HomePageFragment2State extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - TranslationBase.of(context).pharmacyTitle, + TranslationBase.of(context).onlinePharmacy, style: TextStyle( color: Colors.black, fontSize: 14, @@ -526,7 +525,7 @@ class _HomePageFragment2State extends State { ), projectViewModel.isArabic ? mHeight(5) : Container(), Text( - TranslationBase.of(context).pharmacySubtitle, + TranslationBase.of(context).ecommerceSolution, style: TextStyle( color: Colors.black, fontSize: 9, diff --git a/lib/pages/packages_offers/CreateCustomerDailogPage.dart b/lib/pages/packages_offers/CreateCustomerDailogPage.dart index d7a98a64..70291a88 100644 --- a/lib/pages/packages_offers/CreateCustomerDailogPage.dart +++ b/lib/pages/packages_offers/CreateCustomerDailogPage.dart @@ -152,12 +152,12 @@ class _CreateCustomerDialogPageState extends State wit createCustomer() async{ setState(() => _enableInput = false); - loading(true); - var request = PackagesCustomerRequestModel(email: _emailTextController.text, phoneNumber: _phoneTextController.text); - viewModel().service - .createCustomer(request, context: context, showLoading: false) - .then((value) => success()) - .catchError((error) => showError(error)); + // loading(true); + // var request = PackagesCustomerRequestModel(email: _emailTextController.text, phoneNumber: _phoneTextController.text); + // viewModel().service + // .createCustomer(request, context: context, showLoading: false) + // .then((value) => success()) + // .catchError((error) => showError(error)); } diff --git a/lib/pages/packages_offers/OfferAndPackagesPage.dart b/lib/pages/packages_offers/OfferAndPackagesPage.dart index b3b0605d..4005f835 100644 --- a/lib/pages/packages_offers/OfferAndPackagesPage.dart +++ b/lib/pages/packages_offers/OfferAndPackagesPage.dart @@ -1,16 +1,22 @@ import 'package:after_layout/after_layout.dart'; import 'package:carousel_slider/carousel_slider.dart'; +import 'package:diplomaticquarterapp/config/config.dart'; import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; +import 'package:diplomaticquarterapp/core/model/ImagesInfo.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/requests/AddProductToCartRequestModel.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersProductsRequestModel.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart'; +import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart'; +import 'package:diplomaticquarterapp/models/Authentication/select_device_imei_res.dart'; import 'package:diplomaticquarterapp/pages/base/base_view.dart'; import 'package:diplomaticquarterapp/pages/packages_offers/ClinicOfferAndPackagesPage.dart'; import 'package:diplomaticquarterapp/pages/packages_offers/CreateCustomerDailogPage.dart'; import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackageDetailPage.dart'; import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackagesCartPage.dart'; +import 'package:diplomaticquarterapp/routes.dart'; +import 'package:diplomaticquarterapp/services/authentication/auth_provider.dart' as auth; import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/uitl/utils.dart' as utils; @@ -26,8 +32,8 @@ import 'package:flutter_material_pickers/flutter_material_pickers.dart'; dynamic languageID; class PackagesHomePage extends StatefulWidget { - dynamic offersModel; - PackagesHomePage({@required this.offersModel}); + final AuthenticatedUser user; + PackagesHomePage(this.user); @override _PackagesHomePageState createState() => _PackagesHomePageState(); @@ -47,6 +53,7 @@ class _PackagesHomePageState extends State with AfterLayoutMix @override void afterFirstLayout(BuildContext context) async{ + viewModel.service.patientUser = widget.user; viewModel.service.loadOffersPackagesDataForMainPage(context: context, completion: (){ setState((){}); }); @@ -81,9 +88,11 @@ class _PackagesHomePageState extends State with AfterLayoutMix } onProductCartClick(PackagesResponseModel product) async { - if(viewModel.service.customer == null) - viewModel.service.customer = await CreateCustomerDialogPage(context: context).show(); - + if(viewModel.service.customer == null) { + // viewModel.service.customer = await CreateCustomerDialogPage(context: context).show(); + loginCheck(context); + } + if(viewModel.service.customer != null) { var request = AddProductToCartRequestModel(product_id: product.id, customer_id: viewModel.service.customer.id); await viewModel.service.addProductToCart(request, context: context).then((response){ @@ -104,6 +113,8 @@ class _PackagesHomePageState extends State with AfterLayoutMix return appScaffold = AppScaffold( + description: TranslationBase.of(context).offerAndPackagesDetails, + imagesInfo: [ImagesInfo(imageAr: 'https://hmgwebservices.com/Images/MobileApp/CMC/ar/0.png', imageEn: 'https://hmgwebservices.com/Images/MobileApp/CMC/en/0.png')], appBarTitle: TranslationBase.of(context).offerAndPackages, isShowAppBar: true, isPharmacy: false, @@ -409,4 +420,33 @@ class _PackagesHomePageState extends State with AfterLayoutMix )), ); } + + + + loginCheck(context) async { + var data = await sharedPref.getObject(IMEI_USER_DATA); + sharedPref.remove(REGISTER_DATA_FOR_LOGIIN); + if (data != null) { + Navigator.of(context).pushNamed(CONFIRM_LOGIN); + } else { + GifLoaderDialogUtils.showMyDialog(context); + + auth.AuthProvider().selectDeviceImei(DEVICE_TOKEN).then((SelectDeviceIMEIRES value) { + GifLoaderDialogUtils.hideDialog(context); + if (value != null) { + sharedPref.setObject(IMEI_USER_DATA, value); + Navigator.of(context).pushNamed(CONFIRM_LOGIN); + } else { + Navigator.of(context).pushNamed( + WELCOME_LOGIN, + ); + } + }).catchError((err) { + GifLoaderDialogUtils.hideDialog(context); + Navigator.of(context).pushNamed( + WELCOME_LOGIN, + ); + }); + } + } } diff --git a/lib/pages/webRTC/OpenTok/OpenTok.dart b/lib/pages/webRTC/OpenTok/OpenTok.dart new file mode 100644 index 00000000..7abcfca5 --- /dev/null +++ b/lib/pages/webRTC/OpenTok/OpenTok.dart @@ -0,0 +1,180 @@ +import 'package:diplomaticquarterapp/pages/conference/draggable_publisher.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; + +import 'OpenTokPlatformBridge.dart'; + + +class OpenTokConnectCallPage extends StatefulWidget{ + final String sessionId; + final String token; + final String apiKey; + OpenTokConnectCallPage({@required this.sessionId, @required this.token, @required this.apiKey}); + + @override + State createState() => OpenTokState(); + +} + +class OpenTokState extends State{ + OpenTokPlatformBridge openTokPlatform; + OpenTokSDKState sdkState = OpenTokSDKState.LOGGED_OUT; + + initOpenTok(){ + openTokPlatform = OpenTokPlatformBridge.init( + apiKey: widget.apiKey, + sessionID: widget.sessionId, + token: widget.token, + onStateChange: (state) { + setState(() => sdkState = state); + } + ); + } + + @override + void initState() { + initOpenTok(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar( + backgroundColor: Colors.transparent, + actions: [ + TextButton(onPressed: (){ + initOpenTok(); + }, child: Icon(Icons.connect_without_contact_outlined, color: Colors.green,)) + ], + ), + body: Column( + children: [ + Expanded( + child: Stack( + children: [ + if(sdkState == OpenTokSDKState.LOGGED_IN) + ...platformVideoViews(), + + + if(sdkState == OpenTokSDKState.LOGGED_OUT) + Center(child: Text("Logged Out"),), + + if(sdkState == OpenTokSDKState.ERROR) + Center(child: Text("Error opening session"),), + + if(sdkState == OpenTokSDKState.WAIT) + Center(child: CircularProgressIndicator(),), + + if(sdkState == OpenTokSDKState.ON_CALL) + Container(), + ], + ), + ), + Row( + children: [ + Expanded( + child: ElevatedButton( + child: Text('Mute Video') ,onPressed: () => openTokPlatform.toggleVideo() + ), + ), + SizedBox(width: 20,), + Expanded( + child: ElevatedButton( + child: Text('Mute Audio') ,onPressed: () => openTokPlatform.toggleAudio() + ), + ), + SizedBox(width: 20,), + Expanded( + child: ElevatedButton( + child: Text('Change Camera') ,onPressed: () => openTokPlatform.swapCamera() + ), + ), + ], + ) + ], + ), + ); + } + + bool remoteVideoOnFull = true; + List platformVideoViews(){ + return [ + SizedBox( + width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, + child: remoteVideoOnFull ? remotePlatformVideoView() : localPlatformVideoView() + ), + + DraggablePublisher( + onButtonBarHeight: ((double)async*{}(50)), + onButtonBarVisible: ((bool)async*{}(true)), + availableScreenSize: MediaQuery.of(context).size, + child: InkWell( + child: (remoteVideoOnFull ? localPlatformVideoView() : remotePlatformVideoView()), + onTap: (){ + print(''); + }, + ), + ) + ]; + } + + Widget localPlatformVideoView(){ + return PlatformViewLink( + viewType: 'local-video-container', // custom platform-view-type + surfaceFactory: + (BuildContext context, PlatformViewController controller) { + return AndroidViewSurface( + controller: controller, + gestureRecognizers: const >{}, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + + onCreatePlatformView: (PlatformViewCreationParams params) { + return PlatformViewsService.initSurfaceAndroidView( + id: params.id, + viewType: 'local-video-container', + // custom platform-view-type, + layoutDirection: TextDirection.ltr, + creationParams: {}, + creationParamsCodec: StandardMessageCodec(), + ) + ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) + ..create(); + }, + ); + } + + Widget remotePlatformVideoView(){ + return PlatformViewLink( + viewType: 'remote-video-container', // custom platform-view-type + surfaceFactory: + (BuildContext context, PlatformViewController controller) { + return AndroidViewSurface( + controller: controller, + gestureRecognizers: const >{}, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + onCreatePlatformView: (PlatformViewCreationParams params) { + return PlatformViewsService.initSurfaceAndroidView( + id: params.id, + viewType: 'remote-video-container', + // custom platform-view-type, + layoutDirection: TextDirection.ltr, + creationParams: {}, + creationParamsCodec: StandardMessageCodec(), + ) + ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) + ..create(); + }, + ); + } + +} \ No newline at end of file diff --git a/lib/pages/webRTC/OpenTok/OpenTokPlatformBridge.dart b/lib/pages/webRTC/OpenTok/OpenTokPlatformBridge.dart new file mode 100644 index 00000000..bfdb8fb4 --- /dev/null +++ b/lib/pages/webRTC/OpenTok/OpenTokPlatformBridge.dart @@ -0,0 +1,86 @@ + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:permission_handler/permission_handler.dart'; + +enum OpenTokSDKState{ LOGGED_OUT, LOGGED_IN, WAIT, ON_CALL, ERROR } +final _platformMethodChannel = const MethodChannel('OpenTok-Platform-Bridge'); + +class OpenTokPlatformBridge{ + + + OpenTokSDKState _sdkState = OpenTokSDKState.LOGGED_OUT; + bool _publishAudio = true; + bool _publishVideo = true; + + final void Function(OpenTokSDKState) onStateChange; + + OpenTokPlatformBridge.init({@required String sessionID, @required String token, @required String apiKey, this.onStateChange}){ + _initSession({'apiKey':apiKey, 'sessionId':sessionID, 'token':token, }); + _platformMethodChannel.setMethodCallHandler(incomingMethodHadler); + } + + Future incomingMethodHadler(MethodCall methodCall) async { + switch (methodCall.method) { + case 'updateState': + { + var arguments = 'OpenTokSDKState.${methodCall.arguments}'; + _sdkState = OpenTokSDKState.values.firstWhere((v) { + return v.toString() == arguments; + }); + onStateChange(_sdkState); + } + break; + default: + throw MissingPluginException('notImplemented'); + } + } + + + Future _initSession(Map openTokCredentials) async { + await requestPermissions(); + try { + await _platformMethodChannel.invokeMethod('initSession', openTokCredentials); + } on PlatformException catch (e) { + print(e); + } + } + + Future makeCall() async { + try { + await requestPermissions(); + + await _platformMethodChannel.invokeMethod('makeCall'); + } on PlatformException catch (e) { + print(e); + } + } + + Future requestPermissions() async { + await [Permission.microphone, Permission.camera].request(); + } + + Future swapCamera() async { + try { + await _platformMethodChannel.invokeMethod('swapCamera'); + } on PlatformException catch (e) {} + } + + Future toggleAudio() async { + _publishAudio = !_publishAudio; + dynamic params = {'publishAudio': _publishAudio}; + + try { + await _platformMethodChannel.invokeMethod('publishAudio', params); + } on PlatformException catch (e) {} + } + + Future toggleVideo() async { + _publishVideo = !_publishVideo; + dynamic params = {'publishVideo': _publishVideo}; + + try { + await _platformMethodChannel.invokeMethod('toggleVideo', params); + } on PlatformException catch (e) {} + } +} diff --git a/lib/routes.dart b/lib/routes.dart index 5d4ebea4..a8d850e1 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -20,6 +20,7 @@ import 'package:diplomaticquarterapp/pages/settings/settings.dart'; import 'package:diplomaticquarterapp/pages/symptom-checker/info.dart'; import 'package:diplomaticquarterapp/pages/symptom-checker/select-gender.dart'; import 'package:diplomaticquarterapp/pages/symptom-checker/symtom-checker.dart'; +import 'package:diplomaticquarterapp/pages/webRTC/OpenTok/OpenTok.dart'; import 'package:diplomaticquarterapp/splashPage.dart'; const String INIT_ROUTE = '/'; @@ -45,6 +46,7 @@ const String PACKAGES_OFFERS = 'packages-offers'; const String PACKAGES_OFFERS_CART = 'packages-offers-cart'; const String PACKAGES_ORDER_COMPLETED = 'packages-offers-cart'; const String TEST_PAGE = 'test-page'; +const String OPENTOK_CALL_PAGE = 'OPENTOK_CALL_PAGE'; const String HEALTH_WEATHER = 'health-weather'; const APP_UPDATE = 'app-update'; @@ -66,11 +68,15 @@ var routes = { SYMPTOM_CHECKER_INFO: (_) => SymptomInfo(), SELECT_GENDER: (_) => SelectGender(), SETTINGS: (_) => Settings(), - PACKAGES_OFFERS: (_) => PackagesHomePage(), PACKAGES_OFFERS_CART: (_) => PackagesCartPage(), PACKAGES_ORDER_COMPLETED: (_) => PackageOrderCompletedPage(), TEST_PAGE: (_) => TestPage(), HEALTH_WEATHER: (_) => HealthWeatherIndicator(), APP_UPDATE: (_) => AppUpdatePage(), SETTINGS: (_) => Settings(), + OPENTOK_CALL_PAGE: (_) => OpenTokConnectCallPage( + apiKey: '46209962', + sessionId: '1_MX40NjIwOTk2Mn5-MTYzNDY0ODM3NDY2Nn5PcnpnNGM0R1Q3ODZ6UXlFQ01lMDF5YWJ-fg', + token: 'T1==cGFydG5lcl9pZD00NjIwOTk2MiZzaWc9Y2ZhZjMzZjA3MWFkMjkxN2JmYjEwZDkxYTRkOTdhN2NmNWE4YjMyYTpzZXNzaW9uX2lkPTFfTVg0ME5qSXdPVGsyTW41LU1UWXpORFkwT0RNM05EWTJObjVQY25wbk5HTTBSMVEzT0RaNlVYbEZRMDFsTURGNVlXSi1mZyZjcmVhdGVfdGltZT0xNjM0NjQ4Mzc1Jm5vbmNlPTAuNzczMzcxNDUxMDgzMjQyMyZyb2xlPW1vZGVyYXRvciZleHBpcmVfdGltZT0xNjM0NzM0Nzc1JmluaXRpYWxfbGF5b3V0X2NsYXNzX2xpc3Q9' + ), }; diff --git a/lib/uitl/translations_delegate_base.dart b/lib/uitl/translations_delegate_base.dart index d61b4f10..88368bc9 100644 --- a/lib/uitl/translations_delegate_base.dart +++ b/lib/uitl/translations_delegate_base.dart @@ -1395,6 +1395,7 @@ class TranslationBase { String get enablingWifi => localizedValues['enablingWifi'][locale.languageCode]; String get offerAndPackages => localizedValues['offerAndPackages'][locale.languageCode]; + String get offerAndPackagesDetails => localizedValues['offerAndPackagesDetails'][locale.languageCode]; String get invoiceNo => localizedValues['InvoiceNo'][locale.languageCode]; diff --git a/lib/widgets/offers_packages/PackagesCartItemCard.dart b/lib/widgets/offers_packages/PackagesCartItemCard.dart index 3ae405c6..c59c08cd 100644 --- a/lib/widgets/offers_packages/PackagesCartItemCard.dart +++ b/lib/widgets/offers_packages/PackagesCartItemCard.dart @@ -82,8 +82,8 @@ class PackagesCartItemCardState extends State { left: 10, child: Row( children: [ - _totalPrice((widget.itemModel.product.price * widget.itemModel.quantity), context: context), _totalLabel(context: context), + _totalPrice((widget.itemModel.product.price * widget.itemModel.quantity), context: context), ], ), )