Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

The Companion App- Search Engine, GeoLocation Browser and Language Translator at one Place

0.00/5 (No votes)
10 Jul 2013 1  
The Companion App: Its basically a utility app proposed for ultrabook which consists of the likes of a search tool, a geo location browsing tool and a language translator tool.

This article is an entry in our AppInnovation Contest. Articles in this sub-section are not required to be full articles so care should be taken when voting.

Introduction 

For the general introduction of the app, its basically a utility app which consists of the likes of a search tool, a geocode related tool and a translator tool. I have opted to showcase this app as an entry for the AppInnovation Contest.    

The users will have access to search, geocode browsing and a language translator all in one application. One will have the ease to search the web, find their current location and query nearby locations, inter translate different languages. the app will be like a companion to the user serving him with today's basic and most required needs of search, geocode browsing and translate. 

Background 

The app is basically powered by Microsoft Technologies and APIs offered by Microsoft. Moreover the app depends on the user's windows live account key as a prerequisite for access to the search, geocode and translator which the user would have to subscribe from Windows Azure market place at first (free or paid APIs based upon usage and requirement), for more details visit: http://datamarket.azure.com/

Initially, I had conceived the idea to develop this app for Windows Phone 7.1 and I am almost halfway through, but with Ultra books around, I have started transforming the same for the Windows 8 platform too. 

The Walk Through  

Below explained details are currently from my Windows phone app project and as I proceed with the explanation of the app, I will be listing out different modules that I would currently discuss. One can find tutorials on using search, geocode and translator APIs scattered throughout the web. I have used Bing APIs offered by Microsoft for my project. The modules are as follows:

1. Search Module  

2. Geocode Module 

3. Translator Module

A  little bit of GUI

Before proceeding into the modules I would discuss a little bit of GUI part for the app. With the advent of XAML, designing the UI has become a lot easier and fruitful with much less effort. Find the link for tutorial on XAML : http://msdn.microsoft.com/en-us/library/ms752059.aspx Here is my GUI code for one of the pages. 

HomePage.xaml

<phone:PhoneApplicationPage 
    x:Class="MyPhoneApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
    shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">
 
    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="123"/>
            <RowDefinition Height="645"/>
        </Grid.RowDefinitions>
 
        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Margin="0,17,0,644" Grid.RowSpan="2">
            <TextBlock x:Name="ApplicationTitle" Text="Bing Factory!" 
               FontSize="30" Style="{StaticResource PhoneTextNormalStyle}" Width="467" />
            <TextBlock x:Name="PageTitle" Text="Login" Margin="9,-7,0,0" 
              FontSize="50"  Style="{StaticResource PhoneTextTitle1Style}" 
              Height="74" Width="469" />
        </StackPanel>
 
        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Margin="0,0,0,34" Grid.Row="1">
        	<Grid RenderTransformOrigin="0.86,0.64" Margin="8">
        		<Grid.ColumnDefinitions>
        			<ColumnDefinition Width="154*"/>
        			<ColumnDefinition Width="154*"/>
        			<ColumnDefinition Width="154*"/>
        		</Grid.ColumnDefinitions>
        		<Grid.RowDefinitions>
        			<RowDefinition Height="198*"/>
        			<RowDefinition Height="198*"/>
        			<RowDefinition Height="198*"/>
        		</Grid.RowDefinitions>
        		<Image x:Name="img_Maps" Margin="8,29,8,30" 
        		    Source="Images/MapsLogo.png" Stretch="Fill" 
        		    Grid.Column="1" Tap="img_Maps_Tap" />
        		<Image x:Name="img_Search" Margin="8,29,8,30" 
        		  Source="Images/SearchLogo.png" Stretch="Fill" Tap="img_Search_Tap" />
        		<Image x:Name="img_Translator" Grid.Column="2" 
        		  Margin="8,29,8,30" Source="Images/TranslatorLogo.png" 
        		  Stretch="Fill" Tap="img_Translator_Tap" />
        	</Grid>
        </Grid>
    </Grid>
</phone:PhoneApplicationPage>

 Search.xaml

