Thanks for 50,000 downloads in the Windows Phone Store

50kdlWPStore

It was a journey with a lot ups and downs, but I hit the next Milestone as a Windows Phone Developer: 50,000 downloads across all my apps!

You might wonder why I am so happy about that. That’s very easy. I cover very special topics with my apps. For example, I made an app that helps parents to learn the tying of shoe laces with their kids. A very special app, but one that has also some serious background: there are more kids in the age of 2-6 years that are able to use a smartphone than kids that know how to tie their shoes. That was the reason for the app.

Another example is my very first app I ever wrote. After acquiring my fishing license back in 2011, I needed a Fishing Knots app (they are hard to remember as newbie). There was none. As I didn’t even thought about switching to one of the other OS that has such an app, I decided to write it by myself. With literally zero knowledge of programming. That was the beginning of my developer story.

Let’s have a look at my most special app: TweeCoMinder. TweeCoMinder stands for “Tweet Count Reminder” and does the same. If you are watching for special tweet counts like “round” numbers of 25,000 or similar, this app is for you if you don’t want to miss those counts. The app is backed by a Windows Azure Mobile Service that checks  if your count has changed. The app has also some other cool features, but you should have a look yourself:

To celebrate my personal milestone,  I am making TweeCoMinder free during the Weekend!

Don’t wait, head over to the Windows Phone Store an download the app for free!

If you still see the app as paid, it may take some time until the price change is visible for all.

Thanks for 50,000 downloads, and also thanks to my family and all others that supported me over the years.

How to use Text to Speech to read text aloud on Windows Phone 8

tts

Today I started to update my very first app I ever wrote to Windows Phone 8. The app has a read aloud feature that uses the Bing translation service (as TTS was not available on Windows Phone 7).

Of course I am now using the new Windows Phone 8 API, but I had some trouble figuring out how to handle text to speech with different languages and if no language speech pack is installed on a device. I finally found a solution and want to share it with you.

First, we need to declare a new SpeechSynthesizer and an  IEnumerable for VoiceInformation:

SpeechSynthesizer speechSynth = new SpeechSynthesizer();
IEnumerable<VoiceInformation> voices = InstalledVoices.All;

Next, I declared a simple helper method to get the currently used language:

         public string GetCurrentCulture()
         {
             return CultureInfo.CurrentCulture.TwoLetterISOLanguageName.ToString();
         }

I am using the TwoLetterISOLanguageName property because there are different versions of some languages like German or English. This makes it easier to handle throughout other methods.

Now that we are able to read the installed voices and to read the currently used language, we can use a simple Linq query to get the speech language we want to use:

var engVoice = from voice in voices where voice.Language.StartsWith("en") select voice;

The engVoice object contains now all English speech packages – if they are installed. To determine if we have items we can use, we just need to check the Count(). As long as the count is bigger than 0, we can start to let our app reading aloud our text. If not, we should inform the user that there is no matching language pack installed.

                 if (engVoice.Count() > 0)
                 {
                     speechSynth.SetVoice(engVoice.ElementAt(0));
                     await speechSynth.SpeakTextAsync(text);
                 }
                 else
                 {
                     MessageBox.Show("Language package missing");
                 }

My app supports three languages. Because of this, I am using if/else if statements to match the languages. If the user uses another language than the supported ones, I am cancelling all speech attempts and display a message to the user which languages are supported.

Here is my complete method:

         private async void StartReadAloud(string text)
         {
             IEnumerable<VoiceInformation> voices = InstalledVoices.All;

             if (GetCurrentCulture() == "en")
             {
                 var engVoice = from voice in voices where voice.Language.StartsWith("en") select voice;
                 if (engVoice.Count() > 0)
                 {
                     speechSynth.SetVoice(engVoice.ElementAt(0));
                     await speechSynth.SpeakTextAsync(text);
                 }
                 else
                 {
                     MessageBox.Show("Language package missing");
                 }
             }
             else if (GetCurrentCulture() == "it")
             {
                 var itVoice = from voice in voices where voice.Language.StartsWith("it") select voice;
                 if (itVoice.Count() > 0)
                 {
                     speechSynth.SetVoice(itVoice.ElementAt(0));
                     await speechSynth.SpeakTextAsync(text);
                 }                                  
                 else
                 {
                     //msgbox Italian
                 }
             }
             else if (GetCurrentCulture() == "de")
             {
                 var deVoice = from voice in voices where voice.Language.StartsWith("de") select voice;
                 if (deVoice.Count() > 0)
                 {
                     speechSynth.SetVoice(deVoice.ElementAt(0));
                     await speechSynth.SpeakTextAsync(text);
                 }
                 else
                 {
                     //msgbox German
                 }
             }
             else
             {
                 speechSynth.CancelAll();
                 //msgbox notSupported language               
             }
         }

I hope this is helpful for some of you and saves you some trouble.If you have anything to add/correct on my method, feel free to leave a comment below.

Happy coding, everyone!

How to create a time based & cancelable BackgroundWorker for Windows Phone (and others)

backgroundworkerTimebased

While working on my current project, I needed a solution for a running code with a time offset. Well, you might say, no problem after all, and it is true.

The challenge at this point was to find a way to keep it cancelable at any time. And so the fun started.

After digging a bit deeper into the MSDN documentation, the BackgroundWorker class already have a bunch of methods and properties that are very helpful for this case.

As it is always the case when you work with multiple threads, it can cause some headache. But I got around it and thought it might be helpful for some of you.

Here is how I solved the scenario:

First, declare a static BackgroundWorker so you need to set it up only once on the page you want to use it. In the Loaded or OnNavigatedTo event, we are instantiating our BackgroundWorker.

worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;

Now, let’s add two very important properties: WorkerSupportsCancellation and WorkerReportsProgress. Both need to be set to true, otherwise you will get one InvalidOperationException after the other when running the code.

In my case, I created a separate method to start the BackgroundWorker and hook up to all important events:

        private void RunBackgroundWorker()
        {
            worker.RunWorkerCompleted += worker_RunBackgroundWorkerCompleted;

            //delegating the DoWork event
            worker.DoWork += ((s, args) =>
            {
                //generating a loop
                for (int i = 0; i < 100; i++)
                {

                    if (worker.CancellationPending == true)
                    {
                        //set cancel to true to finish the cancellation on the next run in the loop
                        args.Cancel = true;
                    }
                    else
                    {
                        //calculate your time: seconds * 1000 / 100
                        Thread.Sleep(50);
                        worker.ReportProgress(i);
                    }
                }
            });

            //can be used to fill a progress bar/show percentage
            worker.ProgressChanged += worker_ProgressChanged;

            //start the BackgroundWorker
            worker.RunWorkerAsync();
        }

You might notice that I created a loop that runs up to 100. This is necessary for my solution, as this is the key to make the BackgroundWorker cancelable in my solution. Every 50 milliseconds, the CancellationPending property gets checked as I am looping through until the count reaches 100.  I am setting an offset of 50 milliseconds, as I want to have a total offset of 5 seconds.

Next, let us set up a button that cancels the background task (can be used in other events/methods, too):

public void CancelButton_Click(object sender, RoutedEventArgs e)
{
    //check this always to avoid InvalidOperationExceptions
   if (worker.WorkerSupportsCancellation == true)
   {
         //request cancellation of the BackgroundWorker
         //this sets the CancellationPending property to true
        worker.CancelAsync();
    }
 }

Important: Always check if the Cancellation is supported by your BackgroundWorker to avoid those ugly InvalidOperationExceptions. Then, simply call the CancelAsync() method of your BackgroundWorker to set the CancellationPending property to true.

When the BackgroundWorker has completed (or cancelled), the value of the CancellationPending property is passed to the RunBackgroundWorkerCompletedEvent, where you can simply use the e.Cancelled property to continue your code (which will then be back on your main application thread):

private void worker_RunBackgroundWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled == true)
    {
       //your code in case the BackgroundWorker was cancelled
    }
    else
    {
       //your code in case the BackgroundWorker was running until the end
    }
}

The last event I want to mention is the ProgressChanged event. It can be used to display a percentage or similar things, I just use it to see when the cancellation gets active:

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
     //your code here, for example display percentage or fill a progress bar

     //use the line below to check the percentage and if CancellationPending property gets changed
     //Debug.WriteLine(e.ProgressPercentage + "  " + worker.CancellationPending);
}

With this few methods we can create a time based  offset on a BackgroundWorker while keeping it cancelable at any time. The code above  should work in similar form also on other platforms like Windows 8 or Xamarin.

As always, I hope this will be helpful for some of you.

Happy coding!

AppAdditives PromotionalCodes, Telerik trial reminder and how to let users unlock the full app on Windows Phone

I love it when I am discovering new awesome stuff to provide a unique User Experience in my apps. AppAdditives by ExGrip LLC are the newest tools I felt in love with.

AppAdditives allow you to create promo images, social cards and widgets for your blog very easy. On top of that, they provide you an easy to use promotional code system. According to their developers, they are planning even more awesome stuff for Windows Phone and Windows 8 in future to help especially small and indie developers/publishers.

Let’s have a look on how easy it is to generate a list of promo codes after you registered for their free service.

Screenshot (332)

To generate a new list of promo codes, click on Promotional Codes. Then choose which way you provide – one time codes (every code gets invalid after being redeemed) or multi-user codes (one code for up to 2 million users). I am using the one time codes for my app.

Screenshot (330)

After you selected the proper time zone, you will get the following settings menu:

settingsforpromocodes

 

Enter all your settings, and click on start to generate your list of promo codes. It will look like this:

Screenshot (331)

That’s all we need to do here. Let’s fire up Visual Studio. Open NuGet, and install the package ‘ExGrip.PromotionalCodes’ in your preferred way.

First, add your API Key and API Secret to your app (you will find them on the Promotional Codes page). Then, add the following code to your button/function that will redeem the code for your users:

            if (PromoCodeTextBox.Text != string.Empty)
            {
                PromotionCodeManager promoCodeMan = new PromotionCodeManager(AppAdditivesAPIKey, AppAdditivesAPISecret);

                bool validateCode = await promoCodeMan.ValidatePromoCode(this.PromoCodeTextBox.Text);

                if (validateCode == true)
                {
                    progress.Text = "redeeming promo code...";

                    bool redeemCode = await promoCodeMan.RedeemPromoCode(this.PromoCodeTextBox.Text);

                    if (redeemCode == true)
                    {
                        App.isPromoCodeActivated = true;

                        RedeemPromoCodeWindow.IsOpen = false;
                        redeemPromoCode.IsEnabled = false;

                        await RadMessageBox.ShowAsync("You unlocked the full version.", "Success", MessageBoxButtons.OK);

                    }
                    else
                    {
                        await RadMessageBox.ShowAsync("The code you entered cannot be redeemed. Please try again or contact our support.", "unable to redeem your code", MessageBoxButtons.OK);
                    }
                }
                else
                {
                    await RadMessageBox.ShowAsync("The code you entered is not valid. Please try again.", "invalid promo code", MessageBoxButtons.OK);
                }
            }
        }

As you can see, I am using an chain to first validate the promo code, and only if it is valid, I allow to redeem the code.

