Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed issue on Android - wasn't able to see incoming call when phone is locked \ app in background. #9

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
6 changes: 3 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:4.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand All @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply plugin: 'org.jetbrains.kotlin.android.extensions'

android {
compileSdkVersion 28
compileSdkVersion 33

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
Expand All @@ -48,4 +48,4 @@ dependencies {
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation 'com.google.code.gson:gson:2.8.6'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}
}
5 changes: 3 additions & 2 deletions android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#Wed Jul 19 13:11:43 EEST 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
zipStoreBase=GRADLE_USER_HOME
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package com.github.alezhka.flutter_incoming_call
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log

class CallBroadcastReceiver: BroadcastReceiver() {

companion object {
private const val TAG = "CallBroadcastReceiver"
private const val ACTION_STARTED = "ACTION_STARTED"
private const val ACTION_DISMISS = "ACTION_DISMISS"
private const val ACTION_ACCEPT = "ACTION_ACCEPT"
Expand Down Expand Up @@ -64,11 +66,13 @@ class CallBroadcastReceiver: BroadcastReceiver() {
callNotification.clearNotification(callData.notificationId)

Utils.backToForeground(context, FlutterIncomingCallPlugin.activity)
Log.d(TAG, "onReceive: EVENT_CALL_ACCEPT")
sendCallEvent(EVENT_CALL_ACCEPT, callData)
}
ACTION_DISMISS -> {
FlutterIncomingCallPlugin.ringtonePlayer?.stop()
callNotification.clearNotification(callData.notificationId)
Log.d(TAG, "onReceive: EVENT_CALL_DECLINE")
sendCallEvent(EVENT_CALL_DECLINE, callData)
}
ACTION_TIMEOUT -> {
Expand All @@ -79,6 +83,7 @@ class CallBroadcastReceiver: BroadcastReceiver() {
}

private fun sendCallEvent(event: String, callData: CallData) {
Log.d(TAG, "sendCallEvent $event; callData: $callData")
val actionData = mapOf(
"uuid" to callData.uuid,
"name" to callData.name,
Expand All @@ -89,5 +94,4 @@ class CallBroadcastReceiver: BroadcastReceiver() {
)
FlutterIncomingCallPlugin.eventHandler.send(event, actionData)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat


Expand All @@ -26,22 +24,28 @@ class CallNotification(private val context: Context) {
} else {
Notification.PRIORITY_MAX
}

private val pendingIntentFlag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) (PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) else PendingIntent.FLAG_UPDATE_CURRENT
}

fun showCallNotification(callData: CallData, config: PluginConfig) {
if(FlutterIncomingCallPlugin.ringtonePlayer == null) {
FlutterIncomingCallPlugin.ringtonePlayer = CallPlayer(context, config)
}
if (config.ringtone) {
if(FlutterIncomingCallPlugin.ringtonePlayer == null) {
FlutterIncomingCallPlugin.ringtonePlayer = CallPlayer(context, config)
}

FlutterIncomingCallPlugin.ringtonePlayer?.let {
if(!it.isPlaying()) it.play(callData)
FlutterIncomingCallPlugin.ringtonePlayer?.let {
if(!it.isPlaying()) it.play(callData)
}
}

val notificationID = callData.notificationId
val declineIntent = CallBroadcastReceiver.declineIntent(context, callData)
val acceptIntent = CallBroadcastReceiver.acceptIntent(context, callData)
val declinePi = PendingIntent.getBroadcast(context, 0, declineIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val acceptPi = PendingIntent.getBroadcast(context, 0, acceptIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val declinePi = PendingIntent.getBroadcast(context, 0, declineIntent, pendingIntentFlag)
val acceptPi = PendingIntent.getBroadcast(context, 0, acceptIntent, pendingIntentFlag)


val soundUri: Uri = Uri.parse("android.resource://${context.packageName}/${R.raw.nosound}")
val notification: Notification = NotificationCompat.Builder(context, config.channelId)
.setAutoCancel(true)
Expand Down Expand Up @@ -113,7 +117,7 @@ class CallNotification(private val context: Context) {

private fun getCallerActivityPendingIntent(notificationID: Int, callData: CallData): PendingIntent? {
val intent = IncomingCallActivity.start(callData)
return PendingIntent.getActivity(context, notificationID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
return PendingIntent.getActivity(context, notificationID, intent, pendingIntentFlag)
}

private fun createNotificationChannel(manager: NotificationManager, soundUri: Uri?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.annotation.NonNull;

import io.flutter.embedding.engine.plugins.FlutterPlugin
Expand All @@ -19,6 +20,7 @@ import io.flutter.plugin.common.MethodChannel.Result
class FlutterIncomingCallPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {

companion object {
val TAG = "FlutterIncomingCallPlug"
var activity: Activity? = null
val eventHandler = EventStreamHandler()
var ringtonePlayer: CallPlayer? = null
Expand Down Expand Up @@ -60,19 +62,20 @@ class FlutterIncomingCallPlugin: FlutterPlugin, MethodCallHandler, ActivityAware
}
"displayIncomingCall" -> {
if(!isConfigured) {
android.util.Log.e(TAG, "onMethodCall displayIncomingCall: Not Configured")
result.error("not_configured", "Not configured", null)
return
}

val callData = FactoryModels.parseCallData(call)
android.util.Log.d(TAG, "onMethodCall displayIncomingCall callData: $callData")

context?.let {
if(Utils.isDeviceScreenLocked(it)) {
activity?.startActivity(IncomingCallActivity.start(callData))
} else {
notificationCall?.showCallNotification(callData, config!!)
}
Log.d(TAG, "onMethodCall displayIncomingCall")
notificationCall?.showCallNotification(callData, config!!)
it.sendBroadcast(CallBroadcastReceiver.startedIntent(it, callData))
} ?: run {
android.util.Log.e(TAG, "onMethodCall displayIncomingCall: context is null")
}

result.success(null)
Expand Down Expand Up @@ -105,7 +108,7 @@ class FlutterIncomingCallPlugin: FlutterPlugin, MethodCallHandler, ActivityAware
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
context = binding.activity
notificationCall = CallNotification(context!!)
notificationCall = CallNotification(binding.activity)
callPrefs = CallPreferences(binding.activity)
}

Expand All @@ -120,7 +123,7 @@ class FlutterIncomingCallPlugin: FlutterPlugin, MethodCallHandler, ActivityAware
activity = binding.activity
context = binding.activity
callPrefs = CallPreferences(binding.activity)
notificationCall = CallNotification(context!!)
notificationCall = CallNotification(binding.activity)
}

override fun onDetachedFromActivity() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class IncomingCallActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) {
setTurnScreenOn(true)
setShowWhenLocked(true)
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
Expand All @@ -60,9 +62,6 @@ class IncomingCallActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) {
setTurnScreenOn(true)
setShowWhenLocked(true)

val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this, null)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,64 +9,13 @@ import android.content.Intent
import android.os.Build
import android.provider.Settings
import android.telephony.TelephonyManager
import android.os.PowerManager


object Utils {

fun isDeviceScreenLocked(context: Context): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
isDeviceLocked(context)
} else {
isPatternSet(context) || isPassOrPinSet(context)
}
}

/**
* @return true if pattern set, false if not (or if an issue when checking)
*/
private fun isPatternSet(context: Context): Boolean {
val cr: ContentResolver = context.contentResolver
return try {
val lockPatternEnable: Int = Settings.Secure.getInt(cr, Settings.Secure.LOCK_PATTERN_ENABLED)
lockPatternEnable == 1
} catch (e: Settings.SettingNotFoundException) {
false
}
}

/**
* @return true if pass or pin set
*/
private fun isPassOrPinSet(context: Context): Boolean {
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager //api 16+
return keyguardManager.isKeyguardSecure
}

/**
* @return true if pass or pin or pattern locks screen
*/
@TargetApi(Build.VERSION_CODES.M)
private fun isDeviceLocked(context: Context): Boolean {
val telMgr = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val simState = telMgr.simState
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager //api 23+
return keyguardManager.isDeviceSecure && simState != TelephonyManager.SIM_STATE_ABSENT
}

/**
* @return Main activity class.
*/
fun getMainActivityClass(context: Context): Class<*>? {
val packageName = context.packageName
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
val className = launchIntent?.component?.className
return try {
className?.let{ Class.forName(it) }
} catch (e: ClassNotFoundException) {
e.printStackTrace()
null
}
}
object Utils {

/**
* Back main activity to foreground.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ data class PluginConfig(
val channelName: String,
val channelDescription: String,
val vibration: Boolean,
val ringtone: Boolean,
val ringtonePath: String?,
val duration: Long
)
Expand Down Expand Up @@ -46,10 +47,11 @@ object FactoryModels {
val channelId = call.argument("channelId") as String? ?: ""
val channelName = call.argument("channelName") as String? ?: ""
val channelDescription = call.argument("channelDescription") as String? ?: ""
val duration = (call.argument("duration") as Int? ?: 30000).toLong()
val vibration = call.argument("vibration") as Boolean? ?: false
val ringtone = call.argument("ringtone") as Boolean? ?: false
val ringtonePath = call.argument("ringtonePath") as String?
return PluginConfig(appName, channelId, channelName, channelDescription, vibration, ringtonePath, duration)
val duration = (call.argument("duration") as Int? ?: 30000).toLong()
return PluginConfig(appName, channelId, channelName, channelDescription, vibration, ringtone, ringtonePath, duration)
}

fun defaultConfig(): PluginConfig {
Expand All @@ -59,7 +61,8 @@ object FactoryModels {
val channelDescription = "Call channel"
val duration = 30000L
val vibration = false
val ringtone = true
val ringtonePath = "default"
return PluginConfig(appName, channelId, channelName, channelDescription, vibration, ringtonePath, duration)
return PluginConfig(appName, channelId, channelName, channelDescription, vibration, ringtone, ringtonePath, duration)
}
}
1 change: 1 addition & 0 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
android:name="com.github.alezhka.flutter_incoming_call.IncomingCallActivity"
android:theme="@style/AppCompatTheme"
android:screenOrientation="portrait"
android:launchMode="singleTop"
android:showOnLockScreen="true">
<intent-filter>
<action android:name="com.github.alezhka.flutter_incoming_call.activity.ACTION_INCOMING_CALL" />
Expand Down
8 changes: 4 additions & 4 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.6.1"
version: "2.8.1"
boolean_selector:
dependency: transitive
description:
Expand All @@ -28,7 +28,7 @@ packages:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.1"
clock:
dependency: transitive
description:
Expand Down Expand Up @@ -94,7 +94,7 @@ packages:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.7.0"
path:
dependency: transitive
description:
Expand Down Expand Up @@ -148,7 +148,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
version: "0.4.2"
typed_data:
dependency: transitive
description:
Expand Down
7 changes: 2 additions & 5 deletions ios/Classes/SwiftFlutterIncomingCallPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -335,11 +335,9 @@ public class SwiftFlutterIncomingCallPlugin: NSObject, FlutterPlugin, CXProvider

if let callData = callsData[uuidString] {
sendEvent(SwiftFlutterIncomingCallPlugin.EVENT_CALL_DECLINE, callData.toMap())
callsAttended[uuidString] = true
}

callsAttended.removeValue(forKey: uuidString)
callsData.removeValue(forKey: uuidString)


action.fulfill()
}

Expand Down Expand Up @@ -390,5 +388,4 @@ public class SwiftFlutterIncomingCallPlugin: NSObject, FlutterPlugin, CXProvider
let audiosessionData = ToggleAudiosessionData(false)
sendEvent(SwiftFlutterIncomingCallPlugin.EVENT_TOGGLE_AUDIOSESSION, audiosessionData.toMap())
}

}
Loading