<phone:PhoneApplicationPage
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" 
    x:Class="MyPhoneApp.WebSearch"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="PortraitOrLandscape"  Orientation="Portrait"
    mc:Ignorable="d" d:DesignHeight="800" d:DesignWidth="480">    
 
    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
 
        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="The Companion" 
              Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="Search" 
              Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>
 
        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" >
            <Grid.RowDefinitions>
                <RowDefinition Height="80"/>
                <RowDefinition Height="71" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
 
            <TextBox Height="72" HorizontalAlignment="Center"  Margin="1,8,0,0" 
               x:Name="txt_SearchText" VerticalAlignment="Top" Width="455" VerticalContentAlignment="Top" />
            <Button x:Name="btn_Search" Content="Search" Margin="171,79,159,0" 
              VerticalAlignment="Top" HorizontalAlignment="Center" Grid.RowSpan="2" Click="btn_Search_Click" />
            <ScrollViewer x:Name="ContentScroll" Grid.Row="2" 
                      HorizontalScrollBarVisibility="Auto" FontSize="18.667">
            <toolkit:WrapPanel x:Name="WrapPanel_Type" Height="72" Margin="1,14,0,0" 
                      Grid.Row="2" Width="364" VerticalAlignment="Top" HorizontalAlignment="Center" >
        		<CheckBox x:Name="chk_Web" Content="Web"  HorizontalAlignment="Center" 
        		  FontSize="18.667" Margin="0,0,0,-2" VerticalAlignment="Center" Checked="chk_Web_Checked" />
        		<CheckBox x:Name="chk_Images" Content="Images" Width="134" 
        		   HorizontalAlignment="Center" FontSize="18.667" Margin="4,0,0,-2" 
        		   VerticalAlignment="Center" Checked="chk_Images_Checked" />
        		<CheckBox x:Name="chk_Videos" Content="Video" Width="117" 
        		   HorizontalAlignment="Center" FontSize="18.667" Margin="250,-70,0,0" 
        		   Height="72" VerticalAlignment="Center" RenderTransformOrigin="0.752,0.486" 
        		   HorizontalContentAlignment="Center" Checked="chk_Videos_Checked" />
        	</toolkit:WrapPanel>
                </ScrollViewer>
            <ScrollViewer x:Name="ScrollViewerResult" Margin="11,16,5,0" Grid.Row="3">
 
                <ListBox x:Name="ListBox_Result" Height="382" HorizontalAlignment="Left" 
                     Margin="1,1,0,0"  VerticalAlignment="Top" Width="Auto" >
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Vertical" Width="Auto" Height="Auto" >
                                <TextBlock Style="{StaticResource PhoneTextNormalStyle}" 
                                  Text="{Binding Title}" TextWrapping="Wrap"/> 
                                <TextBlock Style="{StaticResource PhoneTextNormalStyle}" 
                                  Text="{Binding Description}" TextWrapping="Wrap" />
                                <TextBlock Style="{StaticResource PhoneTextNormalStyle}" 
                                  Text="{Binding DisplayUrl}" TextWrapping="Wrap" />
                                <TextBlock Style="{StaticResource PhoneTextNormalStyle}" 
                                  Text="{Binding Url}" TextWrapping="Wrap" Tap="Link_Tap" />
                                <Image Source="{Binding MediaUrl}" MaxHeight="40" MaxWidth="40"></Image>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
 
            </ScrollViewer>
        </Grid>
    </Grid>
</phone:PhoneApplicationPage>

Search

Regarding xaml code, I would like to add a point here that to make the UI much interactive, xaml provides transitions effects that would make the app look cool as the user navigates from one page to other. For transitions to take effect in a silverlight app, one has to initialize the rootframe from transitionframe rather from phoneapplicationframe in the InitializePhoneApplication method of app.xaml file. Then one can override the methods which are OnNavigatedTo and OnNavigatedFrom in the called pages for the transition to take place. For more details of transitions refer tutorial: http://www.codeproject.com/Articles/345129/Windows-Phone-7-Navigation-Transitions. There is much to explore regarding designing and effects using xaml code. I would say the more one explores, the more he gets surprised.

Registering APIs  

Now lets move on to some coding stuff. But before we actually start coding, we have to make sure that we have access to APIs that we would be using in our app. In order to use the bing APIs for search, maps and translator, it is required that one must have a windows live account. The live account is then used to register in Windows Azure Market Place for subscribing bing search and microsoft translator APIs. Once done with registration, go to My Account->Account Keys. Here you will find a default account key provided intially. You can also generate a new account key and can use it for authenticating with APIs as you would do using a default key.  

 Account Key 

For using bing maps API, go to https://www.bingmapsportal.com/ and register there with Windows Live Account. After registration, go to create or view keys and generate a key for your app. Now this key can be used to access bing maps API. 

Creating Map Key   
 