But that’s not all. I am using Telerik’s RadTrialApplicationReminder to manage the trial state of my app. Windows Phone does not allow to redeem a code for a full version via the Store, so we need to be creative here.

As I am not limiting features but use a time based trial, I can use three already existing properties of RadTrialApplicationReminder. If my Boolean is true after the app start, I am setting all periods to 9999 days and skip all further reminders:

                if (isPromoCodeActivated == true)
                {
                    trialReminder.FreePeriod = TimeSpan.FromDays(9999);
                    trialReminder.OccurrencePeriod = TimeSpan.FromDays(9999);
                    trialReminder.AllowedTrialPeriod = TimeSpan.FromDays(9999);
                    trialReminder.AreFurtherRemindersSkipped = true;
                }

This way, I can easily provide this clearly missing features by using Promotional Codes from AppAdditives. The only thing I need to do is to tell users to only download the trial version of my app and give them a promo code.

Additional hint: If you have users that switch devices, they are not able to redeem the code again. Just tell that to your users, they will be either asking you for another code – or buy your app, anyways.

As always, I hope this post is helpful for some of you.

Happy coding, everyone!

[Update 3] UserVoice WP8 library for user features

It is done. I made my first library for Windows Phone 8. This blog post is about why I did it and how to use it.

Update: I changed some method names of the library to make it easier to use the library. I updated the code in this post to reflect those changes.

Update 2: I added support to load more pages with clients. On top, I also added my EmojiDetector class, as the UserVoice API does not support Emojis. Check the code below.

Update 3: Now the library supports comments on suggestions and mark as helpful for knowledge base items.

Why UserVoice?

We indie developers have a big problem: We do not have a support team to explain our users how to use our apps or how to solve certain problems/issues. Which leads to our next problem: users are customers. Customers want to be satisfied. It is our job to do this with our apps by providing them a high level user experience and feature rich apps. Often users don’t go the extra mile to send us an email to tell us what is wrong. Or they plan it, but forget about it. Or even worse: they get annoyed and uninstall our apps.

As some of you know, I am working at the hardware support team of a German phone carrier. Over the years, I learned how important it is to listen to customers, pick up their ideas and wishes  and work to get them done if possible. And if it is not possible, you need to tell them that – even that is an important part of customer service!

Many of us have set up a twitter account, a separate mail address, maybe an extra online form to catch all requests from users up. But users tend to not use them for one reason: they are not integrated in our apps. So I spend some time “googling with Bing” (Thanks to @robwirving for that awesome phrase!) on possible solutions.

Uservoice as the best value if using a free subscription, and they have an API that we can use (see also this post on how to get started with uservoice). I made it a very slim library and concentrated on the features we really need in our app on the user side.

The Library!

You can get the library easily via NuGet directly into your app. Just add this package to your app’s packages list:

uservoice_lib_nuget

The library also needs RestSharp, which gets automatically added to your project if you install the library.

After you installed it, you need to declare some variables that we need over and over again while using the library:

Urls.subDomain = "<your subdomain>";
Urls.oAuthCallBackUri = "<your callback url>";
Tokens.ConsumerKey = "<your ConsumerKey>";
Tokens.ConsumerSecret = "<your ConsumerSecret>";

You can get this values out of Settings/Integrations in your UserVoice account.

Additionally, you should save these Tokens to not ask the user for login again and again.

Tokens.AccessToken
Tokens.AccessTokenSecret  
Tokens.OwnerAccessToken
Tokens.OwnerAccessTokenSecret

Another important class you should be aware of  is the RequestParamaters class:

Screenshot (309)

It contains the needed variables for all requests, and you can easily use them to save them for TombStoning or anything else you want to save them.

Let’s have a look at the possible requests:

  • Knowledge Base:
