Chapter 9

Accessing the Device

What's In This Chapter:

  • Understanding the common hardware on the devices
  • Using input sensors
  • Playing multimedia files within applications

Smartphones and tablets include a lot of sensors and information. You can enhance the capabilities of today's applications to include a personalized experience. Today's devices can detect where users are, their velocity of travel, and the contact closest to their current location. The devices include multiple types of messaging tools such as voicemail, text messaging (SMS/MMS), instant messengers such as Skype, video conferencing with FaceTime, and raw TCP/IP communication using WiFi or cellular data networks. Application developers can count on their users having sophisticated hardware in their hands. This chapter explores how to access and use the ever-advancing hardware and software capabilities of today's smartphones.

The mobile application ecosystems have produced many creative apps today. Dropbox and a dozen other cloud storage services have created applications for viewing, editing, and syncing your documents between PCs and mobile computing devices. Services such as Evernote have sprung up to give users seamless integration of digital artifacts from brainstorming to note-taking sessions. Urbanspoon and other information delivery apps leverage location data to present prefiltered data based on location. Dragon Dictation and other voice recognition applications use a user's contacts to more accurately interpret speech, create messages, and initiate voice or text communications using the device's SMS or call functionality. And, with Nintendo being overtaken by both Android and iOS as the most popular gaming platforms, it's evident that accelerometers and multitouch touch screens demonstrate that user input is not limited to touching and typing. The opportunity to innovate with these new user-input capabilities appears limitless.

Many of today's devices come with touch-screen interfaces. Failing to include a touch screen has quickly become a huge detriment to gaining market share. However, eliminating the keyboard in lieu of a software keyboard remains a differentiator among mobile users; some users seek to buy a device with a physical keyboard, whereas others prefer devices with no hard keyboard.

Although keyboard input preferences differ among users, they all want quick and intuitive interactions. When designing user input strategies, one of the driving questions should be, “How can I eliminate clicks and text entry?” Developers can leverage the audio, video, geo-location, and messaging capabilities, along with Personal Information Manager (PIM) information to accelerate interaction with the device.

Beyond creating new ways to interact with applications, the enhanced capabilities of modern model devices give application developers more options for in-app content and accessibility. Applications can use built-in speakers to play instructional audio. You can use the built-in cameras to capture videos and still images, which you can then manipulate in the application or send to servers for off-device utilization or messaging. You can use the accelerometer for clever device simulations such as mimicking a virtual medical device's reaction to the hand motions of a surgeon. The sensory input isn't just for gaming and virtual device simulation. You can use it for simple tasks such as controlling the scroll speed as the user reads lengthy text.

Geo-location is probably the most used of the modern user input sensors. A large percentage of popular applications use the GPS chip available in all cell-enabled mobile devices today. In social media apps, it provides a mechanism for users to share their location without having to key in the information. For retail stores, it enables customers to easily locate the nearest store and to get directions to the store from their current location, as well as providing store information such as hours of operation and contact information such as phone numbers and web addresses. For enterprise applications, the most common use has been to assist with customer lookup or digitally record the location of tasks performed within the application. With the race currently raging among different smartphone and tablet hardware manufacturers, it's generally safe to assume that any well-received input sensors introduced into one platform will soon find their way into devices on all the platforms.

Utilizing Device Audio and Video Playback Capabilities

The initial uses of the audio and video capabilities have been playing music and capturing home video. Skype was the first to create a cross-platform video conferencing network that iOS, Android, and personal computers can use to participate. The Skype mobile applications demonstrate the capability of mobile devices to capture video and stream it in real time. That capability has been leveraged by many apps to capture and share videos — YouTube and Facebook are some of the most popular apps for video sharing. The camera has also been used in medical applications to capture video, manipulate it using complex algorithms, and display the altered video stream to help doctors analyze their patients' medical conditions.

WebEx and others have created networks through which participants can share screens and feed the handset's audio into a conference bridge. Line-of-business applications have included record-a-note features in which audio messages could be recorded, allowing feedback to be sent with less physical interaction on the mobile device.

Perhaps the most interesting approaches have been applications in which users can record audio and video that they can review to make shopping decisions or analyze performance. One app allows users to take a photo of themselves and then apply different apparel graphics over their image. This helps potential customers get a better virtual sense of how the product might look on them. Another, a sales coaching application, uses the audio functionality to capture practice sessions. The users record their best sales pitch and play it back to self-analyze their performance.

Capturing Audio

The audio capture code needs to reside in the individual view classes within your container projects. Each audio interface is particular to the platform and usually includes a user control onscreen that initiates the audio capture. For the purposes of demonstration, the following is a piece of code for capturing audio from each platform and storing it to an object or file that the controller and model code bases can work with.

iOS

iOS provides access to the device's microphone using the MonoTouch.AVFoundation.AVAudioRecorder object. The AVAudioRecorder can record for a specific duration or until the user stops it. The user then can pause and resume the recording as necessary. The object also reports input audio level data that the application can use.

Listing 9.1 shows a basic object you can use to record audio on an iOS device.

1.1
Listing 9.1: Accessing the mic on iOS
using System;
using MonoTouch.AVFoundation;
using MonoTouch.Foundation;
using MonoTouch.AudioToolbox;
using System.IO;

namespace DeviceAccess
{
  class MicAccess
  {
    public MicAccess()
    {
      _mic = new AVAudioRecorder();
    }

    public void StartRecordingFroMic(string FilePath)
    {
      // set some default values for recording settings
      NSObject[] keys = new NSObject[]
            {
                AVAudioSettings.AVSampleRateKey,
                AVAudioSettings.AVFormatIDKey,
                AVAudioSettings.AVNumberOfChannelsKey,
                AVAudioSettings.AVEncoderAudioQualityKey,
            };
      NSObject[] values = new NSObject[]
            {    
                NSNumber.FromFloat(44100.0f),
                NSNumber.FromInt32((int)AudioFileType.WAVE),
                NSNumber.FromInt32(1),
                NSNumber.FromInt32((int)AVAudioQuality.Max),
            };
      NSDictionary settings = NSDictionary.FromObjectsAndKeys(values, keys);

      // define a filename and location for the output file
      string fileName = FilePath;
      string path = 
        Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), fileName);
      outputFileUrl = NSUrl.FromFilename(path);

