How to detect all urls in a string to match Twitter’s requirements (Windows 8(.1) and Windows Phone 8(.1))

url

As you might guess, I am still working on that app I mentioned in my last blog post. As I was diving deeper into the functions I want, I recognized that Twitter does a very well url handling server side.

Like the official documentation says, every url will be shortened with a link that has 22 characters (23 for https urls).

I was trying to write a RegEx expression to detect all the links that can be:

  • http
  • https
  • just plain domain names like “msicc.net”

This is not as easy as it sounds, and so I was a bit struggling. I then talked with @_MadMatt (follow him!) who has a lot of experience with twitter. My first attempt was a bit confusing as I did first only select http and https, then the plain domain names.

I found the names by their domain ending, but had some problems to get their length (which is essential). After the very helpful talk with Matthieu, I finally found a very good working RegEx expression here on GitHub.

I tested it with tons of links, and I got the desired results and it is now also very easy for me to get their length.

Recovering the amount of time I needed for this, I decided to share my solution with you. Here is the method I wrote:

        public int CalculateTweetCountWithLinks(int currentCount, string text)
        {
            int resultCount = 0;

            if (text != string.Empty)
            {
                //detailed explanation: https://gist.github.com/gruber/8891611
                string pattern = @"(?i)\b((?:https?:(?:/{1,3}|[a-z0-9%])|[a-z0-9.\-]+[.](?:com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)/)(?:[^\s()<>{}\[\]]+|\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\))+(?:\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\)|[^\s`!()\[\]{};:'.,<>?«»“”‘’])|(?:(?<!@)[a-z0-9]+(?:[.\-][a-z0-9]+)*[.](?:com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)\b/?(?!@)))";

                //generating a MatchCollection
                MatchCollection linksInText = Regex.Matches(text, pattern, RegexOptions.Multiline);

                //going forward only when links where found
                if (linksInText.Count != 0)
                {
                    //important to set them to 0 to get the correct count
                    int linkValueLength = 0;
                    int httpOrNoneCount = 0;
                    int httpsCount = 0;

                    foreach (Match m in linksInText)
                    {
                        //https urls need 23 characters, http and others 22
                        if (m.Value.Contains("https://"))
                        {
                            httpsCount = httpsCount + 1;
                        }
                        else
                        {
                            httpOrNoneCount = httpOrNoneCount + 1;
                        }

                        linkValueLength = linkValueLength + m.Value.Length;
                    }

                    //generating summaries of character counts
                    int httpOrNoneReplacedValueLength = httpOrNoneCount * 22;
                    int httpsReplacedValueLength = httpsCount * 23;

                    //calculating final count
                    resultCount = (currentCount - linkValueLength) + (httpOrNoneReplacedValueLength + httpsReplacedValueLength);                    
                }
                else
                {
                    resultCount = currentCount;
                }
            }
            return resultCount;
        }

First, we are detecting links in the string using the above mentioned RegEx expression and collect them in a MatchCollection.

As https urls have a 23 character length on t.co (Twitter’s url shortener), I am generating two new counts – one for https, one for all other urls.

The last step is to substract the the length of all Match values and add the newly calculated replaced link values lengths.

Add this little method to your TextChanged event, and you will be able to detect the character count on the fly.

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

Happy coding, everyone!

 

How to use the WebAuthenticationBroker for oAuth in a Windows Phone Runtime WP8.1 app

oAuthDance

After playing around with WP8.1 for a few days (like everyone else), I decided to dig a bit into development of WP8.1.

As oAuth is the common authentication method nowadays for Apps and Websites, I was curios about the implementation of the WebAuthenticationBroker in a WINPRT app.

I used it before with Windows 8 for my TweeCoMinder app, but that’s a few month back (it didn’t make it into the Store yet (another goal, right – porting TweeCoMinder to Universal will be a lot of fun and learning for me ;-)).

Before we continue: This is a pretty huge topic. Be prepared that it may take you more than one time to read and understand what is going on.

Let’s dive into it. Unlike the Windows WebAuthenticationBroker, the Phone version does not use the AuthenticateAsync method. It uses AuthenticateAndContinue instead. This is related to the lifecycle on phone, as it is more likely that an WINPRT app is suspended than on Windows (at least that’s the official reason).

Preparing our App

But we are able to get it working, no worries. First, we need the so called ContinuationManager. This class brings the user back to the app where the fun begun.