KnowledgeBase kb = new KnowledgeBase();`   
//load complete knowledge base  
UservoiceRequests.KnowledgeBase = await kb.getAll();  
//load specific page in your knowledge base
int page = 2;
 UservoiceRequests.KnowledgeBase = await kb.getAll(page);
//load specific topic 
UservoiceRequests.KnowledgeBaseTopic = await kb.getTopic(RequestParameters.topicId);
//load specific page in topic
int page = 2;
UservoiceRequests.KnowledgeBaseTopic = await kb.getTopic(RequestParameters.topicId, page);
//mark article as helpful
var ratingResponse = await kb.markHelpful(RequestParamaeters.articleId);
  •  Suggestions:
Suggestion suggestion = new Suggestion();  
//load all suggestions  
UservoiceRequests.allSuggestions = await suggestion.getAll(RequestParameters.forumId);  
//load specific page in all suggestions
int page = 2;
UservoiceRequests.allSuggestions = await suggestion.getAll(RequestParameters.forumId, page); 
//vote on a suggestion 
UservoiceRequests.voteForSuggestion = await suggestion.vote(RequestParameters.forumId, RequestParameters.suggestionId, RequestParameters.vote);  
//submit new suggestion  
UservoiceRequests.postSuggestion = await suggestion.create(RequestParameters.forumId, RequestParameters.newSuggestionTitle, RequestParameters.newSuggestionText, RequestParameters.newSuggestionReferrer, RequestParameters.newSuggestionVotes); 
//search suggestions  
UservoiceRequests.searchSuggestion = await suggestion.search(RequestParameters.suggestionsSearchQuery);
//get all comments for suggestion:
UservoiceRequests.allCommentsForSuggestion = await suggestion.getComments(RequestParameters.forumId, RequestParameters.suggestionId);
//submit a new comment on suggestion:
UservoiceRequests.postCommentOnSuggestion = await suggestion.comment(RequestParameters.forumId, RequestParameters.suggestionId, RequestParameters.newCommentText);
  •  User data:
User user = new User();  
UservoiceRequests.User = await user.getUser();
  •  Tickets:
//Tickets are not associated with the user from the API side. However, you are able to show all tickets from a user with this:  
ticket = new Ticket();  
UservoiceRequests.AllTicketsFromUser = await ticket.getAll(RequestParameters.userMail); 
//load specific page in all tickets:
int page = 2;
UservoiceRequests.AllTicketsFromUser = await ticket.getAll(RequestParameters.userMail, page);
//submit a new ticket on behalf of the user
UservoiceRequests.newTicket = await ticket.create(RequestParameters.TicketSubject, RequestParameters.TicketMessage);

As you can see, all requests are async.

You don’t need explicitly authenticate a user, because the library is built to detect this automatically. If an authenticated user is required, the user will be redirected to the authentication page of UserVoice.

In the current version, you will need to manual send the request again after the user is authenticated, but I will update the library to make also this automatically soon.

  • EmojiDetector
  string text = "here would be the string from your textboxes that contain emojis";

            //check if Emojis are in text:
            if (EmojiDetector.HasEmojis(text) == true)
            {
                //your code here
            }
            //remove Emojis in text:
            text = EmojiDetector.RemoveEmojis(text);

The UserVoice API does not support Emojis, that’s why I wrote this little helper that you can easily use to remove them with only one line of code or to display a message to your users.

One last point: I don’t use RestSharp’s serializer – all request return the corresponding JSON string. This way, everyone of you can use the serializer of choice (I absolutely recommend JSON.net, though).

Please consider the current version as beta release, and report any issues with that to me via Twitter or mail.

And now enjoy my library & happy coding!

 

 

Detect and remove Emojis from Text on Windows Phone

noemoticonskeyboard

In my recent project, I work with a lot of text that get’s its value from user input. To enable the best possible experience for my users, I choose the InputScope Text on all TextBoxes, because it provides the word suggestions while writing.

The Text will be submitted to a webserver via a REST API. And now the problem starts. The emojis that are part of the Windows Phone OS are not supported by the API and the webserver.

Of course, I was immediately looking for a way to get around this. I thought this might be helpful for some of you, so I am sharing two little helper methods for detecting and removing the emojis .

After publishing the first version of this post, I got some feedback that made me investigating a bit more on this topic.  The emojis are so called unicode symbols, and thanks to the Unicode behind it, they are compatible with all platforms that have the matching Unicode list implemented.

Windows Phone has  a subset of all available Unicode characters in the OS keyboard, coming from different ranges in the Unicode characters charts. Like we have to do sometimes, we have to maintain our own list to make sure that all emojis are covered by our app in this case and update our code if needed.

If you want to learn more about unicode charts, they are officially available here: http://www.unicode.org/charts/

Update 2: I am using this methods in a real world app. Although the underlying Unicode can be used, often normal text will be read as emoji. That’s why I reverted back to my initial version with the emojis in it. I never had any problems with that.

Now let’s have a look at the code I am using. First is my detecting method, that returns a bool after checking the text (input):

             public static bool HasUnsoppertedCharacter(string text)
             {
            string pattern = @"[{allemojisshere}]";

            Regex RegexEmojisKeyboard = new Regex(pattern);

            bool booleanreturnvalue = false;

            if (RegexEmojisKeyboard.IsMatch(text))
            {
                booleanreturnvalue = true;
            }
            else if (!RegexEmojisKeyboard.IsMatch(text))
            {
                booleanreturnvalue = false;
            }
            return booleanreturnvalue;
            }

 

As you can see, I declared a character range with all emojis. If one or more emojis is found, the bool will always return true. This can be used to display a MessageBox for example while the user is typing.

The second method removes the emojis from any text that is passed as input string.

             public static string RemovedUnSoppertedCharacterString(string text)
             {
            string result = string.Empty;
            string cleanedResult = string.Empty;

            string pattern = @"[{allemojishere}]";

            MatchCollection matches = Regex.Matches(text, pattern);

            foreach (Match match in matches)
            {
                result = Regex.Replace(text, pattern, string.Empty);
                cleanedResult = Regex.Replace(result, "  ", " ");
            }
            return cleanedResult;
             }

Also here I am using the character range with all emojis . The method writes all occurrences of emojis into a MatchCollection for Regex. I iterate trough this collection to remove all of them. The Method also checks the string for double spaces in the text and makes it a single space, as this happens while removing the emojis .

User Experience hint:

use this method with care, as it could be seen as a data loss from your users. I am using the first method to display a MessageBox to the user that emojis are not supported and that they will be removed, which I am doing with the second method. This way, my users are informed and they don’t need to do anything to correct that.

You might have noticed that there is a placeholder “{allemojishere}” in the code above. WordPress or the code plugin I use aren’t supporting the Emoticons in code, that’s why I attached my helper class.

As always, I hope this will be helpful for some of you.

Happy coding!

How to build a button based Windows Phone Pivot header with Telerik RadImageButtons

In my recent project Voices Admin for Windows Phone, I used again a Pivot to display various data on different pages. I truly love this control (also with its glitches that happen sometimes), but I wanted a different design for the headers.

This is how it looks like (on my about page):

wp_ss_20140218_0001

 

As you can see, there selected item is highlighted with a colored circle, while all other items are without. There is no text, but the icon pretty much speak for themselves.

The base control for this is a RadImageButton. The RadImageButton allows us to easily use icons or images as Buttons and is highly customizable. Here is the basic code to make the button look like above:

<telerikPrimitives:RadImageButton 
    x:Name="infoButton" 
    RestStateImageSource="/Assets/About.png" 
    HorizontalAlignment="Center" 
    ButtonType="Custom" 
    ButtonBehavior="ToggleButton" 
    ButtonShape="Ellipse"
    Height="80" 
    Width="80"   
    Margin="-12,0,0,0" 
    Tap="infoButton_Tap" 
</telerikPrimitives:RadImageButton>

I changed the ButtonBehavior to ToggleButton. This is how I make it “selected”, and as the rectangle doesn’t look good, I changed the ButtonShape to Ellipse. The Height and Width of the Buttons needs to be 80 pixels, otherwise you won’t be able to add 5 items in a row.  The Margin needs to be set to a negative value due to its positioning inside the Header.

Let’s have a look at the TitlePanel that contains all RadImageButtons:

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="0,-32,0,0" Height="182">
    <StackPanel.Background>
        <SolidColorBrush Color="{StaticResource PhoneAccentColor}" Opacity="0.4"/>
    </StackPanel.Background>
    <Grid x:Name="PivotHeaderGrid" Margin="12,0,0,0" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="90"></ColumnDefinition>
            <ColumnDefinition Width="90"></ColumnDefinition>
            <ColumnDefinition Width="90"></ColumnDefinition>
            <ColumnDefinition Width="90"></ColumnDefinition>
            <ColumnDefinition Width="90"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Border Grid.Row="0" Grid.ColumnSpan="5" Height="32"></Border>

<!-- RadImageButtons in here   -->

    </Grid>        
</StackPanel>

As you can see, I am using the standard StackPanel.

#ProTip: As I want to have the background also covering the SystemTray, I need to set it to a negative Margin of 32 . Only this way we are able to set the TitlePanel and the SystemTray to the same color without having a line between those two.

The rest of the code above is pretty self explaining. Add a Grid with 5 Columns and 3 Rows. The first Row is our SystemTray. To make our app holding always this space free for the SystemTray, we added a Border with a fixed Height of 32.

In Row 1, add a TextBlock with your application name (if you want to). In Row 2, add all RadImageButtons that you need for your application.

But of course, that’s not all. We still need to make the buttons aware of which PivotItem is selected and set their Checked state to true or false depending on that, as well as handle the Tap event of the RadImageButtons.

This is where we need to hook up with the Pivot_SelectionChanged event as well as with the corresponding RadImageButton_Tap events of each button.

Let’s have a look at the Pivot_SelectionChanged event first:

private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{

switch (Pivot.SelectedIndex)
{
    case 0:
        RadImageButton1.IsChecked = true;
        RadImageButton1.IsHitTestVisible = false;
        RadImageButton2.IsChecked = false;
        RadImageButton2.IsHitTestVisible = true;
        RadImageButton3.IsChecked = false;
        RadImageButton3.IsHitTestVisible = true;
        RadImageButton4.IsChecked = false;
        RadImageButton4.IsHitTestVisible = true;
        RadImageButton5.IsChecked = false;
        RadImageButton5.IsHitTestVisible = true;
        break;
    case 1:
        RadImageButton1.IsChecked = false;
        RadImageButton1.IsHitTestVisible = true;
        RadImageButton2.IsChecked = true;
        RadImageButton2.IsHitTestVisible = false;
        RadImageButton3.IsChecked = false;
        RadImageButton3.IsHitTestVisible = true;
        RadImageButton4.IsChecked = false;
        RadImageButton4.IsHitTestVisible = true;
        RadImageButton5.IsChecked = false;
        RadImageButton5.IsHitTestVisible = true;
        break;
    case 2:
        RadImageButton1.IsChecked = false;
        RadImageButton1.IsHitTestVisible = true;
        RadImageButton2.IsChecked = false;
        RadImageButton2.IsHitTestVisible = true;
        RadImageButton3.IsChecked = true;
        RadImageButton3.IsHitTestVisible = false;
        RadImageButton4.IsChecked = false;
        RadImageButton4.IsHitTestVisible = true;
        RadImageButton5.IsChecked = false;
        RadImageButton5.IsHitTestVisible = true;
        break;
    case 3:
        RadImageButton1.IsChecked = false;
        RadImageButton1.IsHitTestVisible = true;
        RadImageButton2.IsChecked = false;
        RadImageButton2.IsHitTestVisible = true;
        RadImageButton3.IsChecked = false;
        RadImageButton3.IsHitTestVisible = true;
        RadImageButton4.IsChecked = true;
        RadImageButton4.IsHitTestVisible = false;
        RadImageButton5.IsChecked = false;
        RadImageButton5.IsHitTestVisible = true;
        break;
    case 4:
        RadImageButton1.IsChecked = false;
        RadImageButton1.IsHitTestVisible = true;
        RadImageButton2.IsChecked = false;
        RadImageButton2.IsHitTestVisible = true;
        RadImageButton3.IsChecked = false;
        RadImageButton3.IsHitTestVisible = true;
        RadImageButton4.IsChecked = false;
        RadImageButton4.IsHitTestVisible = true;
        RadImageButton5.IsChecked = true;
        RadImageButton5.IsHitTestVisible = false;
        break;
}

}

As you can see, we need to change the IsChecked state and the IsHitTestVisible state of the corresponding RadImageButtons in relation to the SelectedIndex of our Pivot. If we won’t set change the IsHitTestVisible property, the Checked state will go away if a user taps a second time on our RadImageButton.

Now let’s have a look at a RadImageButton_Tap event:

 private void RadImageButton1_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            //making sure we are not already at Index 0
            if (Pivot.SelectedIndex != 0)
            {
        //setting the IsChecked and IsHitTestvisible states
                RadImageButton1.IsChecked = true;
                RadImageButton1.IsHitTestVisible = false;
                RadImageButton2.IsChecked = false;
                RadImageButton2.IsHitTestVisible = true;
                RadImageButton3.IsChecked = false;
                RadImageButton3.IsHitTestVisible = true;
                RadImageButton4.IsChecked = false;
                RadImageButton4.IsHitTestVisible = true;
                RadImageButton5.IsChecked = false;
                RadImageButton5.IsHitTestVisible = true;

                //going to Index 0
                Pivot.SelectedIndex = 0;
            }
        }

As you can see, we are doing basically the same. The differences are that we are checking the current SelectedIndex property as well as navigating to the desired PivotItem after setting the RadImageButton’s IsChecked and IsHitTestVisible states.

No you know how you can create an awesome looking and feeling Pivot with Telerik’s RadImageButtons.

As always, I hope this post is helpful for some of you.

Happy coding!

Xamarin: Android Activities, Context, Intents and Views

xamandroid

If you are coming from C#/Silverlight, Android can be a little diffusing. Android does not have the same structure than a Windows or Windows Phone app.

Activities

Android has Activities. Activities are the key classes of Android were all actions take place. Unlike other apps or programs, you do not have a “Main” program that is your starting point when launched.

In Android, the starting point is an Activity. The Activity needs to be declared as the starting point. When you start a new project in Xamarin, an Activity called “MainActivity” gets created automatically.

This Activity has some attributes:

[Activity (Label = "gettingstarted", MainLauncher = true)]

The ‘Label’ attribute is what you will see in the Title bar when launching the app. The attribute ‘MainLauncher=true’ tells the application to start from here. Think of this as your MainPage.xaml.cs in a Windows Phone app.

Every Activity has its own OnCreate event, where you can put all your starting, button handlers, stylings etc. in.

But an Activity still has more. It has events like OnStart(), OnPause(), OnRresume() and OnStop() and OnDestroy() and OnRestart(). I won’t get into deep this time, as the Xamarin documentation has already a very good overview of those events: http://docs.xamarin.com/guides/android/application_fundamentals/activity_lifecycle/.

Those events are important to understand for a lot of your application logic, like:

  • saving data that has to be persistent
  • starting and pausing animations
  • register and unregister external events
  • fetch data that are passed from other Activities
  • and more (we will cover some of them in my further posts)

I absolutely recommend to go to the link above to read and understand those events.

Context

The Context is often needed in an Android application and allows your code to be run within an Activity.

The Context allows you for example:

  • to access Android services,
  • to access Application resources like images or styles,
  • to create views
  • to assign gestures to an Activity

Without Context, often code does not get accepted by the IDE or makes your code getting ignored while running the app. Luckily, all methods that I used so far have been telling me if they want a Context, so you need only learn to find out if you need Context for the Activity resources or Application resources.

Intents

But what if we want to have another page (like a separate about page for example)? How can we navigate between pages or call external functions?

This is what Intents are for. Intents are sending messages through the application if another function needs to be started, like the launch of a second page. Within these intents, we have declare the actions that the app needs to do, and if we need a callback, this will also be passed with an Intent.

Let’s hold this high level, here are some lines of code that navigate to a second page (Activity):

var second = new Intent(this, typeof(SecondActivity)); StartActivity(second);

With this code, we are creating an new Intent with Context to our current running Activity to launch the SecondActivity.

To send data between Activities, we use the PutExtra() method of Intents:

var second = new Intent(this, typeof(SecondActivity)); 

second.PutExtra("FirstPage", "Data from First Page"); 

StartActivity(second);

Of course, we need also some code to read the passed data on our second page:

Intent.GetStringExtra("FirstPage") ?? “Data not available”;

We could now use this data on our second page by assigning it to a control or function.

Passing data between activities works similar to passing QueryStrings in NavigationsService.Navigate() method on WindowsPhone, so you should get familiar with it very fast.

Views

The last part that is very important are Views. In a View you declare how the Activity looks like. Think of it as your MainPage.xaml page in a Windows Phone app.

Views can be very different, too. Let’s start with the simple View. In our getting started project, we also have a Layout file that holds our first View:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/myButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
</LinearLayout>

The View has a main LinearLayout which is acts as  ContentPanel. All other controls like Buttons, TextViews etc. are going into this. There are a lot of properties that can be set, we’ll leave it this easy for now. If you want to know more about those properties, you can have a look at the Android documentation here: http://developer.android.com/guide/topics/ui/declaring-layout.html

This part is totally the same as with the one matching part in the Android SDK, so you will need to get familiar with the Android documentation as well.

To make a View visible, we need to assign it to an Activity. In our sample app, we are doing this with this line of code in our OnCreate event:

SetContentView (Resource.Layout.Main);

This is the easy way for Views. But there are more complex Views in Android, too.

Views are also used in ListViews, where you declare the look of the items and the list in a Layout file, or also if you use the ActionBar with a tab navigation, and also in the menu of an ActionBar. I will cover all of these in my further blog posts, as we also need Adapters to get data binded to Views.

I hope you this post helps you to understand  the high level structure of an Android application.

Until the next time, happy coding!

How to build a custom Application Bar for your Windows Phone app (the easy way)

customappbarexpanded

In one of my recent projects, I was forced to use icons for the ApplicationBarButtons that didn’t fit into the circled template of the standard Windows Phone application bar.

The icons have special circles themselves, and I am not allowed to change anything on that icons (you’re right, it is for the corporate app I am working on). That’s why I needed to find another solution – and I started to write my own “ApplicationBar”.

As I am using Telerik’s Windows Phone Controls, I knew that the RadImageButton have exactly the same behavior than the buttons in the standard ApplicationBar. That point was already save, the only thing I needed to change was the ButtonShape of the RadImageButton from Rectangle to Ellipse – done.

This is the UserControl I created to achieve my goal:

<Border x:Name="customAppBarBorder"  Height="72" VerticalAlignment="Bottom">
    <Grid >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>

        <telerikPrimitives:RadImageButton x:Name="CustomAppBarRadImageButton1" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" ButtonShape="Ellipse" RestStateImageSource="null" ></telerikPrimitives:RadImageButton>
        <telerikPrimitives:RadImageButton x:Name="CustomAppBarRadImageButton2" Grid.Row="0" Grid.Column="2" HorizontalAlignment="Center" RestStateImageSource="null" ButtonShape="Ellipse" ></telerikPrimitives:RadImageButton>
        <telerikPrimitives:RadImageButton x:Name="CustomAppBarRadImageButton3" Grid.Row="0" Grid.Column="3" HorizontalAlignment="Center" ButtonShape="Ellipse" RestStateImageSource="null"></telerikPrimitives:RadImageButton>
        <telerikPrimitives:RadImageButton x:Name="CustomAppBarRadImageButton4" Grid.Row="0" Grid.Column="4" HorizontalAlignment="Center" ButtonShape="Ellipse" RestStateImageSource="null"></telerikPrimitives:RadImageButton>

        <Image x:Name="overflowDots" Grid.Row="0" Grid.Column="5" Width="72" Source="/Assets/AppBar/overflowdots.png" VerticalAlignment="Top" HorizontalAlignment="Right" Tap="overflowDots_Tap"></Image>

        <TextBlock x:Name="CustomAppBarButtonItem1Text" Grid.Row="1" Grid.Column="1" Width="72" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,-4,0,0" />
        <TextBlock x:Name="CustomAppBarButtonItem2Text" Grid.Row="1" Grid.Column="2" Width="72" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,-4,0,0" />
        <TextBlock x:Name="CustomAppBarButtonItem3Text" Grid.Row="1" Grid.Column="3" Width="72" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,-4,0,0" />
        <TextBlock x:Name="CustomAppBarButtonItem4Text" Grid.Row="1" Grid.Column="4" Width="72" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,-4,0,0" />
    </Grid>
</Border>

It was a bit tricky to get all the size to get a similar appearance, but it does look like the original one.

The RadImageButton Control has also a Text that we can enter, but the text was to small or to close to the button, no matter what I did. That’s why there is another row in my Grid with the corresponding text.

You may have recognized the image with the Source “overflowdots.png” above. These are located in the Windows Phone icon folder (in Microsoft SDKs under program files). We need this icon to generate the transition the standard ApplicationBar has. It is done with two simple StoryBoards:

<UserControl.Resources>
    <Storyboard x:Name="FadeCustomAppBarButtonTextIn">
        <DoubleAnimation Storyboard.TargetName="customAppBarBorder"
                         Storyboard.TargetProperty="Height"
                         From="72" To="102" Duration="0:0:0.2"/>
    </Storyboard>

    <Storyboard x:Name="FadeCustomAppBarButtonTextOut">
        <DoubleAnimation Storyboard.TargetName="customAppBarBorder"
                         Storyboard.TargetProperty="Height"
                         From="102" To="72" Duration="0:0:0.2"/>
    </Storyboard>
</UserControl.Resources>

All we need now is a proper EventHandler – the TapEvent of the image inside the control is perfect for that:

private void overflowDots_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
    if (customAppBarBorder.ActualHeight == 72)
    {
        FadeCustomAppBarButtonTextIn.Begin();
    }
    else if (customAppBarBorder.ActualHeight == 102)
    {
        FadeCustomAppBarButtonTextOut.Begin();
    }
}

I am using a Border for the animation because with a Grid it was not as fluent as I wanted. That’s the whole code of the control I created. Let’s have a look at the implementation:

First thing is an additional row in our LayoutRoot Grid,  where we can add our custom app bar control to (set the Height to “Auto”). Add this code to add the app bar:

//declare the control:

public CustomAppBarWP8 customappbar;

//add your data to the app bar:

customappbar = new CustomAppBarWP8();

            customappbar.CustomAppBarBackground = new SolidColorBrush(Colors.Green);

            customappbar.CustomAppBarButtonItem1Text.Text = "test 1";
            customappbar.CustomAppBarButtonItem2Text.Text = "test 2";
            customappbar.CustomAppBarButtonItem3Text.Text = "test 3";
            customappbar.CustomAppBarButtonItem4Text.Text = "test 4";

            customappbar.CustomAppBarRadImageButton1.RestStateImageSource = new BitmapImage(new Uri("Assets/AppBar/microphone.png", UriKind.RelativeOrAbsolute));
            customappbar.CustomAppBarRadImageButton2.RestStateImageSource = new BitmapImage(new Uri("Assets/AppBar/save.png", UriKind.RelativeOrAbsolute));
            customappbar.CustomAppBarRadImageButton3.RestStateImageSource = new BitmapImage(new Uri("Assets/AppBar/delete.png", UriKind.RelativeOrAbsolute));
            customappbar.CustomAppBarRadImageButton4.RestStateImageSource = new BitmapImage(new Uri("Assets/AppBar/questionmark.png", UriKind.RelativeOrAbsolute));

//registering the tap events:

            customappbar.CustomAppBarRadImageButton1.Tap += CustomAppBarRadImageButton1_Tap;
            customappbar.CustomAppBarRadImageButton2.Tap += CustomAppBarRadImageButton2_Tap;
            customappbar.CustomAppBarRadImageButton3.Tap += CustomAppBarRadImageButton3_Tap;
            customappbar.CustomAppBarRadImageButton4.Tap += CustomAppBarRadImageButton4_Tap;

//adding the app bar to the dedicated Grid:
            AppBarGrid.Children.Add(customappbar);

The standard Application Bar does fading out the text on a button tap, so we need to add this line in every tap event. Otherwise, it would remain open all the time.

if (customappbar.ActualHeight == 102)
{
    customappbar.FadeCustomAppBarButtonTextOut.Begin();
}

To get the same result on tapping outside our custom app bar, add the same code to your main Grid’s MouseLeftButtonDown event. This way, you have the same behavior like in the original control.

Additional note: I needed to find a quick way to achieve this, that’s why I may have not been using best practices. I also used the RadImageButton Control to speed things up. I will refine this control when I have more time available for it, as well as add a version without Telerik controls and adding the menu items.

If you have any idea on how to improve this, feel free to left a comment below.

Anyways, you can download the source of the code above here: https://github.com/MSiccDev/CustomAppBar_WP8

As always, I hope this will be helpful for some of you.

How to integrate tickets from uservoice into your Windows Phone app

This time, we will have look on how to integrate tickets from uservoice into your Windows Phone app.

Users love to see a history of their tickets they submitted to a customer service. They can review them again, and maybe help also other users by showing or telling them about it.

However, if you want to get a list of all tickets from a specific user, we need to slightly change our request. We need to login as owner of the account to be allowed to search all tickets.

Let’s have a look on how to log in as owner:

        private void LoginAsOwner()
        {
            string loginAsOwnerPath = "/api/v1/users/login_as_owner";
            var client = new RestClient(BaseUrl)
            {
                Authenticator = OAuth1Authenticator.ForRequestToken(ConsumerKey, ConsumerSecret, oAuthCallBackUri)
            };

            //works only with POST!
            var request = new RestRequest(loginAsOwnerPath, Method.POST);
            request.AddHeader("Accept", "application/json");

            var response = client.ExecuteAsync(request, HandleLoginAsOwnerResponse);
        }

        private void HandleLoginAsOwnerResponse(IRestResponse restResponse)
        {
            var response = restResponse;
            var ownerTokens = JsonConvert.DeserializeObject<UserViaMailClass.Token>(response.Content);

            OwnerAccesToken = ownerTokens.oauth_token;
            OwnerAccesTokenSecret = ownerTokens.oauth_token_secret;

        }

The authorization request is pretty similar to the user’s authentication request. However, if we log in as owner, we are getting another AccessToken and another AccessTokenSecret. That’s why we are using the ‘ForRequestToken’-method with our RestClient.

Important to know is that the request itself works only with the HTTP-method ‘POST’, otherwise the login would be denied.

We are getting back the JSON string of our owner account, which can be deserialized with JSON.net to get our AccessToken and AccessTokenSecret. I attached my ‘UserViaMailClass‘ for easy deserialization (yes, it looks similar to the user class from my authentication post, but has some differences in there).

Now that we have our OwnerAccesToken and OwnerAccessTokenSecret, we are able to search for all tickets from a specific user:

        public void GetAllTicketsFromUser()
        {
            string mailaddress = "<usersmailaddress>";
            string getSearchTicketsPath = "/api/v1/tickets/search.json";
            var client = new RestClient(BaseUrl)
            {
                Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, OwnerAccesToken, OwnerAccesTokenSecret)
            };

            var request = new RestRequest(getSearchTicketsPath, Method.GET);

            request.AddParameter("query", mailaddress);

            var response = client.ExecuteAsync(request, HandleGetAllTicketsFromUserResponse);
        }

        private void HandleGetAllTicketsFromUserResponse(IRestResponse restResponse)
        {
            var response = restResponse;

            var tickets = JsonConvert.DeserializeObject<TicketDataClass.TicketData>(response.Content);
        }

This request is again pretty similar to what we did to get a list of all suggestions. Please find attached my ‘TicketDataClass‘ for easy deserialization.

Of course, users want to be able to submit new tickets/support requests from our app, too. I will show you how to do that:

        public void CreateNewTicketAsUser()
        {
            string ticketsPath = "/api/v1/tickets.json";
            var client = new RestClient(BaseUrl)
            {
                Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret)
            };

            var request = new RestRequest(ticketsPath, Method.POST);
            request.AddParameter("ticket[subject]", "testing the uservoice API");
            request.AddParameter("ticket[message]", "hi there, \n\nwe are just testing the creation of a new uservoice ticket.");

            var response = client.ExecuteAsync(request, HandleCreateNewTicketAsUserResponse);
        }

        private void HandleCreateNewTicketAsUserResponse(IRestResponse restResponse)
        {
            var response = restResponse;
        }

To submit a new ticket, we are using the user’s AccessToken and AccessTokenSecret. This way, the ticket gets automatically assigned to the ticket. We then need to pass the ‘ticket[subject]’ and ‘ticket[message]’ parameters to the request to make it being accepted by the uservoice API.

The response is a json string that contains the ticket id, which can be used to fetch the submitted ticket data. The Alternative is to call again the search method we created before to get the updated list.

Answering to already existing tickets as user seems to be not possible with the current API. Normally, if a user responds to the response mail we answer their support ticket with, it will get assigned to the existing ticket. If we create a new ticket with the same subject, it will be a new ticket that creates a new thread. I already reached out to uservoice if there is a way to do the same from the API. As soon as I have a response that enables me to do so, I will update this post.

Now that we have all important functions for our new support system, I am starting to make a small helper library for your Windows Phone 8 apps. I hope to have it finished by this weekend, and will of course blog about here.

As always, I hope this blog post is helpful for some of you. Until the next post,

Happy coding everyone!