      // pass the configured url to the AVAudioRecorder object 
      NSError error = new NSError();
      _mic = AVAudioRecorder.ToUrl(outputFileUrl, settings, out error);

      if (error != null)
      {
        // prepare and start recording
        _mic.PrepareToRecord();
        _mic.Record();
      }
      else
      {
        throw new Exception("Error loading mic: " + error.ToString());
      }
    }

    public void StopRecording()
    {
      _mic.Stop();

      // prepare object for GC by calling dispose
      _mic.FinishedRecording += delegate
      {
        _mic.Dispose();
        _mic = null;
      };
    }

    AVAudioRecorder _mic;
    private NSUrl outputFileUrl;
  }
}

Found in the DeviceAccess/DeviceAccess.MT/MicAccess.cs file of the download

You can find API documentation at http://developer.apple.com/library/ios/#DOCUMENTATION/AVFoundation/Reference/AVAudioRecorder_ClassReference/Reference/Reference.html.

Android

The Android SDK provides access to the device's microphone using the Android.Media.MediaRecorder object. The object is configured with an audio source, audio file format, and audio file location. Much like the AVAudioRecorder object in iOS, Android's MediaRecorder object should be released as soon as the resource is no longer needed.

Listing 9.2 shows the same basic audio object written to work on the Android OS.

1.1
Listing 9.2: Accessing the mic on Android
using Android.Media;

namespace DeviceAccess
{
  class MicAccess
  {
    public void StartRecordingFroMic(string FileName)
    {
      // set some default values for recording settings
      _mic.SetAudioSource(AudioSource.Mic);
      _mic.SetOutputFormat(OutputFormat.Default);
      _mic.SetAudioEncoder(AudioEncoder.Default);

      // define a filename and location for the output file
      _mic.SetOutputFile(FileName);

      // prepare and start recording
      _mic.Prepare();
      _mic.Start();
    }

    public void StopRecording()
    {
      // stop recording
      _mic.Stop();

      // prepare object for GC by calling dispose
      _mic.Release();
      _mic = null;
    }

    public MicAccess()
    {
      _mic = new MediaRecorder();
    }
    MediaRecorder _mic;
  }
}

Found in the DeviceAccess/DeviceAccess.MD/MicAccess.cs file of the download

You can find API documentation at http://developer.android.com/guide/topics/media/audio-capture.html.

Windows Phone 7

Windows Phone 7 provides access to the device's microphone through the Microsoft.Xna.Framework.Audio.Microphone object. Setting up a Microphone object correctly includes calling FrameworkDispatcher.Update() to manually dispatch messages in the XNA Framework update message queue. You can do this on a timer loop or hand it off to an object implementing the IApplicationService interface with a DispatcherTimer and a Tick event handler.

Listing 9.3 shows the same basic audio object written to work on Windows Phone 7 devices.

1.1
Listing 9.3: Accessing the mic on Windows Phone 7
using System;
using System.IO;
using Microsoft.Xna.Framework.Audio;

namespace DeviceAccess
{
  public class MicAccess
  {
    public void StartRecordingFroMic(string FilePath)
    {
      // close memory steam if left open
      if (_memoryStream != null)
      {
        _memoryStream.Close();
      }

      // start a new memory stream
      _memoryStream = new MemoryStream();

      // start recording
      _mic.Start();
    }

    public void StopRecording()
    {
      // stop recording
      if (_mic.State != MicrophoneState.Stopped)
      {
        _mic.Stop();
      }

      // reset memory buffer
      _memoryStream.Position = 0;
    }

    void ProcessingBuffer(object sender, EventArgs e)
    {

      // read in read values
      byte[] buffer = new byte[4096];
      int bytesRead = _mic.GetData(buffer, 0, buffer.Length);

      // write and read mic buffer contents
      while (bytesRead > 0)
      {
        _memoryStream.Write(buffer, 0, bytesRead);
        bytesRead = _mic.GetData(buffer, 0, buffer.Length);
      }
    }

    public MicAccess()
    {
      _mic.BufferReady += ProcessingBuffer;
    }
    MemoryStream _memoryStream;
    Microphone _mic = Microphone.Default;
  }
}

Found in the DeviceAccess/DeviceAccess.WP/MicAccess.cs file of the download

You can find API documentation at http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.audio.microphone.aspx.

Playing Audio

Playing audio can be something an app does for several different reasons. An audible indicator can provide feedback for an invalid character, a successful touch event, or completion of a long data load. You can use audio for instructional purposes or as part of an on-screen product demonstration.

Playing sounds on the different platforms requires unique data calls. Because of this, the code must reside in the individual view classes implemented in each container. Listings 9-4, 9-5, and 9-6 provide some sample classes to help illustrate how each platform plays sounds.

iOS

You can play audio files from memory or via streaming using the MonoTouch.AVFoundation.AVAudioPlayer object. Apple provides APIs for playing sounds of any duration. You can loop the sound or merge it with several sounds. The API also includes data feedback for playback-level metering.

Listing 9.4 shows a basic audio object you can use to play sounds on iOS devices.

1.1
Listing 9.4: Playing audio on iOS
using System;
using MonoTouch.Foundation;
using MonoTouch.AVFoundation;

namespace DeviceAccess
{
  public class AudioAccess
  {
    public void StartAudioPlayback(string AudioFilePath)
    {
      NSError error = null;
      NSUrl audioFileUrl = NSUrl.FromFilename(AudioFilePath);
      _player = AVAudioPlayer.FromUrl(audioFileUrl, out error);
      if (_player != null)
      {
        _player.PrepareToPlay();
        _player.Play();
      }
      else
      {
        throw new Exception("Could not load Accelerometer sensor");
      }
    }

    public void StopAudioPlayback()
    {
      if (_player != null)
      {
        _player.Stop();
        _player.Dispose();
        _player = null;
      }
    }

    AVAudioPlayer Player
    {
      get
      {
        return _player;
      }
      set
      {
        _player = value;
      }
    }
    AVAudioPlayer _player;
  }
}

Found in the DeviceAccess/DeviceAccess.MT/AudioAccess.cs file of the download

You can find more information about the API at http://developer.apple.com/library/IOS/#documentation/AVFoundation/Reference/AVAudioPlayerClassReference/Reference/Reference.html.

Android

