Wednesday, February 26, 2014

New Jumpido site - learn math by jumps and kicks

About Jumpido

We've recently released our new marketing site. Our product is called Jumpido - learning math games with Kinect. We use Kinect for Windows sensor to capture body gestures. The game is played by jumps, kicks and squats. It implements game-based learning principles and also has adaptive content. The result - really fun math software for the classroom.
Website is available at jumpido.com

Backend

I've used Microsoft backend technologies - ASP.NET MVC 5 hosted on IIS 8. The whole thing is on Azure Virtual Machine. No database in current version.
This setup has pretty good performance with output cache enabled - like 250 requests per second with keep-alive connection. Virtual machine is of type "Small".

Frontend

As every modern site Jumpido should look perfect on any device. So I've implemented Responsive Web Design approach. Maybe the best responsive framework out there is Twitter's free Bootstrap. I've deep-dived into it and LESS styles language. More you use Bootstrap the more you love it. It has really amazing components.
For CDN I always use Amazon's CloudFront. It has great performance from all over the world.

So that's it. Go check jumpido.com

Friday, February 22, 2013

Using lowercase route URL-s in ASP.NET MVC

I've recently started using ASP.NET MVC again and get to the same problem - pascal case generated URL-s. So URL from MVC looks like example.com/Home/About. This is considered bad for search engine optimization, and is ugly in my opinion. Prior to MVC 4 it was easy to trick URL generation engine and use lowercase controllers and actions in ActionLink helper:

@Html.ActionLink("About", "about", "home")

Code above will generate proper route /home/about. In ASP.NET MVC 4 it is much more easier to achieve this by setting LowercaseUrls property to true in routes initialization.  You do this in App_Start/RoutesConfig.cs file. Here is full code snippet:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.LowercaseUrls = true;

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

This is only available in MVC 4.

Thursday, January 27, 2011

Follow 302 redirects with AndroidHttpClient

Recently I had to download images in Android mobile application. I found a fantastic tutorial by Tim Bray in android developers blog - Multithreading For Performance. You are downloading images using AndroidHttpClient and AsyncTask-s.

All was perfect before I realize that some of my images doesn't load because of HTTP code 302 - moved temporary or "The data requested actually resides under a different URL". So this means my request is redirected with "Location" header. I found that Android do not support any redirect following - with RedirectHandler or anything else (in SDK v2.3).

I solved my problem with simply reading headers, finding Location header and recursively call downloading method again. Warning: This technique does not prevent circular redirects, so you have to check for them on your own. Here is my method:

private static Bitmap downloadBitmap(String url) {
    final AndroidHttpClient client = 
        AndroidHttpClient.newInstance("Android");
    final HttpGet request = new HttpGet(url);
    
    try {
        HttpResponse response = client.execute(request);
        final int statusCode = 
            response.getStatusLine().getStatusCode();
        
        if (statusCode != HttpStatus.SC_OK) {
            Header[] headers = response.getHeaders("Location");
            
            if (headers != null && headers.length != 0) {
                String newUrl = 
                    headers[headers.length - 1].getValue();
                // call again with new URL
                return downloadBitmap(newUrl);
            } else {
                return null;
            }
        }
        
        final HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream inputStream = null;
            try {
                inputStream = entity.getContent();
                
                // do your work here
                return BitmapFactory.decodeStream(inputStream);
            } finally {
                if (inputStream != null) {
                    inputStream.close();  
                }
                entity.consumeContent();
            }
        }
    } catch (Exception e) {
        request.abort();
    } finally {
        if (client != null) {
            client.close();
        }
    }
    
    return null;
}

Lets all hope Google engineers will fix following redirects in future versions of Android SDK

Edit

I have found that you shouldn't use AndroidHttpClient but instead use HttpUrlConnection. This class follows up to 5 redirects and supports cache. Here is the improved code snippet:
private static Bitmap downloadBitmap(String stringUrl) {
    URL url = null;
    HttpURLConnection connection = null;
    InputStream inputStream = null;
    
    try {
        url = new URL(stringUrl);
        connection = (HttpURLConnection) url.openConnection();
        connection.setUseCaches(true);
        inputStream = connection.getInputStream();
        
        return BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
    } catch (Exception e) {
        Log.w(TAG, "Error while retrieving bitmap from " + stringUrl, e);
    } finally {
        if (connection != null) {
            connection.disconnect();
        }
    }
    
    return null;
}

Thursday, November 18, 2010

BattleShips for Android

Remember Battleship game from your childhood? Well, there are a lot battleships versions for Android phones. One of them is mine :) And it's one of the best. And it is free! Check its website android-battleships.com

The beginning

All starts as a coursework assignment in the university. We were five people and we were practicing SCRUM agile methodology. The theme of our work was "Battleships Game". We did it very well and got some good marks :) Here are some screenshots of our game:


Looks nice, right? Graphics were my creation. My main tasks were to create some parts of the UI and draw everything.

Rewriting from scratch