NOTE: We are done with registering APIs. As noticed for accessing the APIs we needed account key (for search and translator) and map key (for maps). In case of search and translator APIs they are limitedly free as compared to bing maps API. So I would make the user to configure their own account key in the application to allow them to access their own registered APIs(for search and translator) through the app and make the search and translator to work.  

The Search 

As registration for accessing bing search is complete, we can download the .NET Class Library (BingSearchContainer.cs) from Windows Azure Market place by visiting My Account->My Data->Bing search API page and include it in the project. The class library has inbuilt methods and setting variables for building the query for search pertaining to categories like web, images, video etc. 

From the same location My Account->My Data->Bing search API page, navigate to the "Explore this Dataset" link. Here the Search API url will be found as "Service root URL". Copy the same and use it in the project to execute the search queries. Few example codes are listed below for the reference. 

Private void btn_Search_Click(object sender, RoutedEventArgs e)
{
    try
    {
        var bingContainer = new BingSearchContainer(new Uri(
          "https://api.datamarket.azure.com/Data.ashx/Bing/Search/v1"));
        var accountKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
        
        bingContainer.Credentials = new NetworkCredential(accountKey, accountKey);
               
        if (chk_Web.IsChecked.Value)
        {
            var webQuery = bingContainer.Web(txt_SearchText.Text.Trim(), 
              "EnableHighlighting", "DisableQueryAlterations", 
              "en-IN", null, null, null, null);
            webQuery.BeginExecute(new AsyncCallback(this.WebResultLoadedCallback), webQuery); 
        }
        else if (chk_Images.IsChecked.Value)
        {
            var imageQuery = bingContainer.Image(txt_SearchText.Text.Trim(), 
              "EnableHighlighting","en-IN", null, null, null, string.Empty);
            imageQuery.BeginExecute(new AsyncCallback(this.ImageResultLoadedCallback), imageQuery); 
        }
        else if (chk_Videos.IsChecked.Value)
        {
            //Search the video results only
        }
        else
        {
            //Search All
        }
           
    }
    catch (Exception ex)
    { }

}

In the AsyncCallback method use the results fetched from the service API to bind the search result to any of the data container like grid, list view for displaying the search result.  

NOTE: For using the BingSearchContainer.cs class, it is required to add a reference to System.Data.Services.Client dll in the project. This dll is required to request to an Open Data Protocol (OData) service(Bing API service).For more details on oData protocol refer: http://www.codeproject.com/Articles/90365/An-overview-of-Open-Data-Protocol-OData.

 

The Geocode/Maps 

Bing maps can be used for development in two ways. Either we can use Bing Map Task or Bing Maps API. Incase of bing map task, we call the bing maps installed in the device whereas for Map control we embed the map in our application (Embeding a map in the application consumes more memory). Bing maps API can be used in scenarios where we have to customize the app as per our requirement.

Bing Maps Task: 

Launching map using map tasks provides us with the option to load the installed map app with default center location or a search term and zoom level. Once loaded, we have access to various features like, current position, navigation, search etc. offered by the default installed map app.

bingMapsTask.Center = new GeoCoordinate(<Latitiude>, <Longitude>);
bingMapsTask.ZoomLevel = 2;
bingMapsTask.Show();
bingMapsTask.SearchTerm="search term" ;
bingMapsTask.ZoomLevel = 2;
bingMapsTask.Show();  

Bing Maps API:

Using bing maps API requires embedding the map control provided in the tool box of visual studio. Put the maps control inside a layout and provide the Key that was generated during registration for bing maps api in the CredentialsProperty of the map control. 

<my:Map Height="309" HorizontalAlignment="Left" Margin="115,52,0,0" 
  Name="map1" CredentialsProvider="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" 
  VerticalAlignment="Top" Width="174" x:Name:Map/>

The location service is used to get the location for current position using the GeoCoordinateWatcher. GeocordiateWatcher can be used to update the position of the user whenever the geocode position changes. An example for using the Geocordinate watcher to fetch the current position and also when the current position changes is given below:

Private void GetCurrentPosition(object sender, RoutedEventArgs e) { if (watcher == null)
{
    watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High)
    {
        //---the minimum distance (in meters) to travel before the next
        MovementThreshold = 10
    };
                        
    //---event to fire when a new position is obtained---
    watcher.PositionChanged += new
    EventHandler<GeoPositionChangedEventArgs
    <GeoCoordinate>>(watcher_PositionChanged);

    //---event to fire when there is a status change in the location 
    
    watcher.StatusChanged += new
    EventHandler<GeoPositionStatusChangedEventArgs>
    (watcher_StatusChanged);
    watcher.Start();
}                    