The Android OS SDK provides the MediaManager object to play sounds and videos from a variety of sources. You can use the Android.Media.MediaPlayer to view and manage the state machine, which is used for audio/video playback. If an error state occurs while playing the audio source, you need to either call the MediaManager.Reset() method or create a new MediaManager instance.

Listing 9.5 shows a basic audio object you can use to play sounds on an Android device.

1.1
Listing 9.5: Playing audio on Android
using System;
using System.IO;
using Android.Media;

namespace DeviceAccess
{
  public class AudioAccess
  {
    public void StartAudioPlayback(string AudioFilePath)
    {
      // first stop any active audio on MediaPlayer instance
      if (_player != null) { StopAudioPlayback(); }

      _player = new MediaPlayer();
      if (_player != null)
      {
        _player.SetDataSource(AudioFilePath);
        _player.Prepare();
        _player.Start();
      }
      else
      {
        throw new Exception("Could not load MediaPlayer");
      }
    }

    public void StopAudioPlayback()
    {
      if (_player != null)
      {
        if (_player.IsPlaying)
        {
          _player.Stop();
        }

        _player.Release();
        _player = null;
      }
    }

    public void MultiMediaPlayer(String path, String fileName)
    {
      //set up MediaPlayer    
      MediaPlayer mp = new MediaPlayer();

      try
      {
        mp.SetDataSource(Path.Combine(path, fileName));
      }
      catch (Exception e)
      {
        Console.WriteLine("");
      }
      try
      {
        mp.Prepare();
      }
      catch (Exception e)
      {
        Console.WriteLine(e);
      }
      mp.Start();
    }

    MediaPlayer _player;
  }
}

Found in the DeviceAccess/DeviceAccess.MD/AudioAccess.cs file of the download

You can find more information about the API at http://developer.android.com/reference/android/media/MediaPlayer.html.

Windows Phone 7

Windows Phone 7 can easily play sounds using the Microsoft.Xna.Framework.Audio.SoundEffect object. You can pass the audio source into the object using either a stream or a file path.

Listing 9.6 shows a snippet of code that can play a sound file on a Windows Phone 7 device.

1.1
Listing 9.6: Playing audio on Windows Phone 7
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;

namespace DeviceAccess
{
  public static class AudioAccess
  {
    public static void StartAudioPlayback(string AudioFilePath)
    {
      Stream stream = TitleContainer.OpenStream(AudioFilePath);
      SoundEffect effect = SoundEffect.FromStream(stream);
      FrameworkDispatcher.Update();
      effect.Play();
    }
  }
}

Found in the DeviceAccess/DeviceAccess.WP/AudioAccess.cs file of the download

You can find more information about the API at http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.audio.soundeffect.aspx.

Capturing Video

The camera functionality in today's smartphones makes them powerful for documenting events and presenting instructional content. A captured video can provide facial recognition, capture an image to better explain the layout of a physical space, or facilitate communication in countless other fashions. A picture can be worth a thousand words. Video capture is most popular in communication applications, but its uses are just starting to manifest themselves.

Following are some sample classes to provide a basic understanding for how video capture occurs on each platform.

iOS

The AVCaptureSession object can facilitate video capture. The MonoTouch.AVFoundation.AVCaptureSession object handles the flow of data from the audio and video inputs of the iOS device. You can use the SessionPreset property to customize the bit rate and quality levels. The StartRunning() and StopRunning() methods control the flow of data.

Listing 9.7 shows a simple object with the AVCaptureSession object configured and receiving video.

1.1
Listing 9.7: Capturing video on iOS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MonoTouch.AVFoundation;
using MonoTouch.CoreMedia;
using MonoTouch.UIKit;
using MonoTouch.Foundation;
using MonoTouch.CoreVideo;

namespace DeviceAccess
{
  class VideoRecorder
  {
    public void RecordVideoToPath(UIViewController ViewController, string VideoPath)
    {
      // set up capture device 
      AVCaptureDevice videoRecordingDevice = 
        AVCaptureDevice.DefaultDeviceWithMediaType(AVMediaType.Video);
      NSError error;
      AVCaptureDeviceInput videoInput = new 
        AVCaptureDeviceInput(videoRecordingDevice, out error);

      // create and assign a capture session
      AVCaptureSession captureSession = new AVCaptureSession();
      captureSession.SessionPreset = AVCaptureSession.Preset1280x720;
      captureSession.AddInput(videoInput);

      // Create capture device output
      AVCaptureVideoDataOutput videoOutput = new AVCaptureVideoDataOutput();
      captureSession.AddOutput(videoOutput);
      videoOutput.VideoSettings.PixelFormat = CVPixelFormatType.CV32BGRA;
      videoOutput.MinFrameDuration = new CMTime(1, 30);
      videoOutput.SetSampleBufferDelegatequeue(captureVideoDelegate, 
        System.IntPtr.Zero);

      // create a delegate class for handling capture
      captureVideoDelegate = new CaptureVideoDelegate(ViewController);

      // Start capture session
      captureSession.StartRunning();
    }
    CaptureVideoDelegate captureVideoDelegate;

    public class CaptureVideoDelegate : AVCaptureVideoDataOutputSampleBufferDelegate
    {
      private UIViewController _viewController;

      public CaptureVideoDelegate(UIViewController viewController)
      {
        _viewController = viewController;
      }

      public override void DidOutputSampleBuffer(AVCaptureOutput output, 
        CMSampleBuffer buffer, AVCaptureConnection con)
      {
        //  Implement
        //  - see: http://go-
        //mono.com/docs/index.aspx?link=T%3aMonoTouch.Foundation.ModelAttribute
        //
      }
    }
  }
}

Found in the DeviceAccess/DeviceAccess.MT/VideoRecorder.cs.cs file of the download

You can find more information about the API at http://developer.apple.com/library/ios/#DOCUMENTATION/AVFoundation/Reference/AVCaptureSession_Class/Reference/Reference.html.

Android

You can achieve video capture on Android devices using the same MediaRecorder object used for playing audio. By deviating just a bit from the original AudioAndMicAccess sample class in Listing 9.2, it is easy to include video recording. Recoding video requires many of the same configuration options, with a few additions, to be set on the Android.Media.MediaRecorder object.

Listing 9.8 provides the code necessary to capture video on Android devices.

