Populating the History tab in the sales force application

Now that you know how to use the various mobile phone and OS features, you will need to build it into your sales force application. To jog your memory a little, you've created the HistoryList control in the AccountViewer form to view a list of all incoming and outgoing communication (SMS messages/phone calls) for each account. This screen is shown as follows:

Populating the History tab in the sales force application

To populate this list, you will need to be able to detect SMS messages and phone calls as and when they are sent or received, and then generate the corresponding historical records in the database. You will need to do the following tasks:

  • Create the relevant functions in the data tier to insert new historical records
  • Encapsulate SMS and phone functionality in a class each
  • Create the background application to intercept incoming SMS messages and phone calls—this will generate the corresponding historical records
  • Handle outgoing SMS messages and phone calls—this will also generate the corresponding historical records

Creating the data tier functions to insert historical records

The first thing you need to do is to create the Oracle Lite and SQL Server CE functions to insert historical records into the AccountHistories table. You'll need to define the following functions in the IDataLibPlugin interface:

bool InsertHistoricalRecord(Guid AccountGUID, int
OriginatingSource, string Subject, string Description);
bool InsertHistoricalRecordByPhone(string phoneNumber, int
OriginatingSource, string Subject, string Description);

The following list describes what each function can do:

  • InsertHistoricalRecord(): This function generates a history record for a specified account.
  • InsertHistoricalRecordByPhone(): This function allows you to pass in the incoming phone number. It locates an account with the matching phone number and then calls the InsertHistoricalRecord() function to generate the history record under that account.

You will also notice in the preceding functions that you need to insert an OriginatingSource value into the AccountHistories table. This is an integer value that takes on the value of 0 (incoming) or 1 (outgoing). It would be more intuitive to use an enumerated type to represent this in your code. Add the following enumerated type to the GlobalVariables class in the CRMLiveFramework project.

public enum Communications
{
Incoming=0,
Outgoing=1
}

Now let's take a look at the SQL Server CE implementation for these two functions:

public bool InsertHistoricalRecord(System.Guid AccountGUID,
int OriginatingSource, string Subject, string Description)
{
SqlCeCommand _command;
bool _result;
_command = _globalConnection.CreateCommand();
_command.CommandText = "INSERT INTO
AccountHistories(AccountGUID,OriginatingSource,Subject,
Description,Timestamp) VALUES (@AccountGUID,
@OriginatingSource, @Subject, @Description, GETDATE())";
_command.Parameters.Add("@AccountGUID", AccountGUID);
_command.Parameters.Add("@OriginatingSource",
OriginatingSource);
_command.Parameters.Add("@Subject", Subject);
_command.Parameters.Add("@Description", Description);
try
{
if (_command.ExecuteNonQuery() == 1)
{
_result = true;
}
else
{
_result = false;
}
}
catch (Exception ex)
{
throw (ex);
_result = false;
}
_command.Dispose();
_command = null;
return _result;
}
public bool InsertHistoricalRecordByPhone(string phoneNumber,
int OriginatingSource, string Subject, string Description)
{
SqlCeCommand _command;
SqlCeDataReader _datareader;
Guid _accountGUID;
bool _result;
//First we attempt to retrieve an account that matches the //incoming phone number passed in
_command = _globalConnection.CreateCommand();
_command.CommandText = "SELECT AccountGUID FROM Accounts
WHERE MobPhoneNo LIKE @PhoneNo OR ResPhoneNo LIKE
@PhoneNo";
_command.Parameters.Add("PhoneNo", phoneNumber);
try
{
_datareader = _command.ExecuteReader();
//If an account is found, we create the historical record for it
if (_datareader.Read() == true)
{
_accountGUID = _datareader.GetGuid
(_datareader.GetOrdinal("AccountGUID"));
InsertHistoricalRecord(_accountGUID,
OriginatingSource, Subject, Description);
}
_result = true;
}
catch (Exception ex)
{
_result = false;
throw (ex);
}
_command.Dispose();
_command = null;
return _result;
}

Encapsulating SMS functionality

You could of course access the POOM classes directly from your application, but it is a good idea to encapsulate its functionality in a custom class of your own. The MessagingService class handles both outgoing and incoming SMS messages. Add this class to the CRMLiveFramework project. Let's take a look at the following class:

using System;
using System.Data;
using Microsoft.WindowsMobile.PocketOutlook;
using Microsoft.WindowsMobile.PocketOutlook.
MessageInterception;
using System.Text.RegularExpressions;
namespace CRMLive
{
public class MessagingService
{
private SmsMessage _msg;
private MessageInterceptor _SMSInterceptor;
private string _IncomingSource;
public delegate void
IncomingSMSReceivedEventHandler(string
IncomingPhoneNumber);
private IncomingSMSReceivedEventHandler
IncomingSMSReceivedEvent;
public event IncomingSMSReceivedEventHandler
IncomingSMSReceived
{
add
{
IncomingSMSReceivedEvent =
(IncomingSMSReceivedEventHandler)
System.Delegate.Combine
(IncomingSMSReceivedEvent, value);
}
remove
{
IncomingSMSReceivedEvent =
(IncomingSMSReceivedEventHandler)
System.Delegate.Remove
(IncomingSMSReceivedEvent, value);
}
}
public string IncomingSource
{
get
{
return _IncomingSource;
}
}
public void CreateSMS(string phoneNumber, string
message)
{
_msg = new SmsMessage(phoneNumber, message);
}
public void StartDetector()
{
_SMSInterceptor = new
MessageInterceptor(InterceptionAction.Notify,
true);
_SMSInterceptor.MessageReceived += new
Microsoft.WindowsMobile.PocketOutlook.
MessageInterception.MessageInterceptorEventHandler (SMSReceivedEventHandler);
}

You've seen earlier that the incoming phone number retrieved might look something like this:

Ed <+6598532715>

You can use a simple Regular Expression to extract just the phone number (+6598532715) from between the< > brackets. Let's take a look at this function in detail:

private void SMSReceivedEventHandler(object sender,
MessageInterceptorEventArgs e)
{
_msg = (SmsMessage)e.Message;
_IncomingSource =
_msg.From.Address.ToString().Trim();
if (_IncomingSource.Length > 0)
{
if (Regex.IsMatch(_IncomingSource, "\<(.*)\>")
== true)
{
_IncomingSource = Regex.Match(_IncomingSource,
"\<(.*)\>").Groups[1].Value;
}
if (IncomingSMSReceivedEvent != null)
IncomingSMSReceivedEvent(_IncomingSource);
}
}
public void SendSMS()
{
_msg.Send();
}
public void LaunchSMSComposeWindow()
{
MessagingApplication.DisplayComposeForm(_msg);
}
}
}

Encapsulating phone functionality

The Telephony class is the equivalent class for phone functionality in the sales force application. Add the following class to the CRMLiveFramework project:

using System;
using System.Data;
using Microsoft.WindowsMobile.Telephony;
using Microsoft.WindowsMobile.Status;
using System.Text.RegularExpressions;
namespace CRMLive
{
public class Telephony
{
private SystemState _IncomingCallState;
private SystemState _phoneCaller;
private string _IncomingphoneNumber;
public delegate void IncomingCallReceivedEventHandler
(string incomingPhoneNumber);
private IncomingCallReceivedEventHandler
IncomingCallReceivedEvent;
public event IncomingCallReceivedEventHandler
IncomingCallReceived
{
add
{
IncomingCallReceivedEvent =
(IncomingCallReceivedEventHandler)
System.Delegate.Combine
(IncomingCallReceivedEvent, value);
}
remove
{
IncomingCallReceivedEvent =
(IncomingCallReceivedEventHandler)
System.Delegate.Remove
(IncomingCallReceivedEvent, value);
}
}
public object IncomingPhoneNumber
{
get
{
return _IncomingphoneNumber;
}
}
public object MakePhoneCall(string phoneNumber)
{
Phone _phone = new Phone();
_phone.Talk(phoneNumber);
}
public void StartDetector()
{
_phoneCaller = new SystemState
(SystemProperty.PhoneIncomingCallerNumber, true);
_phoneCaller.Changed += new
Microsoft.WindowsMobile.Status.ChangeEventHandler
(phoneCaller_Changed);
}
private void phoneCaller_Changed(object sender,
ChangeEventArgs args)
{
_IncomingphoneNumber =
SystemState.PhoneIncomingCallerNumber.Trim();
if (_IncomingphoneNumber.Length > 0)
{
if (Regex.IsMatch(_IncomingphoneNumber,
"\<(.*)\>") == true)
{
_IncomingphoneNumber =
Regex.Match(_IncomingphoneNumber,
"\<(.*)\>").Groups[1].Value;
}
if (IncomingCallReceivedEvent != null)
IncomingCallReceivedEvent
(_IncomingphoneNumber);
}
}
}
}

Intercepting incoming SMS messages and phone calls in the background

