Prince Sultan University College of Computer & Information Sciences Department of Computer Science CS387: Mobile Applications Development Win phone 7: Practice Sheet 3 Building a Simple FlashLight Application Purpose: The main purpose of this sheet is to build a simple flash light application that generates a pattern of light signals using a timer, Two types of signal patterns will be generated: SOS (base on Morse code) and a continuous strobe patterns. The user may switch between the two using a couple of application bar buttons. Include Namespaces: using System.Reflection; using System.Windows.Threading; using Microsoft.Phone.Shell; 1 The Setting Class again and the images: The Setting Class Again: using System.IO.IsolatedStorage; namespace FlashLight2 { // Encapsulates a key/value pair stored in Isolated Storage ApplicationSettings public class Setting<T> { string name; T value; T defaultValue; bool hasValue; public Setting(string name, T defaultValue) { this.name = name; this.defaultValue = defaultValue; } public T Value { get { // Check for the cached value if (!this.hasValue) { // Try to get the value from Isolated Storage if (!IsolatedStorageSettings.ApplicationSettings.TryGetValue( this.name, out this.value)) { // It hasn't been set yet this.value = this.defaultValue; IsolatedStorageSettings.ApplicationSettings[this.name] = this.value; 2 } this.hasValue = true; } return this.value; } set { // Save the value to Isolated Storage IsolatedStorageSettings.ApplicationSettings[this.name] = value; this.value = value; this.hasValue = true; } } public T DefaultValue { get { return this.defaultValue; } } // "Clear" cached value: public void ForceRefresh() { this.hasValue = false; } } } The main interface: The following XAML code describes the main interface. As clear from the code, the interface consists of an application bar with two buttons and a Menu Item List. <phone:PhoneApplicationPage.ApplicationBar> <!-- The ApplicationBar object: --> <shell:ApplicationBar Opacity=".5"> <!-- Two buttons: --> <shell:ApplicationBarIconButton Text="sos" IconUri="Images/sos.png" Click="SosButton_Click"/> <shell:ApplicationBarIconButton Text="strobe" IconUri="Images/strobe.png" Click="StrobeButton_Click"/> <!-- Eight menu items: --> <shell:ApplicationBar.MenuItems> <shell:ApplicationBarMenuItem Text="red"/> <shell:ApplicationBarMenuItem Text="orange"/> <shell:ApplicationBarMenuItem Text="yellow"/> <shell:ApplicationBarMenuItem Text="green"/> <shell:ApplicationBarMenuItem Text="cyan"/> <shell:ApplicationBarMenuItem Text="purple"/> <shell:ApplicationBarMenuItem Text="gray"/> <shell:ApplicationBarMenuItem Text="white"/> </shell:ApplicationBar.MenuItems> </shell:ApplicationBar> 3 </phone:PhoneApplicationPage.ApplicationBar> <!-- No content other than a solid background: --> <Grid x:Name="BackgroundGrid" Background="White"/> The main resources (three icon images): The icon images for three application bar buttons are provided for your convenience. These are: Cancel.png SOS.png Strobe.png They should be placed in a subdirectory within the solution explorer Declaring Top-Level Member variables and objects: // All three modes enum FlashlightMode { Solid, Sos, Strobe } The application bar buttons are a special type of buttons that should be declared directly in your code. Note that we declare two timers for the signaling process: // Members for the two application bar buttons: IApplicationBarIconButton sosButton; IApplicationBarIconButton strobeButton; // For the two special modes: SolidColorBrush onBrush; SolidColorBrush offBrush = new SolidColorBrush(Colors.Black); DispatcherTimer strobeTimer = new DispatcherTimer(); DispatcherTimer sosTimer = new DispatcherTimer(); int sosStep; // Remember the chosen color, for future app activations or launches: Setting<Color> savedColor = new Setting<Color>("SavedColor", Colors.White); // The current mode (Solid, Sos, or Strobe) FlashlightMode mode = FlashlightMode.Solid; Initialization (Constructor): As usual, the initialization is performed within the constructor: public MainPage() 4 { InitializeComponent(); // Assign application bar buttons to member fields, because this cannot be // done by InitializeComponent: this.sosButton = this.ApplicationBar.Buttons[0] as IApplicationBarIconButton; this.strobeButton = this.ApplicationBar.Buttons[1] as IApplicationBarIconButton; // Initialize the timer for strobe mode this.strobeTimer.Interval = TimeSpan.FromSeconds(.1); // Not too fast! this.strobeTimer.Tick += StrobeTimer_Tick; // Initialize the timer for SOS mode this.sosTimer.Interval = TimeSpan.Zero; this.sosTimer.Tick += SosTimer_Tick; // Attach the same Click handler to all menu items in the application bar foreach (IApplicationBarMenuItem menuItem in this.ApplicationBar.MenuItems) menuItem.Click += MenuItem_Click; // Restore persisted color this.onBrush = new SolidColorBrush(this.savedColor.Value); this.BackgroundGrid.Background = onBrush; } Programming the SOS Timer Event: void SosTimer_Tick(object sender, EventArgs e) { // Toggle the background, but also adjust the time between each tick in // order to make the dot-dot-dot-dash-dash-dash-dot-dot-dot pattern switch (this.sosStep) { case 1: case 3: case 5: // Each dot in the first S case 13: case 15: case 17: // Each dot in the second S this.BackgroundGrid.Background = this.onBrush; this.sosTimer.Interval = TimeSpan.FromSeconds(.2); // A short value break; case 7: case 9: case 11: // Each dash in the O this.BackgroundGrid.Background = this.onBrush; this.sosTimer.Interval = TimeSpan.FromSeconds(1); // A long value break; case 18: // The space between the end of one SOS // and the beginning of the next one this.BackgroundGrid.Background = this.offBrush; this.sosTimer.Interval = TimeSpan.FromSeconds(1); break; default: // The space between each dot/dash this.BackgroundGrid.Background = this.offBrush; this.sosTimer.Interval = TimeSpan.FromSeconds(.2); break; } 5 // Cycle from 0 - 18 this.sosStep = (this.sosStep + 1) % 19; } The Strobe Timer Event: void StrobeTimer_Tick(object sender, EventArgs e) { // Toggle the background on every tick if (this.BackgroundGrid.Background == this.onBrush) this.BackgroundGrid.Background = this.offBrush; else this.BackgroundGrid.Background = this.onBrush; } The SOS Button Click Event: // The Click handler for the SOS button void SosButton_Click(object sender, EventArgs e) { // First, reset the current state to solid mode FlashlightMode mode = this.mode; RestoreSolidMode(); // If we were already in SOS mode, then this click // cancels it and we are done if (mode == FlashlightMode.Sos) return; // Change to SOS mode // Change the button icon, the mode, a counter, and start the timer (sender as IApplicationBarIconButton).IconUri = new Uri("Images/cancel.png", UriKind.Relative); this.mode = FlashlightMode.Sos; this.sosStep = 0; this.sosTimer.Start(); } The Strobe-Button Click Event: // The Click handler for the strobe button void StrobeButton_Click(object sender, EventArgs e) { // First, reset the current state to solid mode FlashlightMode mode = this.mode; RestoreSolidMode(); // If we were already in strobe mode, then this click // cancels it and we are done if (mode == FlashlightMode.Strobe) return; // Show a warning 6 MessageBoxResult result = MessageBox.Show("Strobe lights can trigger " + "seizures for people with photosensitive epilepsy. " + "Are you sure you want to start the strobe light?", "Warning!", MessageBoxButton.OKCancel); // If the user agreed, change to strobe mode if (result == MessageBoxResult.OK) { // Change the button icon, the mode, and start the timer (sender as IApplicationBarIconButton).IconUri = new Uri("Images/cancel.png", UriKind.Relative); this.mode = FlashlightMode.Strobe; this.strobeTimer.Start(); } } The Menu Item Click Events: // The menu item Click handler that changes the flashlight color void MenuItem_Click(object sender, EventArgs e) { // Grab the text from the menu item to determine the desired color string chosenColor = (sender as IApplicationBarMenuItem).Text; // Use reflection to turn the color name (e.g. "red") into an actual Color Color c = (Color)typeof(Colors).GetProperty(chosenColor, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase). GetValue(null, null); // Persist this choice and set the background color this.savedColor.Value = c; this.onBrush = new SolidColorBrush(this.savedColor.Value); this.BackgroundGrid.Background = onBrush; } Reset Solid Mode: // Reset the state associated with mode switches void RestoreSolidMode() { this.strobeTimer.Stop(); this.sosTimer.Stop(); this.BackgroundGrid.Background = onBrush; this.sosButton.IconUri = new Uri("Images/sos.png", UriKind.Relative); this.strobeButton.IconUri = new Uri("Images/strobe.png", UriKind.Relative); this.mode = FlashlightMode.Solid; } 7 8