1.1
Listing 9.8: Capturing video on Android
using System;
using Android.App;
using Android.OS;
using Android.Views;
using Android.Media;

namespace DeviceAccess
{

  public class RecordVideo
  {
    public void RecordVideoToPath(SurfaceView Sv, string VideoPath)
    {
      // set up and configure recorder
      _mediaRecorder = new MediaRecorder();

      // set the input source
      _mediaRecorder.SetAudioSource(Android.Media.AudioSource.Mic);
      _mediaRecorder.SetVideoSource(Android.Media.VideoSource.Camera);

      // set encoding values 
      _mediaRecorder.SetAudioEncoder(Android.Media.AudioEncoder.Default);
      _mediaRecorder.SetVideoEncoder(Android.Media.VideoEncoder.Default);

      // set the desirable preview display 
      _mediaRecorder.SetPreviewDisplay(Sv.Holder.Surface);

      // set output file locationa and format
      _mediaRecorder.SetOutputFormat(Android.Media.OutputFormat.Default);
      _mediaRecorder.SetOutputFile(VideoPath);

      _mediaRecorder.Prepare();

    }

    public void StopRecording()
    {
      if (_mediaRecorder != null)
      {
        _mediaRecorder.Stop();
        _mediaRecorder.Release();
        _mediaRecorder = null;
      }
    }

    MediaRecorder _mediaRecorder;
  }}

Found in the DeviceAccess/DeviceAccess.MD/VideoRecorder.cs file of the download

You can find more information about the API at http://developer.android.com/reference/android/media/MediaRecorder.html.

Windows Phone 7

Video capture is achieved on Windows Phone devices using the FileSink object. Providing an IsolatedStorageFileStream object from the start is required to store the recorded video, as shown in Listing 9.9.

1.1
Listing 9.9: Capturing video on Windows Phone 7
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;

namespace DeviceAccess
{
  public class VideoAccess
  {
    // Source and device for capturing video.
    CaptureSource captureSource;
    VideoCaptureDevice videoCaptureDevice;

    // File details for storing the recording.        
    IsolatedStorageFileStream isoVideoFile;
    FileSink fileSink;

    // Viewfinder for capturing video.
    VideoBrush videoRecorderBrush;

    public void StartRecording(Rectangle viewfinderRectangle, string filePath)
    {
      InitializeVideoRecorder(viewfinderRectangle);

      // Connect fileSink to captureSource.
      if (captureSource.VideoCaptureDevice != null
          && captureSource.State == CaptureState.Started)
      {
        captureSource.Stop();

        // Connect the input and output of fileSink.
        fileSink.CaptureSource = captureSource;
        fileSink.IsolatedStorageFileName = filePath;
      }

      // Begin recording.
      if (captureSource.VideoCaptureDevice != null
          && captureSource.State == CaptureState.Stopped)
      {
        captureSource.Start();
      }
    }

    public void StopRecording()
    {
      if (captureSource != null)
      {
        // Stop captureSource if it is running.
        if (captureSource.VideoCaptureDevice != null
            && captureSource.State == CaptureState.Started)
        {
          captureSource.Stop();
        }

        // Remove the event handlers for captureSource and the shutter button.
        captureSource.CaptureFailed -= OnCaptureFailed;

        // Remove the video recording objects.
        captureSource = null;
        videoCaptureDevice = null;
        fileSink = null;
        videoRecorderBrush = null;
      }
    }

    void InitializeVideoRecorder(Rectangle viewfinderRectangle)
    {
      if (captureSource == null)
      {
        // Create the VideoRecorder objects.
        captureSource = new CaptureSource();
        fileSink = new FileSink();

        videoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();

        // Add eventhandlers for captureSource.
        captureSource.CaptureFailed += new 
EventHandler<ExceptionRoutedEventArgs>(OnCaptureFailed);

        // Initialize the camera if it exists on the device.
        if (videoCaptureDevice != null)
        {
          // Create the VideoBrush for the viewfinder.
          videoRecorderBrush = new VideoBrush();
          videoRecorderBrush.SetSource(captureSource);

          // Display the viewfinder image on the rectangle.
          viewfinderRectangle.Fill = videoRecorderBrush;

          // Start video capture and display it on the viewfinder.
          captureSource.Start();
        }
        else
        {
          // A camera is not supported on this device
        }
      }
    }

    void OnCaptureFailed(object sender, ExceptionRoutedEventArgs e)
    {
    }
  }
}
}

Found in the DeviceAccess/DeviceAccess.WP/VideoAccess.cs.cs file of the download

You can find more information about the API at http://msdn.microsoft.com/library/system.windows.media.filesink.aspx

Playing Video

Displaying video on a smartphone can be a great tool for sales presentations, self-service product demo applications, collaboration applications, and instructional applications. The lower resolution screens make packaging video less memory-intensive because they don't typically need to display videos in 1080p quality. With the storage space these devices are starting to come bundled with, storing multiple 1080p videos is still a possibility.

iOS

Playing videos within an iOS app is simple. The MonoTouch.MediaPlayer.MPMoviePlayerController object takes an NSUrl object. With a simple URL or file path, you can instantiate the MPMoviePlayerController object, and a simple call to a Play() method can initialize video playback.

This is a simple code snippet. You need to add the object to a view and its frame set:

using MonoTouch.MediaPlayer;

var videoPlayer = new MPMoviePlayerController(new NSUrl("myVideo.m4v"));
videoPlayer.Play ();

You can find more information about the API at http://developer.apple.com/library/IOs/#documentation/MediaPlayer/Reference/MPMoviePlayerController_Class/Reference/Reference.html.

Android

You can also use the versatile Android.Media.MediaPlayer object to view video playback and manage its state machine. If an error state occurs while playing the video source, you need to either call the MediaManager.Reset() method or create a new MediaManager instance, just as you did for playing audio files.

Listing 9.10 shows a method you can use to play videos on an Android device.

1.1
Listing 9.10: Playing video on Android
using Android.Views;
using Android.Media;

namespace DeviceAccess
{
  class VideoPlayer
  {
    public void StartVideoPlayback(SurfaceView surface, string FilePath)
    {
      if (_player != null)
      {
        StopVideoPlayback();
      }
      _player = new MediaPlayer();

      ISurfaceHolder holder = surface.Holder;
      holder.SetType(Android.Views.SurfaceType.PushBuffers);
      holder.SetFixedSize(400, 300);

      _player.SetDisplay(holder);
      _player.SetDataSource(FilePath);
      _player.Prepare();
      _player.Start();
    }