You can download the complete class from here (it is a 1:1 copy from the official MSDN reference). The only thing you need to do is to add your app’s Namespace into it.

The next step we need to do: some modifications at the App.xaml.cs file.

1. Add an OnActivated event (it isn’t there in the Phone template)

        protected async override void OnActivated(IActivatedEventArgs e)
        {
            CreateRootFrame();

            // Restore the saved session state only when appropriate
            if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
            {
                try
                {
                    await SuspensionManager.RestoreAsync();
                }
                catch (SuspensionManagerException)
                {
                    //Something went wrong restoring state.
                    //Assume there is no state and continue
                }
            }

            //Check if this is a continuation
            var continuationEventArgs = e as IContinuationActivatedEventArgs;
            if (continuationEventArgs != null)
            {
                continuationManager.Continue(continuationEventArgs);
            }

            Window.Current.Activate();
        }

First, we check the SuspensionManager and let him restore a saved state – if there is one. If you do not have a Folder  ”Common” with the SuspensionManager, just add a new Basic page. This will generate the Common folder with the SuspenstionManager class for you.

After that, we are checking if the activation is a Continuation. We need this check there, otherwise the app will not be able to receive the Tokens after returning from the WebAuthenticationBroker. Note: declare the ContinuationManager globally in App.xaml.cs with this to avoid multiple instances (which will crash the app for sure).

public static ContinuationManager continuationManager { get; private set; }

2. Add a CreateRootFrame() method with some little changes to the default behavior

        private void CreateRootFrame()
        {
            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame != null)
                return;

            // Create a Frame to act as the navigation context and navigate to the first page
            rootFrame = new Frame();

            //Associate the frame with a SuspensionManager key                                
            SuspensionManager.RegisterFrame(rootFrame, "AppFrame");

            // Set the default language
            rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];

            rootFrame.NavigationFailed += OnNavigationFailed;

            // Place the frame in the current Window
            Window.Current.Content = rootFrame;
        }

This simply creates the RootFrame for our app. Place this one in the OnLaunched and OnActivated events. Instead of creating the RootFrame in the OnLaunched method, declare it globally in App.xaml.cs with this:

public static Frame rootFrame { get; set; }

3. Handle the HardwareButtons_BackPressed event

        private void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
        {
            Frame frame = Window.Current.Content as Frame;
            if (frame == null)
            {
                return;
            }

            if (frame.CanGoBack)
            {
                frame.GoBack();
                e.Handled = true;
            }
        }

This just handles the BackButton press globally (you can still handle it on individual pages/frames/controls).

4. Handle Navigation Errors in the OnNavigationFailed event

private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{

    //change this code for your needs
    throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}

5. Add the Save method to the OnSuspending event

await SuspensionManager.SaveAsync();

This is just to save the current state of our app while the App gets suspended.

Preparing oAuth

Now, we have our app prepared  to be continued after the WebAuthenticationBroker has finished. Pretty much to do so far, but now we are going to start with the real fun: The oAuth process. We are going to use Twitter in this case, as it is very popular to be added as a sharing option in apps (yes, I recently saw a lot of them).

Before we are able to connect to Twitter, we need some preparing methods. The first one is to get a random character chain. Twitter demands a 32 digit chain that contains random alphanumeric values, the so called oAuth-nonce. A lot of samples use a chain that is shorter, which will be fine for the initial authentication, but not for additional request. Here is my method:

string GetNonce()
{
   StringBuilder builder = new StringBuilder();
   Random random = new Random();
   char ch;
   for (int i = 0; i < 32; i++)
   {
     ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
     builder.Append(ch);
   }

   return builder.ToString().ToLower();
 }

We also need a timestamp in Milliseconds, this one is pretty straight forward:

string GetTimeStamp()
{
  TimeSpan SinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
  return Math.Round(SinceEpoch.TotalSeconds).ToString();
}