As hinted earlier, your sales force application would likely need to detect incoming SMS messages and phone calls even if it is not running. The best way to achieve this is to write a separate program that constantly runs as a background service in memory. You should also have this program automatically run when the mobile device starts up.

Tip

If you are wondering why you need to write a separate program, that's because your sales force application is a heavy application—if you take a look at the file size of the generated SalesForce.exe file, you can see it's somewhere around 150Kb by now. It wouldn't be a good idea to keep this whole application constantly running in the background. A separate program on the other hand, only has a 10Kb foot print.

Let's take a look at how you can create such a program. Create a new Windows Forms project (named CRMLiveInterceptor) and add a form called MainForm to your project. You will need to add a reference to the CRMLiveFramework project because this application will make use of the database plugins.

using System;
using System.Windows.Forms;
using System.Reflection;
using System.IO;
using CRMLive;
public class MainForm : Form
{

You will of course need to create a few objects that you will be using in this application:

private PluginManager _PluginManager = new
PluginManager();
private MessagingService _Interceptor = new
MessagingService();
private Telephony _Telephony = new Telephony();
//The constructor for the class. In this constructor we //setup some of the event handlers
public MainForm()
{
this.Activated +=new EventHandler(Form_Activated);
_Interceptor.IncomingSMSReceived += new
MessagingService.IncomingSMSReceivedEventHandler
(Interceptor_IncomingSMSReceived);
_Telephony.IncomingCallReceived += new
_Telephony.IncomingCallReceivedEventHandler
(Telephony_IncomingCallReceived);
//Here we create a shortcut to this application in the
//WindowsStartup folder if it does not exist yet
try
{
CreateStartupShortCut();
_Interceptor.StartDetector();
_Telephony.StartDetector();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Initializing app");
History tab, mobile sales force applicationphone calls, intercepting in background}
}
//Because we want this form to run constantly in the
//background, you will need to hide it when it is activated
public void Form_Activated(object sender, EventArgs e)
{
this.Hide();
}

To get your application to automatically run when the Windows Mobile OS starts up, there are generally two methods that most developers consider:

  • Placing the path to your application in the registry in the HKLMInit key:

    This method works well for native applications. For .NET CF applications, it may or may not execute successfully. This is because there is a possibility your application may run before the .NET CF classes are loaded. For .NET CF applications, this method is not desirable.

  • Placing a link to your application in the WindowsStartup folder:

    The better way to get your application to automatically start up is by placing a link to it in the WindowsStartup folder. You can create your own .LNK (link) file quite easily. The format of the .LNK file follows: <Length of the application file path>#<full application file path>

Let's take a look at the function to create this .LNK file:

private void CreateStartupShortCut()
{
string _startupPath =
Environment.GetFolderPath
(Environment.SpecialFolder.Startup);
string _data = "";
string _path = "";
_startupPath = _startupPath.TrimEnd('') +
"\CRMLiveInterceptor.lnk";
if (File.Exists(_startupPath) == false)
{
_path = """ + Assembly.GetExecutingAssembly()
.GetName().CodeBase + """;
_data = Convert.ToString (_path.Length) + "#" +
_path;
SaveTextToFile(_data, _startupPath);
}
}
public bool SaveTextToFile(string strData, string FullPath)
{
bool _result = false;
StreamWriter objReader = default(StreamWriter);
try
{
objReader = new StreamWriter(FullPath, false);
objReader.Write(strData);
objReader.Close();
_result = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Creating lnk file");
}
return _result;
}

The final two functions in this form are event handlers that connect to the database and write the corresponding historical record to the database using the functions you've created earlier. This is done for every incoming SMS and phone call.

private void Interceptor_IncomingSMSReceived(string
IncomingPhoneNumber)
{
try
{
_PluginManager.GetActivePlugin.ConnectDatabase();
_PluginManager.GetActivePlugin.
InsertHistoricalRecordByPhone
(IncomingPhoneNumber,
(int)GlobalVariables.Communications.Incoming, "SMS received", "");
_PluginManager.GetActivePlugin. DisconnectDatabase();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Incoming SMS
received");
}
}
private void Telephony_IncomingCallReceived(string
incomingPhoneNumber)
{
try
{
_PluginManager.GetActivePlugin.ConnectDatabase();
_PluginManager.GetActivePlugin.
InsertHistoricalRecordByPhone(incomingPhoneNumber,
(int) GlobalVariables.Communications.Incoming,
"Phone call received", "");
_PluginManager.GetActivePlugin.DisconnectDatabase();
}
catch (Exception ex) {
MessageBox.Show(ex.ToString(), "Incoming call
received");
}
}
}

Handling outgoing SMS messages and phone calls

When the user sends an SMS message or places a phone call through the sales force application, you would also need to generate a corresponding historical record.

There is no equivalent way to 'detect' an outgoing SMS message or phone call using POOM or the SystemState class without resorting to the MAPI functions, so we will look at an easier approach.

As you only need to log all outgoing phone calls and SMS messages sent through your application, you can create a general user control to do this (as shown in the following screenshot). This user control will be used in place of the normal text box in your application whenever you display phone numbers.

Handling outgoing SMS messages and phone calls

The Call button allows the application to dial the number contained in the adjacent text box, while the SMS button launches a custom SMS Compose screen. Create the SMS Compose form as shown in the following screenshot and name it SendSMS.

Handling outgoing SMS messages and phone calls

Whenever the user sends an SMS message through this window, you will generate the corresponding history record.

Let's take a look at the following code for this user control (called FlexiControl). The highlighted code generates the historical records. Take note that this user control needs a valid AccountGUID. You will need to pass in this value before using the user control.

using System;
using System.Windows.Forms;
using System.Drawing;
namespace CRMLive
{
public partial class FlexiControl
{
private Guid _AccountGUID;
public Guid AccountGUID
{
get
{
return _AccountGUID;
}
set
{
_AccountGUID = value;
}
}
public override string Text
{
get
{
return txtData.Text;
}
set
{
txtData.Text = value;
}
}
public FlexiControl()
{
// This call is required by the Windows Form
//Designer.
InitializeComponent();
}
public void FlexiControl_Resize(object sender,
System.EventArgs e)
{
this.Height = txtData.Height;
}
public void btnMakeCall_Click(System.Object sender,
System.EventArgs e)
{
Telephony _call = new Telephony();
GlobalArea.PluginManager.GetActivePlugin.
InsertHistoricalRecord(_AccountGUID,
System.Convert.ToInt32
(GlobalVariables.Communications.Outgoing),
"Outgoing phone call", "");
_call.MakePhoneCall(txtData.Text);
}
public void btnSendSMS_Click(System.Object sender,
History tab, mobile sales force applicationoutgoing SMS messages, handlingSystem.EventArgs e)
{
//Here we launch the SendSMS Custom Compose window
SendSMS _sendSMSForm = new SendSMS();
if (_sendSMSForm.ShowDialog() == DialogResult.OK)
{
GlobalArea.PluginManager.GetActivePlugin.
InsertHistoricalRecord(_AccountGUID,
System.Convert.ToInt32
(GlobalVariables.Communications.Outgoing),
"Outgoing SMS", "");
MessagingService _sms = new MessagingService();
_sms.CreateSMS(txtData.Text, sendSMSForm.Message);
_sms.SendSMS();
}
}
}
}

Now that you have done this, you can replace all phone number text boxes in the AccountViewer form with this new user control. As this control still exposes the Text property, data binding will work as usual. The only extra step you need to do is to initialize this control with the current AccountGUID in the form load event of the AccountViewer form (as shown in the following code snippet):

fcLandPhone.AccountGUID = _account.AccountGUID
fcMobilePhone.AccountGUID = _account.AccountGUID

The following screenshot shows what the new AccountViewer screen will look like after your changes:

Handling outgoing SMS messages and phone calls

Testing your code

You can try what you've built so far! Now, obviously you can only test your application with a real device. You will not be able to send or receive an SMS/phone call via the mobile device emulator.

After compiling the relevant projects, run the CRMLiveInterceptor project. You won't be able to see much (as the form is hidden). You will know that the application has started after the Windows Mobile Busy icon goes away.

Create a new lead account and set the Phone (HP) field to your friend's number. (Take care to specify the + and country code before the number and also not to type in any whitespaces in between numbers).

Tip

You can create more robust code by handling whitespaces, country and area codes, number separators (such as brackets), and so on.

Now, get your friend to call your phone (you will need to pick up the call) or send your phone an SMS message. Again, you won't see much happen on your phone. If you go back into the lead account and click on the History tab, you will be able to see that the incoming SMS or phone call has been captured:

Testing your code

Now try using the AccountViewer form to place a phone call directly from a lead account. After the phone call has been successfully made, you can check the History tab again. You will notice a new entry—denoting that the outgoing call has been logged. You can also repeat your experiment with SMS messages.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.170.183