From 4b5aa9798a77eaba1c1726c314c5de74cd37ad6d Mon Sep 17 00:00:00 2001 From: Hien Nguyen Date: Tue, 5 Aug 2025 20:32:48 +0700 Subject: [PATCH 01/11] update yaml --- pubspec.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pubspec.yaml b/pubspec.yaml index f23271c6..68f75134 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,6 +4,9 @@ version: 2.5.8 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/** @@ -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 From e00973477c705954feaa697b4d12fde20a369079 Mon Sep 17 00:00:00 2001 From: Joshua OConnor Date: Thu, 7 Aug 2025 10:23:54 -0700 Subject: [PATCH 02/11] set up call decline native callback logic --- .../CallkitDeclineCallback.kt | 16 +++++++++++ .../CallkitIncomingActivity.kt | 1 + .../CallkitIncomingBroadcastReceiver.kt | 3 +++ .../FlutterCallkitIncomingPlugin.kt | 27 +++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitDeclineCallback.kt diff --git a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitDeclineCallback.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitDeclineCallback.kt new file mode 100644 index 00000000..70d1f627 --- /dev/null +++ b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitDeclineCallback.kt @@ -0,0 +1,16 @@ +package com.hiennv.flutter_callkit_incoming + +import android.os.Bundle + +/** + * Callback interface for handling call decline events natively. + * This allows other plugins or services to receive decline events + * even when the Flutter engine is terminated. + */ +interface CallkitDeclineCallback { + /** + * Called when a call is declined. + * @param callData Bundle containing call information (id, nameCaller, etc.) + */ + fun onCallDeclined(callData: Bundle) +} \ 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 8a274c12..5bb10bed 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 @@ -330,6 +330,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..1989b214 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 @@ -131,6 +131,9 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_DECLINE}" -> { try { + Log.d(TAG, "[CALLKIT] 📱 ACTION_CALL_DECLINE") + // Notify native decline callbacks + FlutterCallkitIncomingPlugin.notifyDeclineCallbacks(data) // clear notification callkitNotificationManager?.clearIncomingNotification(data, false) sendEventFlutter(CallkitConstants.ACTION_CALL_DECLINE, data) 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 95d4d2e2..79a59d70 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 declineCallbacks = 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 decline events natively. + * This allows other plugins/services to handle decline events + * even when Flutter engine is terminated. + */ + fun registerDeclineCallback(callback: CallkitDeclineCallback) { + declineCallbacks.add(WeakReference(callback)) + } + + /** + * Unregister a decline callback. + */ + fun unregisterDeclineCallback(callback: CallkitDeclineCallback) { + declineCallbacks.removeAll { it.get() == callback || it.get() == null } + } + + /** + * Notify all registered decline callbacks. + * Called internally when a call is declined. + */ + internal fun notifyDeclineCallbacks(callData: android.os.Bundle) { + declineCallbacks.reapCollection().forEach { callbackRef -> + callbackRef.get()?.onCallDeclined(callData) + } + } + fun sharePluginWithRegister(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { initSharedInstance( From 441af8361780a03d815f29830dae6bc64e7b5a86 Mon Sep 17 00:00:00 2001 From: Joshua OConnor Date: Thu, 7 Aug 2025 10:40:30 -0700 Subject: [PATCH 03/11] set up accept callback for native android --- .../CallkitAcceptCallback.kt | 16 +++++++++++ .../CallkitIncomingActivity.kt | 1 + .../CallkitIncomingBroadcastReceiver.kt | 2 ++ .../FlutterCallkitIncomingPlugin.kt | 27 +++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitAcceptCallback.kt diff --git a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitAcceptCallback.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitAcceptCallback.kt new file mode 100644 index 00000000..d71d1ae2 --- /dev/null +++ b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitAcceptCallback.kt @@ -0,0 +1,16 @@ +package com.hiennv.flutter_callkit_incoming + +import android.os.Bundle + +/** + * Callback interface for handling call accept events natively. + * This allows other plugins or services to receive accept events + * even when the Flutter engine is terminated. + */ +interface CallkitAcceptCallback { + /** + * Called when a call is accepted. + * @param callData Bundle containing call information (id, nameCaller, etc.) + */ + fun onCallAccepted(callData: Bundle) +} \ 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 5bb10bed..23386cf0 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 @@ -304,6 +304,7 @@ class CallkitIncomingActivity : Activity() { private fun onAcceptClick() { + Log.d("CallkitIncomingActivity", "[CALLKIT] 📱 onAcceptClick") val data = intent.extras?.getBundle(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA) 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 1989b214..eb9dbc78 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 @@ -116,6 +116,8 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_ACCEPT}" -> { try { + Log.d(TAG, "[CALLKIT] 📱 ACTION_CALL_ACCEPT") + FlutterCallkitIncomingPlugin.notifyAcceptCallbacks(data) // start service and show ongoing call when call is accepted CallkitNotificationService.startServiceWithAction( context, 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 79a59d70..358d9890 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 @@ -42,6 +42,7 @@ class FlutterCallkitIncomingPlugin : FlutterPlugin, MethodCallHandler, ActivityA private val eventChannels = mutableMapOf() private val eventHandlers = mutableListOf>() private val declineCallbacks = mutableListOf>() + private val acceptCallbacks = mutableListOf>() fun sendEvent(event: String, body: Map) { eventHandlers.reapCollection().forEach { @@ -81,6 +82,32 @@ class FlutterCallkitIncomingPlugin : FlutterPlugin, MethodCallHandler, ActivityA } } + /** + * Register a callback to receive call accept events natively. + * This allows other plugins/services to handle accept events + * even when Flutter engine is terminated. + */ + fun registerAcceptCallback(callback: CallkitAcceptCallback) { + acceptCallbacks.add(WeakReference(callback)) + } + + /** + * Unregister an accept callback. + */ + fun unregisterAcceptCallback(callback: CallkitAcceptCallback) { + acceptCallbacks.removeAll { it.get() == callback || it.get() == null } + } + + /** + * Notify all registered accept callbacks. + * Called internally when a call is accepted. + */ + internal fun notifyAcceptCallbacks(callData: android.os.Bundle) { + acceptCallbacks.reapCollection().forEach { callbackRef -> + callbackRef.get()?.onCallAccepted(callData) + } + } + fun sharePluginWithRegister(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { initSharedInstance( From af2b73aaca1e1a0cf4727a85eb786688ace43a44 Mon Sep 17 00:00:00 2001 From: Joshua OConnor Date: Thu, 7 Aug 2025 10:55:29 -0700 Subject: [PATCH 04/11] use unified callback --- .../CallkitAcceptCallback.kt | 16 ------ .../CallkitDeclineCallback.kt | 16 ------ .../CallkitEventCallback.kt | 26 +++++++++ .../CallkitIncomingBroadcastReceiver.kt | 4 +- .../FlutterCallkitIncomingPlugin.kt | 53 +++++-------------- 5 files changed, 41 insertions(+), 74 deletions(-) delete mode 100644 android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitAcceptCallback.kt delete mode 100644 android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitDeclineCallback.kt create mode 100644 android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitEventCallback.kt diff --git a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitAcceptCallback.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitAcceptCallback.kt deleted file mode 100644 index d71d1ae2..00000000 --- a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitAcceptCallback.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.hiennv.flutter_callkit_incoming - -import android.os.Bundle - -/** - * Callback interface for handling call accept events natively. - * This allows other plugins or services to receive accept events - * even when the Flutter engine is terminated. - */ -interface CallkitAcceptCallback { - /** - * Called when a call is accepted. - * @param callData Bundle containing call information (id, nameCaller, etc.) - */ - fun onCallAccepted(callData: Bundle) -} \ No newline at end of file diff --git a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitDeclineCallback.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitDeclineCallback.kt deleted file mode 100644 index 70d1f627..00000000 --- a/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitDeclineCallback.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.hiennv.flutter_callkit_incoming - -import android.os.Bundle - -/** - * Callback interface for handling call decline events natively. - * This allows other plugins or services to receive decline events - * even when the Flutter engine is terminated. - */ -interface CallkitDeclineCallback { - /** - * Called when a call is declined. - * @param callData Bundle containing call information (id, nameCaller, etc.) - */ - fun onCallDeclined(callData: Bundle) -} \ No newline at end of file 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/CallkitIncomingBroadcastReceiver.kt b/android/src/main/kotlin/com/hiennv/flutter_callkit_incoming/CallkitIncomingBroadcastReceiver.kt index eb9dbc78..b7174437 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 @@ -117,7 +117,7 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_ACCEPT}" -> { try { Log.d(TAG, "[CALLKIT] 📱 ACTION_CALL_ACCEPT") - FlutterCallkitIncomingPlugin.notifyAcceptCallbacks(data) + FlutterCallkitIncomingPlugin.notifyEventCallbacks(CallkitEventCallback.CallEvent.ACCEPT, data) // start service and show ongoing call when call is accepted CallkitNotificationService.startServiceWithAction( context, @@ -135,7 +135,7 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { try { Log.d(TAG, "[CALLKIT] 📱 ACTION_CALL_DECLINE") // Notify native decline callbacks - FlutterCallkitIncomingPlugin.notifyDeclineCallbacks(data) + FlutterCallkitIncomingPlugin.notifyEventCallbacks(CallkitEventCallback.CallEvent.DECLINE, data) // clear notification callkitNotificationManager?.clearIncomingNotification(data, false) sendEventFlutter(CallkitConstants.ACTION_CALL_DECLINE, data) 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 358d9890..ad28cffc 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,8 +41,7 @@ class FlutterCallkitIncomingPlugin : FlutterPlugin, MethodCallHandler, ActivityA private val methodChannels = mutableMapOf() private val eventChannels = mutableMapOf() private val eventHandlers = mutableListOf>() - private val declineCallbacks = mutableListOf>() - private val acceptCallbacks = mutableListOf>() + private val eventCallbacks = mutableListOf>() fun sendEvent(event: String, body: Map) { eventHandlers.reapCollection().forEach { @@ -57,54 +56,28 @@ class FlutterCallkitIncomingPlugin : FlutterPlugin, MethodCallHandler, ActivityA } /** - * Register a callback to receive call decline events natively. - * This allows other plugins/services to handle decline events + * 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 registerDeclineCallback(callback: CallkitDeclineCallback) { - declineCallbacks.add(WeakReference(callback)) + fun registerEventCallback(callback: CallkitEventCallback) { + eventCallbacks.add(WeakReference(callback)) } /** - * Unregister a decline callback. + * Unregister an event callback. */ - fun unregisterDeclineCallback(callback: CallkitDeclineCallback) { - declineCallbacks.removeAll { it.get() == callback || it.get() == null } + fun unregisterEventCallback(callback: CallkitEventCallback) { + eventCallbacks.removeAll { it.get() == callback || it.get() == null } } /** - * Notify all registered decline callbacks. - * Called internally when a call is declined. + * Notify all registered event callbacks. + * Called internally when a call event occurs. */ - internal fun notifyDeclineCallbacks(callData: android.os.Bundle) { - declineCallbacks.reapCollection().forEach { callbackRef -> - callbackRef.get()?.onCallDeclined(callData) - } - } - - /** - * Register a callback to receive call accept events natively. - * This allows other plugins/services to handle accept events - * even when Flutter engine is terminated. - */ - fun registerAcceptCallback(callback: CallkitAcceptCallback) { - acceptCallbacks.add(WeakReference(callback)) - } - - /** - * Unregister an accept callback. - */ - fun unregisterAcceptCallback(callback: CallkitAcceptCallback) { - acceptCallbacks.removeAll { it.get() == callback || it.get() == null } - } - - /** - * Notify all registered accept callbacks. - * Called internally when a call is accepted. - */ - internal fun notifyAcceptCallbacks(callData: android.os.Bundle) { - acceptCallbacks.reapCollection().forEach { callbackRef -> - callbackRef.get()?.onCallAccepted(callData) + internal fun notifyEventCallbacks(event: CallkitEventCallback.CallEvent, callData: android.os.Bundle) { + eventCallbacks.reapCollection().forEach { callbackRef -> + callbackRef.get()?.onCallEvent(event, callData) } } From 96cdca25142b470562ccbae93eefdbcf045cddf5 Mon Sep 17 00:00:00 2001 From: Josh O'Connor Date: Wed, 13 Aug 2025 10:13:54 -0700 Subject: [PATCH 05/11] remove logs --- .../flutter_callkit_incoming/CallkitIncomingActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 23386cf0..98c1ddda 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 @@ -304,7 +304,7 @@ class CallkitIncomingActivity : Activity() { private fun onAcceptClick() { - Log.d("CallkitIncomingActivity", "[CALLKIT] 📱 onAcceptClick") + // Log.d("CallkitIncomingActivity", "[CALLKIT] 📱 onAcceptClick") val data = intent.extras?.getBundle(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA) @@ -331,7 +331,7 @@ class CallkitIncomingActivity : Activity() { } private fun onDeclineClick() { - Log.d("CallkitIncomingActivity", "[CALLKIT] 📱 onDeclineClick") + // Log.d("CallkitIncomingActivity", "[CALLKIT] 📱 onDeclineClick") val data = intent.extras?.getBundle(CallkitConstants.EXTRA_CALLKIT_INCOMING_DATA) val intent = From 153259a0e39be22b8a5cab2ad56d2b770a7a8200 Mon Sep 17 00:00:00 2001 From: Josh O'Connor Date: Wed, 13 Aug 2025 10:14:57 -0700 Subject: [PATCH 06/11] remove logs --- .../CallkitIncomingBroadcastReceiver.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 b7174437..1cf5c9b8 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 @@ -116,7 +116,7 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_ACCEPT}" -> { try { - Log.d(TAG, "[CALLKIT] 📱 ACTION_CALL_ACCEPT") + // 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( @@ -133,7 +133,7 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_DECLINE}" -> { try { - Log.d(TAG, "[CALLKIT] 📱 ACTION_CALL_DECLINE") + // Log.d(TAG, "[CALLKIT] 📱 ACTION_CALL_DECLINE") // Notify native decline callbacks FlutterCallkitIncomingPlugin.notifyEventCallbacks(CallkitEventCallback.CallEvent.DECLINE, data) // clear notification @@ -256,4 +256,4 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { ) FlutterCallkitIncomingPlugin.sendEvent(event, forwardData) } -} \ No newline at end of file +} From 4cd8993bb797b42f8e5e72cd2f414c34816ffc0d Mon Sep 17 00:00:00 2001 From: AAkira Date: Mon, 18 Aug 2025 14:49:21 +0900 Subject: [PATCH 07/11] Update libraries. --- .gitignore | 5 +- android/.gitignore | 2 + android/build.gradle | 16 ++---- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/app/build.gradle | 56 +++++++------------ example/android/app/google-services.json | 2 +- .../android/app/src/debug/AndroidManifest.xml | 3 +- .../android/app/src/main/AndroidManifest.xml | 3 +- .../app/src/profile/AndroidManifest.xml | 3 +- example/android/build.gradle | 14 ----- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/settings.gradle | 31 +++++++--- example/lib/home_page.dart | 2 + example/pubspec.yaml | 10 ++-- pubspec.yaml | 8 +-- 15 files changed, 70 insertions(+), 89 deletions(-) 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/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..47ea226d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -9,10 +9,7 @@ buildscript { } 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 'com.android.tools.build:gradle:8.3.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -45,20 +42,15 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { - jvmTarget = "17" + jvmTarget = "1.8" } } -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } -} dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 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/example/android/app/build.gradle b/example/android/app/build.gradle index f4f4a74c..56a60e25 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 21 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..d7d2e8bb 100644 --- a/example/android/app/google-services.json +++ b/example/android/app/google-services.json @@ -10,7 +10,7 @@ "client_info": { "mobilesdk_app_id": "1:773665512065:android:5708a78385fe29101a8b80", "android_client_info": { - "package_name": "com.hiennv.testing" + "package_name": "com.example.flutter_callkit_incoming_example" } }, "oauth_client": [ 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/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/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/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..e06424be 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,14 +16,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 From 86cb7ae34d84d969822236bfce56756618ca1f0d Mon Sep 17 00:00:00 2001 From: LohZJ Date: Tue, 19 Aug 2025 10:44:12 +0800 Subject: [PATCH 08/11] Refactor(android): Improve plugin lifecycle and format code. Refactor the Android plugin to handle lifecycle events more robustly. The notification and sound managers are now managed to prevent them from being destroyed prematurely when a foreground service is detached. This change ensures that the plugin remains active and can receive calls even when the main application is in the background. --- .../CallkitIncomingBroadcastReceiver.kt | 20 +++++++++++-------- .../CallkitNotificationService.kt | 16 ++++++++------- .../FlutterCallkitIncomingPlugin.kt | 19 ++++++++++++++---- 3 files changed, 36 insertions(+), 19 deletions(-) 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..b60736dc 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) { @@ -132,7 +135,7 @@ class CallkitIncomingBroadcastReceiver : BroadcastReceiver() { "${context.packageName}.${CallkitConstants.ACTION_CALL_DECLINE}" -> { try { // 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 +146,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 +158,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 +171,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 +180,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) 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..b8c2dc4e 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 @@ -68,6 +68,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 +357,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) { From f5e525c2db9fe7ddd68ea32515479483c1be157d Mon Sep 17 00:00:00 2001 From: Hien Nguyen Date: Fri, 5 Sep 2025 17:51:51 +0700 Subject: [PATCH 09/11] Plugin DSL --- android/build.gradle | 54 +++++------------ .../CallkitNotificationManager.kt | 9 +++ example/android/app/build.gradle | 2 +- example/android/app/google-services.json | 58 +++++++++++++++++-- example/android/gradle.properties | 2 +- 5 files changed, 81 insertions(+), 44 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 47ea226d..2d37e44f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,49 +1,28 @@ -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:8.3.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' } -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -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_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } kotlinOptions { @@ -51,9 +30,8 @@ android { } } - 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' @@ -62,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/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/example/android/app/build.gradle b/example/android/app/build.gradle index 56a60e25..0796f4c2 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -15,7 +15,7 @@ android { defaultConfig { applicationId "com.example.flutter_callkit_incoming_example" - minSdk 21 + minSdk flutter.minSdkVersion targetSdk 35 versionCode flutter.versionCode versionName flutter.versionName diff --git a/example/android/app/google-services.json b/example/android/app/google-services.json index d7d2e8bb..1b1c8981 100644 --- a/example/android/app/google-services.json +++ b/example/android/app/google-services.json @@ -3,12 +3,12 @@ "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.example.flutter_callkit_incoming_example" } @@ -30,6 +30,49 @@ { "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": [ + { + "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" + } } ] } @@ -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/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 From 2509fc22fd84eca3310b84e4f597296da2768659 Mon Sep 17 00:00:00 2001 From: Hien Nguyen Date: Fri, 5 Sep 2025 18:44:07 +0700 Subject: [PATCH 10/11] 3.0.0 --- .github/workflows/main.yml | 2 +- CHANGELOG.md | 6 ++++ README.md | 34 +++++++++++++++++++ .../MainActivity.kt | 29 +++++++++++++++- pubspec.yaml | 2 +- 5 files changed, 70 insertions(+), 3 deletions(-) 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/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/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/pubspec.yaml b/pubspec.yaml index 3f4c37d9..502f4dc4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ 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 From 18d98ec77d561ddce631fd40e491c5514a5b4b02 Mon Sep 17 00:00:00 2001 From: Hien Nguyen Date: Fri, 5 Sep 2025 23:27:17 +0700 Subject: [PATCH 11/11] Update Target deployment --- example/ios/Podfile | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) 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",