On top, we need a signature for our Authentication request:

        string GetSignature(string sigBaseString, string consumerSecretKey, string oAuthTokenSecret=null)
        {
            IBuffer KeyMaterial = CryptographicBuffer.ConvertStringToBinary(consumerSecretKey + "&" + oAuthTokenSecret, BinaryStringEncoding.Utf8);
            MacAlgorithmProvider HmacSha1Provider = MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA1");
            CryptographicKey MacKey = HmacSha1Provider.CreateKey(KeyMaterial);
            IBuffer DataToBeSigned = CryptographicBuffer.ConvertStringToBinary(sigBaseString, BinaryStringEncoding.Utf8);
            IBuffer SignatureBuffer = CryptographicEngine.Sign(MacKey, DataToBeSigned);
            string Signature = CryptographicBuffer.EncodeToBase64String(SignatureBuffer);

            return Signature;
        }

You will find a lot of samples that don’t use the oAuthTokenSecret in the basic method. We will need this for additional requests, so I am using a nullable string overload. This way,  I need only one method after all.

Performing the Authentication process

To start the Authentication process, we are using this code in our Button_Click event:

        private async void connectToTwitterButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                string oAuth_Token = await GetTwitterRequestTokenAsync(TwitterCallBackUri, TwitterConsumerKey);
                string TwitterUrl = "https://api.twitter.com/oauth/authorize?oauth_token=" + oAuth_Token;

                WebAuthenticationBroker.AuthenticateAndContinue(new Uri(TwitterUrl), new Uri(TwitterCallBackUri), null, WebAuthenticationOptions.None);
            }
            catch
            { 
                //error handling here
            }
        }

First, we need a RequestToken that authenticates our request for the oAuthToken, that’s why we are awaiting the GetTwitterRequestTokenAsync(TwitterCallBackUri, TwitterConsumerKey) Task. The Task itself is this one:

        private async Task<string> GetTwitterRequestTokenAsync(string twitterCallbackUrl, string consumerKey)
        {
            //
            // Acquiring a request token
            //
            string TwitterUrl = "https://api.twitter.com/oauth/request_token";

            string nonce = GetNonce();
            string timeStamp = GetTimeStamp();
            string SigBaseStringParams = "oauth_callback=" + Uri.EscapeDataString(twitterCallbackUrl);
            SigBaseStringParams += "&" + "oauth_consumer_key=" + consumerKey;
            SigBaseStringParams += "&" + "oauth_nonce=" + nonce;
            SigBaseStringParams += "&" + "oauth_signature_method=HMAC-SHA1";
            SigBaseStringParams += "&" + "oauth_timestamp=" + timeStamp;
            SigBaseStringParams += "&" + "oauth_version=1.0";
            string SigBaseString = "GET&";
            SigBaseString += Uri.EscapeDataString(TwitterUrl) + "&" + Uri.EscapeDataString(SigBaseStringParams);
            string Signature = GetSignature(SigBaseString, TwitterConsumerSecret);

            TwitterUrl += "?" + SigBaseStringParams + "&oauth_signature=" + Uri.EscapeDataString(Signature);
            HttpClient httpClient = new HttpClient();
            string GetResponse = await httpClient.GetStringAsync(new Uri(TwitterUrl));

            string request_token = null;
            string oauth_token_secret = null;
            string[] keyValPairs = GetResponse.Split('&');

            for (int i = 0; i < keyValPairs.Length; i++)
            {
                string[] splits = keyValPairs[i].Split('=');
                switch (splits[0])
                {
                    case "oauth_token":
                        request_token = splits[1];
                        break;
                    case "oauth_token_secret":
                        oauth_token_secret = splits[1];
                        break;
                }
            }

            return request_token;
        }

After receiving the RequestToken, we are able to call the the AuthenticateAndContinue(Uri, Uri, ValueSet, WebAuthenticationOptions) method. The WebAuthenticationBroker opens and the user needs to login. After finishing, the user will get redirected back into our app.

