Compare commits

...

48 Commits

Author SHA1 Message Date
Aamir Muhammad bceae623ed End Call Android Native 2 years ago
Aamir Muhammad 7eec7e252c End Call Android Native 2 years ago
Aamir Muhammad fb63963905 Merge branch 'aamir_dev' into development_aamir_testing
# Conflicts:
#	lib/api/chat/chat_api_client.dart
#	lib/classes/chat_call_kit.dart
#	lib/main.dart
#	lib/provider/chat_call_provider.dart
#	lib/ui/chat/call/chat_incoming_call_screen.dart
#	lib/ui/chat/chat_detailed_screen.dart
#	lib/ui/landing/dashboard_screen.dart
#	lib/ui/login/login_screen.dart
2 years ago
Aamir Muhammad 1d7de73d28 End Call Android Native 2 years ago
Aamir Muhammad bce79efdd2 End Call Android Native 2 years ago
Aamir Muhammad 7bc36188c4 End Call Android Native 2 years ago
Aamir Muhammad 3a821a1744 End Call Android Native 2 years ago
Aamir Muhammad 3330052f47 Exclude 2 years ago
Aamir Muhammad 5870e80255 End Call Android 2 years ago
Aamir Muhammad 8aa92d127c End Call 2 years ago
Aamir Muhammad 2e22bfb483 End Call 2 years ago
Aamir Muhammad a8b1a001f8 Android Native 2 years ago
Aamir Muhammad fc2aceae0f Merge remote-tracking branch 'origin/development_aamir' into development_aamir 2 years ago
Aamir Muhammad 9e437b7ee9 Call Decline & Fixes 2 years ago
haroon amjad d7d90f620e VoIP udpates 2 years ago
Aamir Muhammad 8daa975be2 Merge branch 'development_aamir' of https://gitlab.com/Cloud_Solution/mohemm-flutter-app into development_aamir 2 years ago
Aamir Muhammad 748e170e02 Changes 2 years ago
Aamir Muhammad 6e85b81c73 Call Decline & Fixes 2 years ago
Aamir Muhammad a992c8dae9 Changes On iOS 2 years ago
Aamir Muhammad b417a158be Changes On iOS 2 years ago
Aamir Muhammad 39bcf5c3d9 Changes On iOS 2 years ago
Aamir Muhammad aed4a9602b Changes On iOS 2 years ago
Aamir Muhammad 8db09f20b7 Changes On iOS 2 years ago
Aamir Muhammad 88ac343203 Changes On iOS 2 years ago
Aamir Muhammad 9dcbdee156 Changes On iOS 2 years ago
Aamir Muhammad 02073faf9d Changes On iOS 2 years ago
Aamir Muhammad 68f4d21a4a Changes On iOS 2 years ago
Aamir Muhammad 02870af3d2 Changes On iOS 2 years ago
Aamir Muhammad 2e05bea49d Merge remote-tracking branch 'origin/development_aamir' into development_aamir
# Conflicts:
#	lib/classes/chat_call_kit.dart
#	lib/classes/notifications.dart
2 years ago
Aamir Muhammad 8623942677 OnIos 2 years ago
Aamir Muhammad fa62bb13d5 Changes 2 years ago
Aamir Muhammad 01939abc2f Merge remote-tracking branch 'origin/development_aamir' into development_aamir
# Conflicts:
#	lib/classes/chat_call_kit.dart
#	lib/classes/consts.dart
3 years ago
Aamir Muhammad 104c93f95a OnIos 3 years ago
Aamir Muhammad 6166b5399f Changes 3 years ago
Aamir Muhammad 9dd4456c51 Merge branch 'master' into development_aamir
# Conflicts:
#	lib/app_state/app_state.dart
#	lib/classes/notifications.dart
#	lib/ui/landing/itg/its_add_screen_video_image.dart
#	lib/ui/login/login_screen.dart
#	pubspec.yaml
3 years ago
Aamir Muhammad c4c7a91d7b Chat Fix & Calling 3 years ago
Aamir Muhammad 11227b00b4 Ios Changes 3 years ago
Aamir Muhammad d1ccaf7f56 Chat Fix & Calling 3 years ago
Aamir Muhammad 7191e25ea5 Chat Redirect Fix 3 years ago
Aamir Muhammad 595c47acbb Changes 3 years ago
Aamir Muhammad 617145fd07 Merge branch 'master' into development_aamir 3 years ago
Aamir Muhammad 7af10ec337 Merge branch 'master' into development_aamir
# Conflicts:
#	lib/ui/landing/dashboard_screen.dart
#	lib/ui/login/login_screen.dart
3 years ago
Aamir Muhammad 55cf39a364 Changes 3 years ago
Aamir Muhammad 5a338199b9 Merge branch 'master' into development_aamir
# Conflicts:
#	lib/ui/login/login_screen.dart
3 years ago
Aamir Muhammad b21142e4af Webrtc Calling 3 years ago
Aamir Muhammad 304f77b9bb Webrtc Calling 3 years ago
Aamir Muhammad acbafd8d84 Merge branch 'master' into development_aamir 3 years ago
Aamir Muhammad 74e01e60fb Chat Fix & Web RTC Methods 3 years ago

@ -71,10 +71,10 @@ android {
signingConfig signingConfigs.debug
}
release {
signingConfig signingConfigs.release
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
@ -85,4 +85,6 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
}

@ -0,0 +1,27 @@
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.huawei.hianalytics.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
-keep class com.huawei.hms.flutter.**{*;}
-keep class com.hiennv.flutter_callkit_incoming.** { *; }
-keep class microsoft.aspnet.signalr.client.hubs.** { *; }
-keep class com.microsoft.signalr.** { *; }
-keep class tvi.webrtc.** { *; }
-keep interface com.microsoft.signalr.** { *; }
-repackageclasses
## Flutter wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-dontwarn io.flutter.embedding.**

@ -8,6 +8,8 @@
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- Chat Web RTC Calling -->
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
@ -22,6 +24,7 @@
android:icon="@mipmap/ic_launcher"
android:label="Mohemm"
android:extractNativeLibs="true"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round">
<activity

@ -0,0 +1,318 @@
//package com.mohem_flutter_app
//import kotlinx.coroutines.Dispatchers
//import kotlinx.coroutines.GlobalScope
//import kotlinx.coroutines.launch
//import okhttp3.*
//import okhttp3.MediaType.Companion.toMediaType
//import okhttp3.RequestBody.Companion.toRequestBody
//import java.io.IOException
//
//class NativeIncomingCallDecline {
// fun declineCall(currentUserID: String, targetUserID: String, token: String) {
// println("--------------- Inside Decline Call ----------------")
// val url = "https://apiderichat.hmg.com/api/user/calldecline"
// val payload = """
// {
// "currentUserId": "$targetUserID",
// "targetUserId": "$currentUserID",
// "secretKey": "derichatmobileuser",
// "targetUserToken": "$token"
// }
// """.trimIndent()
//
// val jsonMediaType = "application/json".toMediaType()
//
// GlobalScope.launch(Dispatchers.IO) {
// val client = OkHttpClient()
// val requestBody = payload.toRequestBody(jsonMediaType)
// val request = Request.Builder()
// .url(url)
// .post(requestBody)
// .build()
//
// client.newCall(request).enqueue(object : Callback {
// override fun onResponse(call: Call, response: Response) {
// if (response.isSuccessful) {
// val responseData = response.body?.string()
// println("API Successful. Response data: $responseData")
// } else {
// val errorMessage = response.body?.string()
// println("API Failed. Error message: $errorMessage")
// }
// }
//
// override fun onFailure(call: Call, e: IOException) {
// println("API Request Failed. Exception: ${e.message}")
// }
// })
// }
// }
//}
//
//This code is included into CallkitIncomingBroadcastReceiver.kt file under Flutter_callKit_incoming Package
//Below is the Whole Code
// Implemented Libraries into Kotlin
// implementation 'com.squareup.okhttp3:okhttp:4.9.1'
// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'
//package com.hiennv.flutter_callkit_incoming
//import android.annotation.SuppressLint
//import android.content.BroadcastReceiver
//import android.content.Context
//import android.content.Intent
//import android.os.Build
//import android.os.Bundle
//import android.util.Log
////http
//import okhttp3.MediaType.Companion.toMediaType
//import okhttp3.OkHttpClient
//import okhttp3.Request
//import okhttp3.RequestBody.Companion.toRequestBody
//
//// Async
//import kotlinx.coroutines.Dispatchers
//import kotlinx.coroutines.GlobalScope
//import kotlinx.coroutines.launch
//
//class CallkitIncomingBroadcastReceiver : BroadcastReceiver() {
//
// companion object {
// private const val TAG = "CallkitIncomingReceiver"
//
// fun getIntent(context: Context, action: String, data: Bundle?) =
// Intent(context, CallkitIncomingBroadcastReceiver::class.java).apply {
// this.action = "${context.packageName}.${action}"
// putExtra(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA, data)
// }
//
// fun getIntentIncoming(context: Context, data: Bundle?) =
// Intent(context, CallkitIncomingBroadcastReceiver::class.java).apply {
// action = "${context.packageName}.${CallkitConstants.ACTION_CALL_INCOMING}"
// putExtra(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA, data)
// }
//
// fun getIntentStart(context: Context, data: Bundle?) =
// Intent(context, CallkitIncomingBroadcastReceiver::class.java).apply {
// action = "${context.packageName}.${CallkitConstants.ACTION_CALL_START}"
// putExtra(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA, data)
// }
//
// fun getIntentAccept(context: Context, data: Bundle?) =
// Intent(context, CallkitIncomingBroadcastReceiver::class.java).apply {
// action = "${context.packageName}.${CallkitConstants.ACTION_CALL_ACCEPT}"
// putExtra(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA, data)
// }
//
// fun getIntentDecline(context: Context, data: Bundle?) =
// Intent(context, CallkitIncomingBroadcastReceiver::class.java).apply {
// action = "${context.packageName}.${CallkitConstants.ACTION_CALL_DECLINE}"
// putExtra(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA, data)
// }
//
// fun getIntentEnded(context: Context, data: Bundle?) =
// Intent(context, CallkitIncomingBroadcastReceiver::class.java).apply {
// action = "${context.packageName}.${CallkitConstants.ACTION_CALL_ENDED}"
// putExtra(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA, data)
// }
//
// fun getIntentTimeout(context: Context, data: Bundle?) =
// Intent(context, CallkitIncomingBroadcastReceiver::class.java).apply {
// action = "${context.packageName}.${CallkitConstants.ACTION_CALL_TIMEOUT}"
// putExtra(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA, data)
// }
//
// fun getIntentCallback(context: Context, data: Bundle?) =
// Intent(context, CallkitIncomingBroadcastReceiver::class.java).apply {
// action = "${context.packageName}.${CallkitConstants.ACTION_CALL_CALLBACK}"
// putExtra(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA, data)
// }
// }
//
//
// @SuppressLint("MissingPermission")
// override fun onReceive(context: Context, intent: Intent) {
// val callkitNotificationManager = CallkitNotificationManager(context)
// val action = intent.action ?: return
// val data = intent.extras?.getBundle(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA) ?: return
//
// when (action) {
// "${context.packageName}.${CallkitConstants.ACTION_CALL_INCOMING}" -> {
// try {
// callkitNotificationManager.showIncomingNotification(data)
// sendEventFlutter(CallkitConstants.ACTION_CALL_INCOMING, data)
// addCall(context, Data.fromBundle(data))
// if (callkitNotificationManager.incomingChannelEnabled()) {
// val soundPlayerServiceIntent =
// Intent(context, CallkitSoundPlayerService::class.java)
// soundPlayerServiceIntent.putExtras(data)
// context.startService(soundPlayerServiceIntent)
// }
// } catch (error: Exception) {
// Log.e(TAG, null, error)
// }
// }
//
// "${context.packageName}.${CallkitConstants.ACTION_CALL_START}" -> {
// try {
// sendEventFlutter(CallkitConstants.ACTION_CALL_START, data)
// addCall(context, Data.fromBundle(data), true)
// } catch (error: Exception) {
// Log.e(TAG, null, error)
// }
// }
//
// "${context.packageName}.${CallkitConstants.ACTION_CALL_ACCEPT}" -> {
// try {
// sendEventFlutter(CallkitConstants.ACTION_CALL_ACCEPT, data)
// context.stopService(Intent(context, CallkitSoundPlayerService::class.java))
// callkitNotificationManager.clearIncomingNotification(data, true)
// addCall(context, Data.fromBundle(data), true)
// } catch (error: Exception) {
// Log.e(TAG, null, error)
// }
// }
//
// "${context.packageName}.${CallkitConstants.ACTION_CALL_DECLINE}" -> {
// try {
// sendEventFlutter(CallkitConstants.ACTION_CALL_DECLINE, data)
// context.stopService(Intent(context, CallkitSoundPlayerService::class.java))
// callkitNotificationManager.clearIncomingNotification(data, false)
// removeCall(context, Data.fromBundle(data))
// println("----------- Code By Aamir on 2222-----------------------");
// var callData = data.getSerializable(CallkitConstants.EXTRA_CALLKIT_EXTRA) as HashMap<String, Any?>
// val token = (callData["loginDetails"] as HashMap<*, *>)["token"] as? String
// val targetUserId = (callData["callerDetails"] as HashMap<*, *>)["targetUserId"] as? Int
// val currentUserId = (callData["callerDetails"] as HashMap<*, *>)["currentUserId"] as? Int
// delineCall(currentUserID = currentUserId, targetUserID = targetUserId, token = token)
// println("----------- Code By Aamir On BroadCast -----------------------");
// } catch (error: Exception) {
// Log.e(TAG, null, error)
// }
// }
//
// "${context.packageName}.${CallkitConstants.ACTION_CALL_ENDED}" -> {
// try {
// sendEventFlutter(CallkitConstants.ACTION_CALL_ENDED, data)
// context.stopService(Intent(context, CallkitSoundPlayerService::class.java))
// callkitNotificationManager.clearIncomingNotification(data, false)
// removeCall(context, Data.fromBundle(data))
// } catch (error: Exception) {
// Log.e(TAG, null, error)
// }
// }
//
// "${context.packageName}.${CallkitConstants.ACTION_CALL_TIMEOUT}" -> {
// try {
// sendEventFlutter(CallkitConstants.ACTION_CALL_TIMEOUT, data)
// context.stopService(Intent(context, CallkitSoundPlayerService::class.java))
// if (data.getBoolean(CallkitConstants.EXTRA_CALLKIT_MISSED_CALL_SHOW, true)) {
// callkitNotificationManager.showMissCallNotification(data)
// }
// removeCall(context, Data.fromBundle(data))
// } catch (error: Exception) {
// Log.e(TAG, null, error)
// }
// }
//
// "${context.packageName}.${CallkitConstants.ACTION_CALL_CALLBACK}" -> {
// try {
// callkitNotificationManager.clearMissCallNotification(data)
// sendEventFlutter(CallkitConstants.ACTION_CALL_CALLBACK, data)
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
// val closeNotificationPanel = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
// context.sendBroadcast(closeNotificationPanel)
// }
// } catch (error: Exception) {
// Log.e(TAG, null, error)
// }
// }
// }
// }
//
//
// private fun delineCall(currentUserID: Int?, targetUserID: Int?, token: String?) {
// println("--------------- Inside Decline Call ----------------");
// val url = "https://apiderichat.hmg.com/api/user/calldecline"
// val payload = """
// {
// "currentUserId": $targetUserID,
// "targetUserId": $currentUserID,
// "secretKey": "derichatmobileuser",
// "targetUserToken": "$token"
// }
// """.trimIndent()
//
//
// val jsonMediaType = "application/json".toMediaType()
//
// GlobalScope.launch(Dispatchers.IO) {
// val client = OkHttpClient()
// val requestBody = payload.toRequestBody(jsonMediaType)
// val request = Request.Builder()
// .url(url)
// .post(requestBody)
// .build()
//
// client.newCall(request).execute().use { response ->
// if (response.isSuccessful) {
// val responseData = response.body?.string()
// println("API Successful. Response data: $responseData")
// } else {
// val errorMessage = response.body?.string()
// println("API Failed. Error message: $errorMessage")
// }
// }
// }
//
//
// }
//
//
// private fun sendEventFlutter(event: String, data: Bundle) {
// val android = mapOf(
// "isCustomNotification" to data.getBoolean(CallkitConstants.EXTRA_CALLKIT_IS_CUSTOM_NOTIFICATION, false),
// "isCustomSmallExNotification" to data.getBoolean(
// CallkitConstants.EXTRA_CALLKIT_IS_CUSTOM_SMALL_EX_NOTIFICATION,
// false
// ),
// "ringtonePath" to data.getString(CallkitConstants.EXTRA_CALLKIT_RINGTONE_PATH, ""),
// "backgroundColor" to data.getString(CallkitConstants.EXTRA_CALLKIT_BACKGROUND_COLOR, ""),
// "backgroundUrl" to data.getString(CallkitConstants.EXTRA_CALLKIT_BACKGROUND_URL, ""),
// "actionColor" to data.getString(CallkitConstants.EXTRA_CALLKIT_ACTION_COLOR, ""),
// "incomingCallNotificationChannelName" to data.getString(
// CallkitConstants.EXTRA_CALLKIT_INCOMING_CALL_NOTIFICATION_CHANNEL_NAME,
// ""
// ),
// "missedCallNotificationChannelName" to data.getString(
// CallkitConstants.EXTRA_CALLKIT_MISSED_CALL_NOTIFICATION_CHANNEL_NAME,
// ""
// ),
// )
// val notification = mapOf(
// "id" to data.getInt(CallkitConstants.EXTRA_CALLKIT_MISSED_CALL_ID),
// "showNotification" to data.getBoolean(CallkitConstants.EXTRA_CALLKIT_MISSED_CALL_SHOW),
// "count" to data.getInt(CallkitConstants.EXTRA_CALLKIT_MISSED_CALL_COUNT),
// "subtitle" to data.getString(CallkitConstants.EXTRA_CALLKIT_MISSED_CALL_SUBTITLE),
// "callbackText" to data.getString(CallkitConstants.EXTRA_CALLKIT_MISSED_CALL_CALLBACK_TEXT),
// "isShowCallback" to data.getBoolean(CallkitConstants.EXTRA_CALLKIT_MISSED_CALL_CALLBACK_SHOW),
// )
// val forwardData = mapOf(
// "id" to data.getString(CallkitConstants.EXTRA_CALLKIT_ID, ""),
// "nameCaller" to data.getString(CallkitConstants.EXTRA_CALLKIT_NAME_CALLER, ""),
// "avatar" to data.getString(CallkitConstants.EXTRA_CALLKIT_AVATAR, ""),
// "number" to data.getString(CallkitConstants.EXTRA_CALLKIT_HANDLE, ""),
// "type" to data.getInt(CallkitConstants.EXTRA_CALLKIT_TYPE, 0),
// "duration" to data.getLong(CallkitConstants.EXTRA_CALLKIT_DURATION, 0L),
// "textAccept" to data.getString(CallkitConstants.EXTRA_CALLKIT_TEXT_ACCEPT, ""),
// "textDecline" to data.getString(CallkitConstants.EXTRA_CALLKIT_TEXT_DECLINE, ""),
// "extra" to data.getSerializable(CallkitConstants.EXTRA_CALLKIT_EXTRA)!!,
// "missedCallNotification" to notification,
// "android" to android
// )
// FlutterCallkitIncomingPlugin.sendEvent(event, forwardData)
// }
//}

