65.9K
CodeProject is changing. Read more.
Home

Smart Alarm Using OpenNetCF and C# for SmartPhone (Windows Mobile 2003)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.58/5 (7 votes)

Jul 12, 2005

CPOL

3 min read

viewsIcon

122604

downloadIcon

1222

An application for SmartPhones to create multiple alarms.

Application's Default Screen Application's Menu In Action
Setting New Alarm Alarm in Action

Introduction

I got myself a SmartPhone Sagem myS-7 for my b'day this year. Having used a lot of other phones I was a bit disappointed by the Alarm option within SmartPhone. It's very very basic. You can only select the time. (Under Settings / More / Date and Time. The Snooze option sleeps for 10 minutes and you don't have an option to change that. So I got myself to writing an alarm which did most of the things I wanted.)

I started with the Timer object and it was firing the event at the right time. The only problem I was having was that it just wouldn't take the phone out of Standby mode. I Googled around a bit and I realized that I was supposed to set system notification in order for the phone to come out of standby. I looked around to executing native code but I found OpenNetCF. It's got a good set of classes and it had the notification functionality as well.

Using the code

The code is pretty straightforward. Set a Timer object to do an hourly check for upcoming Alarm events. If an alarm is found within the next hour it sets another Timer object to execute and set a Notify.RunAppAtTime from OpenNETCF.Win32.Notify. The application actually playing the alarm (rooster at its best) is a standalone SmartPhone app and plays the Wav file using the SoundPlayer control from OpenNETCF.Windows.Forms.

For persisting alarm data, I used DataSet's ReadXml and WriteXml.

private void GetNextNotificationInfo()
{
    this.nextAlarmUp = 0;
    DataSet dsAlarms = new DataSet();
    if(File.Exists(ConfigFile) == true)
    {
        dsAlarms.ReadXml(ConfigFile);
        if(dsAlarms.Tables.Count != 0 && dsAlarms.Tables[0].Rows.Count != 0)
        {
            DateTime dtCurrent = DateTime.Now;
            long CurrentTimeInTicks = dtCurrent.TimeOfDay.Ticks;
            int currentDayOfWeek = (int)dtCurrent.DayOfWeek;
            string filterExpression = "Enabled = true AND TimeInTicks >= " 
                + CurrentTimeInTicks + " AND Days LIKE '%" + 
                Convert.ToString(currentDayOfWeek) + "%'";
            string sortExpression = "TimeInTicks DESC";
            DataRow[] drActiveAlarms = 
              dsAlarms.Tables[0].Select(filterExpression, 
              sortExpression);

            if(drActiveAlarms.Length > 0)
            {
                DataRow drSelectedAlarm = drActiveAlarms[0];
                long SelectedTimeInTicks = 
                  Convert.ToInt64(drSelectedAlarm["TimeInTicks"]);

                if(SelectedTimeInTicks > CurrentTimeInTicks)
                {
                    TimeSpan tsSelectedTime = new TimeSpan(SelectedTimeInTicks);
                    TimeSpan tsCurrentTime = dtCurrent.TimeOfDay;

                    TimeSpan tsDifference = tsSelectedTime - tsCurrentTime;

                    if(tsDifference.TotalSeconds < 3600)
                    {
                        // set timerNotify to fire 1 minute before alarm time
                        // and set the AlarmExec to run after a minute

                        this.nextAlarmUp = Convert.ToInt32(drSelectedAlarm["ID"]);
                        this.timerNotify.Interval = 
                         Convert.ToInt32(tsDifference.TotalMilliseconds - 60*1000);
                        this.timerNotify.Enabled = true;
                    }
                    else
                    {
                        this.nextAlarmUp = 0;
                        this.timerNotify.Enabled = false;
                    }
                }
            }
        }
    }
}

private void timerNotify_Tick(object sender, System.EventArgs e)
{
    this.timerNotify.Enabled = false;
    Notify.RunAppAtTime(this.AppPath + "SDAlarmExec.exe", 
                             DateTime.Now.AddMinutes(1));
    this.ExecuteDisable();
    GetNextNotificationInfo();
}

Points of Interest

By default when you save data as XML, the file is created in the root directory. To get the application's path, you need to use Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName. This returns /Storage Card/Program Files/SmartAlarm.exe. Fetch the assembly name using Assembly.GetExecutingAssembly().GetModules()[0].Name and replace it with "" and you have the path.

I noticed the following points which might help when creating SmartPhone apps:

  • The Build Cab option fails. But it does create a Build.bat file under the Obj/Release folder (if you are doing a release build package).
  • Update the batch file for Cab generation to use C:\Program Files\Windows CE Tools\wce420\SMARTPHONE 2003\Tools\CabwizSP.exe instead of C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\bin\cabwiz.exe.
  • Copy vsd_setup.dll from C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE\wce400\armv4 to C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE\Smartphone\wce400\armv4.
  • Copy vsd_setup.dll from C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE\wce400\x86 to C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE\Smartphone\wce400\x86.
  • For OpenNetCF, manually install OpenNETCF.SDF.smp.armv4.cab in the phone.
  • If you are using OpenNetCF, the emulator doesn't work any more by default. If you would like to use the emulator add the OpenNetCF referenced assemblies as content in the Project.
  • Lastly when the DateTime control receives control, it doesn't allow any other elements to have focus that easily. That is the reason why I have set its receive focus at the very end. It's a limitation of the control.