    public void StopVideoPlayback()
    {
      if (_player != null)
      {
        if (_player.IsPlaying)
        {
          _player.Stop();
        }

        _player.Release();
        _player = null;
      }
    }
    MediaPlayer _player;
  }
}

Found in the DeviceAccess/DeviceAccess.MD/VideoPlayer.cs file of the download

You can find more information about the API at http://developer.android.com/reference/android/media/MediaPlayer.html.

Windows Phone 7

Playing videos within a Windows Phone application is easy. The MediaPlayerLauncher object takes just a few parameters. You can play the video from isolated storage or from the application's directory, which is bundled with the .xap file. Listing 9.11 provides the code necessary.

1.1
Listing 9.11: Playing video on Windows Phone 7
using System;
using Microsoft.Phone.Tasks;

namespace DeviceAccess
{
  public class VideoPlayer
  {
    public void StartAudioPlayback(string AudioFilePath)
    {
      MediaPlayerLauncher objMediaPlayerLauncher = new MediaPlayerLauncher();
      objMediaPlayerLauncher.Media = new Uri(AudioFilePath, UriKind.Relative);
      objMediaPlayerLauncher.Location = MediaLocationType.Install;
      objMediaPlayerLauncher.Controls = MediaPlaybackControls.Pause | 
        MediaPlaybackControls.Stop | MediaPlaybackControls.All;
      objMediaPlayerLauncher.Orientation = MediaPlayerOrientation.Landscape;
      objMediaPlayerLauncher.Show();
    }
  }
}

Found in the DeviceAccess/DeviceAccess.WP/VideoPlayer.cs file of the download

Contacts and Calendar

The root of today's smartphone is the Personal Information Manager (PIM). These electronic PIMs came in the form of Apple's Newton, the HP Jornada line, and of course the PalmPilot. These devices specialized in contact, task, and calendar management.

The iPhone, Android, and Windows Phone 7 devices all come with build-in PIM software. Each of the devices offers a different level of access to the contacts and calendar events stored on the device.

Accessing Contacts

To access the contacts on the devices, some of the platforms require special provisioning to be compiled into the application at build time. The process and level of access is different between the platforms, but ultimately some degree of access is available on all three platforms.

iOS

The ABAddressBook object provides access to the device's contact list. Each contact has a list of numbers, consisting of a label (that is, home, work, iPhone, fax, and so on). Getting contact info from an iOS device is as simple as looping through the ABPerson contained in the Contacts of a iPhoneAddressBook, as illustrated in the following code:

ABAddressBook iPhoneAddressBook = new ABAddressBook();
ABPerson[] Contacts = iPhoneAddressBook.GetPeople();

foreach (ABPerson item in Contacts)
{
    ABMultiValue<NSDictionary> Contact  = item.GetPhones();
    foreach (ABMultiValueEntry<NSDictionary> cont in Contact)
    {
        // a label containing text describing the number
        // the phone number value

    }
}

Android

In Android 2.0, the storage mechanism for storing contacts was transitioned to the Android.Provider.ContactsContract object. The process includes a generic table for storing all contact information. Each row has a data kind that determines the values each of the columns hold.

Each row in the contact table represents data aggregated from one or more raw contact data elements. These RawContacts objects are aggregated by the API and presented in the contacts table. The Contacts API retrieves contacts using a lookup key or each contact.

Before you can access contact records in an application, you must add the following permissions to the AndroidManifest.xml file:

<uses-permission android:name="android.permission.READ_CONTACTS" />

Listing 9.12 shows how to access the contact on an Android device.

1.1
Listing 9.12: Looking up contacts on Android
using System;
using Android.App;
using Android.Content;
using Android.Provider;
using Android.Database;

namespace DeviceAccess
{
  class ContactsAccess : Activity
  {
    public IntPtr LookupContactByName(string name, ContentResolver cr)
    {
      IntPtr handle = IntPtr.Zero;

      ICursor cur = cr.Query(ContactsContract.Contacts.ContentUri,
                                          null, null, null, null);
      if (cur.Count > 0)
      {
        while (cur.MoveToNext())
        {
          int lookupColumn = 
            cur.GetColumnIndex(ContactsContract.ContactsColumnsConsts.LookupKey);
          int columnIndex = cur.GetColumnIndex(lookupColumn.ToString());
          String id = cur.GetString(columnIndex);

          string displayNameColumn = 
            ContactsContract.ContactsColumnsConsts.DisplayName.ToString();
          string displayNameColumnIndex = 
            cur.GetColumnIndex(displayNameColumn).ToString();
          String displayName = 
            cur.GetString(cur.GetColumnIndex(displayNameColumnIndex));
          if (displayName.Contains(name))
          {

            handle = cur.Handle;
          }
        }
      }
      return handle;
    }
  }
}

Found in the DeviceAccess/DeviceAccess.MD/ContactsAccess.cs file of the download

For more information about the API, go to http://developer.android.com/resources/articles/contacts.html.

Windows Phone 7

The Windows Phone platform provides read-only access to contacts, combining the contact list from all the accounts configured on the account. The list is available in the API through the Microsoft.Phone.UserData.Contacts class. Using the Contacts class, perform an asynchronous search and work with the results in the event handler.

Listing 9.13 shows how to access the contacts on a Windows Phone device.

1.1
Listing 9.13: Looking up contacts on Windows Phone 7
using System;
using Microsoft.Phone.UserData;
using System.Collections.Generic;
using System.Threading;

namespace DeviceAccess
{
  public class ContactsAccess
  {
    public List<string> LookupContactName(string Name)
    {
      // create a contact object and it's search handler
      Contacts cons = new Contacts();
      cons.SearchCompleted += new 
            EventHandler<ContactsSearchEventArgs>(Contacts_SearchCompleted);

      // start the search
      cons.SearchAsync(Name, FilterKind.None, string.Empty);

      // block on the search until the async result return
      List<string> results;
      lock (_locker)
      {
        while (name == null)
        {
          Monitor.Pulse(_locker);
        }

        results = name;
        name = null;
      }
      return results;
    }
    static readonly object _locker = new object();
    List<string> name = null;