Then I decided to start writing Android applications and Battleships was a good beginning. I start over everything, rewriting all the code and redrawing all the graphics. I've completed business logic for about 5 days and interface and Android programming for 7-8 days. The game was ready for about 2 weeks.
I've published it to the Android Market and i got 1000 downloads for 1 day. Amazing. :)
Here are some screenshots of version 1.0:


The game has the following advantages: Better UI than other battleships in the market (sliding crosshair) and really fast games (no need to arrange ships and wait long time for your opponent)

The New Design

After a few weeks I realized that current design is ugly and looks bad on my device (HTC Desire). So I started to draw a new and more "fancy" design. This time I've used vector graphics so it was easy to support different screens. Check the result:


This looks really better and the most important - looks good on my phone's screen (amoled displays have some strange color profile and it is hard to make something looks good)

The Website

All good games in Apple's AppStore have website, so do I. I've wrote some html, draw some images and my web page is ready


You can find it here  http://www.android-battleships.com/

Conclusion

This was the beginning. Next application coming soon:) And updates for Battleships too. Users want multi-player and selectable placement of ships. I'll implement those in the near future.

Thanks for the 20,000+ downloads

Thats it, thanks for reading

Saturday, June 26, 2010

Tutorial - Table layout with Zend Framework form decorators

In this tutorial I will show you how to make form with table layout using Zend_Form. Zend_Form is part of Zend Framework, MVC framework written in PHP with ambition to be the official PHP framework.
It is old-school to use tables in HTML for non-table content, but sometimes it is useful and easier to me it "table"-way.
Using Zend_Form is simple task and you can add filter and validators really easy. Changing default appearance can be accomplished by using decorator. Here is the code that you have to add to your Zend_Form creation:
$this->setDecorators(array(
    'FormElements',
    array('HtmlTag', array('tag' => 'table')),
    'Form'
));
First I add "table" element around form elements (inputs, submits, etc.) with setDecorators method. Output would be something like this:
[form] [table] [form elements] [/table] [/form] 
Decorators are applied one-by-one, form elements are rendered first, then they are surrounded by table, and finally form tag is rendered. If you are not familiar with decorator design pattern, please check Decoration Pattern in Wikipedia and Zend Framework documentation.
After I'm done with form itself, I have to add specific decorators to form elements (you know, table row and table cell :) )
$this->setElementDecorators(array(
    'ViewHelper',
    'Errors',
    array(array('data' => 'HtmlTag'), array('tag' => 'td')),
    array('Label', array('tag' => 'td')),
    array(array('row' => 'HtmlTag'), array('tag' => 'tr'))
));
Output will be:
[tr] [td] [label] [/td] [td] [Form Element] [Errors] [/td] [/tr]
Again decorators are applied one-by-one. You can add specific CSS classes for each element so you can style them.

Decorators are hard to understand but they are powerful feature of Zend_Form.

Thursday, June 24, 2010

Tutorial - Create ASP.NET MVC localization with language detection

Introduction

In this tutorial I will show a simple way to create localization (globalization) for web application using APS.NET MVC framework. It should work fine with MVC 1 and 2 and I’m currently using .NET 3.5 SP1, but .NET 4.0 will work as well. All code is in C# and for language translations I use XML files.

Language files with XML

For translations of different languages I use simple xml files. I store them in App_Data/messages/<locale>.xml, for example en-US.xml or de-DE.xml. Here is the xml structure:
<items>
  <item key="home">Home</item>
  <item key="products">Products</item>
  <item key="services">Services</item>
</items>
You should have identical language files for all desired languages. All translation items should be the same (with equal “key” attributes).

Create Translator class

Main translation work will be done by Translator singleton class. Create “Infrastructure” folder in your MVC project and put class Translator there.
First, let’s make class singleton:
private static Translator instance = null;
public static Translator Instance
{
    get
    {
        if (instance == null)
        {
            instance = new Translator();
        }
        return instance;
    }
}
private Translator() { }
Add the following fields and properties to the class:
private static string[] cultures = { "en-US", "bg-BG" };
private string locale = string.Empty;

public string Locale
{
    get
    {
        if (string.IsNullOrEmpty(locale))
        {
            throw new Exception("Locale not set");
        }
        else
        {
            return locale;
        }
    }
    set
    {
        if (Cultures.Contains(value))
        {
            locale = value;
            load();
        }
        else
        {
            throw new Exception("Invalid locale");
        }
    }
}


public static string[] Cultures
{
    get
    {
        return cultures;
    }
}
Field "cultures" lists available cultures. "Locale" keeps current culture. And in "set" part of Locale property you can see invocation of load() method. I will talk about it later.
To keep localization data I will create simple dictionary and then use keys from XML for dictionary keys and XML item values as dictionary values. Simple Translate method will do translation job. I have indexer method for easy access.
private Dictionary data = null;

public string Translate(string key)
{
    if (data != null && data.ContainsKey(key))
    {
        return data[key];
    }
    else
    {
        return ":" + key + ":";
    }
}

public string this[string key]
{
    get
    {
        return Translate(key);
    }
}
If some key cannot be found and translated, I return the key with ":" around it, so you can easy find untranslated items.
Finally, for loading XML I use LINQ to XML. I have static caching dictionary, so I don't need reading XML on every request.
private static Dictionary<string, Dictionary<string, string>> cache = 
  new Dictionary<string, Dictionary<string, string>>();