protected void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
    switch (e.Status)
    {
        case GeoPositionStatus.Disabled:
            //Custom message
            break;
        case GeoPositionStatus.Initializing:
            //Custom message
        case GeoPositionStatus.NoData:
            //Custom message
            break;
        case GeoPositionStatus.Ready:
            //Custom message
            break;
    }
}        
 
protected void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
    if(!e.Position.Location.IsUnknown)
    //Add pushpin to the location data recieved.
    {
        this.map.Center = new GeoCoordinate(e.Position.Location.Latitude, e.Position.Location.Longitude);
        //Removing previous location pushpin
        if (this.map.Children.Count != 0)
        {
            var pushpin = map.Children.FirstOrDefault(p => (p.GetType() == 
              typeof(Pushpin) && ((Pushpin)p).Tag == "locationPushpin"));
            if (pushpin != null)
            { this.map.Children.Remove(pushpin); }
        }
        //Adding current location pushpin
        Pushpin locationPushpin = new Pushpin();
        locationPushpin.Tag = "You are here";
        locationPushpin.Location = watcher.Position.Location;
        this.map.Children.Add(locationPushpin);
        this.map.SetView(watcher.Position.Location, 12.0);
    }
}

One can also query a search location. In that case it is required to build a location search query in the form of rest service request, get the response and finally process the response. Either XML or JASON can be used for this request and response session with the map API rest service. Here's a very good example for creation of location query and processing response in XML: http://msdn.microsoft.com/en-us/library/hh534080.aspx.

//Example of a location query
string UrlRequest = "http://dev.virtualearth.net/REST/v1/Locations/" +
                                            queryString +
                                            "?output=xml" +
                                            " &key=" + BingMapsKey;      

After deserialization of the response, get the current latitude and longitude position and use it to set the new location on map and mark pushpin on it.

Note: For using the map control a reference to System.Devices.Location dll is required and to use the pushpin class a reference to Microsoft.Phone.Controls.Maps DLL is required MSDN.

The Translator

For language translation download the .NET Class Library (TranslatorContainer.cs) from Windows Azure Market place by visiting My Account->My Data->Microsoft Translator API page and include it in the project. The class library has inbuilt methods and setting variables for building the translation queries.

From the same location My Account->My Data->Microsoft Translator API page, navigate to the "Explore this Dataset" link. Here the Translator API url will be found as "Service root URL". Copy the same and use it in the project to execute the translation. Translator API provides two services, one is to translate an input text to a particular language and the other is to detect the language of an input text. Below is listed an example code for translation:

private void button1_Click(object sender, RoutedEventArgs e)
{
    TranslatorContainer translatorContainer = new TranslatorContainer(
      new Uri("https://api.datamarket.azure.com/Bing/MicrosoftTranslator/v1"));
    var accountKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

    translatorContainer.Credentials = new NetworkCredential(accountKey, accountKey);

    var translationQuery = translatorContainer.Translate(
      "Translate this string", targetLanguage.Code, sourceLanguage.Code);

    var translationResults = translationQuery.BeginExecute(
      new AsyncCallback(this.TranslateResultLoadedCallback), translationQuery);
}

In the AsyncCallback method convert the results fetched to a list of type Translation and collect only the first member of the list for the resulting translation. Do remember to convert the result into respective language culture  for displaying in the windows phone. For details on languages supported by Windows Phone refer: http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202918(v=vs.92).aspx.

var translationResults = new List<Translation>();
translationResults = result.ToList<Translation>();
// In case there were multiple results, pick the first one 
string translatedText = translationResults.First().ToString(new CultureInfo(translationCulture));

Note: For using the TranslatorContainer.cs class, it is required to add a reference to System.Data.Services.Client.dll in the project.

 

Points of Interest

Working with APIs seems always interesting and with new OS platforms around it becomes imperative for one to develop new or transform older apps to support the latest environment. I had planned to include speech recognition to implement voice commands in my app, but I was restricted to do so as windows phone or metro apps development environment doesnt have any speech recognition engine as supported by .NET (System.Speech.Recognition). But still there is a way where we can develop our own service using the .NET references and host it locally in the IIS of the system where the application gets installed. In that case speech recognition can work with ease even on Metro Apps as we have to just make call to that service to get identified text from an audio input. Hopefully will be updating the article with this fix as soon as I get this idea working in my testing environment.

History 

First posted on 14 Oct 2012. The Companion App.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here