    void Contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
    {
      lock (_locker)
      {
        // reset list
        name = new List<string>();

        // build new result list
        if (e.Results != null)
        {
          var en = e.Results.GetEnumerator();
          while (en.MoveNext())
          {
            name.Add(en.Current.DisplayName);
          }
        }
        Monitor.Pulse(_locker);
      }
    }
  }
}

Found in the DeviceAccess/DeviceAccess.WP/ContactsAccess.cs file of the download

For more information about the API, go to http://msdn.microsoft.com/en-us/library/hh286414.

Messaging and Communication

Receiving phones call while away from home might have been the root problem cell phones solved. And, the Blackberry solved the problem of receiving e-mails while away from the office. Today's smartphones keep you connected across a multitude of mediums. Facebook, Twitter, e-mail, and SMS all have their place as communication tools. Ultimately voice communication continues to hold a strong position in the communication tool hierarchy.

Initiating a Voice Call

For applications attempting to hook into the devices' phone capabilities, the API for initiating a phone call is easy. In Android, the user still needs to confirm the action before an application can initiate a phone call. For iOS the API provides the option for a user confirmation first. Windows Phone dials immediately.

iOS

Kicking off a data call from inside iOS is simple. Using iOS applications, you can take advantage of deep linking as a simple COM communication style. The operating system can marshal navigation requests to other applications based on registering applications with URI protocols. The built-in phone application in iOS uses the tel protocol, so a URI starting with “tel://” routes to the phone application for handling.

Listing 9.14 shows how to initiate a phone call on an iOS device.

1.1
Listing 9.14: Initiating a phone call on cell-enabled iOS devices
using MonoTouch.Foundation;
using MonoTouch.UIKit;

namespace DeviceAccess
{
  class PhoneAccess
  {
    public bool DialNumber(string PhoneNumber, bool DisplayWarning)
    {
      bool successfulDialing = false;

      // Kick off dialing using iOS's deep linking
      NSUrl url = new NSUrl("tel:" + PhoneNumber);
      if (UIApplication.SharedApplication.OpenUrl(url))
      {
        successfulDialing = true;
      }
      else if (DisplayWarning)
      {
        UIAlertView av =
            new UIAlertView("Dialing Failed", "Dialing not supported", 
              null, "OK", null);
        av.Show();
      }

      return successfulDialing;
    }
  }
}

Found in the DeviceAccess/DeviceAccess.MT/PhoneAccess.cs file of the download

Android

Displaying a confirmation dialog that initiates a phone call is as simple as starting an activity with an Intent of Intent.ActionDial and a phone number wrapped in a Uri:

string uri = string.Format("tel:{0}", "612-555-1212");
Android.Net.Uri phoneNumber = Android.Net.Uri.Parse(uri);
StartActivity(new Intent(Intent.ActionDial, phoneNumber));

Windows Phone 7

Initiating phone calls from within a Windows Phone application is just as easy as initiating a call with an iOS. The mechanics are different, but the resulting lines of code are similar.

The Windows Phone SDK provides Tasks for the application to initiate, which are available in the Microsoft.Phone.Tasks namespace. You set up the PhoneCallTask by initiating its no argument constructor and assigning the PhoneNumber property a string. This starts the phone application in the background and displays a confirmation dialog only when the Show() method is called. When the method is called, the user must interact with a dialog box to initiate the phone call.

Listing 9.15 shows how to initiate a phone call on a device running Windows Phone 7.

1.1
Listing 9.15: Initiating a phone call on Windows Phone 7 devices
using Microsoft.Phone.Tasks;

namespace DeviceAccess
{
  class PhoneAccess
  {
    public void DialNumber(string PhoneNumber)
    {
      PhoneCallTask task = new PhoneCallTask();
      task.PhoneNumber = "651-555-1212";
      task.Show();
    }
  }
}

Found in the DeviceAccess/DeviceAccess.WP/PhoneAccess.cs file of the download

For more information about the API, go to http://msdn.microsoft.com/en-us/library/hh286414.

Geo-location

The GPS chip is, by far, the piece of hardware used most frequently by smartphone apps. Store locators and turn-by-turn navigation apps were the first to arrive on the scene. Applications such as iHeartRadio and other content providers use the GPS chip to filter advertising based on the end user's location. A recent legal controversy has centered on whether in-app advertisement can be delivered based on the user's immediate location. This would allow the local bakery to request their ads be displayed only on devices within a few block radius. The hope is that customers in the vicinity would receive notification of the deals at a time when they could immediately take advantage of them. Store applications have even tried using the GPS functionally to reward shopper loyalty based on number of “check-ins.” In the end, users dictate the level of privacy they wish through user feedback.

Getting GPS Location Information

You can retrieve location information in several ways, but it's generally best to set up a GPS listener in your application code and then reference the listener's cached data values when the view needs to display them.

iOS

GPS information is available to iOS applications using the MonoTouch.CoreLocation.CLLocationManager class. The application must instantiate the CLLocationManager and assign it a delegate to monitor and store data points broadcast through the event.

Listing 9.16 shows how to get heading information from the onboard GPS.

1.1
Listing 9.16: Getting location information on an iOS device
using System.Threading;
using MonoTouch.CoreLocation;

namespace DeviceAccess
{
  public class GpsAccess
  {
    public string ReturnHeading()
    {
      if (_lm != null)
      {
        _lm = new CLLocationManager();
        _lm.UpdatedHeading += HeadingUpdateDelegate;
        _lm.StartUpdatingHeading();
      }

      // block on the search until the async result return
      string result;
      lock (_locker)
      {
        while (location == null)
        {
          Monitor.Pulse(_locker);
        }

        result = location;
        location = null;
      }
      return result;
    }

    void CompassCancel()
    {
      if (_lm != null)
      {
        _lm.StopUpdatingHeading();
        _lm.Dispose();
        _lm = null;
      }
    }
    static CLLocationManager _lm;

    private void HeadingUpdateDelegate(object sender, CLHeadingUpdatedEventArgs e)
    {
      lock (_locker)
      {
        // build new result list
        if (_lm != null && _lm.Heading != null)
        {
          location = _lm.Heading.MagneticHeading.ToString();
        }
        Monitor.Pulse(_locker);
      }
    }
    static readonly object _locker = new object();
    string location = null;
  }
}