If you think that’s all of our code, I need to disappoint you. The fun goes on. We prepared our app to be recognized as continued app. To effectifely continue our app, we need to use the ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args) event from our ContinuationManager class:

 

        public async void ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args)
        {
            WebAuthenticationResult result = args.WebAuthenticationResult;

            if (result.ResponseStatus == WebAuthenticationStatus.Success)
            {
                await GetTwitterUserNameAsync(result.ResponseData.ToString());
            }
            else if (result.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
            {
                MessageDialog HttpErrMsg = new MessageDialog(string.Format("There was an error connecting to Twitter: \n {0}", result.ResponseErrorDetail.ToString()), "Sorry");
                await HttpErrMsg.ShowAsync();
            }
            else
            {
                MessageDialog ErrMsg = new MessageDialog(string.Format("Error returned: \n{0}", result.ResponseStatus.ToString()), "Sorry");
                await ErrMsg.ShowAsync();
            }
        }

The WebAuthenticationResult holds our accessToken and accessTokenSecret, that we are going to exchange for our final oAuthToken and oAuthTokenSecret (we need them for all further requests).

If all goes well, we are now able to call the final Authentication Task GetTwitterUserNameAsync(string webAuthResultResponseData):

        private async Task GetTwitterUserNameAsync(string webAuthResultResponseData)
        {

            string responseData = webAuthResultResponseData.Substring(webAuthResultResponseData.IndexOf("oauth_token"));
            string request_token = null;
            string oauth_verifier = null;
            String[] keyValPairs = responseData.Split('&');

            for (int i = 0; i < keyValPairs.Length; i++)
            {
                String[] splits = keyValPairs[i].Split('=');
                switch (splits[0])
                {
                    case "oauth_token":
                        request_token = splits[1];
                        break;
                    case "oauth_verifier":
                        oauth_verifier = splits[1];
                        break;
                }
            }

            String TwitterUrl = "https://api.twitter.com/oauth/access_token";

            string timeStamp = GetTimeStamp();
            string nonce = GetNonce();

            String SigBaseStringParams = "oauth_consumer_key=" + TwitterConsumerKey;
            SigBaseStringParams += "&" + "oauth_nonce=" + nonce;
            SigBaseStringParams += "&" + "oauth_signature_method=HMAC-SHA1";
            SigBaseStringParams += "&" + "oauth_timestamp=" + timeStamp;
            SigBaseStringParams += "&" + "oauth_token=" + request_token;
            SigBaseStringParams += "&" + "oauth_version=1.0";
            String SigBaseString = "POST&";
            SigBaseString += Uri.EscapeDataString(TwitterUrl) + "&" + Uri.EscapeDataString(SigBaseStringParams);

            String Signature = GetSignature(SigBaseString, TwitterConsumerSecret);

            HttpStringContent httpContent = new HttpStringContent("oauth_verifier=" + oauth_verifier, Windows.Storage.Streams.UnicodeEncoding.Utf8);
            httpContent.Headers.ContentType = HttpMediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
            string authorizationHeaderParams = "oauth_consumer_key=\"" + TwitterConsumerKey + "\", oauth_nonce=\"" + nonce + "\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"" + Uri.EscapeDataString(Signature) + "\", oauth_timestamp=\"" + timeStamp + "\", oauth_token=\"" + Uri.EscapeDataString(request_token) + "\", oauth_version=\"1.0\"";

            HttpClient httpClient = new HttpClient();

            httpClient.DefaultRequestHeaders.Authorization = new HttpCredentialsHeaderValue("OAuth", authorizationHeaderParams);
            var httpResponseMessage = await httpClient.PostAsync(new Uri(TwitterUrl), httpContent);
            string response = await httpResponseMessage.Content.ReadAsStringAsync();

            String[] Tokens = response.Split('&');
            string oauth_token_secret = null;
            string oauth_token = null;
            string screen_name = null;

            for (int i = 0; i < Tokens.Length; i++)
            {
                String[] splits = Tokens[i].Split('=');
                switch (splits[0])
                {
                    case "screen_name":
                        screen_name = splits[1];
                        break;
                    case "oauth_token":
                        oauth_token = splits[1];
                        break;
                    case "oauth_token_secret":
                        oauth_token_secret = splits[1];
                        break;
                }
            }

            if (oauth_token != null)
            {
                App.SettingsStore.TwitteroAuthToken = oauth_token;
            }

            if (oauth_token_secret != null)
            {
                App.SettingsStore.TwitteroAuthTokenSecret = oauth_token_secret;
            }
            if (screen_name != null)
            {
                App.SettingsStore.TwitterName = screen_name;
            }
        }

At the end of this Task, we will have all Tokens and the ScreenName of our user and can save them for further usage.

The whole oAuth process is a pretty huge thing as you can see. I hope this blog post helps you all to understand how to use the WebAuthenticationBroker and get the required Tokens for all further requests.

Here are some useful links to the MSDN, if you want to dive deeper:

As always, this covers my experience. If anyone has tips or tricks to make this all easier (except of using already existing libraries), feel free to add a comment below.

Until the next post,  happy coding!

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!