@ -9,9 +9,18 @@ package com.mohem_flutter_app
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.IOException
class MainActivity : FlutterFragmentActivity() {
class MainActivity: FlutterFragmentActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
}
}

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.6.0'
ext.kotlin_version = '1.8.21'
repositories {
google()
mavenCentral()

@ -3,4 +3,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
#distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip

Binary file not shown.

Binary file not shown.

@ -1,9 +1,11 @@
import UIKit
import PushKit
import Flutter
import Firebase
import flutter_callkit_incoming
import flutter_local_notifications
// PKPushRegistryDelegate
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
@ -14,10 +16,49 @@ import flutter_local_notifications
FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
GeneratedPluginRegistrant.register(with: registry)
}
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
}
// if #available(iOS 10.0, *) {
// UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
// }
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
//Setup VOIP
// let mainQueue = DispatchQueue.main
// let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
// voipRegistry.delegate = self
// voipRegistry.desiredPushTypes = [PKPushType.voIP]
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
// Handle updated push credentials
// func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
// print(credentials.token)
// let deviceToken = credentials.token.map { String(format: "%02x", $0) }.joined()
// //Save deviceToken to your server
// SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(deviceToken)
// }
// func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
// print("didInvalidatePushTokenFor")
// SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP("")
// }
// // Handle incoming pushes
// func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
// print("didReceiveIncomingPushWith")
// guard type == .voIP else { return }
// print(payload.dictionaryPayload)
//// let id = payload.dictionaryPayload["id"] as? String ?? ""
//// let nameCaller = payload.dictionaryPayload["nameCaller"] as? String ?? ""
//// let handle = payload.dictionaryPayload["handle"] as? String ?? ""
// let isVideo = payload.dictionaryPayload["isVideo"] as? Bool ?? false
////
////
// let data = flutter_callkit_incoming.Data(id: "1", nameCaller: "Mohemm", handle: "handle", type: isVideo ? 1 : 0)
//// data.extra = ["user": "abc@123", "platform": "ios"]
//// data.iconName = "Mohemm"
// SwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming(data, fromPushKit: true)
// }
}

@ -2,15 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsForMedia</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
@ -31,6 +22,10 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>sms</string>
@ -41,6 +36,15 @@
<true/>
<key>NFCReaderUsageDescription</key>
<string>This App requires access to NFC to mark your attendance.</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsForMedia</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>This app requires camera access to capture &amp; upload picture as profile image.</string>
<key>NSFaceIDUsageDescription</key>
@ -51,17 +55,19 @@
<string>This App requires access to your location to mark your attendance.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This App requires access to your location to mark your attendance.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires photo library access to select image as document &amp; upload it.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app requires microphone access to for call.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires photo library access to select image as document &amp; upload it.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>processing</string>
<string>fetch</string>
<string>remote-notification</string>
<string>voip</string>
</array>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
@ -85,11 +91,17 @@
<array>
<string>0000</string>
</array>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>
<key>FIVKIconName</key>
<string>AppIcon-VoIPKit</string>
<key>FIVKLocalizedName</key>
<string>VoIP-Kit</string>
<key>FIVKSupportVideo</key>
<true/>
<key>FIVKSkipRecallScreen</key>
<true/>
</dict>
</plist>

@ -31,8 +31,10 @@ class ChatApiClient {
"employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER.toString(),
"password": "FxIu26rWIKoF8n6mpbOmAjDLphzFGmpG",
"isMobile": true,
"deviceToken":AppState().getIsHuawei ? AppState().getHuaweiPushToken : AppState().getDeviceToken,
"deviceToken": AppState().getIsHuawei ? AppState().getHuaweiPushToken : AppState().getDeviceToken,
"isHuaweiDevice": AppState().getIsHuawei,
//"platform" : "", // ios, android
//"voipToken": ""
},
);
@ -71,9 +73,7 @@ class ChatApiClient {
if (!kReleaseMode) {
logger.i("res: " + response.body);
}
return ChatUserModel.fromJson(
json.decode(response.body),
);
return ChatUserModel.fromJson(json.decode(response.body));
} catch (e) {
throw e;
}
@ -188,4 +188,46 @@ class ChatApiClient {
}
return imagesData;
}
// CallUser Login Token
Future<user.UserAutoLoginModel> getUserCallToken({required String userid}) async {
user.UserAutoLoginModel userLoginResponse = user.UserAutoLoginModel();
Response response = await ApiClient().postJsonForResponse(
"${ApiConsts.chatLoginTokenUrl}externaluserlogin",
{
"employeeNumber": userid,
"password": "FxIu26rWIKoF8n6mpbOmAjDLphzFGmpG",
},
);
if (!kReleaseMode) {
logger.i("login-res: " + response.body);
}
if (response.statusCode == 200) {
userLoginResponse = user.userAutoLoginModelFromJson(response.body);
} else if (response.statusCode == 501 || response.statusCode == 502 || response.statusCode == 503 || response.statusCode == 504) {
getUserCallToken(userid: userid);
} else {
userLoginResponse = user.userAutoLoginModelFromJson(response.body);
Utils.showToast(userLoginResponse.errorResponses!.first.message!);
}
return userLoginResponse;
}
// Call Decline On App Terminated State
Future<Response> callDecline({required int cUserID, required int tUserID, required String targetUsertoken}) async {
// var headers = {'Content-Type': 'application/json'};
// var request = http.Request('POST', Uri.parse('https://apiderichat.hmg.com/api/user/calldecline'));
Response response = await ApiClient().postJsonForResponse(
"${ApiConsts.chatLoginTokenUrl}calldecline",
{"currentUserId": cUserID, "targetUserId": tUserID, "secretKey": "derichatmobileuser", "targetUserToken": targetUsertoken},
);
print("res: " + response.body);
if (!kReleaseMode) {
logger.i({"currentUserId": cUserID, "targetUserId": tUserID, "secretKey": "derichatmobileuser", "targetUserToken": targetUsertoken});
print("res: " + response.body);
}
return response;
}
}

@ -0,0 +1,170 @@
import 'dart:convert';
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_callkit_incoming/entities/entities.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:logger/logger.dart';
import 'package:mohem_flutter_app/api/chat/chat_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/call.dart';
import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart';
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as ALM;
import 'package:mohem_flutter_app/models/chat/incoming_call_model.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:path_provider/path_provider.dart';
import 'package:signalr_netcore/hub_connection.dart';
import 'package:signalr_netcore/signalr_client.dart';
class ChatVoipCall {
static final ChatVoipCall _instance = ChatVoipCall._internal();
ChatVoipCall._internal();
factory ChatVoipCall() => _instance;
late ChatProviderModel prov;
late ChatCallProvider cProv;
dynamic inCallData;
bool isUserOnline = false;
dynamic callData;
static const platform = MethodChannel('com.example.httpchannel/http');
Future<void> showCallkitIncoming({required String uuid, RemoteMessage? data, CallDataModel? incomingCallData, bool background = false}) async {
await FlutterCallkitIncoming.endAllCalls();
ALM.Response autoLoginData;
SingleUserChatModel callerData;
if (data!.data["user_token_response"] == null || data.data["user_token_response"].isEmpty) {
// Online & App Logged In
ALM.Response sharedDetails = ALM.Response.fromJson(jsonDecode(await Utils.getStringFromPrefs("userLoginChatDetails")));
autoLoginData = ALM.Response.fromJson(AppState().getchatUserDetails == null ? sharedDetails.toJson() : AppState().getchatUserDetails!.response!.toJson());
dynamic items = jsonDecode(data.data["user_chat_history_response"]);
callerData = SingleUserChatModel(
targetUserId: items["CurrentUserId"],
targetUserEmail: items["CurrentUserEmail"],
targetUserName: items["CurrentUserName"].split("@").first,
currentUserId: autoLoginData.id,
currentUserEmail: autoLoginData.email,
currentUserName: autoLoginData.userName,
chatEventId: 3);
isUserOnline = true;
} else {
// Offline or App in Background or App is At Verify Screen
autoLoginData = ALM.Response.fromJson(jsonDecode(data.data["user_token_response"]));
callerData = SingleUserChatModel.fromJson(json.decode(data.data["user_chat_history_response"]));
}
CallKitParams params = CallKitParams(
id: uuid,
nameCaller: callerData.targetUserName,
appName: 'Mohemm',
handle: '',
type: 0,
duration: 20000,
textAccept: 'Accept',
textDecline: 'Decline',
extra: {
"loginDetails": autoLoginData.toJson(),
"callerDetails": callerData.toJson(),
'isIncomingCall': true,
'isUserOnline': isUserOnline,
'callType': data.data["callType"],
},
android: const AndroidParams(
isCustomNotification: true,
isShowLogo: false,
ringtonePath: 'system_ringtone_default',
backgroundColor: '#0955fa',
backgroundUrl: 'assets/test.png',
actionColor: '#4CAF50',
),
ios: IOSParams(
iconName: 'Mohemm',
handleType: '',
supportsVideo: true,
maximumCallGroups: 2,
maximumCallsPerCallGroup: 1,
audioSessionMode: 'default',
audioSessionActive: true,
audioSessionPreferredSampleRate: 38000.0,
audioSessionPreferredIOBufferDuration: 0.005,
supportsDTMF: true,
supportsHolding: true,
supportsGrouping: false,
supportsUngrouping: false,
ringtonePath: 'system_ringtone_default',
),
);
if (callerData.chatEventId == 3) {
await Utils.saveStringFromPrefs("isIncomingCall", "true");
await FlutterCallkitIncoming.showCallkitIncoming(params);
}
}
Future<void> declineCall({ payload}) async {
IncomingCallModel data = IncomingCallModel.fromJson(jsonDecode(payload));
if (isUserOnline) {
HubConnection _hc = await makeHub(sessionData: data);
await _hc.start();
if (_hc.state == HubConnectionState.Connected) {
if (data.extra != null) {
await _hc.invoke("HangUpAsync", args: [data.extra!.callerDetails!.currentUserId!, data.extra!.callerDetails!.targetUserId!]);
await _hc.invoke("UpdateUserStatusAsync", args: [int.parse(data.extra!.callerDetails!.currentUserId.toString()), 1]);
FlutterCallkitIncoming.endAllCalls();
chatHubConnection = _hc;
}
}
}
//else {
// Future.delayed(const Duration(seconds: 3), () {
// ChatApiClient().callDecline(cUserID: data.extra!.callerDetails!.targetUserId!, tUserID: data.extra!.callerDetails!.currentUserId!, targetUsertoken: data.extra!.loginDetails!.token!);
// });
// HubConnection _hc = await makeHub(sessionData: data);
// await _hc.start();
// if (_hc.state == HubConnectionState.Connected) {
// logger.log(Level.info, "HUB-EVENT");
// await _hc.invoke("HangUpAsync", args: [
// data.extra!.callerDetails!.currentUserId!,
// data.extra!.callerDetails!.targetUserId!,
// ]);
// FlutterCallkitIncoming.endAllCalls();
// await _hc.stop();
// }
// }
//
// try {
// var response = await platform.invokeMethod(
// 'executeHttpPostRequest', {"currentUserID": data.extra!.callerDetails!.targetUserId, "targetUserID": data.extra!.callerDetails!.currentUserId, "token": data.extra!.loginDetails!.token});
// print('HTTP POST response: $response');
// Future.delayed(Duration(seconds: 3), () {
// ChatApiClient().callDecline(cUserID: data.extra!.callerDetails!.targetUserId!, tUserID: data.extra!.callerDetails!.currentUserId!, targetUsertoken: data.extra!.loginDetails!.token!);
// });
// } on PlatformException catch (e) {
// print('Error invoking method: ${e.message}');
// }
//await ChatApiClient().callDecline(cUserID: data.extra!.callerDetails!.targetUserId!, tUserID: data.extra!.callerDetails!.currentUserId!, targetUsertoken: data.extra!.loginDetails!.token!);
// logger.log(Level.error, "API-EVENT-END");
}
Future<HubConnection> makeHub({required IncomingCallModel sessionData}) async {
late HubConnection hc;
try {
HttpConnectionOptions httpOp = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true);
hc = HubConnectionBuilder()
.withUrl(ApiConsts.chatHubConnectionUrl + "?UserId=${sessionData.extra!.loginDetails!.id}&source=Desktop&access_token=${sessionData.extra?.loginDetails!.token}", options: httpOp)
.withAutomaticReconnect(retryDelays: <int>[2000, 5000, 10000, 20000]).build();
return hc;
} catch (e) {
print(e);
return hc;
}
}
}

@ -3,8 +3,8 @@ import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
class ApiConsts {
//static String baseUrl = "http://10.200.204.20:2801/"; // Local server
// static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server
static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server
// static String baseUrl = "https://hmgwebservices.com"; // Live server
// static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server
static String baseUrl = "https://hmgwebservices.com"; // Live server
static String baseUrlServices = baseUrl + "/Services/"; // server
// static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server
static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/";

@ -1,15 +1,17 @@
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
// import 'package:huawei_hmsavailability/huawei_hmsavailability.dart';
import 'package:huawei_push/huawei_push.dart' as huawei_push;
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/chat_call_kit.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:uuid/uuid.dart';
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
@ -44,7 +46,7 @@ class AppNotifications {
// if (Platform.isAndroid) {
// hmsApiAvailability = HmsApiAvailability();
// }
print("Firebase init");
await requestPermissions();
AppState().setDeviceToken = firebaseToken;
await Permission.notification.isDenied.then((bool value) {
@ -52,12 +54,16 @@ class AppNotifications {
Permission.notification.request();
}
});
await FirebaseMessaging.instance.setAutoInitEnabled(true);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(alert: true, badge: true, sound: true);
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) _handleMessage(initialMessage);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
if (message.notification != null) _handleMessage(message);
if (message != null) _handleMessage(message);
});
FirebaseMessaging.onMessageOpenedApp.listen(_handleOpenApp);
@ -113,6 +119,14 @@ class AppNotifications {
void _handleMessage(RemoteMessage message) {
Utils.saveStringFromPrefs("isAppOpendByChat", "false");
if (message.data.isNotEmpty && message.data["messageType"] == 'chat') {
Utils.saveStringFromPrefs("isAppOpendByChat", "true");
Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString());
} else if (message.data.isNotEmpty && message.data["messageType"] == 'call') {
if (Platform.isAndroid) {
ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message);
}
}
}
void _handleOpenApp(RemoteMessage message) {
@ -121,19 +135,26 @@ class AppNotifications {
Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString());
}
}
}
AndroidNotificationChannel channel = const AndroidNotificationChannel(
"high_importance_channel",
"High Importance Notifications",
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description: 'This channel is used for important notifications.', // description
importance: Importance.high,
);
Future<dynamic> backgroundMessageHandler(RemoteMessage message) async {
@pragma('vm:entry-point')
Future<void> backgroundMessageHandler(RemoteMessage message) async {
await Firebase.initializeApp();
Utils.saveStringFromPrefs("isAppOpendByChat", "false");
Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString());
if (message.data.isNotEmpty && message.data["type"] == 'call') {
// ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message);
if (message.data.isNotEmpty && message.data["messageType"] == 'chat') {
Utils.saveStringFromPrefs("isAppOpendByChat", "false");
Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString());
} else if (message.data.isNotEmpty && message.data["messageType"] == 'call') {
if (Platform.isAndroid) {
ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message, background: true);
}
}
}