Found in the DeviceAccess/DeviceAccess.MT/GpsAccess.cs file of the download

For more information about the API, go to http://developer.apple.com/library/mac/#documentation/CoreLocation/Reference/CLLocationManager_Class/CLLocationManager/CLLocationManager.html.

Android

The Android SDK provides access to device sensors through the SensorManager class. The Android.Hardware.SensorManager class can listen to a type of sensor or all the sensors by calling the GetDefaultSensor(SensorType) method on a SensorManager object. The SensorManager requires a registered listener before the application can receive the sensor's data. For the compass functionality, you must set up a listener for the SensorType.Proximity type. The listener is provided information in the Value property of the SensorEvent. Listing 9.17 displays how to set up a listener and read the heading out.

1.1
Listing 9.17: Getting location information on an Android device
using System;
using System.Threading;
using Android.Content;
using Android.Hardware;

namespace DeviceAccess
{
  public class CompassSensor : ISensorEventListener
  {
    protected string ReturnHeading()
    {
      SensorManager sm = _context.GetSystemService(Context.SensorService) 
        as SensorManager;

      Sensor sensor = sm.GetDefaultSensor(SensorType.Orientation);
      if (sensor != null)
      {
        sm.RegisterListener(this, sensor, SensorDelay.Ui);
      }

      // block on the search until the async result return
      string heading;
      lock (_locker)
      {
        while (location == null)
        {
          Monitor.Pulse(_locker);
        }

        heading = location;
        location = null;
      }
      return heading;
    }

    protected void CompassCancel()
    {
      SensorManager sm = _context.GetSystemService(Context.SensorService) 
        as SensorManager;
      Sensor sensor = sm.GetDefaultSensor(SensorType.Orientation);
      sm.UnregisterListener(this, sensor);
    }

    public void OnSensorChanged(SensorEvent e)
    {
      if (e.Sensor.Type == SensorType.Proximity)
      {
        lock (_locker)
        {

          location = e.ToString();
          Monitor.Pulse(_locker);
        }
      }
    }
    static readonly object _locker = new object();
    string location = null;

    Context _context;

    public void OnAccuracyChanged(Sensor sensor, int accuracy)
    {
      return;
    }

    public IntPtr Handle
    {
      get { throw new NotImplementedException(); }
    }
  }
}

Found in the DeviceAccess/DeviceAccess.MD/GpsAccess.cs file of the download

You can find more information about the API at http://developer.android.com/reference/android/hardware/SensorManager.html.

Windows Phone 7

The Window Phone SDK provides access to the Compass class. You can configure the Microsoft.Devices.Sensors.Compass class for different intervals, depending on the latency requirements of the application. An event handler provides the compass readings. The SensorReadingEventArgs object includes properties such as the TrueHeading. Listing 9.18 shows how to set up the event hander and receive compass readings.

1.1
Listing 9.18: Getting location information on a Windows Phone 7 device
using System;
using System.Threading;
using Microsoft.Devices.Sensors;

namespace DeviceAccess
{
  public class GpsAccess
  {
    static Compass _compass;

    public string ReturnHeading()
    {
      if (!Compass.IsSupported)
      {
        throw new Exception("Could not load Compass");
      }
      if (_compass == null)
      {
        _compass = new Compass();
        _compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(100);
        _compass.CurrentValueChanged += new 
           EventHandler<SensorReadingEventArgs<CompassReading>>(_compass_CurrentValueChanged);
        try
        {
          _compass.Start();
        }
        catch (InvalidOperationException e)
        {
          if (_compass != null)
          {
            _compass.Dispose();
            _compass = null;
          }

          throw new Exception("Could not initiate compass readings", e);
        }
      }

      string trueHeading = null;
      if (_compass != null)
      {
        // block on the search until the async result return
        lock (_locker)
        {
          while (_trueHeading == null)
          {
            Monitor.Pulse(_locker);
          }

          trueHeading = _trueHeading;
          _trueHeading = null;
        }
      }
      return trueHeading;
    }
    static readonly object _locker = new object();
    string _trueHeading = null;

    void _compass_CurrentValueChanged(object sender, 
      SensorReadingEventArgs<CompassReading> e)
    {
      lock (_locker)
      {

        _trueHeading = e.SensorReading.TrueHeading.ToString();
        Monitor.Pulse(_locker);
      }
    }

    void CompassCancel()
    {
      if (_compass != null)
      {
        _compass.Stop();
        _compass.Dispose();
        _compass = null;
      }
    }
  }
}

Found in the DeviceAccess/DeviceAccess.WP/GpsAccess.cs file of the download

For more information about the API, go to http://msdn.microsoft.com/en-us/library/microsoft.devices.sensors.compass.

Accelerometer

The accelerometer is popular in gaming applications. You can easily use it to control navigation on the screen, but to date, it has been most successfully implemented in games in which screen navigation is continuous. Reading applications use the sensor to control scrolling speeds. In addition, the accelerometer is useful in applications that use a “shake” movement to kick off a data refresh or initiate a particular workflow, such as refreshing the screen to default values.

Getting X, Y, and Z

For the most part, the accelerometer APIs on each of the platforms provide the same information. It's all about the X, Y, and Z values. Once the values are retrieved, it's up to the application developer to make the application respond creatively.

iOS

Accelerometer information is available on iOS devices using the MonoTouch.UIKit.UIAccelerometer class. The UIAccelerometer object can provide X, Y, and Z value updates based on a specified interval. When an update delegate is assigned, it is provided the XYZ information in the UIAccelerometerEventArgs object passed to it.

Listing 9.19 shows how to get accelerometer information from an iOS device.

1.1
Listing 9.19: Getting accelerometer information on an iOS device
using MonoTouch.UIKit;
using System.Threading;

namespace DeviceAccess
{
  public class AccelerometerAccess
  {
    public string ReturnXyz()
    {
      if (UIAccelerometer.SharedAccelerometer != null)
      {
        UIAccelerometer.SharedAccelerometer.UpdateInterval = 0.05;
        UIAccelerometer.SharedAccelerometer.Acceleration += XyzUpdateDelegate;
      }

      // block on the search until the async result return
      string result;
      lock (_locker)
      {
        while (_xyz == null)
        {
          Monitor.Pulse(_locker);
        }

        result = _xyz;
        _xyz = null;
      }
      return result;
    }
    static readonly object _locker = new object();
    string _xyz = null;

