feat: system alert window permission
This commit is contained in:
parent
4f2d2d5d2b
commit
20228f6389
@ -13,6 +13,7 @@
|
|||||||
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
|
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
|
||||||
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
|
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="Icing Dialer"
|
android:label="Icing Dialer"
|
||||||
@ -22,7 +23,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".activities.MainActivity"
|
android:name=".activities.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTask"
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
@ -57,10 +57,12 @@ class MainActivity : FlutterActivity() {
|
|||||||
"makeGsmCall" -> {
|
"makeGsmCall" -> {
|
||||||
val phoneNumber = call.argument<String>("phoneNumber")
|
val phoneNumber = call.argument<String>("phoneNumber")
|
||||||
if (phoneNumber != null) {
|
if (phoneNumber != null) {
|
||||||
val success = CallService.makeGsmCall(this, phoneNumber)
|
try {
|
||||||
if (success) {
|
val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$phoneNumber"))
|
||||||
|
startActivity(intent)
|
||||||
result.success(mapOf("status" to "calling", "phoneNumber" to phoneNumber))
|
result.success(mapOf("status" to "calling", "phoneNumber" to phoneNumber))
|
||||||
} else {
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to make GSM call: ${e.message}")
|
||||||
result.error("CALL_FAILED", "Failed to initiate call", null)
|
result.error("CALL_FAILED", "Failed to initiate call", null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -101,7 +103,7 @@ class MainActivity : FlutterActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, KEYSTORE_CHANNEL)
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, KEYSTORE_CHANNEL)
|
||||||
.setMethodCallHandler { call, result ->
|
.setMethodCallHandler { call, result ->
|
||||||
KeystoreHelper(call, result).handleMethodCall()
|
KeystoreHelper(call, result).handleMethodCall()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,10 +214,13 @@ class MainActivity : FlutterActivity() {
|
|||||||
intent?.let {
|
intent?.let {
|
||||||
if (it.getBooleanExtra("isIncomingCall", false)) {
|
if (it.getBooleanExtra("isIncomingCall", false)) {
|
||||||
val phoneNumber = it.getStringExtra("phoneNumber")
|
val phoneNumber = it.getStringExtra("phoneNumber")
|
||||||
Log.d(TAG, "Received incoming call intent for $phoneNumber")
|
val showScreen = it.getBooleanExtra("showIncomingCallScreen", false)
|
||||||
MyInCallService.channel?.invokeMethod("incomingCallFromNotification", mapOf(
|
Log.d(TAG, "Received incoming call intent for $phoneNumber, showScreen=$showScreen")
|
||||||
"phoneNumber" to phoneNumber
|
if (showScreen) {
|
||||||
))
|
MyInCallService.channel?.invokeMethod("incomingCallFromNotification", mapOf(
|
||||||
|
"phoneNumber" to phoneNumber
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ class MyInCallService : InCallService() {
|
|||||||
"state" to stateStr
|
"state" to stateStr
|
||||||
))
|
))
|
||||||
if (state == Call.STATE_RINGING) {
|
if (state == Call.STATE_RINGING) {
|
||||||
showIncomingCallNotification(call.details.handle.toString().replace("tel:", ""))
|
showIncomingCallScreen(call.details.handle.toString().replace("tel:", ""))
|
||||||
} else if (state == Call.STATE_DISCONNECTED || state == Call.STATE_DISCONNECTING) {
|
} else if (state == Call.STATE_DISCONNECTED || state == Call.STATE_DISCONNECTING) {
|
||||||
Log.d(TAG, "Call ended: ${call.details.handle}")
|
Log.d(TAG, "Call ended: ${call.details.handle}")
|
||||||
channel?.invokeMethod("callEnded", mapOf("callId" to call.details.handle.toString()))
|
channel?.invokeMethod("callEnded", mapOf("callId" to call.details.handle.toString()))
|
||||||
@ -64,7 +64,7 @@ class MyInCallService : InCallService() {
|
|||||||
"state" to stateStr
|
"state" to stateStr
|
||||||
))
|
))
|
||||||
if (stateStr == "ringing") {
|
if (stateStr == "ringing") {
|
||||||
showIncomingCallNotification(call.details.handle.toString().replace("tel:", ""))
|
showIncomingCallScreen(call.details.handle.toString().replace("tel:", ""))
|
||||||
}
|
}
|
||||||
call.registerCallback(callCallback)
|
call.registerCallback(callCallback)
|
||||||
}
|
}
|
||||||
@ -84,10 +84,19 @@ class MyInCallService : InCallService() {
|
|||||||
channel?.invokeMethod("audioStateChanged", mapOf("route" to state.route))
|
channel?.invokeMethod("audioStateChanged", mapOf("route" to state.route))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showIncomingCallNotification(phoneNumber: String) {
|
private fun showIncomingCallScreen(phoneNumber: String) {
|
||||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
// Launch MainActivity with intent to show IncomingCallPage
|
||||||
|
val intent = Intent(this, MainActivity::class.java).apply {
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
|
putExtra("phoneNumber", phoneNumber)
|
||||||
|
putExtra("isIncomingCall", true)
|
||||||
|
putExtra("showIncomingCallScreen", true) // New flag to signal immediate UI
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
Log.d(TAG, "Launched MainActivity to show incoming call screen for $phoneNumber")
|
||||||
|
|
||||||
// Create notification channel (Android 8.0+)
|
// Optional: Keep notification as a fallback
|
||||||
|
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val channel = NotificationChannel(
|
val channel = NotificationChannel(
|
||||||
NOTIFICATION_CHANNEL_ID,
|
NOTIFICATION_CHANNEL_ID,
|
||||||
@ -101,20 +110,11 @@ class MyInCallService : InCallService() {
|
|||||||
notificationManager.createNotificationChannel(channel)
|
notificationManager.createNotificationChannel(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intent to open MainActivity with phone number
|
|
||||||
val intent = Intent(this, MainActivity::class.java).apply {
|
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
||||||
putExtra("phoneNumber", phoneNumber)
|
|
||||||
putExtra("isIncomingCall", true)
|
|
||||||
}
|
|
||||||
val pendingIntent = PendingIntent.getActivity(
|
val pendingIntent = PendingIntent.getActivity(
|
||||||
this,
|
this, 0, intent,
|
||||||
0,
|
|
||||||
intent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0)
|
PendingIntent.FLAG_UPDATE_CURRENT or (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Build notification
|
|
||||||
val notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
val notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
||||||
.setSmallIcon(android.R.drawable.ic_dialog_alert) // Replace with your app icon
|
.setSmallIcon(android.R.drawable.ic_dialog_alert) // Replace with your app icon
|
||||||
.setContentTitle("Incoming Call")
|
.setContentTitle("Incoming Call")
|
||||||
@ -122,7 +122,7 @@ class MyInCallService : InCallService() {
|
|||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setOngoing(true) // Keep visible until call ends
|
.setOngoing(true)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||||
|
@ -117,7 +117,7 @@ class CallService {
|
|||||||
print('CallService: Making GSM call to $phoneNumber');
|
print('CallService: Making GSM call to $phoneNumber');
|
||||||
final result = await _channel.invokeMethod('makeGsmCall', {"phoneNumber": phoneNumber});
|
final result = await _channel.invokeMethod('makeGsmCall', {"phoneNumber": phoneNumber});
|
||||||
print('CallService: makeGsmCall result: $result');
|
print('CallService: makeGsmCall result: $result');
|
||||||
final resultMap = Map<String, dynamic>.from(result as Map); // Safe cast
|
final resultMap = Map<String, dynamic>.from(result as Map);
|
||||||
if (resultMap["status"] != "calling") {
|
if (resultMap["status"] != "calling") {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text("Failed to initiate call")),
|
SnackBar(content: Text("Failed to initiate call")),
|
||||||
@ -138,7 +138,7 @@ class CallService {
|
|||||||
print('CallService: Hanging up call');
|
print('CallService: Hanging up call');
|
||||||
final result = await _channel.invokeMethod('hangUpCall');
|
final result = await _channel.invokeMethod('hangUpCall');
|
||||||
print('CallService: hangUpCall result: $result');
|
print('CallService: hangUpCall result: $result');
|
||||||
final resultMap = Map<String, dynamic>.from(result as Map); // Safe cast
|
final resultMap = Map<String, dynamic>.from(result as Map);
|
||||||
if (resultMap["status"] != "ended") {
|
if (resultMap["status"] != "ended") {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text("Failed to end call")),
|
SnackBar(content: Text("Failed to end call")),
|
||||||
|
Loading…
Reference in New Issue
Block a user