@ -4,6 +4,7 @@ import 'package:mohem_flutter_app/ui/attendance/add_vacation_rule_screen.dart';
import 'package:mohem_flutter_app/ui/attendance/monthly_attendance_screen.dart';
import 'package:mohem_flutter_app/ui/attendance/vacation_rule_screen.dart';
import 'package:mohem_flutter_app/ui/bottom_sheets/attendence_details_bottom_sheet.dart';
import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart';
import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart';
import 'package:mohem_flutter_app/ui/chat/chat_home.dart';
import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart';
@ -184,6 +185,7 @@ class AppRoutes {
static const String chat = "/chat";
static const String chatDetailed = "/chatDetailed";
static const String chatFavoriteUsers = "/chatFavoriteUsers";
static const String chatStartCall = "/chatStartCall";
//Marathon
static const String marathonIntroScreen = "/marathonIntroScreen";
@ -296,6 +298,8 @@ class AppRoutes {
chat: (BuildContext context) => ChatHome(),
chatDetailed: (BuildContext context) => ChatDetailScreen(),
chatFavoriteUsers: (BuildContext context) => ChatFavoriteUsersScreen(),
chatStartCall: (BuildContext context) => StartCallPage(),
// Marathon
marathonIntroScreen: (BuildContext context) => MarathonIntroScreen(),

@ -7,16 +7,13 @@
// ignore_for_file: depend_on_referenced_packages
import 'package:audio_session/audio_session_web.dart';
import 'package:camera_web/camera_web.dart';
import 'package:file_picker/_internal/file_picker_web.dart';
import 'package:firebase_core_web/firebase_core_web.dart';
import 'package:firebase_messaging_web/firebase_messaging_web.dart';
import 'package:fluttertoast/fluttertoast_web.dart';
import 'package:geolocator_web/geolocator_web.dart';
import 'package:google_maps_flutter_web/google_maps_flutter_web.dart';
import 'package:image_picker_for_web/image_picker_for_web.dart';
import 'package:just_audio_web/just_audio_web.dart';
import 'package:record_web/record_web.dart';
import 'package:shared_preferences_web/shared_preferences_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
import 'package:video_player_web/video_player_web.dart';
@ -26,16 +23,13 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart';
// ignore: public_member_api_docs
void registerPlugins(Registrar registrar) {
AudioSessionWeb.registerWith(registrar);
CameraPlugin.registerWith(registrar);
FilePickerWeb.registerWith(registrar);
FirebaseCoreWeb.registerWith(registrar);
FirebaseMessagingWeb.registerWith(registrar);
FluttertoastWebPlugin.registerWith(registrar);
GeolocatorPlugin.registerWith(registrar);
GoogleMapsPlugin.registerWith(registrar);
ImagePickerPlugin.registerWith(registrar);
JustAudioPlugin.registerWith(registrar);
RecordPluginWeb.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);
VideoPlayerPlugin.registerWith(registrar);

@ -18,8 +18,9 @@ import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:month_year_picker/month_year_picker.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
import 'package:signalr_netcore/hub_connection.dart';
import 'package:sizer/sizer.dart';
late HubConnection chatHubConnection;
Logger logger = Logger(
// filter: null, // Use the default LogFilter (-> only log in debug mode)
printer: PrettyPrinter(
@ -70,9 +71,9 @@ Future<void> main() async {
ChangeNotifierProvider<MarathonProvider>(
create: (_) => MarathonProvider(),
),
// ChangeNotifierProvider<ChatCallProvider>(
// create: (_) => ChatCallProvider(),
// ),
ChangeNotifierProvider<ChatCallProvider>(
create: (_) => ChatCallProvider(),
),
],
child: const MyApp(),
),

@ -7,19 +7,31 @@ import 'dart:convert';
class CallDataModel {
CallDataModel({
this.callerId,
this.callerDetails,
this.callerName,
this.callerEmail,
this.callerTitle,
this.callerPhone,
this.receiverId,
this.receiverDetails,
this.receiverName,
this.receiverEmail,
this.receiverTitle,
this.receiverPhone,
this.title,
this.calltype,
this.callType,
});
String? callerId;
CallerDetails? callerDetails;
String? receiverId;
ReceiverDetails? receiverDetails;
dynamic title;
String? calltype;
int? callerId;
String? callerName;
String? callerEmail;
String? callerTitle;
dynamic callerPhone;
int? receiverId;
String? receiverName;
String? receiverEmail;
dynamic receiverTitle;
dynamic receiverPhone;
String? title;
String? callType;
factory CallDataModel.fromRawJson(String str) => CallDataModel.fromJson(json.decode(str));
@ -27,171 +39,130 @@ class CallDataModel {
factory CallDataModel.fromJson(Map<String, dynamic> json) => CallDataModel(
callerId: json["callerID"],
callerDetails: json["callerDetails"] == null ? null : CallerDetails.fromJson(json["callerDetails"]),
callerName: json["callerName"],
callerEmail: json["callerEmail"],
callerTitle: json["callerTitle"],
callerPhone: json["callerPhone"],
receiverId: json["receiverID"],
receiverDetails: json["receiverDetails"] == null ? null : ReceiverDetails.fromJson(json["receiverDetails"]),
receiverName: json["receiverName"],
receiverEmail: json["receiverEmail"],
receiverTitle: json["receiverTitle"],
receiverPhone: json["receiverPhone"],
title: json["title"],
calltype: json["calltype"],
callType: json["callType"],
);
Map<String, dynamic> toJson() => {
"callerID": callerId,
"callerDetails": callerDetails?.toJson(),
"callerName": callerName,
"callerEmail": callerEmail,
"callerTitle": callerTitle,
"callerPhone": callerPhone,
"receiverID": receiverId,
"receiverDetails": receiverDetails?.toJson(),
"receiverName": receiverName,
"receiverEmail": receiverEmail,
"receiverTitle": receiverTitle,
"receiverPhone": receiverPhone,
"title": title,
"calltype": calltype,
"callType": callType,
};
}
class CallerDetails {
CallerDetails({
this.response,
this.errorResponses,
// To parse this JSON data, do
//
// final callSessionPayLoad = callSessionPayLoadFromJson(jsonString);
class CallSessionPayLoad {
CallSessionPayLoad({
this.target,
this.caller,
this.sdp,
});
Response? response;
dynamic errorResponses;
int? target;
int? caller;
Sdp? sdp;
factory CallerDetails.fromRawJson(String str) => CallerDetails.fromJson(json.decode(str));
factory CallSessionPayLoad.fromRawJson(String str) => CallSessionPayLoad.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory CallerDetails.fromJson(Map<String, dynamic> json) => CallerDetails(
response: json["response"] == null ? null : Response.fromJson(json["response"]),
errorResponses: json["errorResponses"],
factory CallSessionPayLoad.fromJson(Map<String, dynamic> json) => CallSessionPayLoad(
target: json["target"],
caller: json["caller"],
sdp: json["sdp"] == null ? null : Sdp.fromJson(json["sdp"]),
);
Map<String, dynamic> toJson() => {
"response": response?.toJson(),
"errorResponses": errorResponses,
"target": target,
"caller": caller,
"sdp": sdp?.toJson(),
};
}
class Response {
Response({
this.id,
this.userName,
this.email,
this.phone,
this.title,
this.token,
this.isDomainUser,
this.isActiveCode,
this.encryptedUserId,
this.encryptedUserName,
class Sdp {
Sdp({
this.type,
this.sdp,
});
int? id;
String? userName;
String? email;
dynamic phone;
String? title;
String? token;
bool? isDomainUser;
bool? isActiveCode;
String? encryptedUserId;
String? encryptedUserName;
String? type;
String? sdp;
factory Response.fromRawJson(String str) => Response.fromJson(json.decode(str));
factory Sdp.fromRawJson(String str) => Sdp.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Response.fromJson(Map<String, dynamic> json) => Response(
id: json["id"],
userName: json["userName"],
email: json["email"],
phone: json["phone"],
title: json["title"],
token: json["token"],
isDomainUser: json["isDomainUser"],
isActiveCode: json["isActiveCode"],
encryptedUserId: json["encryptedUserId"],
encryptedUserName: json["encryptedUserName"],
factory Sdp.fromJson(Map<String, dynamic> json) => Sdp(
type: json["type"],
sdp: json["sdp"],
);
Map<String, dynamic> toJson() => {
"id": id,
"userName": userName,
"email": email,
"phone": phone,
"title": title,
"token": token,
"isDomainUser": isDomainUser,
"isActiveCode": isActiveCode,
"encryptedUserId": encryptedUserId,
"encryptedUserName": encryptedUserName,
"type": type,
"sdp": sdp,
};
}
class ReceiverDetails {
ReceiverDetails({
this.id,
this.userName,
this.email,
this.phone,
this.title,
this.userStatus,
this.image,
this.unreadMessageCount,
this.userAction,
this.isPin,
this.isFav,
this.isAdmin,
this.rKey,
this.totalCount,
// final iosCallPayload = iosCallPayloadFromJson(jsonString);
class IosCallPayload {
String? incomingCallType;
String? incomingCallerId;
String? incomingCallerName;
String? uuid;
IosCallPayload({
this.incomingCallType,
this.incomingCallerId,
this.incomingCallerName,
this.uuid,
});
int? id;
String? userName;
String? email;
dynamic phone;
dynamic title;
int? userStatus;
String? image;
int? unreadMessageCount;
dynamic userAction;
bool? isPin;
bool? isFav;
bool? isAdmin;
String? rKey;
int? totalCount;
factory ReceiverDetails.fromRawJson(String str) => ReceiverDetails.fromJson(json.decode(str));
factory IosCallPayload.fromRawJson(String str) => IosCallPayload.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory ReceiverDetails.fromJson(Map<String, dynamic> json) => ReceiverDetails(
id: json["id"],
userName: json["userName"],
email: json["email"],
phone: json["phone"],
title: json["title"],
userStatus: json["userStatus"],
image: json["image"],
unreadMessageCount: json["unreadMessageCount"],
userAction: json["userAction"],
isPin: json["isPin"],
isFav: json["isFav"],
isAdmin: json["isAdmin"],
rKey: json["rKey"],
totalCount: json["totalCount"],
factory IosCallPayload.fromJson(Map<String, dynamic> json) => IosCallPayload(
incomingCallType: json["incoming_call_type"],
incomingCallerId: json["incoming_caller_id"],
incomingCallerName: json["incoming_caller_name"],
uuid: json["uuid"],
);
Map<String, dynamic> toJson() => {
"id": id,
"userName": userName,
"email": email,
"phone": phone,
"title": title,
"userStatus": userStatus,
"image": image,
"unreadMessageCount": unreadMessageCount,
"userAction": userAction,
"isPin": isPin,
"isFav": isFav,
"isAdmin": isAdmin,
"rKey": rKey,
"totalCount": totalCount,
"incoming_call_type": incomingCallType,
"incoming_caller_id": incomingCallerId,
"incoming_caller_name": incomingCallerName,
"uuid": uuid,
};
}

@ -0,0 +1,333 @@
// To parse this JSON data, do
//
// final incomingCallModel = incomingCallModelFromJson(jsonString);
import 'dart:convert';
class IncomingCallModel {
String? actionColor;
String? appName;
Args? args;
String? avatar;
String? backgroundColor;
String? backgroundUrl;
int? duration;
Extra? extra;
String? from;
String? handle;
Args? headers;
String? id;
bool? isAccepted;
bool? isCustomNotification;
bool? isCustomSmallExNotification;
bool? isShowCallback;
bool? isShowLogo;
bool? isShowMissedCallNotification;
String? nameCaller;
String? ringtonePath;
String? textAccept;
String? textCallback;
String? textDecline;
String? textMissedCall;
int? type;
String? uuid;
IncomingCallModel({
this.actionColor,
this.appName,
this.args,
this.avatar,
this.backgroundColor,
this.backgroundUrl,
this.duration,
this.extra,
this.from,
this.handle,
this.headers,
this.id,
this.isAccepted,
this.isCustomNotification,
this.isCustomSmallExNotification,
this.isShowCallback,
this.isShowLogo,
this.isShowMissedCallNotification,
this.nameCaller,
this.ringtonePath,
this.textAccept,
this.textCallback,
this.textDecline,
this.textMissedCall,
this.type,
this.uuid,
});
factory IncomingCallModel.fromRawJson(String str) => IncomingCallModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory IncomingCallModel.fromJson(Map<String, dynamic> json) => IncomingCallModel(
actionColor: json["actionColor"],
appName: json["appName"],
args: json["args"] == null ? null : Args.fromJson(json["args"]),
avatar: json["avatar"],
backgroundColor: json["backgroundColor"],
backgroundUrl: json["backgroundUrl"],
duration: json["duration"] == null ? null : json["duration"].toInt(),
extra: json["extra"] == null ? null : Extra.fromJson(json["extra"]),
from: json["from"],
handle: json["handle"],
headers: json["headers"] == null ? null : Args.fromJson(json["headers"]),
id: json["id"],
isAccepted: json["isAccepted"],
isCustomNotification: json["isCustomNotification"],
isCustomSmallExNotification: json["isCustomSmallExNotification"],
isShowCallback: json["isShowCallback"],
isShowLogo: json["isShowLogo"],
isShowMissedCallNotification: json["isShowMissedCallNotification"],
nameCaller: json["nameCaller"],
ringtonePath: json["ringtonePath"],
textAccept: json["textAccept"],
textCallback: json["textCallback"],
textDecline: json["textDecline"],
textMissedCall: json["textMissedCall"],
type: json["type"] == null ? null : json["type"].toInt(),
uuid: json["uuid"],
);
Map<String, dynamic> toJson() => {
"actionColor": actionColor,
"appName": appName,
"args": args?.toJson(),
"avatar": avatar,
"backgroundColor": backgroundColor,
"backgroundUrl": backgroundUrl,
"duration": duration,
"extra": extra?.toJson(),
"from": from,
"handle": handle,
"headers": headers?.toJson(),
"id": id,
"isAccepted": isAccepted,
"isCustomNotification": isCustomNotification,
"isCustomSmallExNotification": isCustomSmallExNotification,
"isShowCallback": isShowCallback,
"isShowLogo": isShowLogo,
"isShowMissedCallNotification": isShowMissedCallNotification,
"nameCaller": nameCaller,
"ringtonePath": ringtonePath,
"textAccept": textAccept,
"textCallback": textCallback,
"textDecline": textDecline,
"textMissedCall": textMissedCall,
"type": type,
"uuid": uuid,
};
}
class Args {
Args();
factory Args.fromRawJson(String str) => Args.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Args.fromJson(Map<String, dynamic> json) => Args();
Map<String, dynamic> toJson() => {};
}
class Extra {
LoginDetails? loginDetails;
bool? isIncomingCall;
CallerDetails? callerDetails;
String? callType;
Extra({
this.loginDetails,
this.isIncomingCall,
this.callerDetails,
this.callType,
});
factory Extra.fromRawJson(String str) => Extra.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Extra.fromJson(Map<String, dynamic> json) => Extra(
loginDetails: json["loginDetails"] == null ? null : LoginDetails.fromJson(json["loginDetails"]),
isIncomingCall: json["isIncomingCall"],
callType: json["callType"],
callerDetails: json["callerDetails"] == null ? null : CallerDetails.fromJson(json["callerDetails"]),
);
Map<String, dynamic> toJson() => {
"loginDetails": loginDetails?.toJson(),
"isIncomingCall": isIncomingCall,
"callType": callType,
"callerDetails": callerDetails?.toJson(),
};
}
class CallerDetails {
int? userChatHistoryId;
String? contant;
FileTypeResponse? fileTypeResponse;
String? currentUserName;
String? targetUserEmail;
String? conversationId;
String? encryptedTargetUserId;
int? targetUserId;
bool? isSeen;
int? userChatHistoryLineId;
bool? isDelivered;
String? targetUserName;
int? currentUserId;
DateTime? createdDate;
String? currentUserEmail;
String? contantNo;
int? chatEventId;
String? encryptedTargetUserName;
int? chatSource;
CallerDetails({
this.userChatHistoryId,
this.contant,
this.fileTypeResponse,
this.currentUserName,
this.targetUserEmail,
this.conversationId,
this.encryptedTargetUserId,
this.targetUserId,
this.isSeen,
this.userChatHistoryLineId,
this.isDelivered,
this.targetUserName,
this.currentUserId,
this.createdDate,
this.currentUserEmail,
this.contantNo,
this.chatEventId,
this.encryptedTargetUserName,
this.chatSource,
});
factory CallerDetails.fromRawJson(String str) => CallerDetails.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory CallerDetails.fromJson(Map<String, dynamic> json) => CallerDetails(
userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"].toInt(),
contant: json["contant"],
fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]),
currentUserName: json["currentUserName"],
targetUserEmail: json["targetUserEmail"],
conversationId: json["conversationId"],
encryptedTargetUserId: json["encryptedTargetUserId"],
targetUserId: json["targetUserId"] == null ? null : json["targetUserId"].toInt(),
isSeen: json["isSeen"],
userChatHistoryLineId: json["userChatHistoryLineId"] == null ? null : json["userChatHistoryLineId"].toInt(),
isDelivered: json["isDelivered"],
targetUserName: json["targetUserName"],
currentUserId: json["currentUserId"] == null ? null : json["currentUserId"].toInt(),
createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]),
currentUserEmail: json["currentUserEmail"],
contantNo: json["contantNo"],
chatEventId: json["chatEventId"] == null ? null : json["chatEventId"].toInt(),
encryptedTargetUserName: json["encryptedTargetUserName"],
chatSource: json["chatSource"] == null ? null : json["chatSource"].toInt(),
);
Map<String, dynamic> toJson() => {
"userChatHistoryId": userChatHistoryId,
"contant": contant,
"fileTypeResponse": fileTypeResponse?.toJson(),
"currentUserName": currentUserName,
"targetUserEmail": targetUserEmail,
"conversationId": conversationId,
"encryptedTargetUserId": encryptedTargetUserId,
"targetUserId": targetUserId,
"isSeen": isSeen,
"userChatHistoryLineId": userChatHistoryLineId,
"isDelivered": isDelivered,
"targetUserName": targetUserName,
"currentUserId": currentUserId,
"createdDate": createdDate?.toIso8601String(),
"currentUserEmail": currentUserEmail,
"contantNo": contantNo,
"chatEventId": chatEventId,
"encryptedTargetUserName": encryptedTargetUserName,
"chatSource": chatSource,
};
}
class FileTypeResponse {
int? fileTypeId;
FileTypeResponse({
this.fileTypeId,
});
factory FileTypeResponse.fromRawJson(String str) => FileTypeResponse.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory FileTypeResponse.fromJson(Map<String, dynamic> json) => FileTypeResponse(
fileTypeId: json["fileTypeId"].toInt(),
);
Map<String, dynamic> toJson() => {
"fileTypeId": fileTypeId,
};
}
class LoginDetails {
bool? isActiveCode;
int? id;
String? encryptedUserName;
String? userName;
String? title;
String? encryptedUserId;
String? email;
bool? isDomainUser;
String? token;
LoginDetails({
this.isActiveCode,
this.id,
this.encryptedUserName,
this.userName,
this.title,
this.encryptedUserId,
this.email,
this.isDomainUser,
this.token,
});
factory LoginDetails.fromRawJson(String str) => LoginDetails.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory LoginDetails.fromJson(Map<String, dynamic> json) => LoginDetails(
isActiveCode: json["isActiveCode"],
id: json["id"] == null ? null : json["id"].toInt(),
encryptedUserName: json["encryptedUserName"],
userName: json["userName"],
title: json["title"],
encryptedUserId: json["encryptedUserId"],
email: json["email"],
isDomainUser: json["isDomainUser"],
token: json["token"],
);
Map<String, dynamic> toJson() => {
"isActiveCode": isActiveCode,
"id": id,
"encryptedUserName": encryptedUserName,
"userName": userName,
"title": title,
"encryptedUserId": encryptedUserId,
"email": email,
"isDomainUser": isDomainUser,
"token": token,
};
}

@ -0,0 +1,61 @@
// To parse this JSON data, do
//
// final remoteIceCandidatePayLoad = remoteIceCandidatePayLoadFromJson(jsonString);
import 'dart:convert';
class RemoteIceCandidatePayLoad {
RemoteIceCandidatePayLoad({
this.target,
this.candidate,
});
int? target;
Candidate? candidate;
factory RemoteIceCandidatePayLoad.fromRawJson(String str) => RemoteIceCandidatePayLoad.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory RemoteIceCandidatePayLoad.fromJson(Map<String, dynamic> json) => RemoteIceCandidatePayLoad(
target: json["target"],
candidate: json["candidate"] == null ? null : Candidate.fromJson(json["candidate"]),
);
Map<String, dynamic> toJson() => {
"target": target,
"candidate": candidate?.toJson(),
};
}
class Candidate {
Candidate({
this.candidate,
this.sdpMid,
this.sdpMLineIndex,
this.usernameFragment,
});
String? candidate;
String? sdpMid;
int? sdpMLineIndex;
String? usernameFragment;
factory Candidate.fromRawJson(String str) => Candidate.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Candidate.fromJson(Map<String, dynamic> json) => Candidate(
candidate: json["candidate"],
sdpMid: json["sdpMid"],
sdpMLineIndex: json["sdpMLineIndex"],
usernameFragment: json["usernameFragment"],
);
Map<String, dynamic> toJson() => {
"candidate": candidate,
"sdpMid": sdpMid,
"sdpMLineIndex": sdpMLineIndex,
"usernameFragment": usernameFragment,
};
}

@ -1,52 +1,89 @@
import 'dart:convert';
import 'dart:ui';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:just_audio/just_audio.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/call.dart';
import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart';
import 'package:mohem_flutter_app/models/chat/webrtc_payloads.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:signalr_netcore/hub_connection.dart';
class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
///////////////////// Web RTC Video Calling //////////////////////
// Video Call
late RTCPeerConnection _peerConnection;
RTCVideoRenderer _localVideoRenderer = RTCVideoRenderer();
final RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
late RTCPeerConnection _pc;
late ChatProviderModel chatProvModel;
RTCVideoRenderer? localVideoRenderer;
RTCVideoRenderer? remoteRenderer;
final AudioPlayer player = AudioPlayer();
MediaStream? _localStream;
MediaStream? _remoteStream;
late CallDataModel outGoingCallData;
bool isMicOff = false;
bool isLoudSpeaker = false;
bool isCamOff = false;
bool isCallEnded = false;
bool isVideoCall = false;
bool isAudioCall = false;
bool isCallStarted = false;
bool isFrontCamera = true;
late SingleUserChatModel incomingCallData;
void initCallListeners() {
/// WebRTC Connection Variables
bool isIncomingCallLoader = true;
bool isIncomingCall = false;
bool isOutGoingCall = false;
bool isUserOnline = false;
late BuildContext providerContext;
void initCallListeners({required BuildContext context}) {
providerContext = context;
if (kDebugMode) {
print("=================== Call Listeners Registered =======================");
}
chatHubConnection.on("OnCallAcceptedAsync", onCallAcceptedAsync);
chatHubConnection.on("OnIceCandidateAsync", onIceCandidateAsync);
chatHubConnection.on("OnOfferAsync", onOfferAsync);
chatHubConnection.on("OnAnswerOffer", onAnswerOffer);
chatHubConnection.on("OnHangUpAsync", onHangUpAsync);
chatHubConnection.on("OnCallDeclinedAsync", onCallDeclinedAsync);
// chatHubConnection.on("OnIncomingCallAsync", OnIncomingCallAsync);
}
//Video Constraints
var videoConstraints = {
Map<String, Object> videoConstraints = {
"video": {
"mandatory": {
"width": {"min": 320},
"height": {"min": 180}
"width": {"min": 1280},
"height": {"min": 720}
},
"optional": [
{
"width": {"max": 1280}
},
{"frameRate": 25},
{"frameRate": 60},
{"facingMode": "user"}
]
},
"frameRate": 25,
"width": 420, //420,//640,//1280,
"height": 240 //240//480//720
"frameRate": 60,
"width": 1280, //420,//640,//1280,
"height": 720, //240//480//720
"audio": true,
};
// Audio Constraints
var audioConstraints = {
Map<String, Object> audioConstraints = {
"sampleRate": 8000,
"sampleSize": 16,
"channelCount": 2,
@ -54,128 +91,316 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
"audio": true,
};
Future<RTCPeerConnection> _createPeerConnection() async {
// {"url": "stun:stun.l.google.com:19302"},
Map<String, dynamic> configuration = {
"iceServers": [
{"urls": 'stun:15.185.116.59:3478'},
{"urls": "turn:15.185.116.59:3479", "username": "admin", "credential": "admin"}
]
};
Future<void> init() async {
_pc = await creatOfferWithCon();
Future.delayed(const Duration(seconds: 2), () {
connectIncomingCall();
});
}
Map<String, dynamic> offerSdpConstraints = {
"mandatory": {
"OfferToReceiveAudio": true,
"OfferToReceiveVideo": true,
},
"optional": [],
};
///////////////////////////////////////////////OutGoing Call////////////////////////////////////////////////////
RTCPeerConnection pc = await createPeerConnection(configuration, offerSdpConstraints);
// if (pc != null) print(pc);
//pc.addStream(widget.localStream);
Future<void> initLocalCamera({required ChatProviderModel chatProvmodel, required callData, required BuildContext context, bool isIncomingCall = false}) async {
isCallEnded = false;
chatProvModel = chatProvmodel;
outGoingCallData = callData;
await initStreams();
await startCall(callType: isVideoCall ? "Video" : "Audio", context: context);
_pc = await creatOfferWithCon();
notifyListeners();
}
pc.onIceCandidate = (e) {
if (e.candidate != null) {
print(json.encode({
'candidate': e.candidate.toString(),
'sdpMid': e.sdpMid.toString(),
'sdpMlineIndex': e.sdpMLineIndex,
}));
}
};
pc.onIceConnectionState = (e) {
print(e);
};
pc.onAddStream = (stream) {
print('addStream: ' + stream.id);
_remoteRenderer.srcObject = stream;
};
return pc;
Future<void> startCall({required String callType, required BuildContext context}) async {
chatProvModel.isTextMsg = true;
chatProvModel.isAttachmentMsg = false;
chatProvModel.isVoiceMsg = false;
chatProvModel.isReplyMsg = false;
chatProvModel.isCall = true;
chatProvModel.message.text = "Start $callType call ${outGoingCallData.receiverName.toString().replaceAll(".", " ")}";
chatProvModel.sendChatMessage(
context,
targetUserId: outGoingCallData.receiverId!,
userStatus: 1,
userEmail: outGoingCallData.receiverEmail!,
targetUserName: outGoingCallData.receiverName!,
);
await invoke(
invokeMethod: "CallUserAsync",
currentUserID: outGoingCallData.callerId!,
targetUserID: outGoingCallData.receiverId!,
);
await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 4);
}
void init() {
initRenderers();
_createPeerConnection().then((pc) {
_peerConnection = pc;
// _setRemoteDescription(widget.info);
});
// OutGoing Listeners
void onCallAcceptedAsync(List<Object?>? params) async {
dynamic items = params!.toList();
RTCSessionDescription description = await _createOffer();
await _pc.setLocalDescription(description);
dynamic payload = {"target": items[0]["id"], "caller": outGoingCallData.callerId, "sdp": description.toMap()};
invoke(invokeMethod: "OfferAsync", currentUserID: outGoingCallData.callerId!, targetUserID: items[0]["id"], data: jsonEncode(payload));
}
void initRenderers() {
_localVideoRenderer.initialize();
_remoteRenderer.initialize();
initLocalCamera();
Future<void> onIceCandidateAsync(List<Object?>? params) async {
dynamic items = params!.toList();
if (isIncomingCall) {
RemoteIceCandidatePayLoad data = RemoteIceCandidatePayLoad.fromJson(jsonDecode(items.first.toString()));
if (_pc != null) {
await _pc.addCandidate(RTCIceCandidate(data.candidate!.candidate, data.candidate!.sdpMid, data.candidate!.sdpMLineIndex));
}
} else {
if (kDebugMode) {
logger.i("res: " + items.toString());
}
RemoteIceCandidatePayLoad data = RemoteIceCandidatePayLoad.fromJson(jsonDecode(items.first.toString()));
if (_pc != null) {
await _pc.addCandidate(RTCIceCandidate(data.candidate!.candidate, data.candidate!.sdpMid, data.candidate!.sdpMLineIndex));
if (!isCallStarted) {
isCallStarted = true;
notifyListeners();
if (isCallStarted) {
isIncomingCallLoader = false;
isOutGoingCall = true;
Navigator.push(
providerContext,
MaterialPageRoute(
builder: (BuildContext context) => StartCallPage(),
)).then((value) {
Navigator.of(providerContext).pop();
});
}
}
}
notifyListeners();
}
}
void initLocalCamera() async {
_localStream = await navigator.mediaDevices.getUserMedia({'video': true, 'audio': true});
_localVideoRenderer.srcObject = _localStream;
// _localVideoRenderer.srcObject = await navigator.mediaDevices
// .getUserMedia({'video': true, 'audio': true});
print('this source Object');
print('this suarce ${_localVideoRenderer.srcObject != null}');
Future<void> onOfferAsync(List<Object?>? params) async {
dynamic items = params!.toList();
var data = jsonDecode(items.toString());
if (isIncomingCall) {
_pc.setRemoteDescription(RTCSessionDescription(data[0]["sdp"]["sdp"], data[0]["sdp"]["type"]));
RTCSessionDescription description = await _createAnswer();
await _pc.setLocalDescription(description);
dynamic payload = {"target": data[0]["caller"], "caller": AppState().chatDetails!.response!.id, "sdp": description.toMap()};
invoke(invokeMethod: "AnswerOfferAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, data: jsonEncode(payload));
}
// else {
// RTCSessionDescription description = await _createAnswer();
// await _pc.setLocalDescription(description);
// var payload = {"target": items[0]["id"], "caller": outGoingCallData.callerId, "sdp": description.toMap()};
// invoke(invokeMethod: "AnswerOffer", currentUserID: outGoingCallData.callerId!, targetUserID: items[0]["id"], data: jsonEncode(payload));
// }
notifyListeners();
}
void startCall({required String callType}) {}
void endCall() {}
void checkCall(Map<String, dynamic> message) {
switch (message["callStatus"]) {
case 'connected':
{}
break;
case 'offer':
{}
break;
case 'accept':
{}
break;
case 'candidate':
{}
break;
case 'bye':
{}
break;
case 'leave':
{}
break;
//////////////////////////// OutGoing Call End ///////////////////////////////////////
Future<bool> endCall({required bool isUserOnline}) async {
if (isIncomingCall) {
logger.i("-----------------------Endeddddd By Me---------------------------");
if (chatHubConnection.state == HubConnectionState.Connected) {
await invoke(invokeMethod: "HangUpAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, userStatus: 0);
await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, userStatus: 1);
}
isCallStarted = false;
isVideoCall = false;
isCamOff = false;
isMicOff = false;
isLoudSpeaker = false;
isIncomingCall = false;
isOutGoingCall = false;
isAudioCall = false;
if (isCallConnected) {
if (_pc.connectionState == RTCPeerConnectionState.RTCPeerConnectionStateConnected) {
print("------------------ PC Stopped ----------------------------");
_pc.close();
_pc.dispose();
}
}
if (remoteRenderer != null) {
remoteRenderer!.dispose();
remoteRenderer = null;
}
if (localVideoRenderer != null) {
localVideoRenderer!.dispose();
localVideoRenderer = null;
}
if (_localStream != null) {
_localStream!.dispose();
_localStream = null;
}
if (chatHubConnection != null && !isUserOnline) {
chatHubConnection.stop();
}
await FlutterCallkitIncoming.endAllCalls();
return true;
} else {
if (isOutGoingCall) {
await invoke(invokeMethod: "HangUpAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1);
await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1);
} else if (isIncomingCall) {
await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, userStatus: 1);
}
isCallStarted = false;
isVideoCall = false;
isCamOff = false;
isMicOff = false;
isLoudSpeaker = false;
if (isCallConnected) {
if (_pc.connectionState == RTCPeerConnectionState.RTCPeerConnectionStateConnected) {
_pc.close();
_pc.dispose();
}
}
if (remoteRenderer != null) {
remoteRenderer!.dispose();
remoteRenderer = null;
}
if (localVideoRenderer != null) {
localVideoRenderer!.dispose();
localVideoRenderer = null;
}
if (_localStream != null) {
_localStream!.dispose();
_localStream = null;
}
isOutGoingCall = false;
isIncomingCall = false;
isAudioCall = false;
return true;
}
}
//// Listeners Methods ////
void onCallAcceptedAsync(List<Object?>? params) {}
// Incoming Listeners
void onIceCandidateAsync(List<Object?>? params) {}
void onAnswerOffer(List<Object?>? payload) async {
// if (isIncomingCall) {
// // print("--------------------- On Answer Offer Async ---------------------------------------");
// //await invoke(invokeMethod: "InvokeMobile", currentUserID: AppState().getchatUserDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, debugData: {"On Answer Offer Async"});
// } else {
var items = payload!.toList();
if (kDebugMode) {
logger.i("res: " + items.toString());
}
CallSessionPayLoad data = CallSessionPayLoad.fromJson(jsonDecode(items.first.toString()));
RTCSessionDescription description = RTCSessionDescription(data.sdp!.sdp, 'answer');
_pc.setRemoteDescription(description);
// }
}
void onOfferAsync(List<Object?>? params) {}
void onHangUpAsync(List<Object?>? params) {
print("--------------------- onHangUp ---------------------------------------");
void onAnswerOffer(List<Object?>? params) {}
endCall(isUserOnline: isUserOnline).then((bool value) {
if (isCallConnected) {
Navigator.of(AppRoutes.navigatorKey.currentContext!).pop();
isCallConnected = false;
}
isCallEnded = true;
});
}
void onHangUpAsync(List<Object?>? params) {}
// Future<void> OnIncomingCallAsync(List<Object?>? params) async {
// print("--------------------- On Incoming Call ---------------------------------------");
// dynamic items = params!.toList();
// logger.d(items);
// // Map<String, dynamic> json = {
// // "callerID": items[0]["id"],
// // "callerName": items[0]["userName"],
// // "callerEmail": items[0]["email"],
// // "callerTitle": items[0]["title"],
// // "callerPhone": null,
// // "receiverID": AppState().chatDetails!.response!.id,
// // "receiverName": AppState().chatDetails!.response!.userName,
// // "receiverEmail": AppState().chatDetails!.response!.email,
// // "receiverTitle": AppState().chatDetails!.response!.title,
// // "receiverPhone": AppState().chatDetails!.response!.phone,
// // "title": AppState().chatDetails!.response!.userName!.replaceAll(".", " "),
// // "callType": items[1] ? "Video" : "Audio",
// // };
// // CallDataModel callData = CallDataModel.fromJson(json);
// // ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), isOnline: true, incomingCallData: callData);
// //
// // if (!isOnIncomingCallPage) {
// // Map<String, dynamic> json = {
// // "callerID": items[0]["id"],
// // "callerName": items[0]["userName"],
// // "callerEmail": items[0]["email"],
// // "callerTitle": items[0]["title"],
// // "callerPhone": null,
// // "receiverID": AppState().chatDetails!.response!.id,
// // "receiverName": AppState().chatDetails!.response!.userName,
// // "receiverEmail": AppState().chatDetails!.response!.email,
// // "receiverTitle": AppState().chatDetails!.response!.title,
// // "receiverPhone": AppState().chatDetails!.response!.phone,
// // "title": AppState().chatDetails!.response!.userName!.replaceAll(".", " "),
// // "callType": items[1] ? "Video" : "Audio",
// // };
// // CallDataModel callData = CallDataModel.fromJson(json);
// // await Navigator.push(
// // providerContext,
// // MaterialPageRoute(
// // builder: (BuildContext context) => IncomingCall(
// // isVideoCall: items[1] ? true : false,
// // outGoingCallData: callData,
// // ),
// // ),
// // );
// // isOnIncomingCallPage = true;
// // }
// }
void onCallDeclinedAsync(List<Object?>? params) {}
void onCallDeclinedAsync(List<Object?>? params) {
print("================= On Declained ========================");
logger.d(params);
// endCall().then((bool value) {
// if (value) {
// isCallEnded = true;
// notifyListeners();
// }
// });
if (params != null) {
endCall(isUserOnline: isUserOnline).then((bool value) {
// if (isCallConnected) {
Navigator.of(AppRoutes.navigatorKey.currentContext!).pop();
// isCallConnected = false;
// }
isCallEnded = true;
});
}
}
//// Invoke Methods
Future<void> invoke({required String invokeMethod, required String currentUserID, required String targetUserID, bool isVideoCall = false, var data}) async {
Future<void> invoke({required String invokeMethod, required int currentUserID, required int targetUserID, var data, int userStatus = 1, var debugData}) async {
List<Object> args = [];
if (invokeMethod == "answerCallAsync") {
args = [currentUserID, targetUserID];
} else if (invokeMethod == "CallUserAsync") {
if (invokeMethod == "CallUserAsync") {
args = [currentUserID, targetUserID, isVideoCall];
} else if (invokeMethod == "answerCallAsync") {
args = [currentUserID, targetUserID];
} else if (invokeMethod == "IceCandidateAsync") {
args = [targetUserID, data];
} else if (invokeMethod == "OfferAsync") {
args = [targetUserID, data];
} else if (invokeMethod == "AnswerOfferAsync") {
args = [targetUserID, data];
//json In Data
// json In Data
} else if (invokeMethod == "UpdateUserStatusAsync") {
args = [currentUserID, userStatus];
} else if (invokeMethod == "HangUpAsync") {
args = [currentUserID, targetUserID];
} else if (invokeMethod == "InvokeMobile") {
args = [debugData];
}
try {
await chatHubConnection.invoke("$invokeMethod", args: args);
} catch (e) {
logger.w(e);
}
await chatHubConnection.invoke(invokeMethod, args: args);
}
void stopListeners() async {
@ -184,4 +409,210 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
chatHubConnection.off('OnIceCandidateAsync');
chatHubConnection.off('OnAnswerOffer');
}
void playRingtone() async {
player.stop();
await player.setVolume(1.0);
String audioAsset = "";
if (Platform.isAndroid) {
audioAsset = "assets/audio/ring_60Sec.mp3";
} else {
audioAsset = "assets/audio/ring_30Sec.caf";
}
try {
await player.setAsset(audioAsset);
await player.load();
player.play();
} catch (e) {
print("Error: $e");
}
}
//////////////////// Web RTC Offers & Connections ////////////////////////
Future<RTCPeerConnection> creatOfferWithCon() async {
Map<String, dynamic> configuration = {
"sdpSemantics": "plan-b",
'iceServers': [
{
'urls': 'stun:15.185.116.59:3478',
},
{
'urls': 'turn:15.185.116.59:3479',
'username': 'admin',
'credential': 'admin',
},
]
};
Map<String, dynamic> offerSdpConstraints = {
'mandatory': {
'OfferToReceiveAudio': true,
'OfferToReceiveVideo': true,
},
'optional': []
};
RTCPeerConnection pc = await createPeerConnection(configuration, offerSdpConstraints);
await pc!.addStream(_localStream!);
pc?.onConnectionState = (RTCPeerConnectionState state) {};
pc?.onAddStream = (MediaStream stream) {
remoteRenderer!.srcObject = stream;
notifyListeners();
};
pc!.onIceCandidate = (RTCIceCandidate e) async {
if (isIncomingCall) {
if (e.candidate != null) {
var payload = {"target": incomingCallData.targetUserId, "candidate": e.toMap()};
invoke(invokeMethod: "IceCandidateAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, data: jsonEncode(payload));
notifyListeners();
}
} else {
if (e.candidate != null) {
var payload = {"target": outGoingCallData.callerId, "candidate": e.toMap()};
invoke(invokeMethod: "IceCandidateAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, data: jsonEncode(payload));
}
}
};
// pc!.onTrack = (RTCTrackEvent event) async {
//
// String streamId = const Uuid().toString();
// MediaStream remoteStream = await createLocalMediaStream(streamId);
// event.streams[0].getTracks().forEach((MediaStreamTrack element) {
// logger.i("Stream Track: " + element.id.toString());
// // remoteRenderer.srcObject = element;
// remoteStream.addTrack(element);
// });
// };
pc!.onSignalingState = (RTCSignalingState state) {
logger.i("signaling state: " + state.name);
// invoke(
// invokeMethod: "InvokeMobile",
// currentUserID: AppState().getchatUserDetails!.response!.id!,
// targetUserID: incomingCallData.targetUserId!,
// debugData: {"location": "Signaling", "parms": state.name});
};
pc!.onIceGatheringState = (RTCIceGatheringState state) {
logger.i("rtc ice gathering state: " + state.name);
};
pc!.onIceConnectionState = (RTCIceConnectionState state) {
if (RTCIceConnectionState.RTCIceConnectionStateFailed == state ||
RTCIceConnectionState.RTCIceConnectionStateDisconnected == state ||
RTCIceConnectionState.RTCIceConnectionStateClosed == state) {
logger.i("Ice Connection State:" + state.name);
// endCall().then((value) {
// notifyListeners();
// });
}
};
// pc!.onRenegotiationNeeded = _onRenegotiate;
return pc;
}
// void _onRenegotiate() async {
// try {
// print('onRenegotiationNeeded start');
// // makingOffer = true;
// await _pc.setLocalDescription(await _pc.createOffer(videoConstraints));
// print('onRenegotiationNeeded state after setLocalDescription: ' + _pc.signalingState.toString());
// // send offer via callManager
// var localDesc = await _pc.getLocalDescription();
// // callManager.sendCallMessage(MsgType.rtc_offer, RtcOfferAnswer(localDesc.sdp, localDesc.type));
// print('onRenegotiationNeeded; offer sent');
// } catch (e) {
// print("onRenegotiationNeeded error: " + e.toString());
// } finally {
// // makingOffer = false;
// print('onRenegotiationNeeded done');
// }
// }
Future<RTCSessionDescription> _createOffer() async {
RTCSessionDescription description = await _pc!.createOffer();
// _offer = true;
return description;
}
Future<RTCSessionDescription> _createAnswer() async {
RTCSessionDescription description = await _pc!.createAnswer();
// _offer = false;
return description;
}
//////////////////// Web RTC End Offers ////////////////////
//////////////////// CallPage Buttons //////////////////////
void micOff() {
isMicOff = !isMicOff;
_localStream!.getAudioTracks().forEach((track) {
track.enabled = !track.enabled;
});
notifyListeners();
}
void camOff() {
isCamOff = !isCamOff;
_localStream!.getVideoTracks().forEach((track) {
track.enabled = !track.enabled;
});
// if (isCamOff) {
// isVideoCall = false;
// } else {
// isVideoCall = true;
// }
notifyListeners();
}
void loudOn() {
isLoudSpeaker = !isLoudSpeaker;
remoteRenderer!.srcObject?.getAudioTracks().forEach((track) {
if (isLoudSpeaker) {
track.enableSpeakerphone(true);
} else {
track.enableSpeakerphone(false);
}
});
notifyListeners();
}
void switchCamera() {
isFrontCamera = !isFrontCamera;
Helper.switchCamera(_localStream!.getVideoTracks()[0]);
notifyListeners();
}
///////////////// Incoming Call ///////////////////////////////
Future<void> initStreams() async {
localVideoRenderer = RTCVideoRenderer();
remoteRenderer = RTCVideoRenderer();
await localVideoRenderer!.initialize();
_localStream ??= await navigator.mediaDevices.getUserMedia(isVideoCall ? videoConstraints : audioConstraints);
localVideoRenderer!.srcObject = _localStream;
await remoteRenderer!.initialize();
notifyListeners();
}
Future<void> startIncomingCallViaKit({bool isVCall = true, required var inCallData}) async {
Utils.saveStringFromPrefs("isIncomingCall", "false");
if (isVCall) {
isVideoCall = isVCall;
} else {
isAudioCall = true;
}
await initStreams();
isIncomingCall = true;
incomingCallData = SingleUserChatModel.fromJson(inCallData);
loudOn();
// notifyListeners();
}
void connectIncomingCall() {
invoke(invokeMethod: "answerCallAsync", currentUserID: AppState().getchatUserDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!);
isIncomingCallLoader = false;
isIncomingCall = true;
// isVideoCall = true;
notifyListeners();
}
}

@ -24,12 +24,14 @@ import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.da
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as userLoginToken;
import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav;
import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:mohem_flutter_app/widgets/image_picker.dart';
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:signalr_netcore/hub_connection.dart';
import 'package:signalr_netcore/signalr_client.dart';
import 'package:uuid/uuid.dart';
@ -79,30 +81,50 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
List<ChatUser>? chatUsersList = [];
int pageNo = 1;
bool disbaleChatForThisUser = false;
bool disableChatForThisUser = false;
bool isUserOnline = false;
bool isCall = false;
userLoginToken.UserAutoLoginModel userLoginData = userLoginToken.UserAutoLoginModel();
Future<void> getUserAutoLoginToken() async {
userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
if (userLoginResponse.response != null) {
AppState().setchatUserDetails = userLoginResponse;
} else {
AppState().setchatUserDetails = userLoginResponse;
Utils.showToast(
userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr",
);
disbaleChatForThisUser = true;
try {
userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
if (userLoginResponse.response != null) {
AppState().setchatUserDetails = userLoginResponse;
Utils.saveStringFromPrefs("userLoginChatDetails", jsonEncode(userLoginResponse.response));
isUserOnline = true;
} else {
AppState().setchatUserDetails = userLoginResponse;
Utils.saveStringFromPrefs("userLoginChatDetails", "null");
Utils.showToast(
userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr",
);
disableChatForThisUser = true;
isUserOnline = false;
notifyListeners();
}
} catch (e) {
disableChatForThisUser = true;
isUserOnline = false;
notifyListeners();
}
}
Future<void> buildHubConnection() async {
chatHubConnection = await getHubConnection();
Future<void> buildHubConnection({required BuildContext context, required ChatCallProvider ccProvider}) async {
try {
chatHubConnection = await getHubConnection();
} catch (e) {
Utils.showToast(e.toString());
}
await chatHubConnection.start();
if (kDebugMode) {
logger.i("Hub Conn: Startedddddddd");
}
chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
chatHubConnection.on("OnGetChatConversationCount", onNewChatConversion);
ccProvider.initCallListeners(context: context);
}
Future<HubConnection> getHubConnection() async {
@ -354,6 +376,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
Future<void> onMsgReceived(List<Object?>? parameters) async {
List<SingleUserChatModel> data = [], temp = [];
for (dynamic msg in parameters!) {
data = getSingleUserChatModel(jsonEncode(msg));
temp = getSingleUserChatModel(jsonEncode(msg));
@ -363,7 +386,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
data.first.currentUserId = temp.first.targetUserId;
data.first.currentUserName = temp.first.targetUserName;
data.first.currentUserEmail = temp.first.targetUserEmail;
if (data.first.fileTypeId == 12 || data.first.fileTypeId == 4 || data.first.fileTypeId == 3) {
data.first.image = await ChatApiClient().downloadURL(fileName: data.first.contant!, fileTypeDescription: data.first.fileTypeResponse!.fileTypeDescription ?? "image/jpg");
}
@ -400,7 +422,9 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
);
}
}
setMsgTune();
if (data.first.chatEventId != 3) {
setMsgTune();
}
if (isChatScreenActive && data.first.currentUserId == receiverID) {
userChatHistory.insert(0, data.first);
} else {
@ -429,10 +453,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
void OnSubmitChatAsync(List<Object?>? parameters) {
print(isChatScreenActive);
print(receiverID);
print(isChatScreenActive);
logger.i(parameters);
List<SingleUserChatModel> data = [], temp = [];
for (dynamic msg in parameters!) {
data = getSingleUserChatModel(jsonEncode(msg));
@ -446,7 +466,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
if (isChatScreenActive && data.first.currentUserId == receiverID) {
int index = userChatHistory.indexWhere((SingleUserChatModel element) => element.userChatHistoryId == 0);
logger.d(index);
userChatHistory[index] = data.first;
}
@ -570,7 +589,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
msg = voiceFile!.path.split("/").last;
} else {
msg = message.text;
logger.w(msg);
}
SingleUserChatModel data = SingleUserChatModel(
userChatHistoryId: 0,
@ -632,7 +650,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return;
}
sendChatToServer(
chatEventId: 1,
chatEventId: isCall ? 3 : 1,
fileTypeId: null,
targetUserId: targetUserId,
targetUserName: targetUserName,
@ -1014,7 +1032,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
void disposeData() {
if (!disbaleChatForThisUser) {
if (!disableChatForThisUser) {
search.clear();
isChatScreenActive = false;
receiverID = 0;
@ -1030,6 +1048,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
favUsersList.clear();
searchedChats?.clear();
pChatHistory?.clear();
// callP.stopListeners();
chatHubConnection.stop();
AppState().chatDetails = null;
}
@ -1457,21 +1476,4 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
return Material.TextDirection.ltr;
}
void openChatByNoti(BuildContext context) async {
SingleUserChatModel nUser = SingleUserChatModel();
Utils.saveStringFromPrefs("isAppOpendByChat", "false");
if (await Utils.getStringFromPrefs("notificationData") != "null") {
nUser = SingleUserChatModel.fromJson(jsonDecode(await Utils.getStringFromPrefs("notificationData")));
Utils.saveStringFromPrefs("notificationData", "null");
Future.delayed(const Duration(seconds: 2));
for (ChatUser user in searchedChats!) {
if (user.id == nUser.targetUserId) {
Navigator.pushNamed(context, AppRoutes.chatDetailed, arguments: ChatDetailedScreenParams(user, false));
return;
}
}
}
Utils.saveStringFromPrefs("notificationData", "null");
}
}

@ -1,381 +1,558 @@
import 'dart:convert';
import 'dart:core';
import 'dart:io';
import 'dart:ui';
import 'package:camera/camera.dart';
import 'package:draggable_widget/draggable_widget.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/models/chat/call.dart';
class IncomingCall extends StatefulWidget {
CallDataModel incomingCallData;
bool? isVideoCall;
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart';
import 'package:mohem_flutter_app/models/chat/incoming_call_model.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:provider/provider.dart';
IncomingCall({Key? key, required this.incomingCallData, this.isVideoCall}) : super(key: key);
bool isCallConnected = false;
class StartCallPage extends StatefulWidget {
@override
_IncomingCallState createState() => _IncomingCallState();
_StartCallPageState createState() => _StartCallPageState();
}
class _IncomingCallState extends State<IncomingCall> with SingleTickerProviderStateMixin {
AnimationController? _animationController;
CameraController? _controller;
Future<void>? _initializeControllerFuture;
bool isCameraReady = false;
class _StartCallPageState extends State<StartCallPage> {
DragController dragController = DragController();
bool isOutGoingCall = false;
bool isIncomingCall = false;
late ChatCallProvider cProv;
late ChatProviderModel provider;
@override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: const Duration(
milliseconds: 500,
),
);
//_runAnimation();
// connectSignaling();
WidgetsBinding.instance.addPostFrameCallback(
(_) => _runAnimation(),
);
super.initState();
}
@override
void dispose() {
super.dispose();
}
void startCall() async {
IncomingCallModel? sessionData;
dynamic calls = await FlutterCallkitIncoming.activeCalls();
if (calls.isNotEmpty) {
sessionData = IncomingCallModel.fromRawJson(jsonEncode(calls[0]));
print(sessionData.toRawJson());
if (provider.isUserOnline) {
cProv.isUserOnline = provider.isUserOnline;
if (kDebugMode) {
print("====== Processing Incoming Call in Online State =========");
}
await cProv.startIncomingCallViaKit(inCallData: sessionData!.extra!.callerDetails!.toJson(), isVCall: sessionData.extra!.callType == "video" ? true : false);
cProv.init();
isCallConnected = true;
} else {
if (kDebugMode) {
print("====== Processing Incoming Call =========");
}
cProv.isUserOnline = provider.isUserOnline;
await cProv.startIncomingCallViaKit(inCallData: sessionData!.extra!.callerDetails!.toJson(), isVCall: sessionData.extra!.callType == "video" ? true : false);
try {
AppState().setchatUserDetails = UserAutoLoginModel(response: Response.fromJson(sessionData.extra!.loginDetails!.toJson()), errorResponses: null);
await provider.buildHubConnection(context: context, ccProvider: cProv).whenComplete(() {
cProv.init();
isCallConnected = true;
});
} catch (e) {
logger.w(e);
}
}
}
}
void startIosCall() {}
@override
Widget build(BuildContext context) {
cProv = context.read<ChatCallProvider>();
provider = context.read<ChatProviderModel>();
if (Platform.isAndroid) {
startCall();
} else if (Platform.isIOS) {
startIosCall();
}
return Scaffold(
body: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Stack(
extendBody: true,
body: Consumer2<ChatCallProvider, ChatProviderModel>(
builder: (BuildContext context, ChatCallProvider provider, ChatProviderModel cpm, Widget? child) {
return provider.isIncomingCallLoader
? const SizedBox(
width: double.infinity,
height: double.infinity,
child: Center(child: CircularProgressIndicator()),
)
: provider.isIncomingCall
? SizedBox(
width: double.infinity,
height: double.infinity,
child: Stack(
alignment: FractionalOffset.center,
children: <Widget>[
if (widget.isVideoCall!)
if (!provider.isAudioCall && provider.isVideoCall)
Positioned.fill(
child: AspectRatio(
aspectRatio: _controller!.value.aspectRatio,
child: CameraPreview(
_controller!,
child: RTCVideoView(
provider.remoteRenderer!,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
key: const Key('remote'),
),
),
if (provider.isVideoCall)
DraggableWidget(
bottomMargin: 20,
topMargin: 40,
intialVisibility: true,
horizontalSpace: 20,
shadowBorderRadius: 50,
initialPosition: AnchoringPosition.topLeft,
dragController: dragController,
normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
child: SizedBox(
height: 200,
width: 140,
child: RTCVideoView(
provider.localVideoRenderer!,
mirror: true,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
),
),
),
Positioned.fill(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
decoration: BoxDecoration(
color: MyColors.grey57Color.withOpacity(
0.7,
if (!provider.isVideoCall)
Positioned.fill(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
decoration: BoxDecoration(
color: MyColors.grey57Color.withOpacity(
0.3,
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
margin: const EdgeInsets.all(21.0),
child: Row(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
40.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
"assets/images/logos/main_mohemm_logo.png",
height: 70,
width: 70,
),
Container(
margin: const EdgeInsets.only(
left: 10.0,
right: 10.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const <Widget>[
// todo @aamir, need to use extension mehtods
Text(
"Aamir Saleem Ahmad",
style: TextStyle(
fontSize: 21,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
margin: const EdgeInsets.all(21.0),
child: Container(
margin: const EdgeInsets.only(
left: 10.0,
right: 10.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
SvgPicture.asset(
"assets/images/user.svg",
height: 70,
width: 70,
fit: BoxFit.cover,
),
10.height,
Text(
provider.incomingCallData.targetUserName!,
style: const TextStyle(
fontSize: 21,
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
),
),
),
Text(
"Calling...",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(
0xffC6C6C6,
const Text(
"On Call",
style: TextStyle(
fontSize: 16,
decoration: TextDecoration.none,
fontWeight: FontWeight.w600,
color: Color(
0xffC6C6C6,
),
letterSpacing: -0.48,
height: 23 / 24,
),
letterSpacing: -0.48,
height: 23 / 24,
),
),
SizedBox(
height: 2,
),
],
const SizedBox(
height: 2,
),
],
),
),
),
],
),
],
),
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.only(
bottom: 20,
left: 40,
right: 40,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// if (provider.isVideoCall)
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.loudOn();
},
elevation: 2.0,
fillColor: provider.isLoudSpeaker ? MyColors.textMixColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.volume_up,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.camOff();
},
elevation: 2.0,
fillColor: provider.isCamOff ? MyColors.textMixColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isCamOff ? Icons.videocam_off : Icons.videocam,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.switchCamera();
},
elevation: 2.0,
fillColor: provider.isFrontCamera ? Colors.grey : MyColors.textMixColor,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isFrontCamera ? Icons.switch_camera_outlined : Icons.switch_camera,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.micOff();
},
elevation: 2.0,
fillColor: provider.isMicOff ? MyColors.textMixColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isMicOff ? Icons.mic_off : Icons.mic,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.endCall(isUserOnline: cpm.isUserOnline).then((bool value) {
if (value) {
Navigator.of(context).pop();
// print("Reintiiiiiiitttiiiiiiii");
// provider.initStreams();
}
});
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.call_end,
color: MyColors.white,
size: 30.0,
),
),
],
),
),
),
],
),
)
: provider.isOutGoingCall
? SizedBox(
width: double.infinity,
height: double.infinity,
child: Stack(
alignment: FractionalOffset.center,
children: <Widget>[
if (!provider.isAudioCall && provider.isVideoCall)
Positioned.fill(
child: RTCVideoView(
provider.remoteRenderer!,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
key: const Key('remote'),
),
),
if (provider.isVideoCall)
DraggableWidget(
bottomMargin: 20,
topMargin: 40,
intialVisibility: true,
horizontalSpace: 20,
shadowBorderRadius: 50,
initialPosition: AnchoringPosition.topLeft,
dragController: dragController,
normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
child: SizedBox(
height: 200,
width: 140,
child: RTCVideoView(
provider.localVideoRenderer!,
mirror: true,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
),
),
),
if (!provider.isVideoCall)
Positioned.fill(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
decoration: BoxDecoration(
color: MyColors.grey57Color.withOpacity(
0.3,
),
// Container(
// margin: const EdgeInsets.all(21.0),
// width: MediaQuery.of(context).size.width,
// decoration: cardRadius(15.0, color: MyColors.black, elevation: null),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// Container(
// padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 6.0),
// child: Text(
// "TranslationBase.of(context).appoInfo",
// style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.white, letterSpacing: -0.64, height: 23 / 12),
// ),
// ),
// Container(
// padding: const EdgeInsets.only(left: 16.0, right: 16.0),
// child: Text(
// "widget.incomingCallData.appointmentdate + widget.incomingCallData.appointmenttime",
// style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600),
// ),
// ),
// Container(
// padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 21.0),
// child: Text(
// "widget.incomingCallData.clinicname",
// style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600),
// ),
// ),
// ],
// ),
// ),
const Spacer(),
Container(
margin: const EdgeInsets.only(
bottom: 70.0,
left: 49,
right: 49,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
40.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RotationTransition(
turns: Tween(
begin: 0.0,
end: -.1,
)
.chain(
CurveTween(
curve: Curves.elasticIn,
),
)
.animate(
_animationController!,
),
child: RawMaterialButton(
onPressed: () {
_submit();
},
elevation: 2.0,
fillColor: MyColors.green2DColor,
padding: const EdgeInsets.all(
15.0,
Container(
margin: const EdgeInsets.all(21.0),
child: Container(
margin: const EdgeInsets.only(
left: 10.0,
right: 10.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.call,
color: MyColors.white,
size: 35.0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
SvgPicture.asset(
"assets/images/user.svg",
height: 70,
width: 70,
fit: BoxFit.cover,
),
10.height,
Text(
provider.outGoingCallData.receiverName!,
style: const TextStyle(
fontSize: 21,
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
),
),
const Text(
"On Call",
style: TextStyle(
fontSize: 16,
decoration: TextDecoration.none,
fontWeight: FontWeight.w600,
color: Color(
0xffC6C6C6,
),
letterSpacing: -0.48,
height: 23 / 24,
),
),
const SizedBox(
height: 2,
),
],
),
),
),
RawMaterialButton(
onPressed: () {
backToHome();
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.call_end,
color: MyColors.white,
size: 35.0,
),
),
],
),
),
],
],
),
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.only(
bottom: 20,
left: 40,
right: 40,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// if (provider.isVideoCall)
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.loudOn();
},
elevation: 2.0,
fillColor: provider.isLoudSpeaker ? MyColors.textMixColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.volume_up,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.camOff();
},
elevation: 2.0,
fillColor: provider.isCamOff ? MyColors.textMixColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isCamOff ? Icons.videocam_off : Icons.videocam,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.switchCamera();
},
elevation: 2.0,
fillColor: provider.isFrontCamera ? Colors.grey : MyColors.textMixColor,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isFrontCamera ? Icons.switch_camera_outlined : Icons.switch_camera,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.micOff();
},
elevation: 2.0,
fillColor: provider.isMicOff ? MyColors.textMixColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isMicOff ? Icons.mic_off : Icons.mic,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.endCall(isUserOnline: cpm.isUserOnline).then((bool value) {
if (value) {
Navigator.of(context).pop();
}
});
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.call_end,
color: MyColors.white,
size: 30.0,
),
),
],
),
),
),
],
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
),
)
: const SizedBox();
},
),
);
}
void _runAnimation() async {
List<CameraDescription> cameras = await availableCameras();
CameraDescription firstCamera = cameras[1];
_controller = CameraController(
firstCamera,
ResolutionPreset.medium,
);
_initializeControllerFuture = _controller!.initialize();
setState(() {});
// setAudioFile();
for (int i = 0; i < 100; i++) {
await _animationController!.forward();
await _animationController!.reverse();
}
}
Future<void> _submit() async {
try {
// backToHome();
// final roomModel = RoomModel(name: widget.incomingCallData.name, token: widget.incomingCallData.sessionId, identity: widget.incomingCallData.identity);
await _controller?.dispose();
// changeCallStatusAPI(4);
// if (_session != null && _signaling != null) {
// await Navigator.of(context).pushReplacement(
// MaterialPageRoute(
// // fullscreenDialog: true,
// builder: (BuildContext context) {
// // if (widget.incomingCallData.isWebRTC == "true") {
// return StartVideoCall(signaling: _signaling, session: _session);
//
// // else {
// // return OpenTokConnectCallPage(apiKey: OPENTOK_API_KEY, sessionId: widget.incomingCallData.sessionId, token: widget.incomingCallData.token);
// // }
//
// // return VideoCallWebPage(receiverId: widget.incomingCallData.receiverID, callerId: widget.incomingCallData.callerID); // Web WebRTC VideoCall
//
// // return CallHomePage(receiverId: widget.incomingCallData.receiverID, callerId: widget.incomingCallData.callerID); // App WebRTC VideoCall
// },
// ),
// );
// } else {
// // Invalid Params/Data
// Utils.showToast("Failed to establish connection with server");
// }
} catch (err) {
print(err);
// await PlatformExceptionAlertDialog(
// exception: err,
// ).show(context);
Utils.showToast(err.toString());
}
}
// void changeCallStatusAPI(int sessionStatus) {
// LiveCareService service = new LiveCareService();
// service.endCallAPI(widget.incomingCallData.sessionId, sessionStatus, context).then((res) {}).catchError((err) {
// print(err);
// });
// }
void backToHome() async {
// final connected = await signaling.declineCall(widget.incomingCallData.callerID, widget.incomingCallData.receiverID);
// LandingPage.isOpenCallPage = false;
// _signaling
_animationController!.dispose();
// player.stop();
// changeCallStatusAPI(3);
// _signaling.bye(_session, callRejected: true);
// _signaling.callDisconnected(_session, callRejected: true);
Navigator.of(context).pop();
}
//
// void disposeAudioResources() async {
// await player.dispose();
// }
//
// void setAudioFile() async {
// player.stop();
// await player.setVolume(1.0); // full volume
// try {
// await player.setAsset('assets/sounds/ring_60Sec.mp3').then((value) {
// player.setLoopMode(LoopMode.one); // loop ring sound
// player.play();
// }).catchError((err) {
// print("Error: $err");
// });
// } catch (e) {
// print("Error: $e");
// }
// }
//
// void connectSignaling({@required bool iAmCaller = false}) async {
// print("----------------- + Signaling Connection Started ---------------------------");
// var caller = widget.incomingCallData.callerID;
// var receiver = widget.incomingCallData.receiverID;
// var host = widget.incomingCallData.server;
//
// var selfRole = iAmCaller ? "Caller" : "Receiver";
// var selfId = iAmCaller ? caller : receiver;
// var selfUser = SocketUser(id: selfId, name: "$selfRole-$selfId", userAgent: DeviceInfo.userAgent, moreInfo: {});
//
// var remoteRole = !iAmCaller ? "Caller" : "Receiver";
// var remoteId = !iAmCaller ? caller : receiver;
// var remoteUser = SocketUser(id: remoteId, name: "$remoteRole-$remoteId", userAgent: DeviceInfo.userAgent, moreInfo: {});
//
// var sessionId = "$caller-$receiver";
// _session = SessionOneToOne(id: sessionId, local_user: selfUser, remote_user: remoteUser);
//
// _signaling = Signaling(host, session: _session);
// await _signaling.connect();
//
// if (_signaling.state == SignalingState.Open) {
// return;
// }
// }
BoxDecoration cardRadius(double radius, {required Color color, double? elevation}) {
return BoxDecoration(
shape: BoxShape.rectangle,
color: color ?? Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(radius),
),
boxShadow: <BoxShadow>[
BoxShadow(
color: const Color(
0xff000000,
).withOpacity(
.05,
),
//spreadRadius: 5,
blurRadius: elevation ?? 27,
offset: const Offset(
-2,
3,
),
),
],
);
}
}
/// if (Platform.isAndroid) {
// SystemNavigator.pop();
// } else if (Platform.isIOS) {
// exit(0);
// }

@ -1,16 +1,15 @@
import 'dart:convert';
import 'dart:ui';
import 'package:camera/camera.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/call.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:provider/provider.dart';
class OutGoingCall extends StatefulWidget {
@ -23,407 +22,169 @@ class OutGoingCall extends StatefulWidget {
_OutGoingCallState createState() => _OutGoingCallState();
}
class _OutGoingCallState extends State<OutGoingCall> with SingleTickerProviderStateMixin {
AnimationController? _animationController;
late CameraController controller;
late List<CameraDescription> _cameras;
Future<void>? _initializeControllerFuture;
bool isCameraReady = false;
bool isMicOff = false;
bool isLoudSpeaker = false;
bool isCamOff = false;
late ChatCallProvider callProviderd;
class _OutGoingCallState extends State<OutGoingCall> {
late ChatCallProvider callProvider;
late ChatProviderModel chatProvider;
@override
void initState() {
callProviderd = Provider.of<ChatCallProvider>(context, listen: false);
_animationController = AnimationController(
vsync: this,
duration: const Duration(
milliseconds: 500,
),
);
// _runAnimation();
// connectSignaling();
WidgetsBinding.instance.addPostFrameCallback(
(_) => _runAnimation(),
);
super.initState();
}
void init() {
widget.isVideoCall ? callProvider.isVideoCall = true : callProvider.isVideoCall = false;
callProvider.isOutGoingCall = true;
callProvider.initLocalCamera(chatProvmodel: chatProvider, callData: widget.outGoingCallData, context: context);
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
chatProvider = Provider.of<ChatProviderModel>(context, listen: false);
callProvider = Provider.of<ChatCallProvider>(context, listen: false);
init();
return Scaffold(
body: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Stack(
alignment: FractionalOffset.center,
children: <Widget>[
if (widget.isVideoCall)
Positioned.fill(
child: CameraPreview(
controller,
body: Consumer<ChatCallProvider>(builder: (BuildContext context, ChatCallProvider chatcp, Widget? child) {
if (chatcp.isCallEnded) {
Navigator.pop(context);
}
return Stack(
alignment: FractionalOffset.center,
children: <Widget>[
if (chatcp.isVideoCall)
Positioned.fill(
child: RTCVideoView(
chatcp.localVideoRenderer!,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
),
),
Positioned.fill(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
decoration: BoxDecoration(
color: MyColors.grey57Color.withOpacity(
0.3,
),
),
),
Positioned.fill(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
decoration: BoxDecoration(
color: MyColors.grey57Color.withOpacity(
0.3,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
40.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
40.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(21.0),
child: Container(
margin: const EdgeInsets.only(
left: 10.0,
right: 10.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
SvgPicture.asset(
"assets/images/user.svg",
height: 70,
width: 70,
fit: BoxFit.cover,
),
10.height,
Text(
widget.outGoingCallData.title,
style: const TextStyle(
fontSize: 21,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
),
),
const Text(
"Ringing...",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(
0xffC6C6C6,
),
letterSpacing: -0.48,
height: 23 / 24,
),
),
const SizedBox(
height: 2,
),
],
),
),
),
],
),
// Container(
// margin: const EdgeInsets.all(21.0),
// width: MediaQuery.of(context).size.width,
// decoration: cardRadius(15.0, color: MyColors.black, elevation: null),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// Container(
// padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 6.0),
// child: Text(
// "TranslationBase.of(context).appoInfo",
// style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.white, letterSpacing: -0.64, height: 23 / 12),
// ),
// ),
// Container(
// padding: const EdgeInsets.only(left: 16.0, right: 16.0),
// child: Text(
// "widget.OutGoingCallData.appointmentdate + widget.OutGoingCallData.appointmenttime",
// style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600),
// ),
// ),
// Container(
// padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 21.0),
// child: Text(
// "widget.OutGoingCallData.clinicname",
// style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600),
// ),
// ),
// ],
// ),
// ),
const Spacer(),
Container(
margin: const EdgeInsets.only(
bottom: 70.0,
left: 49,
right: 49,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
if (widget.isVideoCall)
RawMaterialButton(
onPressed: () {
_camOff();
},
elevation: 2.0,
fillColor: isCamOff ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: Icon(
isCamOff ? Icons.videocam_off : Icons.videocam,
color: MyColors.white,
size: 35.0,
),
)
else
RawMaterialButton(
onPressed: () {
_loudOn();
},
elevation: 2.0,
fillColor: isLoudSpeaker ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.volume_up,
margin: const EdgeInsets.all(21.0),
child: Container(
margin: const EdgeInsets.only(
left: 10.0,
right: 10.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
SvgPicture.asset(
"assets/images/user.svg",
height: 70,
width: 70,
fit: BoxFit.cover,
),
10.height,
Text(
widget.outGoingCallData.receiverName.toString().replaceAll(".", " "),
style: const TextStyle(
fontSize: 21,
fontWeight: FontWeight.bold,
color: MyColors.white,
size: 35.0,
letterSpacing: -1.26,
height: 23 / 12,
),
),
RawMaterialButton(
onPressed: () {
_micOff();
},
elevation: 2.0,
fillColor: isMicOff ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: Icon(
isMicOff ? Icons.mic_off : Icons.mic,
color: MyColors.white,
size: 35.0,
),
),
RawMaterialButton(
onPressed: () {
backToHome();
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
15.0,
const Text(
"Ringing...",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(
0xffC6C6C6,
),
letterSpacing: -0.48,
height: 23 / 24,
),
),
shape: const CircleBorder(),
child: const Icon(
Icons.call_end,
color: MyColors.white,
size: 35.0,
const SizedBox(
height: 2,
),
),
],
],
),
),
),
],
),
),
const Spacer(),
Container(
margin: const EdgeInsets.only(
bottom: 70.0,
left: 49,
right: 49,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RawMaterialButton(
onPressed: () {
chatcp.endCall(isUserOnline: chatProvider.isUserOnline).then((bool value) {
if (value) {
Navigator.of(context).pop();
// print("Reintiiiiiiitttzzzz");
// chatcp.initStreams();
}
});
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.call_end,
color: MyColors.white,
size: 35.0,
),
),
],
),
),
],
),
),
),
],
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
),
),
],
);
}),
);
}
void _runAnimation() async {
_cameras = await availableCameras();
CameraDescription firstCamera = _cameras[1];
controller = CameraController(firstCamera, ResolutionPreset.medium);
_initializeControllerFuture = controller.initialize();
setState(() {});
// setAudioFile();
for (int i = 0; i < 100; i++) {
await _animationController!.forward();
await _animationController!.reverse();
}
}
void _micOff() {
setState(() {
isMicOff = !isMicOff;
});
}
void _camOff() {
setState(() {
isCamOff = !isCamOff;
});
}
void _loudOn() {
setState(() {
isLoudSpeaker = !isLoudSpeaker;
});
}
Future<void> _submit() async {
try {
// backToHome();
// final roomModel = RoomModel(name: widget.OutGoingCallData.name, token: widget.OutGoingCallData.sessionId, identity: widget.OutGoingCallData.identity);
await controller?.dispose();
// changeCallStatusAPI(4);
// if (_session != null && _signaling != null) {
// await Navigator.of(context).pushReplacement(
// MaterialPageRoute(
// // fullscreenDialog: true,
// builder: (BuildContext context) {
// // if (widget.OutGoingCallData.isWebRTC == "true") {
// return StartVideoCall(signaling: _signaling, session: _session);
//
// // else {
// // return OpenTokConnectCallPage(apiKey: OPENTOK_API_KEY, sessionId: widget.OutGoingCallData.sessionId, token: widget.OutGoingCallData.token);
// // }
//
// // return VideoCallWebPage(receiverId: widget.OutGoingCallData.receiverID, callerId: widget.OutGoingCallData.callerID); // Web WebRTC VideoCall
//
// // return CallHomePage(receiverId: widget.OutGoingCallData.receiverID, callerId: widget.OutGoingCallData.callerID); // App WebRTC VideoCall
// },
// ),
// );
// } else {
// // Invalid Params/Data
// Utils.showToast("Failed to establish connection with server");
// }
} catch (err) {
print(err);
// await PlatformExceptionAlertDialog(
// exception: err,
// ).show(context);
Utils.showToast(err.toString());
}
}
// void changeCallStatusAPI(int sessionStatus) {
// LiveCareService service = new LiveCareService();
// service.endCallAPI(widget.OutGoingCallData.sessionId, sessionStatus, context).then((res) {}).catchError((err) {
// print(err);
// });
// }
void backToHome() async {
// final connected = await signaling.declineCall(widget.OutGoingCallData.callerID, widget.OutGoingCallData.receiverID);
// LandingPage.isOpenCallPage = false;
// _signaling
_animationController!.dispose();
// player.stop();
// changeCallStatusAPI(3);
// _signaling.bye(_session, callRejected: true);
// _signaling.callDisconnected(_session, callRejected: true);
Navigator.of(context).pop();
}
//
// void disposeAudioResources() async {
// await player.dispose();
// }
//
// void setAudioFile() async {
// player.stop();
// await player.setVolume(1.0); // full volume
// try {
// await player.setAsset('assets/sounds/ring_60Sec.mp3').then((value) {
// player.setLoopMode(LoopMode.one); // loop ring sound
// player.play();
// }).catchError((err) {
// print("Error: $err");
// });
// } catch (e) {
// print("Error: $e");
// }
// }
//
// void connectSignaling({@required bool iAmCaller = false}) async {
// print("----------------- + Signaling Connection Started ---------------------------");
// var caller = widget.OutGoingCallData.callerID;
// var receiver = widget.OutGoingCallData.receiverID;
// var host = widget.OutGoingCallData.server;
//
// var selfRole = iAmCaller ? "Caller" : "Receiver";
// var selfId = iAmCaller ? caller : receiver;
// var selfUser = SocketUser(id: selfId, name: "$selfRole-$selfId", userAgent: DeviceInfo.userAgent, moreInfo: {});
//
// var remoteRole = !iAmCaller ? "Caller" : "Receiver";
// var remoteId = !iAmCaller ? caller : receiver;
// var remoteUser = SocketUser(id: remoteId, name: "$remoteRole-$remoteId", userAgent: DeviceInfo.userAgent, moreInfo: {});
//
// var sessionId = "$caller-$receiver";
// _session = SessionOneToOne(id: sessionId, local_user: selfUser, remote_user: remoteUser);
//
// _signaling = Signaling(host, session: _session);
// await _signaling.connect();
//
// if (_signaling.state == SignalingState.Open) {
// return;
// }
// }
BoxDecoration cardRadius(double radius, {required Color color, double? elevation}) {
return BoxDecoration(
shape: BoxShape.rectangle,
color: color ?? Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(radius),
),
boxShadow: <BoxShadow>[
BoxShadow(
color: const Color(
0xff000000,
).withOpacity(
.05,
),
//spreadRadius: 5,
blurRadius: elevation ?? 27,
offset: const Offset(
-2,
3,
),
),
],
borderRadius: BorderRadius.all(Radius.circular(radius)),
boxShadow: <BoxShadow>[BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: elevation ?? 27, offset: const Offset(-2, 3))],
);
}
}

@ -0,0 +1,171 @@
// import 'dart:async';
// import 'dart:io';
// import 'package:flutter/material.dart';
//
// class DraggableCam extends StatefulWidget {
// //final Size availableScreenSize;
// final Widget child;
// final double scaleFactor;
// // final Stream<bool> onButtonBarVisible;
// // final Stream<double> onButtonBarHeight;
//
// const DraggableCam({
// Key? key,
// //@required this.availableScreenSize,
// required this.child,
// // @required this.onButtonBarVisible,
// // @required this.onButtonBarHeight,
//
// /// The portion of the screen the DraggableWidget should use.
// this.scaleFactor = .25,
// }) : assert(scaleFactor != null && scaleFactor > 0 && scaleFactor <= .4),
// // assert(availableScreenSize != null),
// // assert(onButtonBarVisible != null),
// // assert(onButtonBarHeight != null),
// super(key: key);
//
// @override
// _DraggablePublisherState createState() => _DraggablePublisherState();
// }
//
// class _DraggablePublisherState extends State<DraggableCam> {
// bool _isButtonBarVisible = true;
// double _buttonBarHeight = 0;
// late double _width;
// late double _height;
// late double _top;
// late double _left;
// late double _viewPaddingTop;
// late double _viewPaddingBottom;
// final double _padding = 8.0;
// final Duration _duration300ms = const Duration(milliseconds: 300);
// final Duration _duration0ms = const Duration(milliseconds: 0);
// late Duration _duration;
// late StreamSubscription _streamSubscription;
// late StreamSubscription _streamHeightSubscription;
//
// @override
// void initState() {
// super.initState();
// _duration = _duration300ms;
// _width = widget.availableScreenSize.width * widget.scaleFactor;
// _height = _width * (widget.availableScreenSize.height / widget.availableScreenSize.width);
// _top = widget.availableScreenSize.height - (_buttonBarHeight + _padding) - _height;
// _left = widget.availableScreenSize.width - _padding - _width;
//
// _streamSubscription = widget.onButtonBarVisible.listen(_buttonBarVisible);
// _streamHeightSubscription = widget.onButtonBarHeight.listen(_getButtonBarHeight);
// }
//
// @override
// void didChangeDependencies() {
// var mediaQuery = MediaQuery.of(context);
// _viewPaddingTop = mediaQuery.viewPadding.top;
// _viewPaddingBottom = mediaQuery.viewPadding.bottom;
// super.didChangeDependencies();
// }
//
// @override
// void dispose() {
// _streamSubscription.cancel();
// _streamHeightSubscription.cancel();
// super.dispose();
// }
//
// void _getButtonBarHeight(double height) {
// setState(() {
// _buttonBarHeight = height;
// _positionWidget();
// });
// }
//
// void _buttonBarVisible(bool visible) {
// if (!mounted) {
// return;
// }
// setState(() {
// _isButtonBarVisible = visible;
// if (_duration == _duration300ms) {
// // only position the widget when we are not currently dragging it around
// _positionWidget();
// }
// });
// }
//
// @override
// Widget build(BuildContext context) {
// return AnimatedPositioned(
// top: _top,
// left: _left,
// width: _width,
// height: _height,
// duration: _duration,
// child: Listener(
// onPointerDown: (_) => _duration = _duration0ms,
// onPointerMove: (PointerMoveEvent event) {
// setState(() {
// _left = (_left + event.delta.dx).roundToDouble();
// _top = (_top + event.delta.dy).roundToDouble();
// });
// },
// onPointerUp: (_) => _positionWidget(),
// onPointerCancel: (_) => _positionWidget(),
// child: ClippedVideo(
// height: _height,
// width: _width,
// child: widget.child,
// ),
// ),
// );
// }
//
// double _getCurrentStatusBarHeight() {
// if (_isButtonBarVisible) {
// return _viewPaddingTop;
// }
// final _defaultViewPaddingTop = Platform.isIOS ? 20.0 : Platform.isAndroid ? 24.0 : 0.0;
// if (_viewPaddingTop > _defaultViewPaddingTop) {
// // There must be a hardware notch in the display.
// return _viewPaddingTop;
// }
// return 0.0;
// }
//
// double _getCurrentButtonBarHeight() {
// if (_isButtonBarVisible) {
// return _buttonBarHeight + _viewPaddingBottom;
// }
// return _viewPaddingBottom;
// }
//
// void _positionWidget() {
// // Determine the center of the object being dragged so we can decide
// // in which corner the object should be placed.
// var dx = (_width / 2) + _left;
// dx = dx < 0 ? 0 : dx >= widget.availableScreenSize.width ? widget.availableScreenSize.width - 1 : dx;
// var dy = (_height / 2) + _top;
// dy = dy < 0 ? 0 : dy >= widget.availableScreenSize.height ? widget.availableScreenSize.height - 1 : dy;
// final draggableCenter = Offset(dx, dy);
//
// setState(() {
// _duration = _duration300ms;
// if (Rect.fromLTRB(0, 0, widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
// // Top-left
// _top = _getCurrentStatusBarHeight() + _padding;
// _left = _padding;
// } else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, 0, widget.availableScreenSize.width, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
// // Top-right
// _top = _getCurrentStatusBarHeight() + _padding;
// _left = widget.availableScreenSize.width - _padding - _width;
// } else if (Rect.fromLTRB(0, widget.availableScreenSize.height / 2, widget.availableScreenSize.width / 2, widget.availableScreenSize.height).contains(draggableCenter)) {
// // Bottom-left
// _top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
// _left = _padding;
// } else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2, widget.availableScreenSize.width, widget.availableScreenSize.height).contains(draggableCenter)) {
// // Bottom-right
// _top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
// _left = widget.availableScreenSize.width - _padding - _width;
// }
// });
// }
// }

@ -1,5 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@ -10,11 +10,9 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/call.dart';
import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart';
import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart';
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/custom_auto_direction.dart';
@ -23,9 +21,9 @@ import 'package:mohem_flutter_app/ui/chat/chat_bubble.dart';
import 'package:mohem_flutter_app/ui/chat/common.dart';
import 'package:mohem_flutter_app/widgets/chat_app_bar_widge.dart';
import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:signalr_netcore/signalr_client.dart';
import 'package:swipe_to/swipe_to.dart';
class ChatDetailedScreenParams {
@ -78,7 +76,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
Widget build(BuildContext context) {
params = ModalRoute.of(context)!.settings.arguments as ChatDetailedScreenParams;
data = Provider.of<ChatProviderModel>(context, listen: false);
// callPro = Provider.of<ChatCallProvider>(context, listen: false);
// callPro = Provider.of<ChatCallProvider>(context, listen: false);
if (params != null) {
data.getSingleUserChatHistory(
senderUID: AppState().chatDetails!.response!.id!.toInt(),
@ -98,14 +96,32 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
showTyping: true,
chatUser: params!.chatUser,
actions: [
// SvgPicture.asset("assets/icons/chat/call.svg", width: 21, height: 23).onPress(() {
// makeCall(callType: "AUDIO");
// }),
// 24.width,
// SvgPicture.asset("assets/icons/chat/video_call.svg", width: 21, height: 18).onPress(() {
// makeCall(callType: "VIDEO");
// }),
// 21.width,
// if (Platform.isAndroid)
SvgPicture.asset("assets/icons/chat/call.svg", width: 21, height: 23).onPress(() async {
Future<PermissionStatus> micPer = Permission.microphone.request();
if (await micPer.isGranted) {
makeCall(callType: "AUDIO");
} else {
Permission.microphone.request().isGranted.then((value) {
makeCall(callType: "AUDIO");
});
}
}),
// if (Platform.isAndroid)
24.width,
// if (Platform.isAndroid)
SvgPicture.asset("assets/icons/chat/video_call.svg", width: 21, height: 18).onPress(() async {
Future<PermissionStatus> camPer = Permission.camera.request();
if (await camPer.isGranted) {
makeCall(callType: "VIDEO");
} else {
Permission.camera.request().isGranted.then((value) {
makeCall(callType: "VIDEO");
});
}
}),
// if (Platform.isAndroid)
21.width,
],
),
body: SafeArea(
@ -149,7 +165,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
);
},
).onPress(() async {
logger.w(m.userChatHistory[i].toJson());
// logger.w(m.userChatHistory[i].toJson());
if (m.userChatHistory[i].fileTypeResponse != null && m.userChatHistory[i].fileTypeId != null) {
if (m.userChatHistory[i].fileTypeId! == 1 ||
m.userChatHistory[i].fileTypeId! == 5 ||
@ -352,29 +368,21 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
}
void makeCall({required String callType}) async {
callPro.initCallListeners();
print("================== Make call Triggered ============================");
Map<String, dynamic> json = {
"callerID": AppState().chatDetails!.response!.id!.toString(),
"callerDetails": AppState().chatDetails!.toJson(),
"receiverID": params!.chatUser!.id.toString(),
"receiverDetails": params!.chatUser!.toJson(),
"callerID": AppState().chatDetails!.response!.id,
"callerName": AppState().chatDetails!.response!.userName,
"callerEmail": AppState().chatDetails!.response!.email,
"callerTitle": AppState().chatDetails!.response!.title,
"callerPhone": AppState().chatDetails!.response!.phone,
"receiverID": params!.chatUser!.id,
"receiverName": params!.chatUser!.userName,
"receiverEmail": params!.chatUser!.email,
"receiverTitle": params!.chatUser!.title,
"receiverPhone": params!.chatUser!.phone,
"title": params!.chatUser!.userName!.replaceAll(".", " "),
"calltype": callType == "VIDEO" ? "Video" : "Audio",
"callType": callType == "VIDEO" ? "Video" : "Audio",
};
logger.w(json);
CallDataModel callData = CallDataModel.fromJson(json);
await Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => OutGoingCall(
isVideoCall: callType == "VIDEO" ? true : false,
outGoingCallData: callData,
),
),
).then((value) {
print("then");
callPro.stopListeners();
});
await Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => OutGoingCall(isVideoCall: callType == "VIDEO" ? true : false, outGoingCallData: callData)));
}
}

@ -7,6 +7,8 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/chat_home_screen.dart';
import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart';
@ -27,12 +29,15 @@ class _ChatHomeState extends State<ChatHome> {
int tabIndex = 0;
PageController controller = PageController();
late ChatProviderModel data;
late ChatCallProvider callProvider;
@override
void initState() {
super.initState();
data = Provider.of<ChatProviderModel>(context, listen: false);
callProvider = Provider.of<ChatCallProvider>(context, listen: false);
data.registerEvents();
// callProvider.initCallListeners();
}
@override
@ -44,7 +49,7 @@ class _ChatHomeState extends State<ChatHome> {
void fetchAgain() {
if (chatHubConnection.state != HubConnectionState.Connected) {
data.getUserAutoLoginToken().whenComplete(() async {
await data.buildHubConnection();
await data.buildHubConnection(context: context, ccProvider: callProvider);
data.getUserRecentChats();
});
return;
@ -52,11 +57,6 @@ class _ChatHomeState extends State<ChatHome> {
if (data.searchedChats == null || data.searchedChats!.isEmpty) {
data.isLoading = true;
data.getUserRecentChats().whenComplete(() async {
// String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat");
// String notificationData = await Utils.getStringFromPrefs("notificationData");
// if (isAppOpendByChat != "null" || isAppOpendByChat == "true" && notificationData != "null") {
// data.openChatByNoti(context);
// }
});
}
}

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:math';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/material.dart';
@ -187,3 +188,40 @@ class WaveBubble extends StatelessWidget {
);
}
}
class CallTimer extends StatefulWidget {
const CallTimer({Key? key}) : super(key: key);
@override
State<CallTimer> createState() => _CallTimerState();
}
class _CallTimerState extends State<CallTimer> {
late Timer _timer;
int _timeExpandedBySeconds = 0;
@override
void initState() {
// TODO: implement initState
super.initState();
_timer = Timer.periodic(const Duration(seconds: 1), (Timer timer) {
setState(() {
_timeExpandedBySeconds += 1;
});
});
}
@override
Widget build(BuildContext context) {
return Text(
_timeExpandedBySeconds.toString(),
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
),
);
}
}

@ -1,14 +1,18 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_callkit_incoming/entities/call_event.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_countdown_timer/flutter_countdown_timer.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mohem_flutter_app/api/dashboard_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/chat_call_kit.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
@ -16,10 +20,16 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/incoming_call_model.dart';
import 'package:mohem_flutter_app/models/itg/itg_main_response.dart';
import 'package:mohem_flutter_app/models/itg/itg_response_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart';
import 'package:mohem_flutter_app/models/privilege_list_model.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart';
import 'package:mohem_flutter_app/ui/landing/widget/app_drawer.dart';
import 'package:mohem_flutter_app/ui/landing/widget/menus_widget.dart';
import 'package:mohem_flutter_app/ui/landing/widget/services_widget.dart';
@ -32,8 +42,7 @@ import 'package:mohem_flutter_app/widgets/shimmer/offers_shimmer_widget.dart';
import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:signalr_netcore/signalr_client.dart';
late HubConnection chatHubConnection;
import 'package:http/http.dart' as http;
class DashboardScreen extends StatefulWidget {
DashboardScreen({Key? key}) : super(key: key);
@ -48,20 +57,23 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
late DashboardProviderModel data;
late MarathonProvider marathonProvider;
late ChatProviderModel cProvider;
late ChatCallProvider chatCallProvider;
final GlobalKey<ScaffoldState> _scaffoldState = GlobalKey();
final RefreshController _refreshController = RefreshController(initialRefresh: false);
int currentIndex = 0;
@override
void initState() {
WidgetsBinding.instance.addObserver(this);
super.initState();
if (Platform.isAndroid) {
callListeners();
}
scheduleMicrotask(() {
data = Provider.of<DashboardProviderModel>(context, listen: false);
marathonProvider = Provider.of<MarathonProvider>(context, listen: false);
cProvider = Provider.of<ChatProviderModel>(context, listen: false);
chatCallProvider = Provider.of<ChatCallProvider>(context, listen: false);
if (checkIfPrivilegedForChat()) {
_bHubCon();
}
@ -69,6 +81,52 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
});
}
Future<void> callListeners() async {
try {
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) async {
switch (event!.event) {
case Event.actionCallIncoming:
break;
case Event.actionCallStart:
break;
case Event.actionCallAccept:
if (mounted) {
moveToCallScreen();
}
break;
case Event.actionCallDecline:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
FlutterCallkitIncoming.endAllCalls();
break;
case Event.actionCallEnded:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
FlutterCallkitIncoming.endAllCalls();
break;
case Event.actionCallTimeout:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
FlutterCallkitIncoming.endAllCalls();
break;
}
});
} on Exception {}
}
Future<void> moveToCallScreen() async {
dynamic calls = await FlutterCallkitIncoming.activeCalls();
if (calls is List) {
if (calls.isNotEmpty) {
Future.delayed(const Duration(seconds: 1)).whenComplete(() {
MaterialPageRoute pageRoute = MaterialPageRoute(builder: (BuildContext context) => StartCallPage());
Navigator.push(context, pageRoute);
});
}
}
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
@ -88,24 +146,24 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
if (!cProvider.disbaleChatForThisUser) {
if (!cProvider.disableChatForThisUser) {
chatHubConnection.stop();
}
}
void _bHubCon() {
cProvider.getUserAutoLoginToken().whenComplete(() async {
if (!cProvider.disbaleChatForThisUser) {
if (!cProvider.disableChatForThisUser) {
String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat");
if (isAppOpendByChat != "null" && isAppOpendByChat == "true") {
Utils.showLoading(context);
cProvider.buildHubConnection();
await cProvider.buildHubConnection(context: context, ccProvider: chatCallProvider);
Future.delayed(const Duration(seconds: 2), () async {
cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!);
gotoChat(context);
});
} else {
cProvider.buildHubConnection();
await cProvider.buildHubConnection(context: context, ccProvider: chatCallProvider);
Future.delayed(const Duration(seconds: 2), () {
cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!);
});
@ -153,17 +211,17 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
if (isFromInit) {
checkERMChannel();
}
if (!cProvider.disbaleChatForThisUser && !isFromInit) checkHubCon();
if (!cProvider.disableChatForThisUser && !isFromInit) checkHubCon();
_refreshController.refreshCompleted();
}
void checkERMChannel() {
data.getITGNotification().then((val) {
data.getITGNotification().then((MohemmItgResponseItem? val) {
if (val!.result!.data != null) {
print("-------------------- Survey ----------------------------");
if (val.result!.data!.notificationType == "Survey") {
DashboardApiClient().getAdvertisementDetail(val.result!.data!.notificationMasterId ?? "").then(
(value) {
(ItgMainRes? value) {
if (value!.mohemmItgResponseItem!.statusCode == 200) {
if (value.mohemmItgResponseItem!.result!.data != null) {
// Navigator.pushNamed(context, AppRoutes.survey, arguments: val.result!.data);
@ -179,13 +237,22 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
} else {
print("------------------------------------------- Ads --------------------");
DashboardApiClient().getAdvertisementDetail(val.result!.data!.notificationMasterId ?? "").then(
(value) {
(ItgMainRes? value) {
if (value!.mohemmItgResponseItem!.statusCode == 200) {
if (value.mohemmItgResponseItem!.result!.data != null) {
Navigator.pushNamed(context, AppRoutes.advertisement, arguments: {
"masterId": val.result!.data!.notificationMasterId,
"advertisement": value.mohemmItgResponseItem!.result!.data!.advertisement,
});
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (BuildContext context) => ITGAdsScreen(
// addMasterId: val.result!.data!.notificationMasterId!,
// advertisement: value.mohemmItgResponseItem!.result!.data!.advertisement!,
// ),
// ),
// );
}
}
},
@ -199,6 +266,46 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldState,
// appBar: AppBar(
// actions: [
// IconButton(
// onPressed: () {
// data.getITGNotification().then((val) {
// if (val!.result!.data != null) {
// print("-------------------- Survey ----------------------------");
// if (val.result!.data!.notificationType == "Survey") {
// Navigator.pushNamed(context, AppRoutes.survey, arguments: val.result!.data);
// } else {
// print("------------------------------------------- Ads --------------------");
// DashboardApiClient().getAdvertisementDetail(val.result!.data!.notificationMasterId ?? "").then(
// (value) {
// if (value!.mohemmItgResponseItem!.statusCode == 200) {
// if (value.mohemmItgResponseItem!.result!.data != null) {
// Navigator.pushNamed(context, AppRoutes.advertisement, arguments: {
// "masterId": val.result!.data!.notificationMasterId,
// "advertisement": value.mohemmItgResponseItem!.result!.data!.advertisement,
// });
//
// // Navigator.push(
// // context,
// // MaterialPageRoute(
// // builder: (BuildContext context) => ITGAdsScreen(
// // addMasterId: val.result!.data!.notificationMasterId!,
// // advertisement: value.mohemmItgResponseItem!.result!.data!.advertisement!,
// // ),
// // ),
// // );
// }
// }
// },
// );
// }
// }
// });
// },
// icon: Icon(Icons.add))
// ],
// ),
body: Column(
children: [
Row(
@ -271,106 +378,106 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
child: Consumer<DashboardProviderModel>(
builder: (BuildContext context, DashboardProviderModel model, Widget? child) {
return (model.isAttendanceTrackingLoading
? GetAttendanceTrackingShimmer()
: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: const LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [
MyColors.gradiantEndColor,
MyColors.gradiantStartColor,
]),
),
child: Stack(
alignment: Alignment.center,
? GetAttendanceTrackingShimmer()
: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: const LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [
MyColors.gradiantEndColor,
MyColors.gradiantStartColor,
]),
),
child: Stack(
alignment: Alignment.center,
children: [
if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"),
Column(
LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true),
if (model.isTimeRemainingInSeconds == 0) DateTime.now().toString().split(" ")[0].toText12(color: Colors.white),
if (model.isTimeRemainingInSeconds != 0)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
9.height,
Directionality(
textDirection: ui.TextDirection.ltr,
child: CountdownTimer(
endTime: model.endTime,
onEnd: null,
endWidget: "00:00:00".toText14(color: Colors.white, isBold: true),
textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold),
),
),
LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white),
9.height,
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20)),
child: LinearProgressIndicator(
value: model.progress,
minHeight: 8,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
backgroundColor: const Color(0xff196D73),
),
),
],
),
],
).paddingOnly(top: 12, right: 15, left: 12),
),
Row(
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true),
if (model.isTimeRemainingInSeconds == 0) DateTime.now().toString().split(" ")[0].toText12(color: Colors.white),
if (model.isTimeRemainingInSeconds != 0)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
9.height,
Directionality(
textDirection: ui.TextDirection.ltr,
child: CountdownTimer(
endTime: model.endTime,
onEnd: null,
endWidget: "00:00:00".toText14(color: Colors.white, isBold: true),
textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold),
),
),
LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white),
9.height,
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20)),
child: LinearProgressIndicator(
value: model.progress,
minHeight: 8,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
backgroundColor: const Color(0xff196D73),
),
),
],
),
],
).paddingOnly(top: 12, right: 15, left: 12),
),
Row(
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.checkIn.tr().toText12(color: Colors.white),
(model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn)
.toString()
.toText14(color: Colors.white, isBold: true),
4.height,
],
).paddingOnly(left: 12, right: 12),
),
Container(
margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0),
width: 45,
height: 45,
padding: const EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: Color(0xff259EA4),
borderRadius: BorderRadius.only(
bottomRight: AppState().isArabic(context) ? Radius.circular(0) : Radius.circular(15),
bottomLeft: AppState().isArabic(context) ? Radius.circular(15) : Radius.circular(0),
),
),
child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"),
).onPress(() {
showMyBottomSheet(
context,
callBackFunc: () {},
child: MarkAttendanceWidget(model, isFromDashboard: true),
);
}),
],
),
LocaleKeys.checkIn.tr().toText12(color: Colors.white),
(model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn)
.toString()
.toText14(color: Colors.white, isBold: true),
4.height,
],
).paddingOnly(left: 12, right: 12),
),
Container(
margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0),
width: 45,
height: 45,
padding: const EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: Color(0xff259EA4),
borderRadius: BorderRadius.only(
bottomRight: AppState().isArabic(context) ? Radius.circular(0) : Radius.circular(15),
bottomLeft: AppState().isArabic(context) ? Radius.circular(15) : Radius.circular(0),
),
),
],
),
).onPress(
() {
Navigator.pushNamed(context, AppRoutes.todayAttendance);
},
))
child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"),
).onPress(() {
showMyBottomSheet(
context,
callBackFunc: () {},
child: MarkAttendanceWidget(model, isFromDashboard: true),
);
}),
],
),
],
),
],
),
).onPress(
() {
Navigator.pushNamed(context, AppRoutes.todayAttendance);
},
))
.animatedSwither();
},
),
@ -431,48 +538,48 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
return model.isOffersLoading
? const OffersShimmerWidget()
: InkWell(
onTap: () {
navigateToDetails(data.getOffersList[index]);
},
child: SizedBox(
onTap: () {
navigateToDetails(data.getOffersList[index]);
},
child: SizedBox(
width: 73,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 73,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 73,
height: 73,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(
Radius.circular(100),
),
border: Border.all(color: MyColors.lightGreyE3Color, width: 1),
),
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(50),
),
child: Hero(
tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString()!,
transitionOnUserGestures: true,
child: Image.network(
data.getOffersList[index].logo ?? "",
fit: BoxFit.contain,
),
),
),
),
4.height,
Expanded(
child: AppState().isArabic(context)
? data.getOffersList[index].titleAr!.toText12(isCenter: true, maxLine: 1)
: data.getOffersList[index].titleEn!.toText12(isCenter: true, maxLine: 1),
height: 73,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(
Radius.circular(100),
),
border: Border.all(color: MyColors.lightGreyE3Color, width: 1),
),
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(50),
),
child: Hero(
tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString()!,
transitionOnUserGestures: true,
child: Image.network(
data.getOffersList[index].logo ?? "",
fit: BoxFit.contain,
),
],
),
),
),
);
4.height,
Expanded(
child: AppState().isArabic(context)
? data.getOffersList[index].titleAr!.toText12(isCenter: true, maxLine: 1)
: data.getOffersList[index].titleEn!.toText12(isCenter: true, maxLine: 1),
),
],
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => 8.width,
itemCount: 9),
@ -565,30 +672,29 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
icon: Stack(
alignment: Alignment.centerLeft,
children: [
SvgPicture.asset(
"assets/icons/chat/chat.svg",
color: !checkIfPrivilegedForChat()
? MyColors.lightGreyE3Color
: currentIndex == 4
? MyColors.grey3AColor
: cProvider.disbaleChatForThisUser
? MyColors.lightGreyE3Color
: MyColors.grey98Color,
).paddingAll(4),
SvgPicture.asset("assets/icons/chat/chat.svg", color: !checkIfPrivilegedForChat() ? MyColors.lightGreyE3Color : MyColors.grey98Color
// currentIndex == 4
// ? MyColors.grey3AColor
// : cProvider.disableChatForThisUser
// ? MyColors.lightGreyE3Color
// : MyColors.grey98Color,
)
.paddingAll(4),
Consumer<ChatProviderModel>(
builder: (BuildContext cxt, ChatProviderModel data, Widget? child) {
return !checkIfPrivilegedForChat()
? const SizedBox()
: Positioned(
right: 0,
top: 0,
child: Container(
padding: const EdgeInsets.only(left: 4, right: 4),
alignment: Alignment.center,
decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)),
child: data.chatUConvCounter.toString().toText10(color: Colors.white),
),
);
right: 0,
top: 0,
child: Container(
padding: const EdgeInsets.only(left: 4, right: 4),
alignment: Alignment.center,
decoration: BoxDecoration(color: data.disableChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)),
child: data.chatUConvCounter.toString().toText10(color: Colors.white),
),
);
},
),
],
@ -612,7 +718,7 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
} else if (index == 3) {
Navigator.pushNamed(context, AppRoutes.itemsForSale);
} else if (index == 4) {
if (!cProvider.disbaleChatForThisUser && checkIfPrivilegedForChat()) {
if (!cProvider.disableChatForThisUser) {
Navigator.pushNamed(context, AppRoutes.chat);
}
}
@ -637,6 +743,7 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
}
}
});
Navigator.pushNamed(context, AppRoutes.offersAndDiscountsDetails, arguments: getOffersDetailList);
}