    private void XyzUpdateDelegate(object sender, UIAccelerometerEventArgs e)
    {
      lock (_locker)
      {
        _xyz =
            string.Format("{0:0.00}, {1:0.00}, {2:0.00})", 
              e.Acceleration.X, e.Acceleration.Y, e.Acceleration.Z);

        Monitor.Pulse(_locker);
      }
    }

    void AccelerometerCancel()
    {
      UIAccelerometer.SharedAccelerometer.UpdateInterval = 0.0;
    }

  }
}

Found in the DeviceAccess/DeviceAccess.MT/AccelerometerAccess.cs file of the download

You can find more information about the API at http://iosapi.xamarin.com/index.aspx?link=C%3AMonoTouch.UIKit.UIAccelerometer.

Android

The Android SDK provides access to the device's accelerometer information using the same SensorManager class as the GPS. The Android.Hardware.SensorManager class can listen to accelerometer information by calling the GetDefaultSensor(SensorType) method and passing it the SensorType.Accelerometer value. Once the sensor is retrieved, set up a listener to read in and evaluate the Value property of the SensorEvent. Listing 9.20 contains a class that registers and unregisters a listener from the accelerometer sensor.

1.1
Listing 9.20: Getting accelerometer information on an Android device
using System;
using Android.Content;
using Android.Hardware;

namespace DeviceAccess
{
  class AccelerometerSensor : ISensorEventListener
  {
    Context _context;

    protected void AccelerometerStart()
    {
      SensorManager sm = _context.GetSystemService(Context.SensorService) 
as SensorManager;
      Sensor sensor = sm.GetDefaultSensor(SensorType.Accelerometer);
      if (sensor != null)
      {
        sm.RegisterListener(this, sensor, SensorDelay.Ui);
      }
      else
      {
        throw new Exception("Could not load Accelerometer sensor");
      }
    }

    void AccelerometerCancel()
    {
      SensorManager sm = _context.GetSystemService(Context.SensorService) 
        as SensorManager;
      Sensor sensor = sm.GetDefaultSensor(SensorType.Accelerometer);
      sm.UnregisterListener(this);
    }

    public void OnAccuracyChanged(Sensor sensor, int accuracy)
    {
    }

    public void OnSensorChanged(SensorEvent e)
    {
      string js = string.Empty;
      switch (e.Sensor.Type)
      {
        case SensorType.Accelerometer:
          js = string.Format(
            "javascript:accelerometer.onAccelerometerSuccess({0:0.00})",
              e.Values[0], e.Values[1], e.Values[2]);
          break;
      }
      if (js.Length > 0)
      {

      }
    }

    public IntPtr Handle
    {
      get { throw new NotImplementedException(); }
    }
  }
}

Found in the DeviceAccess/DeviceAccess.MD/AccelerometerAccess.cs file of the download

For more information about the API, go to http://developer.android.com/reference/android/hardware/SensorManager.html.

Windows Phone 7

The Window Phone SDK provides access to the accelerometer information using the Accelerometer class, as shown in Listing 9.21. The Microsoft.Devices.Sensors.Accelerometer class provides an event that a handler can be wired to and be provided with the accelerometer's readings. The same SensorReadingEventArgs object used with GPS data includes properties for the accelerometer's X, Y, and Z values.

1.1
Listing 9.21: Getting accelerometer information on a Windows Phone 7 device
using System;
using System.Threading;
using Microsoft.Devices.Sensors;

namespace DeviceAccess
{
  public class AccelerometerAccess
  {
    Accelerometer _accelerometer = new Accelerometer();

    public string GetAccelerometerXyz()
    {
      if (!Accelerometer.IsSupported)
      {
        throw new Exception("Not supported on this device");
      }
      try
      {
        // Start accelerometer for detecting compass axis
        _accelerometer = new Accelerometer();
        _accelerometer.CurrentValueChanged +=
          new EventHandler<SensorReadingEventArgs<AccelerometerReading>>
                                      (_accelerometer_CurrentValueChanged);
        _accelerometer.Start();
      }
      catch (InvalidOperationException e)
      {
        throw new Exception("Error starting accelerometer", e);
      }

      string xyz = null;
      if (_accelerometer != null)
      {
        // block on the search until the async result return
        lock (_locker)
        {
          while (_xyz == null)
          {
            Monitor.Pulse(_locker);
          }

          xyz = _xyz;
          _xyz = null;
        }

      }
      return xyz;
    }
    static readonly object _locker = new object();
    string _xyz = null;

    private void _accelerometer_CurrentValueChanged(object sender, 
      SensorReadingEventArgs<AccelerometerReading> e)
    {
      lock (_locker)
      {
        _xyz = string.Format("{0:0.00}, {1:0.00}, {2:0.00}", 
                                e.SensorReading.Acceleration.X,
                                 e.SensorReading.Acceleration.Y,
                                  e.SensorReading.Acceleration.Z);
        Monitor.Pulse(_locker);
      }
    }

    public void AccelerometerCancel()
    {
      if (_accelerometer != null)
      {
        _accelerometer.Stop();
        _accelerometer.Dispose();
        _accelerometer = null;
      }
    }
  }
}

Found in the DeviceAccess/DeviceAccess.WP/AccelerometerAccess.cs file of the download

For more information about the API, go to http://msdn.microsoft.com/en-us/library/microsoft.devices.sensors.accelerometer.

Summary

In this chapter you dug into the assortment of APIs that exist for accessing a device's built-in hardware sensors and communication chipsets, as well as the user's PIM information stored on the device. Today's mobile apps are targeted for a user experience that blends into a user's workday and lifestyle. Smartphones and tablets are convenience and productivity devices with capabilities that only software can release. Creating the next wave of interactive software is here today. Developers are now empowered with new types of user inputs; computing systems are no longer shackled to keyboard and mouse input.

In the following chapter you learn about utility access. We discuss file storage systems and encrypting data. The discussion extends to network functionality, threading, and logging. In addition, the chapter touches on the serialization of objects to JSON or XML and how the MonoCross Utilities can simplify that effort when coding for cross-platform code portability.

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

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