diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d1dea7d3..4630fff4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,7 +11,7 @@ on: - 1.* env: - FLUTTER_VERSION: "3.10.x" + FLUTTER_VERSION: "3.32.x" WORKING_DIRECTORY: ./example diff --git a/.gitignore b/.gitignore index 1525bb4b..e79ade4b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,7 @@ build/ pubspec.lock example/ios/Podfile.lock -example/pubspec.lock \ No newline at end of file +example/pubspec.lock + +.idea/* + diff --git a/CHANGELOG.md b/CHANGELOG.md index ffb65eca..2be30c9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 3.0.0 +* Using Plugin DSL for Android, thank @AAkira https://github.com/hiennguyen92/flutter_callkit_incoming/pull/743 +* Add Android native callback, thank @joshoconnor89 https://github.com/hiennguyen92/flutter_callkit_incoming/pull/736 +* Improve plugin lifecycle, thank @lohzi97 https://github.com/hiennguyen92/flutter_callkit_incoming/pull/746 +* Fixed some bugs. + ## 2.5.8 * Fix OnGoing notification Android * Add missed call notification for iOS(notification/callback action - need to setup more in AppDelegate.swift) diff --git a/README.md b/README.md index 533ade33..87f8e3be 100644 --- a/README.md +++ b/README.md @@ -543,6 +543,40 @@ FlutterCallkitIncomingPlugin.getInstance().sendEventCustom(body: Map **Please check full example:** [Example](https://github.com/hiennguyen92/flutter_callkit_incoming/blob/master/example/ios/Runner/AppDelegate.swift) +**MainActivity.kt:** +```kotlin +class MainActivity: FlutterActivity(){ + + private var callkitEventCallback = object: CallkitEventCallback{ + override fun onCallEvent(event: CallkitEventCallback.CallEvent, callData: Bundle) { + when (event) { + CallkitEventCallback.CallEvent.ACCEPT -> { + // Do something with answer + } + CallkitEventCallback.CallEvent.DECLINE -> { + // Do something with decline + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + FlutterCallkitIncomingPlugin.registerEventCallback(callkitEventCallback) + } + + override fun onDestroy() { + FlutterCallkitIncomingPlugin.unregisterEventCallback(callkitEventCallback) + super.onDestroy() + } + + +} +``` + +> **Please check full example:** [Example](https://github.com/hiennguyen92/flutter_callkit_incoming/blob/master/example/android/app/src/main/kotlin/com/example/flutter_callkit_incoming_example/MainActivity.kt +) + ## 📋 Properties ### Main Properties diff --git a/android/.gitignore b/android/.gitignore index c6cbe562..9b2613f7 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -6,3 +6,5 @@ .DS_Store /build /captures +.idea/* + diff --git a/android/build.gradle b/android/build.gradle index 23a15a75..2d37e44f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,67 +1,37 @@ -group 'com.example.flutter_callkit_incoming' -version '1.0-SNAPSHOT' - -buildscript { - ext.kotlin_version = '1.8.0' - repositories { - google() - mavenCentral() - } - - dependencies { - //classpath 'com.android.tools.build:gradle:4.1.0' - //classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - - classpath 'com.android.tools.build:gradle:7.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' } -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' +group = "com.example.flutter_callkit_incoming" +version = "1.0-SNAPSHOT" android { - compileSdk 35 + namespace "com.hiennv.flutter_callkit_incoming" + compileSdk = 35 - if (project.android.hasProperty("namespace")) { - namespace "com.hiennv.flutter_callkit_incoming" + defaultConfig { + minSdk = 16 + targetSdk = 35 + consumerProguardFiles 'consumer-rules.pro' } - + sourceSets { main.java.srcDirs += 'src/main/kotlin' } - defaultConfig { - minSdk 16 - targetSdk 35 - - consumerProguardFiles 'consumer-rules.pro' - } compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } kotlinOptions { - jvmTarget = "17" - } -} - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) + jvmTarget = "1.8" } } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'de.hdodenhof:circleimageview:3.1.0' @@ -70,4 +40,4 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.1' //noinspection GradleDependency implementation "io.coil-kt:coil:1.4.0" -} +} \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index cc5527d7..1126aa09 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip diff --git a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitEventCallback.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitEventCallback.kt new file mode 100644 index 00000000..ccdcc628 --- /dev/null +++ b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitEventCallback.kt @@ -0,0 +1,26 @@ +package com.hiennv.flutter_callkit_incoming + +import android.os.Bundle + +/** + * Unified callback interface for handling call accept and decline events natively. + * This allows other plugins or services to receive call events + * even when the Flutter engine is terminated. + */ +interface CallkitEventCallback { + + /** + * Called when a call is accepted or declined. + * @param event The type of call event (ACCEPT or DECLINE) + * @param callData Bundle containing call information (id, nameCaller, etc.) + */ + fun onCallEvent(event: CallEvent, callData: Bundle) + + /** + * Enum representing call events we handle + */ + enum class CallEvent { + ACCEPT, + DECLINE + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitIncomingActivity.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitIncomingActivity.kt index 439b0bb6..529ca20d 100644 --- a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitIncomingActivity.kt +++ b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitIncomingActivity.kt @@ -305,6 +305,7 @@ class CallkitIncomingActivity : Activity() { private fun onAcceptClick() { + // Log.d("CallkitIncomingActivity", "[CALLKIT] 📱 onAcceptClick") val data = intent.extras?.getBundle(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA) @@ -331,6 +332,7 @@ class CallkitIncomingActivity : Activity() { } private fun onDeclineClick() { + // Log.d("CallkitIncomingActivity", "[CALLKIT] 📱 onDeclineClick") val data = intent.extras?.getBundle(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA) val intent = diff --git a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitIncomingBroadcastReceiver.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitIncomingBroadcastReceiver.kt index ea86d3d0..8709cbf5 100644 --- a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitIncomingBroadcastReceiver.kt +++ b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitIncomingBroadcastReceiver.kt @@ -81,7 +81,10 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { } } - private val callkitNotificationManager: CallkitNotificationManager? = FlutterCallkitIncomingPlugin.getInstance()?.getCallkitNotificationManager() + // Get notification manager dynamically to handle plugin lifecycle properly + private fun getCallkitNotificationManager(): CallkitNotificationManager? { + return FlutterCallkitIncomingPlugin.getInstance()?.getCallkitNotificationManager() + } @SuppressLint("MissingPermission") @@ -91,7 +94,7 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { when (action) { "${context.packageName}.${CallkitConstants.ACTION_CALL_INCOMING}" -> { try { - callkitNotificationManager?.showIncomingNotification(data) + getCallkitNotificationManager()?.showIncomingNotification(data) sendEventFlutter(CallkitConstants.ACTION_CALL_INCOMING, data) addCall(context, Data.fromBundle(data)) } catch (error: Exception) { @@ -116,6 +119,8 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_ACCEPT}" -> { try { + // Log.d(TAG, "[CALLKIT] 📱 ACTION_CALL_ACCEPT") + FlutterCallkitIncomingPlugin.notifyEventCallbacks(CallkitEventCallback.CallEvent.ACCEPT, data) // start service and show ongoing call when call is accepted CallkitNotificationService.startServiceWithAction( context, @@ -131,8 +136,11 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_DECLINE}" -> { try { + // Log.d(TAG, "[CALLKIT] 📱 ACTION_CALL_DECLINE") + // Notify native decline callbacks + FlutterCallkitIncomingPlugin.notifyEventCallbacks(CallkitEventCallback.CallEvent.DECLINE, data) // clear notification - callkitNotificationManager?.clearIncomingNotification(data, false) + getCallkitNotificationManager()?.clearIncomingNotification(data, false) sendEventFlutter(CallkitConstants.ACTION_CALL_DECLINE, data) removeCall(context, Data.fromBundle(data)) } catch (error: Exception) { @@ -143,7 +151,7 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_ENDED}" -> { try { // clear notification and stop service - callkitNotificationManager?.clearIncomingNotification(data, false) + getCallkitNotificationManager()?.clearIncomingNotification(data, false) CallkitNotificationService.stopService(context) sendEventFlutter(CallkitConstants.ACTION_CALL_ENDED, data) removeCall(context, Data.fromBundle(data)) @@ -155,8 +163,9 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_TIMEOUT}" -> { try { // clear notification and show miss notification - callkitNotificationManager?.clearIncomingNotification(data, false) - callkitNotificationManager?.showMissCallNotification(data) + val notificationManager = getCallkitNotificationManager() + notificationManager?.clearIncomingNotification(data, false) + notificationManager?.showMissCallNotification(data) sendEventFlutter(CallkitConstants.ACTION_CALL_TIMEOUT, data) removeCall(context, Data.fromBundle(data)) } catch (error: Exception) { @@ -167,7 +176,7 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_CONNECTED}" -> { try { // update notification on going connected - callkitNotificationManager?.showOngoingCallNotification(data, true) + getCallkitNotificationManager()?.showOngoingCallNotification(data, true) sendEventFlutter(CallkitConstants.ACTION_CALL_CONNECTED, data) } catch (error: Exception) { Log.e(TAG, null, error) @@ -176,7 +185,7 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_CALLBACK}" -> { try { - callkitNotificationManager?.clearMissCallNotification(data) + getCallkitNotificationManager()?.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) @@ -251,4 +260,4 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { ) FlutterCallkitIncomingPlugin.sendEvent(event, forwardData) } -} \ No newline at end of file +} diff --git a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitNotificationManager.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitNotificationManager.kt index 159aa91e..c3e9ab39 100644 --- a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitNotificationManager.kt +++ b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitNotificationManager.kt @@ -839,6 +839,15 @@ class CallkitNotificationManager( targetInComingAvatarCustom?.isCancelled = true targetInComingAvatarCustom = null } + + targetOnGoingAvatarDefault?.let { + targetOnGoingAvatarDefault?.isCancelled = true + targetOnGoingAvatarDefault = null + } + targetOnGoingAvatarCustom?.let { + targetOnGoingAvatarCustom?.isCancelled = true + targetOnGoingAvatarCustom = null + } } fun clearMissCallNotification(data: Bundle) { diff --git a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitNotificationService.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitNotificationService.kt index 57396cb2..c39c627e 100644 --- a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitNotificationService.kt +++ b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitNotificationService.kt @@ -45,8 +45,10 @@ class CallkitNotificationService : Service() { } - private val callkitNotificationManager: CallkitNotificationManager? = - FlutterCallkitIncomingPlugin.getInstance()?.getCallkitNotificationManager() + // Get notification manager dynamically to handle plugin lifecycle properly + private fun getCallkitNotificationManager(): CallkitNotificationManager? { + return FlutterCallkitIncomingPlugin.getInstance()?.getCallkitNotificationManager() + } override fun onCreate() { @@ -58,8 +60,7 @@ class CallkitNotificationService : Service() { intent.getBundleExtra(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA) ?.let { if(it.getBoolean(CallkitConstants.EXTRA_CALLKIT_CALLING_SHOW, true)) { - FlutterCallkitIncomingPlugin.getInstance()?.getCallkitNotificationManager() - ?.createNotificationChanel(it) + getCallkitNotificationManager()?.createNotificationChanel(it) showOngoingCallNotification(it) }else { stopSelf() @@ -69,7 +70,7 @@ class CallkitNotificationService : Service() { if (intent?.action === CallkitConstants.ACTION_CALL_ACCEPT) { intent.getBundleExtra(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA) ?.let { - callkitNotificationManager?.clearIncomingNotification(it, true) + getCallkitNotificationManager()?.clearIncomingNotification(it, true) if (it.getBoolean(CallkitConstants.EXTRA_CALLKIT_CALLING_SHOW, true)) { showOngoingCallNotification(it) }else { @@ -84,7 +85,7 @@ class CallkitNotificationService : Service() { private fun showOngoingCallNotification(bundle: Bundle) { val callkitNotification = - this.callkitNotificationManager?.getOnGoingCallNotification(bundle, false) + getCallkitNotificationManager()?.getOnGoingCallNotification(bundle, false) if (callkitNotification != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { startForeground( @@ -101,7 +102,8 @@ class CallkitNotificationService : Service() { override fun onDestroy() { super.onDestroy() - callkitNotificationManager?.destroy() + // Don't destroy the notification manager here as it's shared across the app + // The plugin will handle cleanup when all engines are detached } override fun onBind(p0: Intent?): IBinder? { diff --git a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/FlutterCallkitIncomingPlugin.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/FlutterCallkitIncomingPlugin.kt index 9c50f66f..33676359 100644 --- a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/FlutterCallkitIncomingPlugin.kt +++ b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/FlutterCallkitIncomingPlugin.kt @@ -41,6 +41,7 @@ class FlutterCallkitIncomingPlugin : FlutterPlugin, MethodCallHandler, ActivityA private val methodChannels = mutableMapOf() private val eventChannels = mutableMapOf() private val eventHandlers = mutableListOf>() + private val eventCallbacks = mutableListOf>() fun sendEvent(event: String, body: Map) { eventHandlers.reapCollection().forEach { @@ -54,6 +55,32 @@ class FlutterCallkitIncomingPlugin : FlutterPlugin, MethodCallHandler, ActivityA } } + /** + * Register a callback to receive call events (accept/decline) natively. + * This allows other plugins/services to handle call events + * even when Flutter engine is terminated. + */ + fun registerEventCallback(callback: CallkitEventCallback) { + eventCallbacks.add(WeakReference(callback)) + } + + /** + * Unregister an event callback. + */ + fun unregisterEventCallback(callback: CallkitEventCallback) { + eventCallbacks.removeAll { it.get() == callback || it.get() == null } + } + + /** + * Notify all registered event callbacks. + * Called internally when a call event occurs. + */ + internal fun notifyEventCallbacks(event: CallkitEventCallback.CallEvent, callData: android.os.Bundle) { + eventCallbacks.reapCollection().forEach { callbackRef -> + callbackRef.get()?.onCallEvent(event, callData) + } + } + fun sharePluginWithRegister(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { initSharedInstance( @@ -68,6 +95,12 @@ class FlutterCallkitIncomingPlugin : FlutterPlugin, MethodCallHandler, ActivityA instance.callkitSoundPlayerManager = CallkitSoundPlayerManager(context) instance.callkitNotificationManager = CallkitNotificationManager(context, instance.callkitSoundPlayerManager) instance.context = context + } else { + // Re-initialize managers if they were destroyed but instance still exists + if (instance.callkitNotificationManager == null) { + instance.callkitSoundPlayerManager = CallkitSoundPlayerManager(context) + instance.callkitNotificationManager = CallkitNotificationManager(context, instance.callkitSoundPlayerManager) + } } val channel = MethodChannel(binaryMessenger, "flutter_callkit_incoming") @@ -351,10 +384,15 @@ class FlutterCallkitIncomingPlugin : FlutterPlugin, MethodCallHandler, ActivityA override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { methodChannels.remove(binding.binaryMessenger)?.setMethodCallHandler(null) eventChannels.remove(binding.binaryMessenger)?.setStreamHandler(null) - instance.callkitSoundPlayerManager?.destroy() - instance.callkitNotificationManager?.destroy() - instance.callkitSoundPlayerManager = null - instance.callkitNotificationManager = null + + // Only destroy managers when all engine bindings are detached + // This prevents issues when foreground services detach but main app is still running + if (methodChannels.isEmpty() && eventChannels.isEmpty()) { + instance.callkitSoundPlayerManager?.destroy() + instance.callkitNotificationManager?.destroy() + instance.callkitSoundPlayerManager = null + instance.callkitNotificationManager = null + } } override fun onAttachedToActivity(binding: ActivityPluginBinding) { diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index f4f4a74c..0796f4c2 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,48 +1,36 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "com.google.gms.google-services" + id "dev.flutter.flutter-gradle-plugin" } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'com.google.gms.google-services' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { - compileSdk 34 + namespace "com.example.flutter_callkit_incoming_example" + compileSdk 35 sourceSets { main.java.srcDirs += 'src/main/kotlin' } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.hiennv.testing" - minSdk 19 + applicationId "com.example.flutter_callkit_incoming_example" + minSdk flutter.minSdkVersion targetSdk 35 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName + versionCode flutter.versionCode + versionName flutter.versionName multiDexEnabled true } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } + buildTypes { release { // TODO: Add your own signing config for the release build. @@ -56,7 +44,3 @@ android { flutter { source '../..' } - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/example/android/app/google-services.json b/example/android/app/google-services.json index 749d241a..1b1c8981 100644 --- a/example/android/app/google-services.json +++ b/example/android/app/google-services.json @@ -3,14 +3,50 @@ "project_number": "773665512065", "firebase_url": "https://project-working-for-testing-default-rtdb.firebaseio.com", "project_id": "project-working-for-testing", - "storage_bucket": "project-working-for-testing.appspot.com" + "storage_bucket": "project-working-for-testing.firebasestorage.app" }, "client": [ { "client_info": { - "mobilesdk_app_id": "1:773665512065:android:5708a78385fe29101a8b80", + "mobilesdk_app_id": "1:773665512065:android:eab94871a69e9baa1a8b80", "android_client_info": { - "package_name": "com.hiennv.testing" + "package_name": "com.example.flutter_callkit_incoming_example" + } + }, + "oauth_client": [ + { + "client_id": "773665512065-1shc4ufo4sfdt8v4lvmd0amhj1jqr3cl.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBv5X-BDvO8km6lVG8gllj4bY6lJLDUguY" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "773665512065-1shc4ufo4sfdt8v4lvmd0amhj1jqr3cl.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "773665512065-jm4t6cbmce962n5deevi0vbd3kh21kca.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.hiennv.testing" + } + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:773665512065:android:2dcf6a0c607b57001a8b80", + "android_client_info": { + "package_name": "com.hiennv.app" } }, "oauth_client": [ @@ -30,6 +66,13 @@ { "client_id": "773665512065-1shc4ufo4sfdt8v4lvmd0amhj1jqr3cl.apps.googleusercontent.com", "client_type": 3 + }, + { + "client_id": "773665512065-jm4t6cbmce962n5deevi0vbd3kh21kca.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.hiennv.testing" + } } ] } @@ -37,9 +80,9 @@ }, { "client_info": { - "mobilesdk_app_id": "1:773665512065:android:92f4f47a935afbf01a8b80", + "mobilesdk_app_id": "1:773665512065:android:5708a78385fe29101a8b80", "android_client_info": { - "package_name": "com.scripturequest" + "package_name": "com.hiennv.testing" } }, "oauth_client": [ @@ -59,6 +102,13 @@ { "client_id": "773665512065-1shc4ufo4sfdt8v4lvmd0amhj1jqr3cl.apps.googleusercontent.com", "client_type": 3 + }, + { + "client_id": "773665512065-jm4t6cbmce962n5deevi0vbd3kh21kca.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.hiennv.testing" + } } ] } diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index abb79773..f880684a 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 6fd31471..9a3fca8b 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/example/android/app/src/main/kotlin/com/example/flutter_callkit_incoming_example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/flutter_callkit_incoming_example/MainActivity.kt index 8c8b7795..93c5a718 100644 --- a/example/android/app/src/main/kotlin/com/example/flutter_callkit_incoming_example/MainActivity.kt +++ b/example/android/app/src/main/kotlin/com/example/flutter_callkit_incoming_example/MainActivity.kt @@ -1,7 +1,34 @@ package com.example.flutter_callkit_incoming_example +import android.os.Bundle +import com.hiennv.flutter_callkit_incoming.CallkitEventCallback +import com.hiennv.flutter_callkit_incoming.FlutterCallkitIncomingPlugin import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() { +class MainActivity: FlutterActivity(){ + + private var callkitEventCallback = object: CallkitEventCallback{ + override fun onCallEvent(event: CallkitEventCallback.CallEvent, callData: Bundle) { + when (event) { + CallkitEventCallback.CallEvent.ACCEPT -> { + // Do something with answer + } + CallkitEventCallback.CallEvent.DECLINE -> { + // Do something with decline + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + FlutterCallkitIncomingPlugin.registerEventCallback(callkitEventCallback) + } + + override fun onDestroy() { + FlutterCallkitIncomingPlugin.unregisterEventCallback(callkitEventCallback) + super.onDestroy() + } + } diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml index abb79773..f880684a 100644 --- a/example/android/app/src/profile/AndroidManifest.xml +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/example/android/build.gradle b/example/android/build.gradle index 80e59f0d..b42eb3a7 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,17 +1,3 @@ -buildscript { - ext.kotlin_version = '1.8.0' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.gms:google-services:4.3.3' - } -} - allprojects { repositories { google() diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 94adc3a3..598d13fe 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx4G android.useAndroidX=true android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index f1344a02..0b5a8a3d 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sat May 21 00:09:47 ICT 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 44e62bcf..ac5d961c 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,11 +1,26 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.3.2" apply false + id "org.jetbrains.kotlin.android" version "2.2.10" apply false + id "com.google.gms.google-services" version "4.4.3" apply false +} + +include ":app" diff --git a/example/ios/Podfile b/example/ios/Podfile index cfeefd51..a94f7b76 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 4cce6c41..c11048cd 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -421,7 +421,7 @@ DEVELOPMENT_TEAM = U92Q3WLA29; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -553,7 +553,7 @@ DEVELOPMENT_TEAM = U92Q3WLA29; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -579,7 +579,7 @@ DEVELOPMENT_TEAM = U92Q3WLA29; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/example/lib/home_page.dart b/example/lib/home_page.dart index 04c5aa0a..5d6a3133 100644 --- a/example/lib/home_page.dart +++ b/example/lib/home_page.dart @@ -264,6 +264,8 @@ class HomePageState extends State { // TOTO: have check correct current call NavigationService.instance.popUntil(AppRoute.homePage); break; + case Event.actionCallConnected: + break; case Event.actionCallTimeout: // TODO: missed an incoming call break; diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 4cc88f79..4b0b319b 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -4,7 +4,7 @@ description: Demonstrates how to use the flutter_callkit_incoming plugin. publish_to: "none" environment: - sdk: ">=2.18.0 <4.0.0" + sdk: ">=3.4.0 <4.0.0" dependencies: flutter: @@ -13,10 +13,10 @@ dependencies: flutter_callkit_incoming: path: ../ - firebase_core: ^2.12.0 - firebase_messaging: ^14.6.0 - http: ^0.13.4 - uuid: ^3.0.7 + firebase_core: ^3.15.2 + firebase_messaging: ^15.2.0 + http: ^1.5.0 + uuid: ^4.5.1 dev_dependencies: flutter_test: diff --git a/pubspec.yaml b/pubspec.yaml index f23271c6..502f4dc4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,9 +1,12 @@ name: flutter_callkit_incoming description: Flutter Callkit Incoming to show callkit screen in your Flutter app. -version: 2.5.8 +version: 3.0.0 homepage: https://github.com/hiennguyen92/flutter_callkit_incoming repository: https://github.com/hiennguyen92/flutter_callkit_incoming issue_tracker: https://github.com/hiennguyen92/flutter_callkit_incoming/issues +screenshots: + - description: "lock screen" + path: images/image6.jpg false_secrets: - /example/** @@ -16,14 +19,14 @@ environment: dependencies: flutter: sdk: flutter - json_annotation: ^4.8.0 + json_annotation: ^4.9.0 dev_dependencies: - build_runner: ^2.4.4 - flutter_lints: ^2.0.1 + build_runner: ^2.7.0 + flutter_lints: ^6.0.0 flutter_test: sdk: flutter - json_serializable: ^6.6.1 + json_serializable: ^6.10.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -72,3 +75,8 @@ flutter: # # For details regarding fonts in packages, see # https://flutter.dev/custom-fonts/#from-packages + +topics: + - callkit + - incoming + - flutter-callkit-incoming