@ -1,3 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
@ -8,10 +10,17 @@ import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_callkit_incoming/entities/call_event.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_ios_voip_kit/call_state_type.dart';
import 'package:flutter_ios_voip_kit/flutter_ios_voip_kit.dart';
import 'package:logger/logger.dart';
import 'package:mohem_flutter_app/api/chat/chat_api_client.dart';
// import 'package:huawei_hmsavailability/huawei_hmsavailability.dart';
import 'package:mohem_flutter_app/api/login_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/chat_call_kit.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/classes/notifications.dart';
@ -21,11 +30,15 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/call.dart';
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart';
import 'package:mohem_flutter_app/models/check_mobile_app_version_model.dart';
import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart';
import 'package:mohem_flutter_app/models/member_information_list_model.dart';
import 'package:mohem_flutter_app/models/member_login_list_model.dart';
import 'package:mohem_flutter_app/models/privilege_list_model.dart';
import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/button/hmg_connectivity_button.dart';
import 'package:mohem_flutter_app/widgets/input_widget.dart';
@ -42,7 +55,7 @@ class LoginScreen extends StatefulWidget {
}
}
class _LoginScreenState extends State<LoginScreen> {
class _LoginScreenState extends State<LoginScreen> with WidgetsBindingObserver{
TextEditingController username = TextEditingController();
TextEditingController password = TextEditingController();
@ -50,6 +63,8 @@ class _LoginScreenState extends State<LoginScreen> {
MemberLoginListModel? _memberLoginList;
late final FirebaseMessaging _firebaseMessaging;
IosCallPayload? _iosCallPayload;
bool _autoLogin = false;
@ -59,9 +74,14 @@ class _LoginScreenState extends State<LoginScreen> {
bool isRealDevice = false;
bool isOnExternalStorage = false;
bool isDevelopmentModeEnable = false;
bool isIncomingCall = false;
// late HmsApiAvailability hmsApiAvailability;
final voIPKit = FlutterIOSVoIPKit.instance;
late Timer timeOutTimer;
@override
void initState() {
super.initState();
@ -70,6 +90,92 @@ class _LoginScreenState extends State<LoginScreen> {
// if (kReleaseMode) {
// checkDeviceSafety();
// }
WidgetsBinding.instance.addObserver(this);
if (Platform.isAndroid) {
callListeners();
checkAndNavigationCallingPage();
}
setupVoIPCallBacks();
}
void _timeOut({
int seconds = 15,
}) async {
timeOutTimer = Timer(Duration(seconds: seconds), () async {
print('🎈 example: timeOut');
var incomingCallerName = await voIPKit.getIncomingCallerName();
voIPKit.unansweredIncomingCall(
skipLocalNotification: false,
missedCallTitle: '📞 Missed call',
missedCallBody: 'There was a call from $incomingCallerName',
);
});
}
void setupVoIPCallBacks() {
if (Platform.isIOS) {
voIPKit.getVoIPToken().then((value) {
print('🎈 example: getVoIPToken: $value');
});
voIPKit.onDidUpdatePushToken = (String token) {
print('🎈 example: onDidUpdatePushToken: $token');
};
}
voIPKit.onDidReceiveIncomingPush = (
Map<String, dynamic> payload,
) async {
_iosCallPayload = IosCallPayload.fromJson(payload);
logger.d(_iosCallPayload!.incomingCallerId!.split("-").last);
print('🎈 example: onDidReceiveIncomingPush $payload');
_timeOut();
};
voIPKit.onDidRejectIncomingCall = (
String uuid,
String callerId,
) async {
try {
var logText = "did reject call $callerId";
} catch (err) {}
};
voIPKit.onDidAcceptIncomingCall = (
String uuid,
String callerId,
) async {
var callerID = "did accept call $callerId";
debugPrint(callerID);
logger.d(_iosCallPayload!.incomingCallerId!.split("-").last);
debugPrint(_iosCallPayload!.incomingCallerId);
debugPrint(_iosCallPayload!.incomingCallerName);
debugPrint(_iosCallPayload!.incomingCallType);
await connectCall();
await voIPKit.acceptIncomingCall(callerState: CallStateType.calling);
await voIPKit.callConnected();
Future.delayed(const Duration(milliseconds: 2500), () {
voIPKit.endCall().then((value) async {});
});
timeOutTimer.cancel();
};
}
Future<void> connectCall() async {
try {
UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserCallToken(userid: _iosCallPayload!.incomingCallerId!.split("-").last);
if (userLoginResponse.response != null) {
AppState().setchatUserDetails = userLoginResponse;
Utils.saveStringFromPrefs("userLoginChatDetails", jsonEncode(userLoginResponse.response));
}
} catch (e) {
logger.d(e);
}
}
// void checkDeviceSafety() async {
@ -87,6 +193,103 @@ class _LoginScreenState extends State<LoginScreen> {
// print(error);
// }
// }
// Future<void> connectCall() async {
// try {
// UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserCallToken(userid: _iosCallPayload!.incomingCallerId!.split("-").last);
// if (userLoginResponse.response != null) {
// AppState().setchatUserDetails = userLoginResponse;
// Utils.saveStringFromPrefs("userLoginChatDetails", jsonEncode(userLoginResponse.response));
// }
// } catch (e) {
// logger.d(e);
// }
// }
Future<void> callListeners() async {
try {
print("Call Listeners Init");
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) async {
switch (event!.event) {
case Event.actionCallIncoming:
break;
case Event.actionCallStart:
break;
case Event.actionCallAccept:
if (mounted) {
isIncomingCall = true;
moveToCallScreen();
}
break;
case Event.actionCallDecline:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
FlutterCallkitIncoming.endAllCalls();
break;
case Event.actionCallEnded:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
FlutterCallkitIncoming.endAllCalls();
break;
case Event.actionCallTimeout:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
FlutterCallkitIncoming.endAllCalls();
break;
}
print( '${event.toString()}\n');
});
} on Exception {
logger.log(Level.info, "EXCEPTION-ON-EVENTS");
}
}
Future<void> moveToCallScreen() async {
dynamic calls = await FlutterCallkitIncoming.activeCalls();
if (calls is List) {
if (calls.isNotEmpty) {
if (Platform.isAndroid) {
Utils.hideLoading(context);
}
var pageRoute = MaterialPageRoute(builder: (context) => StartCallPage());
Navigator.push(context, pageRoute).whenComplete(() {
checkFirebaseToken();
});
} else {
FlutterCallkitIncoming.endAllCalls();
Utils.showToast("Something wen't wrong");
}
}
}
Future<dynamic> getCurrentCall() async {
//check current call from pushkit if possible
var calls = await FlutterCallkitIncoming.activeCalls();
if (calls is List) {
if (calls.isNotEmpty) {
print('DATA: $calls');
return calls[0];
} else {
return null;
}
}
}
Future<void> checkAndNavigationCallingPage() async {
var currentCall = await getCurrentCall();
if (currentCall != null && Platform.isAndroid) {
isIncomingCall = true;
Utils.hideLoading(context);
Navigator.push(context, MaterialPageRoute(builder: (context) => StartCallPage()));
}
}
@override
void dispose() {
@ -204,7 +407,14 @@ class _LoginScreenState extends State<LoginScreen> {
// 13777
// Ab12345cd
}
if (isAppOpenBySystem!) checkFirebaseToken();
// if (isAppOpenBySystem!) checkFirebaseToken();
Utils.showLoading(context);
Future.delayed(const Duration(seconds: 2)).whenComplete(() {
if (!isIncomingCall) {
if (isAppOpenBySystem!) checkFirebaseToken();
}
});
}
// username.text = "15444";

@ -3,6 +3,8 @@ import 'dart:io';
import 'package:easy_localization/src/public_ext.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_callkit_incoming/entities/call_event.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_svg/svg.dart';
import 'package:local_auth/auth_strings.dart';
import 'package:local_auth/local_auth.dart';
@ -20,6 +22,7 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/basic_member_information_model.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart';
import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart';
import 'package:mohem_flutter_app/ui/dialogs/id/business_card_dialog.dart';
import 'package:mohem_flutter_app/ui/dialogs/id/employee_digital_id_dialog.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
@ -52,15 +55,75 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
void initState() {
_getAvailableBiometrics();
// setDefault();
if (Platform.isAndroid) {
callListeners();
}
super.initState();
}
Future<void> callListeners() async {
try {
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) async {
switch (event!.event) {
case Event.actionCallIncoming:
// await ChatVoipCall().declineCall(payload: jsonEncode(event.body));
break;
case Event.actionCallStart:
break;
case Event.actionCallAccept:
if (mounted) {
// isIncomingCall = true;
moveToCallScreen();
}
break;
case Event.actionCallDecline:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
FlutterCallkitIncoming.endAllCalls();
break;
case Event.actionCallEnded:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
FlutterCallkitIncoming.endAllCalls();
break;
case Event.actionCallTimeout:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
FlutterCallkitIncoming.endAllCalls();
break;
}
});
} on Exception {
}
}
Future<void> moveToCallScreen() async {
dynamic calls = await FlutterCallkitIncoming.activeCalls();
if (calls is List) {
if (calls.isNotEmpty) {
if (Platform.isAndroid) {
Utils.hideLoading(context);
}
var pageRoute = MaterialPageRoute(builder: (context) => StartCallPage());
Navigator.push(context, pageRoute).whenComplete(() {
// checkFirebaseToken();
});
} else {
FlutterCallkitIncoming.endAllCalls();
Utils.showToast("Something wen't wrong");
}
}
}
@override
Widget build(BuildContext context) {
mobileLoginInfoListModel ??= ModalRoute.of(context)!.settings.arguments as GetMobileLoginInfoListModel;
// String empName = AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr! : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn!;
String empName = mobileLoginInfoListModel!.employeeName!;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
@ -289,7 +352,7 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
width: 38,
color: isDisable ? MyColors.darkTextColor.withOpacity(0.7) : null,
),
_title.toText16(height: 20/16)
_title.toText16(height: 20 / 16)
],
),
),
@ -404,5 +467,4 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
// // isLoading = isTrue;
// });
// }
}