private void load()
{
    if (cache.ContainsKey(locale) == false) // CACHE MISS !
    {
        var doc = XDocument.Load(
            HttpContext.Current.Server.MapPath(
               "~/App_Data/messages/" + locale + ".xml"));

        cache[locale] = (from item in doc.Descendants("item")
                         where item.Attribute("key") != null
                         select new
                         {
                             Key = item.Attribute("key").Value,
                             Data = item.Value,
                         }).ToDictionary(i => i.Key, i => i.Data);
    }

    data = cache[locale];
}

public static void ClearCache()
{
    cache = new Dictionary<string, Dictionary<string, string>>();
}
You can use translator in your controller like this:
Translator.Instance[key];
After load() methid I have ClearCache method for easy developing (you know, once read, data is cached and you have to restart IIS Application Pool to refresh localization data).
Translator class is ready, I will show you how to use it later.

Create localization helpers

Create static class LocalizationHelpers and put it in "Helpers" folder in your project.
public static string CurrentCulture(this HtmlHelper html)
{
    return Translator.Instance.Locale;
}

public static string T(this HtmlHelper html, string key)
{
    return html.Encode(Translator.Instance[key]);
}

public static string T(this HtmlHelper html, string key, 
    params object[] args)
{
    return html.Encode(string.Format(
        Translator.Instance[key], args));
}
I will use this in html views for translation like this
<%= Html.T("products") %>
If you want params in translated values you can use second T implementation like string.Format. First helper CurrentCulture is used in language select user control to determine current culture.

Create BaseController class

Create BaseController class that extends Controller and put it in "Infrastructure" folder of your MVC project. You should extend all your controller classes from this class. Create simple property for current selected culture (locale)
public string CurrentCulture
{
    get
    {
        return Translator.Instance.Locale;
    }
}
You will use this in your controller when you initialize your model, for example.
In the following code I will explain language detection and saving with cookie.
private void initCulture(RequestContext requestContext)
{
    string cultureCode = getCulture(requestContext.HttpContext);

    requestContext.HttpContext.Response.Cookies.Add(
        new HttpCookie("Culture", cultureCode)
        {
            Expires = DateTime.Now.AddYears(1),
            HttpOnly = true,
        }
    );

    Translator.Instance.Locale = cultureCode;

    CultureInfo culture = new CultureInfo(cultureCode);
    System.Threading.Thread.CurrentThread.CurrentCulture = culture;
    System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
}

private string getCulture(HttpContextBase context)
{
    string code = getCookieCulture(context);

    if (string.IsNullOrEmpty(code))
    {
        code = getCountryCulture(context);
    }

    return code;
}

private string getCookieCulture(HttpContextBase context)
{
    HttpCookie cookie = context.Request.Cookies["Culture"];

    if (cookie == null || string.IsNullOrEmpty(cookie.Value) || 
         !Translator.Cultures.Contains(cookie.Value))
    {
        return string.Empty;
    }

    return cookie.Value;
}

private string getCountryCulture(HttpContextBase context)
{
    // some GeoIp magic here
    return "en-US";
}
First I try to get language cookie if there is any (if this is not first time visit). If there is no cookie you can detect browser language, make GeoIP IP address lookup and so on. After finding some valid locale/culture I set response cookie for next page visits. After this I change current thread culture. This is useful if you want to format some date or currency values.
You should call initCulture in overridden Initialize method.

Changes in HomeController

Don't forget to change parent class of all your controller to BaseController. Add following code to your HomeController, so you can change current culture. When you open specified URL, a cookie is set and user is redirected to index page. This URL is like example.com/home/culture/en-US. Clear cache method is for deleting current cache without restarting application pool. Access it with example.com/home/ClearLanguageCache
public ActionResult Culture(string id)
{
    HttpCookie cookie = Request.Cookies["Culture"];
    cookie.Value = id;
    cookie.Expires = DateTime.Now.AddYears(1);
    Response.SetCookie(cookie);

    return Redirect("/");
}

public ActionResult ClearLanguageCache(string id)
{
    Translator.ClearCache();

    return Redirect("/");
}
To change current language I will create special user control which will be included in may Master.Site layout. Create CultureUserControl.ascx and put it in Views/Shared/ folder of your MVC project. Here is the code:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<% if (Html.CurrentCulture() == "bg-BG") { %>
    <a id="lang" href="/home/culture/en-US">en</a>
<% } else { %>
    <a id="lang" href="/home/culture/bg-BG">bg</a>
<% } %>
In my layout I use <% Html.RenderPartial("CultureUserControl"); %> to include it.

Conclusion

In this simple tutorial I've created localization infrastructure for ASP.NET MVC web application. Translations of different languages are stored in XML files. Then I use Translator class to load them. Current user culture is kept in cookie. You can access Translator class in html views using some helpers. Also all the translation data i cached so it will not be loaded form XML every request.
Hope this tutorial helps.