diff --git a/Android/src/com/phonegap/plugins/twilioclient/IncomingConnectionActivity.java b/Android/src/com/phonegap/plugins/twilioclient/IncomingConnectionActivity.java index 83390b4..ad2bdff 100644 --- a/Android/src/com/phonegap/plugins/twilioclient/IncomingConnectionActivity.java +++ b/Android/src/com/phonegap/plugins/twilioclient/IncomingConnectionActivity.java @@ -3,9 +3,10 @@ import android.app.Activity; import android.content.Intent; import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; -import com.twilio.client.Connection; -import com.twilio.client.Device; +//import com.twilio.client.Connection; +//import com.twilio.client.Device; /** * @@ -19,6 +20,7 @@ public class IncomingConnectionActivity extends Activity { @Override public void onNewIntent(Intent intent) { + Log.d("TCPlugin", "ON NEW INTENT IN CONNECTION ACTIVITY"); super.onNewIntent(intent); setIntent(intent); } @@ -26,12 +28,13 @@ public void onNewIntent(Intent intent) @Override public void onResume() { + Log.d("TCPlugin", "ON RESUME IN CONNECTION ACTIVITY"); super.onResume(); Intent intent = getIntent(); LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); intent.setAction(ACTION_NAME); lbm.sendBroadcast(intent); - + finish(); } diff --git a/Android/src/com/phonegap/plugins/twilioclient/TCPlugin.java b/Android/src/com/phonegap/plugins/twilioclient/TCPlugin.java index df1cf16..762c973 100644 --- a/Android/src/com/phonegap/plugins/twilioclient/TCPlugin.java +++ b/Android/src/com/phonegap/plugins/twilioclient/TCPlugin.java @@ -12,7 +12,7 @@ import org.json.JSONException; import org.json.JSONObject; -import android.R; +//import android.R; import android.app.Activity; import android.app.NotificationManager; import android.app.PendingIntent; @@ -21,10 +21,12 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.media.AudioManager; import android.support.v4.app.NotificationCompat; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; + import com.twilio.client.Connection; import com.twilio.client.ConnectionListener; import com.twilio.client.Device; @@ -54,6 +56,7 @@ public class TCPlugin extends CordovaPlugin implements DeviceListener, private JSONArray mInitDeviceSetupArgs; private int mCurrentNotificationId = 1; private String mCurrentNotificationText; + private TCPlugin plugin = this; @@ -62,38 +65,13 @@ public class TCPlugin extends CordovaPlugin implements DeviceListener, public void onReceive(Context context, Intent intent) { // mDevice = intent.getParcelableExtra(Device.EXTRA_DEVICE); mConnection = intent.getParcelableExtra(Device.EXTRA_CONNECTION); - Log.d(TAG, "incoming intent received with connection: " - + mConnection.getState().name()); + mConnection.setConnectionListener(plugin); + Log.d(TAG, "incoming intent received with connection: "+ mConnection.getState().name()); JSONObject connection = new JSONObject(); - try { - connection.putOpt("parameters", getJSONObject(mConnection.getParameters())); - - if (mCurrentNotificationText != null) { - - - PackageManager pm = context.getPackageManager(); - Intent notificationIntent = pm.getLaunchIntentForPackage(context.getPackageName()); - notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - - PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); - - NotificationCompat.Builder mBuilder = - new NotificationCompat.Builder(context) - .setSmallIcon(R.drawable.btn_star_big_on) - .setContentTitle("Answer") - .setContentText(mCurrentNotificationText) - .setContentIntent(pendingIntent); - // Gets an instance of the NotificationManager service - NotificationManager mNotifyMgr = - (NotificationManager) TCPlugin.this.webView.getContext().getSystemService(Activity.NOTIFICATION_SERVICE); - // Builds the notification and issues it. - mNotifyMgr.notify(mCurrentNotificationId, mBuilder.build()); - } - - } catch (JSONException e) { - Log.e(TAG,e.getLocalizedMessage(), e); + String constate = mConnection.getState().name(); + if(constate.equals("PENDING")) { + TCPlugin.this.javascriptCallback("onincoming", connection, mInitCallbackContext); } - TCPlugin.this.javascriptCallback("onincoming", connection, mInitCallbackContext); } }; @@ -153,6 +131,7 @@ public boolean execute(final String action, final JSONArray args, connectionStatus(callbackContext); return true; } else if ("rejectConnection".equals(action)) { + rejectConnection(args, callbackContext); return true; } else if ("showNotification".equals(action)) { showNotification(args,callbackContext); @@ -160,6 +139,9 @@ public boolean execute(final String action, final JSONArray args, } else if ("cancelNotification".equals(action)) { cancelNotification(args,callbackContext); return true; + } else if ("setSpeaker".equals(action)) { + setSpeaker(args,callbackContext); + return true; } return false; @@ -188,21 +170,13 @@ private void deviceSetup(JSONArray arguments, } mDevice = Twilio.createDevice(arguments.optString(0), this); - // handle incoming phone requests - // 1) configure Twilio - Intent intent = new Intent(this.cordova.getActivity(), - IncomingConnectionActivity.class); - PendingIntent pendingIntent = PendingIntent.getActivity( - this.cordova.getActivity(), 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT); + Intent intent = new Intent(this.cordova.getActivity(), IncomingConnectionActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(this.cordova.getActivity(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); mDevice.setIncomingIntent(pendingIntent); - // 2) configure the local broadcast manager - LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(cordova - .getActivity()); - lbm.registerReceiver(mBroadcastReceiver, new IntentFilter( - IncomingConnectionActivity.ACTION_NAME)); + + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(cordova.getActivity()); + lbm.registerReceiver(mBroadcastReceiver, new IntentFilter(IncomingConnectionActivity.ACTION_NAME)); javascriptCallback("onready", callbackContext); - } private void connect(JSONArray arguments, CallbackContext callbackContext) { @@ -253,6 +227,11 @@ private void acceptConnection(JSONArray arguments, CallbackContext callbackConte callbackContext.success(); } + private void rejectConnection(JSONArray arguments, CallbackContext callbackContext) { + mConnection.reject(); + callbackContext.success(); + } + private void disconnectConnection(JSONArray arguments, CallbackContext callbackContext) { mConnection.disconnect(); callbackContext.success(); @@ -336,10 +315,28 @@ private void connectionStatus(CallbackContext callbackContext) { private void showNotification(JSONArray arguments, CallbackContext context) { + Context acontext = TCPlugin.this.webView.getContext(); NotificationManager mNotifyMgr = - (NotificationManager) TCPlugin.this.webView.getContext().getSystemService(Activity.NOTIFICATION_SERVICE); + (NotificationManager) acontext.getSystemService(Activity.NOTIFICATION_SERVICE); mNotifyMgr.cancelAll(); - mCurrentNotificationText = arguments.optString(0); + mCurrentNotificationText = arguments.optString(0); + + + PackageManager pm = acontext.getPackageManager(); + Intent notificationIntent = pm.getLaunchIntentForPackage(acontext.getPackageName()); + notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + notificationIntent.putExtra("notificationTag", "BVNotification"); + + PendingIntent pendingIntent = PendingIntent.getActivity(acontext, 0, notificationIntent, 0); + int notification_icon = acontext.getResources().getIdentifier("notification", "drawable", acontext.getPackageName()); + NotificationCompat.Builder mBuilder = + new NotificationCompat.Builder(acontext) + .setSmallIcon(notification_icon) + .setContentTitle("Incoming Call") + .setContentText(mCurrentNotificationText) + .setContentIntent(pendingIntent); + mNotifyMgr.notify(mCurrentNotificationId, mBuilder.build()); + context.success(); } @@ -350,6 +347,28 @@ private void cancelNotification(JSONArray arguments, CallbackContext context) { context.success(); } + /** + * Changes sound from earpiece to speaker and back + * + * @param mode Speaker Mode + * */ + public void setSpeaker(JSONArray arguments, final CallbackContext callbackContext) { + Context context = cordova.getActivity().getApplicationContext(); + AudioManager m_amAudioManager; + m_amAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + String mode = arguments.optString(0); + if(mode.equals("on")) { + Log.d("TCPlugin", "SPEAKER"); + m_amAudioManager.setMode(AudioManager.MODE_NORMAL); + m_amAudioManager.setSpeakerphoneOn(true); + } + else { + Log.d("TCPlugin", "EARPIECE"); + m_amAudioManager.setMode(AudioManager.MODE_IN_CALL); + m_amAudioManager.setSpeakerphoneOn(false); + } + } + // DeviceListener methods @Override @@ -371,20 +390,20 @@ public void onPresenceChanged(Device device, PresenceEvent presenceEvent) { @Override public void onStartListening(Device device) { // What to do here? The JS library doesn't have an event for this. - + } @Override public void onStopListening(Device device) { // TODO Auto-generated method stub - + Log.d(TAG, "onStopListening"); } @Override public void onStopListening(Device device, int errorCode, String errorMessage) { // this.javascriptErrorback(errorCode, errorMessage); - + Log.d(TAG, "onStopListeningWithError"); } @Override @@ -407,7 +426,7 @@ public void onInitialized() { // Twilio Connection Listener methods @Override - public void onConnected(Connection connection) { + public void onConnected(Connection connection) { Log.d(TAG, "onConnected()"); fireDocumentEvent("onconnect"); if (connection.isIncoming()) { @@ -433,8 +452,7 @@ public void onDisconnected(Connection connection) { } @Override - public void onDisconnected(Connection connection, int errorCode, - String errorMessage) { + public void onDisconnected(Connection connection, int errorCode, String errorMessage) { // TODO: Pass error back Log.d(TAG, "onDisconnected(): " + errorMessage); onDisconnected(connection); @@ -466,8 +484,8 @@ private void javascriptCallback(String event, javascriptCallback(event, null, callbackContext); } - private void javascriptErrorback(int errorCode, String errorMessage, - CallbackContext callbackContext) { + + private void javascriptErrorback(int errorCode, String errorMessage, CallbackContext callbackContext) { JSONObject object = new JSONObject(); try { object.putOpt("message", errorMessage); diff --git a/Android/src/com/phonegap/plugins/twilioclient/tcPlugin.js b/Android/src/com/phonegap/plugins/twilioclient/tcPlugin.js index d61d3e1..1175d36 100644 --- a/Android/src/com/phonegap/plugins/twilioclient/tcPlugin.js +++ b/Android/src/com/phonegap/plugins/twilioclient/tcPlugin.js @@ -72,7 +72,6 @@ var status = Cordova.exec(null,null,"TCPlugin","deviceStatus",[]); } - // Noops until I figure out why the hell using sounds in Phonegap gives EXC_BAD_ACCESS TwilioPlugin.Device.prototype.sounds = { incoming: function(boolean) {}, outgoing: function(boolean) {}, @@ -99,6 +98,11 @@ Cordova.exec(null, null, "TCPlugin", "cancelNotification", []); } + TwilioPlugin.Connection.prototype.setSpeaker = function(mode) { + // "on" or "off" + Cordova.exec(null, null, "TCPlugin", "setSpeaker", [mode]); + } + TwilioPlugin.Connection.prototype.reject = function() { Cordova.exec(null,null,"TCPlugin","rejectConnection",[]); } diff --git a/README.md b/README.md index 45f1e5f..d32cfee 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ These are Phonegap plugins that expose the same JS API as Twilio Client for web ``` - Add tcPlugin.js to your application's www folder (in the assets directory) +- You need to add a notification.png file to your applications res/drawable-ldpi, res/drawable-mdpi & res/drawable-hdpi or res/drawable-xhdpi directories (depending on what resolutions you want to support). - Copy the two plugin .java files into your application's src folder, keeping the com/phonegap/plugins/twilioclient directory structure - Last, add the plugin to config.xml (in res/xml) @@ -42,6 +43,24 @@ These are Phonegap plugins that expose the same JS API as Twilio Client for web ``` +## Additional Features + +In addition to the standard features of the Twilio Client JS Library, you can also use the included showNotification and cancelNotification functions to display a UILocalNotifcation to the user when during an incoming call while the app is running in the background: + +```javascript +Twilio.Connection.showNotification("Notification Text", "notification_sound.wav"); +``` + +```javascript +Twilio.Connection.cancelNotification(); +``` + +You can also turn the device's speaker phone on or off during a call using the following method: + +```javascript +Twilio.Connection.setSpeaker("on"); +``` + ## Limitations This is plugin is a first cut and should be considered alpha. Please use it and break it :) Report any issues using the Github issue tracker. diff --git a/iOS/TCPlugin.h b/iOS/TCPlugin.h index 4ac8be6..265a7b7 100644 --- a/iOS/TCPlugin.h +++ b/iOS/TCPlugin.h @@ -33,7 +33,11 @@ -(void)disconnectAll:(NSArray *)arguments withDict:(NSMutableDictionary *)options; -(void)acceptConnection:(NSArray *)arguments withDict:(NSMutableDictionary *)options; -(void)disconnectConnection:(NSArray *)arguments withDict:(NSMutableDictionary *)options; +-(void)rejectConnection:(NSArray *)arguments withDict:(NSMutableDictionary *)options; -(void)muteConnection:(NSArray *)arguments withDict:(NSMutableDictionary *)options; --(void)sendDigits:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options; +-(void)sendDigits:(CDVInvokedUrlCommand*)command; +-(void)showNotification:(CDVInvokedUrlCommand*)command; +-(void)cancelNotification:(CDVInvokedUrlCommand*)command; +-(void)setSpeaker:(CDVInvokedUrlCommand*)command; @end \ No newline at end of file diff --git a/iOS/TCPlugin.m b/iOS/TCPlugin.m index 0360734..fd7f2be 100644 --- a/iOS/TCPlugin.m +++ b/iOS/TCPlugin.m @@ -1,6 +1,6 @@ // // TCPlugin.h -// Twilio Client plugin for PhoneGap +// Twilio Client plugin for PhoneGap / Cordova // // Copyright 2012 Stevie Graham. // @@ -16,6 +16,7 @@ @interface TCPlugin() { @property(nonatomic, strong) TCDevice *device; @property(nonatomic, strong) NSString *callback; @property(atomic, strong) TCConnection *connection; +@property(atomic, strong) UILocalNotification *ringNotification; -(void)javascriptCallback:(NSString *)event; -(void)javascriptCallback:(NSString *)event withArguments:(NSDictionary *)arguments; @@ -28,6 +29,7 @@ @implementation TCPlugin @synthesize device = _device; @synthesize callback = _callback; @synthesize connection = _connection; +@synthesize ringNotification = _ringNotification; # pragma mark device delegate method @@ -36,6 +38,7 @@ -(void)device:(TCDevice *)device didStopListeningForIncomingConnections:(NSError } -(void)device:(TCDevice *)device didReceiveIncomingConnection:(TCConnection *)connection { + self.connection = connection; [self javascriptCallback:@"onincoming"]; } @@ -79,9 +82,9 @@ -(void)deviceSetup:(NSMutableArray *)arguments withDict:(NSMutableDictionary*)op self.device = [[TCDevice alloc] initWithCapabilityToken:[arguments pop] delegate:self]; // Disable sounds. was getting EXC_BAD_ACCESS - self.device.incomingSoundEnabled = NO; - self.device.outgoingSoundEnabled = NO; - self.device.disconnectSoundEnabled = NO; + //self.device.incomingSoundEnabled = NO; + //self.device.outgoingSoundEnabled = NO; + //self.device.disconnectSoundEnabled = NO; [self javascriptCallback:@"onready"]; } @@ -128,6 +131,10 @@ -(void)disconnectConnection:(NSArray *)arguments withDict:(NSMutableDictionary * [self.connection disconnect]; } +-(void)rejectConnection:(NSArray *)arguments withDict:(NSMutableDictionary *)options { + [self.connection reject]; +} + -(void)muteConnection:(NSArray *)arguments withDict:(NSMutableDictionary *)options { if(self.connection.isMuted) { self.connection.muted = NO; @@ -136,8 +143,8 @@ -(void)muteConnection:(NSArray *)arguments withDict:(NSMutableDictionary *)optio } } --(void)sendDigits:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options { - [self.connection sendDigits:[arguments pop]]; +-(void)sendDigits:(CDVInvokedUrlCommand*)command { + [self.connection sendDigits:[command.arguments objectAtIndex:0]]; } -(void)connectionStatus:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options { @@ -166,6 +173,55 @@ -(void)connectionStatus:(NSMutableArray *)arguments withDict:(NSMutableDictionar [self performSelectorOnMainThread:@selector(writeJavascript:) withObject:[result toSuccessCallbackString:[arguments pop]] waitUntilDone:NO]; } + +-(void)showNotification:(CDVInvokedUrlCommand*)command { + @try { + [[UIApplication sharedApplication] cancelAllLocalNotifications]; + } + @catch(NSException *exception) { + NSLog(@"Couldn't Cancel Notification"); + } + + NSString *alertBody = [command.arguments objectAtIndex:0]; + + NSString *ringSound = @"incoming.wav"; + if([command.arguments count] == 2) { + ringSound = [command.arguments objectAtIndex:1]; + } + + _ringNotification = [[UILocalNotification alloc] init]; + _ringNotification.alertBody = alertBody; + _ringNotification.alertAction = @"Answer"; + _ringNotification.soundName = ringSound; + _ringNotification.fireDate = [NSDate date]; + [[UIApplication sharedApplication] scheduleLocalNotification:_ringNotification]; + +} + +-(void)cancelNotification:(CDVInvokedUrlCommand*)command { + [[UIApplication sharedApplication] cancelLocalNotification:_ringNotification]; +} + +-(void)setSpeaker:(CDVInvokedUrlCommand*)command { + NSString *mode = [command.arguments objectAtIndex:0]; + if([mode isEqual: @"on"]) { + UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; + AudioSessionSetProperty ( + kAudioSessionProperty_OverrideAudioRoute, + sizeof (audioRouteOverride), + &audioRouteOverride + ); + } + else { + UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_None; + AudioSessionSetProperty ( + kAudioSessionProperty_OverrideAudioRoute, + sizeof (audioRouteOverride), + &audioRouteOverride + ); + } +} + # pragma mark private methods -(void)javascriptCallback:(NSString *)event withArguments:(NSDictionary *)arguments { diff --git a/iOS/tcPlugin.js b/iOS/tcPlugin.js index 8b2581d..a1310b9 100644 --- a/iOS/tcPlugin.js +++ b/iOS/tcPlugin.js @@ -36,7 +36,7 @@ } TwilioPlugin.Device.prototype.disconnectAll = function() { - Cordova.exec(null,null,"TCPlugin","disconnectAll",[]); + Cordova.exec(null, null, "TCPlugin", "disconnectAll", []); } TwilioPlugin.Device.prototype.disconnect = function(fn) { @@ -68,33 +68,50 @@ } TwilioPlugin.Device.prototype.status = function() { - var status = Cordova.exec(null,null,"TCPlugin","deviceStatus",[]); + Cordova.exec(null, null, "TCPlugin", "deviceStatus", []); } - - // Noops until I figure out why the hell using sounds in Phonegap gives EXC_BAD_ACCESS + TwilioPlugin.Device.prototype.sounds = { incoming: function(boolean) {}, outgoing: function(boolean) {}, disconnect: function(boolean) {} } + TwilioPlugin.Connection.prototype.accept = function(argument) { if (typeof(argument) == 'function') { delegate['onaccept'] = argument; } else { - Cordova.exec(null,null,"TCPlugin","acceptConnection",[]); + Cordova.exec(null, null, "TCPlugin", "acceptConnection", [argument]); } } + TwilioPlugin.Connection.prototype.showNotification = function(alertBody, ringSound) { + var args = [alertBody, ringSound]; + if(ringSound == undefined) { + args = [alertBody]; + } + Cordova.exec(null, null, "TCPlugin", "showNotification", args); + } + + TwilioPlugin.Connection.prototype.cancelNotification = function() { + Cordova.exec(null, null, "TCPlugin", "cancelNotification", []); + } + + TwilioPlugin.Connection.prototype.setSpeaker = function(mode) { + // "on" or "off" + Cordova.exec(null, null, "TCPlugin", "setSpeaker", [mode]); + } + TwilioPlugin.Connection.prototype.reject = function() { - Cordova.exec(null,null,"TCPlugin","rejectConnection",[]); + Cordova.exec(null, null, "TCPlugin", "rejectConnection", []); } TwilioPlugin.Connection.prototype.disconnect = function(fn) { if (typeof(argument) == 'function') { delegate['onconnectiondisconnect'] = argument; } else { - Cordova.exec(null,null,"TCPlugin","disconnectConnection",[]); + Cordova.exec(null, null, "TCPlugin", "disconnectConnection", []); } } @@ -103,15 +120,16 @@ } TwilioPlugin.Connection.prototype.mute = function() { - Cordova.exec(null,null,"TCPlugin","muteConnection",[]); + Cordova.exec(null, null, "TCPlugin", "muteConnection", []); } TwilioPlugin.Connection.prototype.unmute = function() { - Cordova.exec(null,null,"TCPlugin","muteConnection",[]); + Cordova.exec(null, null, "TCPlugin", "muteConnection", []); } TwilioPlugin.Connection.prototype.sendDigits = function(string) { - Cordova.exec(null,null,"TCPlugin","sendDigits", [string]); + //Cordova.exec("TCPlugin.sendDigits", string); + Cordova.exec(null, null, "TCPlugin", "sendDigits", [string]); } TwilioPlugin.Connection.prototype.status = function(fn) {