From d0986fcaf68dcc3f3b3e616966f3d327138b96f6 Mon Sep 17 00:00:00 2001 From: Zohaib Iqbal Kambrani <> Date: Mon, 18 Oct 2021 17:44:01 +0300 Subject: [PATCH] OpenTok SDK applied and method channels written to native --- android/app/build.gradle | 5 +- .../diplomaticquarterapp/MainActivity.kt | 11 +- .../diplomaticquarterapp/opentok/OpenTok.kt | 170 +++++++++++ .../diplomaticquarterapp/opentok/Video.kt | 63 ++++ .../utils/OpenTokPlatformBridge.kt | 50 ++++ .../utils/PlatformBridge.kt | 7 +- android/build.gradle | 7 +- ios/Flutter/.last_build_id | 2 +- ios/Podfile | 1 + ios/Podfile.lock | 26 +- ios/Runner.xcodeproj/project.pbxproj | 16 + ios/Runner/AppDelegate.swift | 6 +- ios/Runner/Helper/HMGPlatformBridge.swift | 2 +- ios/Runner/Helper/OpenTokPlatformBridge.swift | 59 ++++ ios/Runner/OpenTok/OpenTok.swift | 273 ++++++++++++++++++ 15 files changed, 682 insertions(+), 16 deletions(-) create mode 100644 android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/OpenTok.kt create mode 100644 android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/Video.kt create mode 100644 android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/OpenTokPlatformBridge.kt create mode 100644 ios/Runner/Helper/OpenTokPlatformBridge.swift create mode 100644 ios/Runner/OpenTok/OpenTok.swift diff --git a/android/app/build.gradle b/android/app/build.gradle index 35dd8a50..f13bce1b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,7 +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/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/OpenTok.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/OpenTok.kt new file mode 100644 index 00000000..5606343f --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/OpenTok.kt @@ -0,0 +1,170 @@ +package com.example.basic_video_chat_flutter + +import android.content.Context +import android.os.Handler +import android.os.Looper +import android.util.AttributeSet +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.opentok.android.* +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.StandardMessageCodec +import io.flutter.plugin.platform.PlatformView +import io.flutter.plugin.platform.PlatformViewFactory + + +enum class SdkState { + LOGGED_OUT, + LOGGED_IN, + WAIT, + ERROR +} + +class OpenTok(private var context: Context, private var flutterEngine: FlutterEngine){ + private lateinit var opentokVideoPlatformView: OpentokVideoPlatformView + + init { + opentokVideoPlatformView = OpentokVideoFactory.getViewInstance(context) + flutterEngine + .platformViewsController + .registry + // opentok-video-container is a custom platform-view-type + .registerViewFactory("opentok-video-container", OpentokVideoFactory()) + } + + 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) + opentokVideoPlatformView.publisherContainer.addView(view) + } + + notifyFlutter(SdkState.LOGGED_IN) + session.publish(publisher) + } + + override fun onDisconnected(session: Session) { + notifyFlutter(SdkState.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) + + opentokVideoPlatformView.subscriberContainer.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 + + opentokVideoPlatformView.subscriberContainer.removeAllViews() + } + } + + override fun onError(session: Session, opentokError: OpentokError) { + Log.d("MainActivity", "Session error: " + opentokError.message) + notifyFlutter(SdkState.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(SdkState.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(SdkState.LOGGED_OUT) + } + + override fun onError(subscriberKit: SubscriberKit, opentokError: OpentokError) { + Log.d("MainActivity", "SubscriberKit onError: " + opentokError.message) + notifyFlutter(SdkState.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(SdkState.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: SdkState) { + 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/Video.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/Video.kt new file mode 100644 index 00000000..0153908a --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/opentok/Video.kt @@ -0,0 +1,63 @@ +package com.example.basic_video_chat_flutter + +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 io.flutter.plugin.common.StandardMessageCodec +import io.flutter.plugin.platform.PlatformView +import io.flutter.plugin.platform.PlatformViewFactory + +class OpentokVideoFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) { + + companion object { + private lateinit var view: OpentokVideoPlatformView + + fun getViewInstance(context: Context): OpentokVideoPlatformView { + if(!this::view.isInitialized) { + view = OpentokVideoPlatformView(context) + } + + return view + } + } + + override fun create(context: Context, viewId: Int, args: Any?): PlatformView { + return getViewInstance(context) + } +} + +class OpentokVideoPlatformView(context: Context) : PlatformView { + private val videoContainer: OpenTokVideoContainer = OpenTokVideoContainer(context) + + val subscriberContainer get() = videoContainer.subscriberContainer + val publisherContainer get() = videoContainer.publisherContainer + + override fun getView(): View { + return videoContainer + } + + override fun dispose() {} +} + +class OpenTokVideoContainer @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = 0, + defStyleRes: Int = 0 +) : LinearLayout(context, attrs, defStyle, defStyleRes) { + + var subscriberContainer: FrameLayout + private set + + var publisherContainer: FrameLayout + private set + + init { + val view = LayoutInflater.from(context).inflate(R.layout.view_video, this, true) + subscriberContainer = view.findViewById(R.id.subscriber_container) + publisherContainer = view.findViewById(R.id.publisher_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..29ded185 --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/OpenTokPlatformBridge.kt @@ -0,0 +1,50 @@ +package com.ejada.hmg.utils + +import android.content.Context +import android.net.wifi.WifiManager +import android.util.Log +import com.ejada.hmg.MainActivity +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 com.example.basic_video_chat_flutter.OpenTok +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 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..3aa2cd1e 100644 --- a/ios/Flutter/.last_build_id +++ b/ios/Flutter/.last_build_id @@ -1 +1 @@ -e52eba3667a38bec777870899c15ae7d \ No newline at end of file +3f3d14a0ae775b56806906c2cb14a1f0 \ 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..9505487e 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 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 +52,8 @@ /* 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 = ""; }; + 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 +103,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 306FE6C9271D8B54002D6EFC /* OpenTok */ = { + isa = PBXGroup; + children = ( + 306FE6CA271D8B73002D6EFC /* OpenTok.swift */, + ); + path = OpenTok; + sourceTree = ""; + }; 555EAAA626EFB641859EF0BE /* Frameworks */ = { isa = PBXGroup; children = ( @@ -153,6 +165,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 306FE6C9271D8B54002D6EFC /* OpenTok */, E91B539E256AAC1400E96549 /* GuestPOC_Certificate.cer */, E91B539D256AAC1400E96549 /* GuestPOC_Certificate.p12 */, E91B5390256AAA6500E96549 /* Controllers */, @@ -182,6 +195,7 @@ E923EFD125863FDF00E3E751 /* GeoZoneModel.swift */, E923EFD3258645C100E3E751 /* HMG_Geofence.swift */, E923EFD52587443800E3E751 /* HMGPlatformBridge.swift */, + 306FE6C7271D790C002D6EFC /* OpenTokPlatformBridge.swift */, E9F7623A25922BCE00FB5CCF /* FlutterConstants.swift */, ); path = Helper; @@ -375,6 +389,7 @@ 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 */, @@ -382,6 +397,7 @@ E923EFD4258645C100E3E751 /* HMG_Geofence.swift in Sources */, E923EFD62587443800E3E751 /* HMGPlatformBridge.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 */, 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..3cb7fdbe --- /dev/null +++ b/ios/Runner/OpenTok/OpenTok.swift @@ -0,0 +1,273 @@ +// +// OpenTok.swift +// Runner +// +// Created by Zohaib Iqbal Kambrani on 18/10/2021. +// + +import Foundation +import OpenTok + +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? + init(mainViewController:MainFlutterVC, registrar:FlutterPluginRegistrar?){ + self.mainViewController = mainViewController + self.registrar = registrar + + let factory = OpentokVideoFactory(messenger: registrar!.messenger()) + registrar?.register(factory, withId: "opentok-video-container") + } + + var otSession: OTSession? + var vonageChannel: FlutterMethodChannel? + + 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) { + vonageChannel?.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 OpentokVideoFactory.view == nil { + OpentokVideoFactory.viewToAddPub = pubView + } else { + OpentokVideoFactory.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: 200, height: 300) + + if OpentokVideoFactory.view == nil { + OpentokVideoFactory.viewToAddSub = subView + } else { + OpentokVideoFactory.view?.addSubscriberView(subView) + } + } + } + + func subscriber(_ subscriber: OTSubscriberKit, didFailWithError error: OTError) { + print("Subscriber failed: \(error.localizedDescription)") + } + +} + +class OpentokVideoFactory: NSObject, FlutterPlatformViewFactory { + static var view: OpentokVideoPlatformView? + + static var viewToAddSub: UIView? + static var viewToAddPub: UIView? + + static func getViewInstance( + frame: CGRect, + viewId: Int64, + args: Any?, + messenger: FlutterBinaryMessenger? + ) -> OpentokVideoPlatformView{ + if(view == nil) { + view = OpentokVideoPlatformView() + if viewToAddSub != nil { + view?.addSubscriberView(viewToAddSub!) + } + 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 OpentokVideoFactory.getViewInstance( + frame: frame, + viewId: viewId, + args: args, + messenger: messenger) + } +} + +class OpentokVideoPlatformView: NSObject, FlutterPlatformView { + private let videoContainer: OpenTokVideoContainer + + override init() { + videoContainer = OpenTokVideoContainer() + super.init() + } + + public func addSubscriberView(_ view: UIView) { + videoContainer.addSubscriberView(view) + } + + public func addPublisherView(_ view: UIView) { + videoContainer.addPublisherView(view) + } + + func view() -> UIView { + return videoContainer + } +} + +final class OpenTokVideoContainer: UIView { + private let subscriberContainer = UIView() + private let publisherContainer = UIView() + + init() { + super.init(frame: .zero) + addSubview(subscriberContainer) + addSubview(publisherContainer) + } + + + public func addSubscriberView(_ view: UIView) { + subscriberContainer.addSubview(view) + } + + 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 + + let videoWidth = width / 2 + subscriberContainer.frame = CGRect(x: 0, y: 0, width: videoWidth, height: height) + publisherContainer.frame = CGRect(x: videoWidth, y: 0, width: videoWidth, height: height) + } +}