@ -76,7 +76,7 @@ class _OffersAndDiscountsDetailsState extends State<OffersAndDiscountsDetails> {
: getOffersList[0].titleEn!.toText22(isBold: true, color: const Color(0xff2B353E)).center,
Html(
data: AppState().isArabic(context) ? getOffersList[0].descriptionAr! : getOffersList[0].descriptionEn ?? "",
onLinkTap: (String? url, RenderContext context, Map<String, String> attributes, _) {
onLinkTap: (String? url, Map<String, String> attributes, _) {
launchUrl(Uri.parse(url!));
},
),

@ -87,11 +87,12 @@ dependencies:
signalr_netcore: ^1.3.3
logging: ^1.0.1
swipe_to: ^1.0.2
flutter_webrtc: ^0.9.16
camera: ^0.10.3
flutter_local_notifications: ^10.0.0
#firebase_analytics: any
flutter_webrtc: ^0.9.20
draggable_widget: ^2.0.0
flutter_local_notifications: any
flutter_callkit_incoming: ^2.0.0+1
#Chat Voice Message Recoding & Play
audio_waveforms: ^0.1.5+1
rxdart: ^0.27.7
@ -114,9 +115,12 @@ dependencies:
carousel_slider: ^4.2.1
#Huawei Specified
# store_checker: ^1.1.0
store_checker: ^1.1.0
google_api_availability: ^3.0.1
flutter_ios_voip_kit: ^0.1.0
file: ^6.1.4
dependency_overrides:
firebase_core_platform_interface: 4.5.1

Loading…
Cancel
Save