2626import java .util .Map ;
2727
2828/**
29+ * <h3>General Operation</h3>
2930 * <p>RCDevice represents an abstraction of a communications device able to make and receive calls, send and receive messages etc. Remember that
3031 * in order to be notified of Restcomm Client events you need to set a listener to RCDevice and implement the applicable methods, and also 'register'
3132 * to the applicable intents by calling RCDevice.setPendingIntents() and provide one intent for whichever activity will be receiving calls and another
5152 * <li>The actual message text via string extra named RCDevice.INCOMING_MESSAGE_TEXT, like: <i>intent.getStringExtra(RCDevice.INCOMING_MESSAGE_TEXT)</i></li>
5253 * </ol>
5354 * </p>
55+ * <p>
56+ * <h3>Taking advantage of Android Notifications</h3>
57+ * The Restcomm SDK comes integrated with Android Notifications. This means that while your App is in the
58+ * background all events from incoming calls/messages are conveyed via (Heads up) Notifications. Depending on the type of event the designated Intent is used
59+ * to deliver the event. For example if an incoming text message arrives and your App is in the background, then the message will be shown as a notification and if
60+ * the user taps on it, then whichever intent you passed as RCDevice.ParameterKeys.INTENT_INCOMING_MESSAGE in RCDevice.initialize() will be sent to your App with
61+ * the extras already discussed previously in normal (i.e. foreground) operation. Likewise, if an incoming call arrives and your App is in the background, then
62+ * the call will be shown as a notification and the user will be able to:
63+ * <ol>
64+ * <li>Decline it by swiping the notification left, right, or tapping on the hang up Action Button. Then, no intent is sent to the App, as the user most likely doesn't want it opened at this point.</li>
65+ * <li>Accept it as audio & video call by tapping on the video Action Button. In this case, whichever intent you passed as RCDevice.ParameterKeys.INTENT_INCOMING_MESSAGE in RCDevice.initialize() will be sent to your App with
66+ * the extras already discussed previously in normal (i.e. foreground) operation. The only difference is the action, which will be ACTION_INCOMING_CALL_ANSWER_VIDEO. So from App perspective when you get this action
67+ * you should answer the call as audio & video</li>
68+ * <li>Accept it as audio-only call by tapping on the audio Action Button. Same as previously with the only difference that the action will be ACTION_INCOMING_CALL_ANSWER_AUDIO</li>
69+ * </ol>
70+ * Keep in mind that once the call is answered then you get a foreground (i.e. sticky) notification in the Notification Drawer for the duration of the call via which you can either mute audio or hang up the call
71+ * while working on other Android Apps without interruption.
72+ * </p>
5473 * @see RCConnection
5574 */
5675public class RCDevice extends Service implements SignalingClient .SignalingClientListener {
@@ -131,36 +150,89 @@ public static class ParameterKeys {
131150 private static final String TAG = "RCDevice" ;
132151
133152 // Service Intent actions sent from RCDevice Service -> Call Activity
153+
154+ /**
155+ * Call Activity Intent action sent when a new call is requested by tapping on a missed call on the Notification Drawer
156+ */
134157 public static String ACTION_OUTGOING_CALL = "org.restcomm.android.sdk.ACTION_OUTGOING_CALL" ;
158+
159+ /**
160+ * Call Activity Intent action sent when a incoming call arrives
161+ */
135162 public static String ACTION_INCOMING_CALL = "org.restcomm.android.sdk.ACTION_INCOMING_CALL" ;
163+ /**
164+ * Call Activity Intent action sent when an incoming call is requested to be answered with audio only via Notification Drawer. Notice that
165+ * the application still has control and needs to answer the call depending on its preference with RCConnection.accept(). Also, extras are
166+ * exactly the same as for ACTION_OUTGOING_CALL, for example the peer DID will be at 'EXTRA_DID'
167+ */
136168 public static String ACTION_INCOMING_CALL_ANSWER_AUDIO = "org.restcomm.android.sdk.ACTION_INCOMING_CALL_ANSWER_AUDIO" ;
169+ /**
170+ * Call Activity Intent action sent when an incoming call is requested to be answered with audio & video via Notification Drawer. Notice that
171+ * the application still has control and needs to answer the call depending on its preference with RCConnection.accept(). Also, extras are
172+ * exactly the same as for ACTION_OUTGOING_CALL, for example the peer DID will be at 'EXTRA_DID'
173+ */
137174 public static String ACTION_INCOMING_CALL_ANSWER_VIDEO = "org.restcomm.android.sdk.ACTION_INCOMING_CALL_ANSWER_VIDEO" ;
138- public static String ACTION_INCOMING_CALL_DECLINE = "org.restcomm.android.sdk.ACTION_INCOMING_CALL_DECLINE" ;
139- public static String ACTION_OPEN_MESSAGE_SCREEN = "org.restcomm.android.sdk.ACTION_OPEN_MESSAGE_SCREEN" ;
140- public static String ACTION_INCOMING_MESSAGE = "org.restcomm.android.sdk.ACTION_INCOMING_MESSAGE" ;
175+
176+ /**
177+ * Call Activity Intent action sent when a live background call is resumed via Notification Drawer. The Application
178+ * should just allow the existing Call Activity to open.
179+ */
180+ public static String ACTION_RESUME_CALL = "org.restcomm.android.sdk.ACTION_RESUME_CALL" ;
181+ /**
182+ * Call Activity Intent action sent when a ringing call was declined via Notification Drawer. You don't have to act on that,
183+ * but it usually provides a better user experience if you do. If you don't act on that then the Call Activity
184+ * will be opened, right after the call is disconnected from the SDK. This is usually poor experience as it takes you back
185+ * to the App for a call you just disconnected, and hence of little interest. Instead, a better approach close the
186+ * Call Activity behind the scenes and remain in you current workflow.
187+ */
188+ //public static String ACTION_INCOMING_CALL_DECLINE = "org.restcomm.android.sdk.ACTION_INCOMING_CALL_DECLINE";
189+ /**
190+ * Call Activity Intent action sent when a live call was disconnected via Notification Drawer. You don't have to act on that,
191+ * but it usually provides a better user experience if you do. If you don't act on that then the Call Activity
192+ * will be opened, right after the call is disconnected from the SDK. This is usually poor experience as it takes you back
193+ * to the App for a call you just disconnected, and hence of little interest. Instead, a better approach close the
194+ * Call Activity behind the scenes and remain in you current workflow.
195+ */
141196 public static String ACTION_CALL_DISCONNECT = "org.restcomm.android.sdk.ACTION_CALL_DISCONNECT" ;
197+ /**
198+ * Message Activity Intent action sent by the SDK when an incoming text message arrived
199+ */
200+ public static String ACTION_INCOMING_MESSAGE = "org.restcomm.android.sdk.ACTION_INCOMING_MESSAGE" ;
142201
143- // Intents sent by Notification subsystem -> RCDevice Service when user acts on the Notifications
202+
203+ // Internal intents sent by Notification subsystem -> RCDevice Service when user acts on the Notifications
144204 // Used when user taps in a missed call, where we want it to trigger a new call towards the caller
145- public static String ACTION_NOTIFICATION_CALL_DEFAULT = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_DEFAULT" ;
205+ private static String ACTION_NOTIFICATION_CALL_DEFAULT = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_DEFAULT" ;
146206 // Used when there's an active call and a foreground notification, and hence the user wants to go back the existing call activity without doing anything else
147- public static String ACTION_NOTIFICATION_CALL_OPEN = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_OPEN" ;
207+ //private static String ACTION_NOTIFICATION_CALL_OPEN = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_OPEN";
148208 // Used when there's an active call and a foreground notification, and the user wants to disconnect
149- public static String ACTION_NOTIFICATION_CALL_DISCONNECT = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_DISCONNECT" ;
209+ private static String ACTION_NOTIFICATION_CALL_DISCONNECT = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_DISCONNECT" ;
150210 // User deleted the notification (by swiping on the left/right, or deleting all notifications)
151- public static String ACTION_NOTIFICATION_CALL_DELETE = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_DELETE" ;
152- public static String ACTION_NOTIFICATION_CALL_ACCEPT_VIDEO = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_ACCEPT_VIDEO" ;
153- public static String ACTION_NOTIFICATION_CALL_ACCEPT_AUDIO = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_ACCEPT_AUDIO" ;
154- public static String ACTION_NOTIFICATION_CALL_DECLINE = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_DECLINE" ;
155- public static String ACTION_NOTIFICATION_MESSAGE_DEFAULT = "org.restcomm.android.sdk.ACTION_NOTIFICATION_MESSAGE_DEFAULT" ;
156- public static String ACTION_NOTIFICATION_CALL_MUTE_AUDIO = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_MUTE_AUDIO" ;
211+ private static String ACTION_NOTIFICATION_CALL_DELETE = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_DELETE" ;
212+ private static String ACTION_NOTIFICATION_CALL_ACCEPT_VIDEO = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_ACCEPT_VIDEO" ;
213+ private static String ACTION_NOTIFICATION_CALL_ACCEPT_AUDIO = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_ACCEPT_AUDIO" ;
214+ private static String ACTION_NOTIFICATION_CALL_DECLINE = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_DECLINE" ;
215+ private static String ACTION_NOTIFICATION_MESSAGE_DEFAULT = "org.restcomm.android.sdk.ACTION_NOTIFICATION_MESSAGE_DEFAULT" ;
216+ private static String ACTION_NOTIFICATION_CALL_MUTE_AUDIO = "org.restcomm.android.sdk.ACTION_NOTIFICATION_CALL_MUTE_AUDIO" ;
157217
158218 // Intent EXTRAs keys
219+
220+ /**
221+ * The actual message text
222+ */
159223 public static String EXTRA_MESSAGE_TEXT = "org.restcomm.android.sdk.EXTRA_MESSAGE_TEXT" ;
224+ /**
225+ * The caller-id for the incoming call or message
226+ */
160227 public static String EXTRA_DID = "org.restcomm.android.sdk.EXTRA_DID" ;
228+ /**
229+ * Potential custom SIP headers sent by Restcomm-Connect
230+ */
161231 public static String EXTRA_CUSTOM_HEADERS = "org.restcomm.android.sdk.CUSTOM_HEADERS" ;
232+ /**
233+ * Whether the peer started the incoming call with video enabled or not
234+ */
162235 public static String EXTRA_VIDEO_ENABLED = "org.restcomm.android.sdk.VIDEO_ENABLED" ;
163- //public static String EXTRA_ACCEPT_WITH_VIDEO = "org.restcomm.android.sdk.EXTRA_ACCEPT_WITH_VIDEO";
164236
165237 // Notification ids for calls and messages. Key is the sender and value in the notification id. We mainly need those
166238 // to make sure that notifications from a specific user are shown in a single slot, to avoid confusing the user. This means
@@ -178,7 +250,7 @@ public static class ParameterKeys {
178250 // is an incoming call ringing, triggered by the Notification subsystem?
179251 private boolean activeCallNotification = false ;
180252 // Numbers here refer to: delay duration, on duration 1, off duration 1, on duration 2, off duration2, ...
181- long [] notificationVibrationPattern = { 0 , 300 , 300 , 300 , 300 };
253+ long [] notificationVibrationPattern = { 0 , 100 , 200 , 100 , 200 };
182254 int notificationColor = Color .parseColor ("#3c5866" );
183255 int [] notificationColorPattern = { 2000 , 2000 };
184256 private boolean foregroundNoticationActive = false ;
@@ -262,7 +334,7 @@ public int onStartCommand(Intent intent, int flags, int startId)
262334 if (intentAction .equals (ACTION_NOTIFICATION_CALL_DEFAULT ) || intentAction .equals (ACTION_NOTIFICATION_CALL_ACCEPT_VIDEO ) ||
263335 intentAction .equals (ACTION_NOTIFICATION_CALL_ACCEPT_AUDIO ) || intentAction .equals (ACTION_NOTIFICATION_CALL_DECLINE ) ||
264336 intentAction .equals (ACTION_NOTIFICATION_CALL_DELETE ) || intentAction .equals (ACTION_NOTIFICATION_MESSAGE_DEFAULT ) ||
265- intentAction .equals (ACTION_NOTIFICATION_CALL_OPEN ) || intentAction .equals (ACTION_NOTIFICATION_CALL_DISCONNECT ) ||
337+ /* intentAction.equals(ACTION_NOTIFICATION_CALL_OPEN) || */ intentAction .equals (ACTION_NOTIFICATION_CALL_DISCONNECT ) ||
266338 intentAction .equals (ACTION_NOTIFICATION_CALL_MUTE_AUDIO )) {
267339 onNotificationIntent (intent );
268340 }
@@ -374,7 +446,7 @@ public boolean initialize(Context activityContext, HashMap<String, Object> param
374446 throw new RuntimeException (RCClient .errorText (RCClient .ErrorCodes .ERROR_DEVICE_MISSING_INTENTS ));
375447 }
376448
377- setPendingIntents ((Intent ) parameters .get (RCDevice .ParameterKeys .INTENT_INCOMING_CALL ),
449+ setIntents ((Intent ) parameters .get (RCDevice .ParameterKeys .INTENT_INCOMING_CALL ),
378450 (Intent ) parameters .get (ParameterKeys .INTENT_INCOMING_MESSAGE ));
379451
380452 // TODO: check if those headers are needed
@@ -703,7 +775,7 @@ public DeviceState getState()
703775 * @param callIntent an intent that will be sent on an incoming call
704776 * @param messageIntent an intent that will be sent on an incoming text message
705777 */
706- public void setPendingIntents (Intent callIntent , Intent messageIntent )
778+ public void setIntents (Intent callIntent , Intent messageIntent )
707779 {
708780 RCLogger .i (TAG , "setPendingIntents()" );
709781
@@ -1008,14 +1080,15 @@ public void onMessageArrivedEvent(String jobId, String peer, String messageText)
10081080 {
10091081 RCLogger .i (TAG , "onMessageArrivedEvent(): id: " + jobId + ", peer: " + peer + ", text: " + messageText );
10101082
1011- audioManager .playMessageSound ();
10121083 HashMap <String , String > parameters = new HashMap <String , String >();
10131084 // filter out potential '<' and '>' and leave just the SIP URI
10141085 String peerSipUri = peer .replaceAll ("^<" , "" ).replaceAll (">$" , "" );
10151086
10161087 //parameters.put(RCConnection.ParameterKeys.CONNECTION_PEER, from);
10171088
10181089 if (isServiceAttached ) {
1090+ audioManager .playMessageSound ();
1091+
10191092 messageIntent .setAction (ACTION_INCOMING_MESSAGE );
10201093 messageIntent .putExtra (EXTRA_DID , peerSipUri );
10211094 messageIntent .putExtra (EXTRA_MESSAGE_TEXT , messageText );
@@ -1170,7 +1243,7 @@ void onNotificationMessage(String peerSipUri, String messageText)
11701243 .setSmallIcon (R .drawable .ic_chat_24dp )
11711244 .setContentTitle (peerUsername )
11721245 .setContentText (messageText )
1173- // .setSound(Uri.parse("android.resource://" + getPackageName() + "/" + audioManager.getResourceIdForKey(ParameterKeys.RESOURCE_SOUND_MESSAGE))) // R.raw.message_sample)) //
1246+ .setSound (Uri .parse ("android.resource://" + getPackageName () + "/" + audioManager .getResourceIdForKey (ParameterKeys .RESOURCE_SOUND_MESSAGE ))) // R.raw.message_sample)) //
11741247 // Need this to show up as Heads-up Notification
11751248 .setPriority (NotificationCompat .PRIORITY_HIGH )
11761249 .setAutoCancel (true ) // cancel notification when user acts on it
@@ -1217,6 +1290,7 @@ void onNotificationIntent(Intent intent)
12171290 }
12181291
12191292 Intent actionIntent = null ;
1293+ /*
12201294 if (intentAction.equals(ACTION_NOTIFICATION_CALL_OPEN)) {
12211295 RCConnection connection = getLiveConnection();
12221296 if (connection != null) {
@@ -1232,7 +1306,8 @@ void onNotificationIntent(Intent intent)
12321306 actionIntent = callIntent;
12331307 }
12341308 }
1235- else if (intentAction .equals (ACTION_NOTIFICATION_CALL_DEFAULT )) {
1309+ */
1310+ if (intentAction .equals (ACTION_NOTIFICATION_CALL_DEFAULT )) {
12361311 callIntent .setAction (ACTION_INCOMING_CALL );
12371312 // don't forget to copy the extras to callIntent
12381313 callIntent .putExtras (intent );
@@ -1406,7 +1481,7 @@ private void notificationHandleForegroundUpdate(RCConnection connection)
14061481 {
14071482 String peerUsername = connection .getPeer ().replaceAll (".*?sip:" , "" ).replaceAll ("@.*$" , "" );
14081483
1409- callIntent .setAction (ACTION_OUTGOING_CALL );
1484+ callIntent .setAction (ACTION_RESUME_CALL );
14101485 callIntent .setFlags (Intent .FLAG_ACTIVITY_SINGLE_TOP );
14111486
14121487 // Intent to open the call activity (for when tapping on the general notification area)
0 commit comments