diff --git a/plugin.xml b/plugin.xml index 377da74..495ac31 100644 --- a/plugin.xml +++ b/plugin.xml @@ -24,6 +24,11 @@ + + + + + @@ -37,7 +42,9 @@ /> + android:exported="true" + android:foregroundServiceType="phoneCall" + > diff --git a/src/android/CallActionReceiver.java b/src/android/CallActionReceiver.java index 318b673..870eab1 100644 --- a/src/android/CallActionReceiver.java +++ b/src/android/CallActionReceiver.java @@ -23,21 +23,11 @@ public void onReceive(Context context, Intent intent) { conn.onReject(); } else if (action.equals("answerCall")) { conn.onAnswer(); - } else if (action.equals("hangUpCall")) { - conn.onDisconnect(); } else { throw new RuntimeException("Invalid action: " + action); } } else { Log.d(TAG, "Exiting, connection no longer exists. pushMessagePayload: " + pushMessagePayload); - if (intent.hasExtra("notificationID")) { - // For safety ensure any associated notification is closed (avoids bad UX if the normal logic - // that closes the notification when the connection is disconnected fails/crashes/etc.) - int notificationID = intent.getIntExtra("notificationID", 0); - Log.e(TAG, "Closing orphaned notification, ID: " + notificationID); - NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - nm.cancel(notificationID); - } } } } diff --git a/src/android/CallNotification.java b/src/android/CallNotification.java index aa5779a..21676be 100644 --- a/src/android/CallNotification.java +++ b/src/android/CallNotification.java @@ -1,5 +1,6 @@ package com.dmarc.cordovacall; +import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; @@ -10,9 +11,6 @@ import android.media.RingtoneManager; import android.net.Uri; import android.os.Build; -import android.os.Handler; -import android.telecom.Connection; -import android.telecom.DisconnectCause; import android.util.Log; import androidx.core.app.NotificationCompat; @@ -20,7 +18,6 @@ import org.json.JSONException; import org.json.JSONObject; -import java.net.URI; import java.util.Random; @@ -31,8 +28,6 @@ public class CallNotification { private Integer notificationID; private Context context; private NotificationManager notificationManager; - private Runnable timeoutRunnable; - private Handler timeoutHandler = new Handler(); private static final String NOTIFICATION_CHANNEL_ID = "incoming_calls"; @@ -50,11 +45,11 @@ public CallNotification(String pushMessagePayload, Context context) { this.createNotificationChannel(); } - public void show(Style style) { + public Notification build(Style style) { this.show(style, NotificationCompat.PRIORITY_HIGH); } - public void show(Style style, int priority) { + public Notification build(Style style, int priority) { int timeout = 30000; // NOTE: "Notifications should only launch a BroadcastReceiver from notification actions" @@ -171,31 +166,13 @@ public void show(Style style, int priority) { } Log.d(TAG, "launching call notification via android NotificationManager notify()..."); - notificationManager.notify(this.notificationID, builder.build()); + Notification notification = builder.build(); - if (this.timeoutRunnable != null) { - this.timeoutHandler.removeCallbacks(this.timeoutRunnable); - } - - this.timeoutRunnable = new Runnable() { - @Override - public void run() { - Log.d(TAG, "call missed, closing connection and call notification,"); - Connection conn = MyConnectionService.getConnectionByPayload(pushMessagePayload); - conn.setDisconnected(new DisconnectCause(DisconnectCause.MISSED)); - close(); - } - }; - - this.timeoutHandler.postDelayed(timeoutRunnable, timeout); + return notification; } - public void close() { - Log.d(TAG, "closing call notification"); - this.notificationManager.cancel(this.notificationID); - if (this.timeoutRunnable != null) { - this.timeoutHandler.removeCallbacks(this.timeoutRunnable); - } + public int getNotificationID() { + return this.notificationID; } private Uri getRingtoneURI() { diff --git a/src/android/MyConnectionService.java b/src/android/MyConnectionService.java index 5b639b7..5a52a8d 100644 --- a/src/android/MyConnectionService.java +++ b/src/android/MyConnectionService.java @@ -1,14 +1,18 @@ package com.dmarc.cordovacall; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL; + import org.apache.cordova.PluginResult; import org.json.JSONException; import org.json.JSONObject; +import android.app.Notification; import android.content.Intent; import android.content.Context; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.graphics.drawable.Icon; +import android.os.Build; import android.os.Bundle; import android.telecom.Connection; import android.telecom.ConnectionRequest; @@ -221,7 +225,9 @@ public void run() { CordovaCall.registerMainActivityStateChangeListener(this.mainActivityChangeListener); this.callNotification = new CallNotification(payloadString, context); - this.callNotification.show(CallNotification.Style.INCOMING_CALL); + Notification notification = this.callNotification.build(CallNotification.Style.INCOMING_CALL); + + startForeground(this.callNotification.getNotificationID(), notification, FOREGROUND_SERVICE_TYPE_PHONE_CALL); } private void updateNotification() { @@ -234,14 +240,10 @@ private void updateNotification() { int priority = (style == CallNotification.Style.ONGOING_CALL && isInForeground) ? NotificationCompat.PRIORITY_MIN : NotificationCompat.PRIORITY_HIGH; Log.d(TAG, "updating notification style: " + style + " priority: " + priority); - this.callNotification.show(style, priority); - } + Notification notification = this.callNotification.build(style, priority); - private void closeNotification() { - if (this.callNotification != null) { - this.callNotification.close(); - this.callNotification = null; - } + // Call startForeground again which allows for updating the associated notification of the same ID + startForeground(this.callNotification.getNotificationID(), notification, FOREGROUND_SERVICE_TYPE_PHONE_CALL); } @Override @@ -251,7 +253,6 @@ public void onAnswer() { activeConnectionUUID = callUUID; showWebApp("answerCall", payloadString); - CordovaCall.emitEvent("answer", new PluginResult(PluginResult.Status.OK, payloadString)); } @@ -288,7 +289,7 @@ public void onStateChanged(int state) { updateNotification(); // Will update it to an "ongoing" CallStyle notification break; case Connection.STATE_DISCONNECTED: - this.closeNotification(); + stopForeground(STOP_FOREGROUND_REMOVE); // Cancels the associated notification connectionMap.remove(callUUID); if (activeConnectionUUID != null && activeConnectionUUID.equals(callUUID)) { activeConnectionUUID = null;