Monday, February 18, 2013

Work with WinJS ListView Control - The ASP.NET Way

In this post a Windows 8 JavaScript app is created to display a list of phones with sorting and editing functions. The WinJS ListView control is used in the app much like what we do with GridView in typical ASP.NET web application.

Note that this post presents a way to resolve some common problem and demos how we can work wiht Windows 8 WinJS. It doesn't mean that's the recommended way or the best practice.

HTML (home.html)

Create a Windows 8 JavaScript project using Navigation App template (other template should also work), update the home.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>homePage</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

    <script src="/js/jquery-1.8.2.js"></script>
    <link href="/css/default.css" rel="stylesheet" />
    <link href="/pages/home/home.css" rel="stylesheet" />
    <script src="/pages/home/home.js"></script>

</head>
<body>
    <!-- The content that will be loaded and displayed. -->
    <div class="fragment homepage">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
        </header>

        <section aria-label="Main content" role="main">

            <div id="listviewPage">
                <div class="header">
                    <h1>My Smartphone List</h1>
                </div>
                <div class="row title">
                    <span id="titleName">Name</span>
                    <span id="titleBland">Bland</span>
                    <span id="titleOS">OS</span>
                    <span id="titleSize">Size</span>
                    <span id="titlePrice">PriceFrom</span>
                    <span id="titleAdd">Add</span>
                </div>
                <div id="lvPhonesTemplate" data-win-control="WinJS.Binding.Template">
                    <div class="row">
                        <span data-win-bind="innerText: Name"></span>
                        <span data-win-bind="innerText: Bland"></span>
                        <span data-win-bind="innerText: OS"></span>
                        <span data-win-bind="innerText: Size"></span>
                        <span class="price" data-win-bind="innerText: PriceFrom"></span>
                        <span class="edit">Edit</span>
                    </div>
                </div>
                <div class="listview">
                    <div id="lvPhones" data-win-control="WinJS.UI.ListView"
                        data-win-options="{tapBehavior: 'none', selectionMode: 'none', layout: {type: WinJS.UI.ListLayout}}">
                    </div>
                </div>
            </div>

            <div id="editPage">
                <div class="header">
                    <h1><label id="editTitle">Edit</label></h1>
                </div>
                <div class="form">
                    <div class="row">
                        <label for="editName">Name</label><input id="editName" type="text" required="required" />
                    </div>
                    <div class="row">
                        <label for="editBland">Bland</label><input id="editBland" type="text" required="required" />
                    </div>
                    <div class="row">
                        <label for="editOS">OS</label><input id="editOS" type="text" required="required" />
                    </div>
                    <div class="row">
                        <label for="editSize">Size (inch)</label><input id="editSize" type="text" required="required" />
                    </div>
                    <div class="row">
                        <label for="editPrice">Price From</label><input id="editPrice" type="text" required="required" />
                    </div>
                    <div class="error"></div>
                    <div class="footer">
                        <button id="btnCancel">Cancel</button>
                        <button id="btnEdit">Update</button>
                    </div>
                </div>
            </div>
        </section>
    </div>
</body>
</html>

The UI is quite simply. It contains two sections: a page to display tabular data using a ListView and a template for the ListView, and an edit form for editing an existing phone or adding a new phone entry. The jQuery is included so we can manipulate DOM elements the way we deal with traditional web apps.

CSS (home.css)

#listviewPage, #editPage { width: 900px; }
#listviewPage .header, #editPage .header { height: 50px; text-align: center; margin-bottom: 30px; }
#listviewPage .listview { width: 900px; overflow: auto; border: solid; }
#listviewPage .row.title { padding-left: 15px; border-bottom-style: none; }
#listviewPage .row { height: 30px; padding: 5px; border-bottom-style: solid; }
#listviewPage .row span:nth-child(6) { width: 50px; font-weight: bold; }
#listviewPage .row span { display: inline-block; width: 150px; }

#editPage .form { width: 800px; padding: 10px; }
#editPage .row { height: 40px; }
#editPage .row input { width: 400px; }
#editPage .row label { display: inline-block; width: 200px; padding: 10px; text-align: right; }
#editPage .footer { margin: 40px 200px; float: right; }
#editPage .footer button { margin: 10px; }

.homepage section[role=main] { margin-left: 120px; }

JavaScript (home.js)

(function () {
    "use strict";
    WinJS.Binding.optimizeBindingReferences = true;
    WinJS.UI.disableAnimations(); // Use jQuery animation instead

    var editedPhone = null;
    var smartphones = generateSampleData();
    var smartphoneList = new WinJS.Binding.List(smartphones);
    
    function generateSampleData() {
        var blands = ["Apple", "Nokia", "Samsung"];
        var oss = ["iOS", "Windows Phone", "Android"];
        var phones = [
            { ID: 1, Name: "iPhone 4", Bland: blands[0], OS: oss[0], Size: 3.5, PriceFrom: 449 },
            { ID: 2, Name: "iPhone 4S", Bland: blands[0], OS: oss[0], Size: 3.5, PriceFrom: 549 },
            { ID: 3, Name: "iPhone 5", Bland: blands[0], OS: oss[0], Size: 4.0, PriceFrom: 649 },
            { ID: 4, Name: "Lumia 820", Bland: blands[1], OS: oss[1], Size: 4.3, PriceFrom: 399 },
            { ID: 5, Name: "Lumia 900", Bland: blands[1], OS: oss[1], Size: 4.3, PriceFrom: 349 },
            { ID: 6, Name: "Lumia 920", Bland: blands[1], OS: oss[1], Size: 4.5, PriceFrom: 449 },
            { ID: 7, Name: "Galaxy S2", Bland: blands[2], OS: oss[2], Size: 4.3, PriceFrom: 349 },
            { ID: 8, Name: "Galaxy S3", Bland: blands[2], OS: oss[2], Size: 4.8, PriceFrom: 499 },
            { ID: 9, Name: "Galaxy Note", Bland: blands[2], OS: oss[2], Size: 5.3, PriceFrom: 599 },
            { ID: 10, Name: "Galaxy Note2", Bland: blands[2], OS: oss[2], Size: 5.5, PriceFrom: 699 },
        ];
        return phones;
    }

    // Listview item databound event  
    function onItemDataBound(container, itemData) {
        var $price = $(".price", container);
        var price = parseFloat($price.text());
        if (price < 400)
            $price.css("color", "green");
        else if ( price > 600)
            $price.css("color", "red");
    }

    // Listview template function
    function listViewItemTemplateFunction(itemPromise) {
        return itemPromise.then(function (item) {
            var template = document.getElementById("lvPhonesTemplate");
            var container = document.createElement("div");
            template.winControl.render(item.data, container);
            onItemDataBound(container, item.data);
            return container;
        });
    }

    // Display listview page
    function showListviewPage(skipDatabinding) {
        $(editPage).hide();
        if (!skipDatabinding) {
            smartphoneList = new WinJS.Binding.List(smartphones);
            lvPhones.winControl.itemDataSource = smartphoneList.dataSource;
            lvPhones.winControl.itemTemplate = listViewItemTemplateFunction;
        }
        $(listviewPage).fadeIn();
    }

    WinJS.UI.Pages.define("/pages/home/home.html", {
        ready: function (element, options) {
            showListviewPage();
        }
    });
})();

A listViewItemTemplateFunction function is defined for the ListView's itemTemplate property so we have granular control on each ListView item at run-time. The template function gets the template elements defined in home.html and injects them inside a div container, then use template's render function to emit the content. The ListView is fully populated at this point.

How to do more business logic for each item like what we do in GridView's itemDataBound event in ASP.NET? Here we define another onItemDataBound function to simulate such process. In our example we apply a logic to show different price color based on its amount: green if less than $400 and red if greater than $600. In ASP.NET we find the control inside template by its ID, here we locate an HTML element by its CSS class. Later we will set the edit button handler inside the onItemDataBound function.

The ListView screen looks like:

Sorting Implementation

The single-column sorting is implemented by updating the datasource of the ListView:
    var sortors = { Name: "asc", Bland: "asc", OS: "asc", Size: "asc", PriceFrom: "asc" };

    // Sorting event handler
    function sortingChanged(title) {
        try {
            var sorter = sortors[title];
            smartphones.sort(function (first, second) {
                var firstValue = first[title];
                var secondValue = second[title];
                if (typeof firstValue == "string")
                    return sorter == "asc" ?
                        firstValue.localeCompare(secondValue) : secondValue.localeCompare(firstValue);
                else {
                    if (firstValue == secondValue)
                        return 0;
                    else if (firstValue > secondValue)
                        return sorter == "asc" ? 1 : -1;
                    else
                        return sorter == "asc" ? -1 : 1;
                }
            });
            sortors[title] = sorter == "asc" ? "desc" : "asc";
            showListviewPage();
        } catch (e) {
            console.log("sort error: " + e.message);
        }
    }

    WinJS.UI.Pages.define("/pages/home/home.html", {
        ready: function (element, options) {
            // listview sorting
            $(".row.title", listviewPage).children().each(function (index, columnTitle) {
                var titleText = $(columnTitle).text();
                if (titleText != "Add") {
                    $(columnTitle).on("click", function () {
                        sortingChanged(titleText);
                    });
                }
            });
   //...
        }
    });

When the user clicks the header (title) of one column, the header text is passed to sortingChanged function so the sorting function knows which column is to sort. For simply demo purpose the "Add" button is put as the Edit column header which is not sortable, so it's exluded from the sorting event binding.

Add and Edit Interaction

The HTML has already included a simple edit form but the related logic is missing. We need to define the navigation handling between the list view and the edit form:

    // Listview item databound event  
    function onItemDataBound(container, itemData) {
        //...
        var $edit = $(".edit", container);
        $edit.on("click", itemData, showEditPage);
    }
    
 // Display Add/Edit form page
    function showEditPage(event) {
        if (event && event.data && event.data.ID) {
            editTitle.textContent = "Edit Smartphone";
            btnEdit.textContent = "Update";
            editedPhone = event.data;
            editName.value = editedPhone.Name;
            editBland.value = editedPhone.Bland;
            editOS.value = editedPhone.OS;
            editSize.value = editedPhone.Size;
            editPrice.value = editedPhone.PriceFrom;
        } else {
            editedPhone = null;
            editTitle.textContent = "Add A New Smartphone ";
            btnEdit.textContent = "Add";
            $("input", editPage).each(function (index, input) {
                $(input).val("");  // Cleanup all the input textbox
            });
        }
        $(listviewPage).fadeOut(function () {
            $(editPage).fadeIn();
        });
    }

    // Add or update an item
    function updateOrAddPhone() {
        if (editedPhone) { // Update existing phone
            editedPhone.Name = editName.value;
            editedPhone.Bland = editBland.value;
            editedPhone.OS = editOS.value;
            editedPhone.Size = editSize.value;
            editedPhone.PriceFrom = editPrice.value;
        } else { // Add a new phone
            var phone = {
                ID: smartphones.length + 1,
                Name: editName.value,
                Bland: editBland.value,
                OS: editOS.value,
                Size: editSize.value,
                PriceFrom: editPrice.value
            };
            smartphones.push(phone);
        }
        showListviewPage();
    }
    
    WinJS.UI.Pages.define("/pages/home/home.html", {
        ready: function (element, options) {
            titleAdd.addEventListener("click", showEditPage);
            btnEdit.addEventListener("click", updateOrAddPhone);
            btnCancel.addEventListener("click", function () { showListviewPage(true); });
            //...
        }
    });

The add and edit share the same form. We toggle the display between listView and edit form page section when editting or finishing editting and list item. In this demo the code doesn't have validation for the edit form, but in reality we should always implement validation logic for the user input.

Another note is that in order to get input elements inside the ListView control to work, such as textbox and select dropdowns, you need to add "win-interactive" class for those elements:

    <input id="amount" class="win-interactive" type="number" />
The Add and Edit forms' screen-shot:

The Final JavaScript (home.js)

(function () {
    "use strict";
    WinJS.Binding.optimizeBindingReferences = true;
    WinJS.UI.disableAnimations(); // Use jQuery animation instead

    var editedPhone = null;
    var smartphones = generateSampleData();
    var smartphoneList = new WinJS.Binding.List(smartphones);
    var sortors = { Name: "asc", Bland: "asc", OS: "asc", Size: "asc", PriceFrom: "asc" };

    function generateSampleData() {
        var blands = ["Apple", "Nokia", "Samsung"];
        var oss = ["iOS", "Windows Phone", "Android"];
        var phones = [
            { ID: 1, Name: "iPhone 4", Bland: blands[0], OS: oss[0], Size: 3.5, PriceFrom: 449 },
            { ID: 2, Name: "iPhone 4S", Bland: blands[0], OS: oss[0], Size: 3.5, PriceFrom: 549 },
            { ID: 3, Name: "iPhone 5", Bland: blands[0], OS: oss[0], Size: 4.0, PriceFrom: 649 },
            { ID: 4, Name: "Lumia 820", Bland: blands[1], OS: oss[1], Size: 4.3, PriceFrom: 399 },
            { ID: 5, Name: "Lumia 900", Bland: blands[1], OS: oss[1], Size: 4.3, PriceFrom: 349 },
            { ID: 6, Name: "Lumia 920", Bland: blands[1], OS: oss[1], Size: 4.5, PriceFrom: 449 },
            { ID: 7, Name: "Galaxy S2", Bland: blands[2], OS: oss[2], Size: 4.3, PriceFrom: 349 },
            { ID: 8, Name: "Galaxy S3", Bland: blands[2], OS: oss[2], Size: 4.8, PriceFrom: 499 },
            { ID: 9, Name: "Galaxy Note", Bland: blands[2], OS: oss[2], Size: 5.3, PriceFrom: 599 },
            { ID: 10, Name: "Galaxy Note2", Bland: blands[2], OS: oss[2], Size: 5.5, PriceFrom: 699 },
        ];
        return phones;
    }

    // Listview item databound event  
    function onItemDataBound(container, itemData) {
        var $price = $(".price", container);
        var price = parseFloat($price.text());
        if (price < 400)
            $price.css("color", "green");
        else if ( price > 600)
            $price.css("color", "red");
        var $edit = $(".edit", container);
        $edit.on("click", itemData, showEditPage);
    }

    // Listview template function
    function listViewItemTemplateFunction(itemPromise) {
        return itemPromise.then(function (item) {
            var template = document.getElementById("lvPhonesTemplate");
            var container = document.createElement("div");
            template.winControl.render(item.data, container);
            onItemDataBound(container, item.data);
            return container;
        });
    }

    // Display listview page
    function showListviewPage(skipDatabinding) {
        $(editPage).hide();
        if (!skipDatabinding) {
            smartphoneList = new WinJS.Binding.List(smartphones);
            lvPhones.winControl.itemDataSource = smartphoneList.dataSource;
            lvPhones.winControl.itemTemplate = listViewItemTemplateFunction;
        }
        $(listviewPage).fadeIn();
    }

    // Display Add/Edit form page
    function showEditPage(event) {
        if (event && event.data && event.data.ID) {
            editTitle.textContent = "Edit Smartphone";
            btnEdit.textContent = "Update";
            editedPhone = event.data;
            editName.value = editedPhone.Name;
            editBland.value = editedPhone.Bland;
            editOS.value = editedPhone.OS;
            editSize.value = editedPhone.Size;
            editPrice.value = editedPhone.PriceFrom;
        } else {
            editedPhone = null;
            editTitle.textContent = "Add A New Smartphone ";
            btnEdit.textContent = "Add";
            $("input", editPage).each(function (index, input) {
                $(input).val("");  // Cleanup all the input textbox
            });
        }
        $(listviewPage).fadeOut(function () {
            $(editPage).fadeIn();
        });
    }

    // Add or update an item
    function updateOrAddPhone() {
        if (editedPhone) { // Update existing phone
            editedPhone.Name = editName.value;
            editedPhone.Bland = editBland.value;
            editedPhone.OS = editOS.value;
            editedPhone.Size = editSize.value;
            editedPhone.PriceFrom = editPrice.value;
        } else { // Add a new phone
            var phone = {
                ID: smartphones.length + 1,
                Name: editName.value,
                Bland: editBland.value,
                OS: editOS.value,
                Size: editSize.value,
                PriceFrom: editPrice.value
            };
            smartphones.push(phone);
        }
        showListviewPage();
    }

    // Sorting event handler
    function sortingChanged(title) {
        try {
            var sorter = sortors[title];
            smartphones.sort(function (first, second) {
                var firstValue = first[title];
                var secondValue = second[title];
                if (typeof firstValue == "string")
                    return sorter == "asc" ?
                        firstValue.localeCompare(secondValue) : secondValue.localeCompare(firstValue);
                else {
                    if (firstValue == secondValue)
                        return 0;
                    else if (firstValue > secondValue)
                        return sorter == "asc" ? 1 : -1;
                    else
                        return sorter == "asc" ? -1 : 1;
                }
            });
            sortors[title] = sorter == "asc" ? "desc" : "asc";
            showListviewPage();
        } catch (e) {
            console.log("sort error: " + e.message);
        }
    }

    WinJS.UI.Pages.define("/pages/home/home.html", {
        ready: function (element, options) {
            // Edit form buttons event handler
            titleAdd.addEventListener("click", showEditPage);
            btnEdit.addEventListener("click", updateOrAddPhone);
            btnCancel.addEventListener("click", function () { showListviewPage(true); });

            // listview sorting
            $(".row.title", listviewPage).children().each(function (index, columnTitle) {
                var titleText = $(columnTitle).text();
                if (titleText != "Add") {
                    $(columnTitle).on("click", function () {
                        sortingChanged(titleText);
                    });
                }
            });

            showListviewPage(); // Show ListView by default
        }
    });

})();

Saturday, February 16, 2013

Android Thread Handling in Configuration Change

The Activity Recreating Issue During Configuration Change

When configuration change occurs in an Android device, e.g. rotating the screen from landscape to portrait mode, the Activity will be destroyed and recreated. This could introduce some issues if some tasks inside Activity are not completed during the configuration change. For instance a worker thread may still be running in background and leaking the memory during the configuration change. We assume that the worker thread here in discussion ties to Activity and will communicate back to Activity instance when it completes its task.

Let's take a look at following Activity code:

public class MainActivity extends Activity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private static int instanceCount = 0;
    private Handler handler;
    private Thread thread;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        instanceCount++;
        Log.d(TAG, "onCreate()");

        textView = (TextView)findViewById(R.id.textView1);
        textView.setText("Activity Instance " + String.valueOf(instanceCount));
            
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                Log.d(TAG, "Handler thread - " + getThreadInfo());
            }
        };

        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "Worker thread - " + getThreadInfo());
                try {
                    int count = 10;
                    while(count-- > 0) { // pause 10 seconds
                        Thread.sleep(1000); 
                    }
                    Log.d(TAG, "Worker thread sendMmessage to handler");
                    handler.sendEmptyMessage(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }    
    
    @Override
    protected void onDestroy() {
        Log.d(TAG, "onDestroy()");
        super.onDestroy();
    }

    private static String getThreadInfo()
    {
        Thread currentThread = Thread.currentThread();
        String info = String.format("%1$s ID: %2$d Priority: %3$s",  
                currentThread.getName(), currentThread.getId(), currentThread.getPriority());
        return info;
    }
}

A separate thread sleeps for 10 seconds to simulate a long-run task, then updates a UI view by a handler. If the the screen is rotated within 10 seconds, the activity will be recreated, so as a new thread and a new handler. However the old thread is still running in background, consuming resource and leaking the memory. The old Activity object will not be garbage collected at the time of destroy since the handler and thread are referencing it. The view switches from "Activity Instance 1" to "Activity Instance 2", and the LogCat shows:

Disabling Dangling Thread

The easiest method to resolve the issue is set a flag when the activity is destroyed to control the stale thread:

public class MainActivity extends Activity {
    private boolean stopThread = false;
    //...
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    //...
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "Worker thread - " + getThreadInfo());
                try {
                    int count = 10;
                    while(count-- > 0 && !stopThread) { // pause 10 seconds
                        Thread.sleep(1000); 
                    }
                    if (!stopThread) {
                        Log.d(TAG, "Worker thread sendMmessage to handler");
                        handler.sendEmptyMessage(0);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        }
    }
    
    @Override
    protected void onDestroy() {
        Log.d(TAG, "onDestroy()");
        super.onDestroy();
        stopThread = true;
        handler.removeCallbacksAndMessages(null);
    }
    
    //...
 }
The LogCat logs:

Now the first worker thread is cancelled along with its partially completed task. To save the work by first thread, we can use onSaveInstanceState() callback to store the partial result, so later the second worker thread can use it as an initial start point, as described in this post.

Using Static Thread Object

The solution above is not perfect: multiple thread instances created during configuration change which is inefficient and expensive. We can use static thread variable to maintain one thread instance:

public class MainActivity extends Activity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private static int instanceCount = 0;
    private static WorkerThread thread;
    private TextView textView;
    
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.d(TAG, "Handler thread - " + getThreadInfo());
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        instanceCount++;
        Log.d(TAG, "onCreate()");

        textView = (TextView)findViewById(R.id.textView1);
        textView.setText("Activity Instance " + String.valueOf(instanceCount));
        
        if (savedInstanceState != null && thread != null && thread.isAlive()) {
            thread.setHandler(handler);
        } else {
            thread = new WorkerThread(handler);
            thread.start();
        }
    }    
    
    @Override
    protected void onDestroy() {
        Log.d(TAG, "onDestroy()");
        super.onDestroy();
        handler.removeCallbacksAndMessages(null);
        if (thread.isAlive()) {
            thread.setHandler(null);
        }
    }

    private static String getThreadInfo()
    {
        Thread currentThread = Thread.currentThread();
        String info = String.format("%1$s ID: %2$d Priority: %3$s",  
                currentThread.getName(), currentThread.getId(), currentThread.getPriority());
        return info;
    }
    
    private static class WorkerThread extends Thread {
        private Handler handler;

        public WorkerThread(Handler handler) {
            super();
            this.handler = handler;
        }

        public void setHandler(Handler handler) {
            this.handler = handler;
        }

        @Override
        public void run() {
            Log.d(TAG, "Worker thread - " + getThreadInfo());
            try {
                int count = 10;
                while (count-- > 0) { // pause 10 seconds
                    Thread.sleep(1000);
                }
                if (handler != null) {
                    Log.d(TAG, "Worker thread sendMmessage to handler");
                    handler.sendEmptyMessage(0);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Notice the extended WorkerThread class is also static to avoid memory leak, as in Java non-static inner and anonymous classes will implicitly hold an reference to their outer class. Now the LogCat logs:

As a side note, be cautious to use static variables within Activity to avoid memory leak. If you have to use the static variables, do not forget to cleanup the resources/references in the Activity.onDestroy() callback.

Using Fragment to Retain Thread

Another option, also the recommended way from Android Developer Guild, is to use Fragment with RetainInstance set to true to retain one instance of thread. The worker thread is wrapped into the non-UI Fragment:

public class ThreadFragment extends Fragment {
      private static final String TAG = ThreadFragment.class.getSimpleName();
      private Handler handler;
      private Thread thread;
      private boolean stopThread;

      public ThreadFragment(Handler handler) {
          this.handler = handler;
      }
      
      public void setHandler(Handler handler) {
          this.handler = handler;
      }
      
      @Override
      public void onCreate(Bundle savedInstanceState) { 
        Log.d(TAG, "onCreate()");
        super.onCreate(savedInstanceState);

        setRetainInstance(true); // retain one Fragment instance in configuration change
        
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "Worker thread - " + MainActivity.getThreadInfo());
                try {
                    int count = 10;
                    while(count-- > 0 && !stopThread) { // pause 10 seconds
                        Thread.sleep(1000); 
                    }
                    if (handler != null) {
                        Log.d(TAG, "Worker thread sendMmessage to handler");
                        handler.sendEmptyMessage(0);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
      }

      @Override
      public void onDestroy() {
        Log.d(TAG, "onDestroy()");
        super.onDestroy();
        handler = null;
        stopThread = true;
      }
 }

The Fragment feature was added from Android 3.0 Honeycomb. For older versions you need to include the Android Support package (android-support-v4.jar) to get the Fragment work. With Fragment setup, the main activity will dynamically create or activate existence of ThreadFragment:

public class MainActivity extends Activity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private static int instanceCount = 0;
    private ThreadFragment fragment;
    private TextView textView;
    
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.d(TAG, "Handler thread - " + getThreadInfo());
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        instanceCount++;
        Log.d(TAG, "onCreate()");

        textView = (TextView)findViewById(R.id.textView1);
        textView.setText("Activity Instance " + String.valueOf(instanceCount));
        
        FragmentManager fm = getFragmentManager();
        fragment = (ThreadFragment) fm.findFragmentByTag("thread");

        if (fragment == null) {
            fragment = new ThreadFragment(handler);
            fm.beginTransaction().add(fragment, "thread").commit();
        } else { // retained across configuration changes
            fragment.setHandler(handler);
        }
    }    
    
    @Override
    protected void onDestroy() {
        Log.d(TAG, "onDestroy()");
        super.onDestroy();

        fragment.setHandler(handler);
        handler.removeCallbacksAndMessages(null);
    }

    public static String getThreadInfo()
    {
        Thread currentThread = Thread.currentThread();
        String info = String.format("%1$s ID: %2$d Priority: %3$s",  
                currentThread.getName(), currentThread.getId(), currentThread.getPriority());
        return info;
    }
}

The LogCat result:

Thread Safety

The code snippets demoed about are not thread-safe. To make the code thread-safe, we can set the variable as volatile and wrap the setting inside a synchronized method so that only one thread updates the values at any time:

public class ThreadFragment extends Fragment {
    //...
    private volatile Handler handler;
    private volatile boolean stopThread;  
    //...
    
    public void setHandler(Handler handler) {
        synchronized( this.handler ) {
            this.handler = handler;
        }
        if (handler == null){
            requestStop();
        }
    }      

    public synchronized void requestStop() {
        stopThread = true;
    }
    
    thread = new Thread(new Runnable() {
        @Override
        public void run() {
           //...
           synchronized (handler) {
                if (handler != null) {
                    //...
                    handler.sendEmptyMessage(0);
                }
            }
        }
    }
      
    @Override
    public void onDestroy() {
        //...
        requestStop();
        handler = null;
    }
    
    //...
}

Above code avoids the scenarios like the work thread goes into the block after checking handler is not null, but right at that moment the handler is set to null by the main thread. However I am not so sure if such implementation is necessary. Unlike server side services that may be invoked by multiple callers at the same time, Android apps run locally so this kind of race conditions would rarely occur.

Monday, February 04, 2013

WinJS Unhandled Exceptions and Error Messages

In Windows 8 store app unhandled exceptions can be caught by WinJS.Application.onerror event handler. The app will terminate if such onerror handler is not defined, or the onerror handler returns false. Handling those unexpected errors to avoid app crash is considered a good practice.

The code below examines the detail error message of unhandled exceptions:

    function error1() {
        var test1 = undefefinedObejct.name;
    }

    function error2() {
        throw 'error from error2';
    }

    function error3() {
        throw new WinJS.ErrorFromName('error2', 'error from error3');
    }

    function error4() {
        WinJS.Promise.as().then(function () { throw 'error from error4'; });
    }
    
    // unhandled exception caught in application onerror event:
    WinJS.Application.onerror = function (error) {
        console.log(error);
        return true; // app terminates if false
    }
    
    function errorTest() {
        //error1.type = "error",
        //error1.detail.errorLine = 12,
        //error1.detail.errorMessage = "'undefefinedObejct' is undefined",
        //error1.detail.Url = "ms-appx://errortest.js/js/default.js',
        error1();
        
        //error2.type = "error",
        //error2.detail.errorLine = 16,
        //error2.detail.errorMessage = "error from error2",
        //error2.detail.Url = "ms-appx://errortest.js/js/default.js',
        error2();
        
        //error3.type = "error",
        //error3.detail.errorLine = 20,
        //error3.detail.errorMessage = "error2: error from error3",
        //error3.detail.Url = "ms-appx://errortest.js/js/default.js',
        error3();
        
        //error4.type = "error",
        //error4.detail.exception = "",
        //error4.detail.promise = {promise object},
        error4();
    }
We can see that the error message thrown from WinJS promise is very different from the regular JavaScript code. WinJS.Promise.timeout function has two major usage:
  • WinJS.Promise.timeout(100).then(function () {}): pause 100 milliseconds then continue the next promise function.
  • WinJS.Promise.timeout(100, anotherPromise).then(function () {}): start a timer, if anotherPromise is completed within 100 millisconds, then next promise will continue to run, otherwise an error will be thrown with a 'Canceled' message.
Following code snippet tests the WinJS.Promise.timeout function:
      function dotimeTests() {
        timeoutTest(100, 1000).done(function (data) {
            var result = data; // data = 'result from timeoutTest(100,1000)
        });
        timeoutTest(1000, 100).done(function (data) {
            var result = data; // data.name = 'Canceled', data.message = 'Canceled', data.description = 'Canceled'
        });
        timeoutExceptionTest(100, 1000).done(function (data) {
            var result = data; // data = 'error from timeoutExceptionTest(100, 1000)'
        });
        timeoutExceptionTest(1000, 100).done(function (data) {
            var result = data; // data.name = 'Canceled', data.message = 'Canceled', data.description = 'Canceled'
        });
    }

    function timeoutTest(actionTime, timeout) {
        return new WinJS.Promise(function (c, e) {
            // simulate an async call that takes actionTime
            var promiseAction = WinJS.Promise.timeout(actionTime).then(function (data) {
                return 'result from timeoutTest(' + actionTime + ',' + timeout + ')';
            });
            WinJS.Promise.timeout(timeout, promiseAction).done(function (result) {
                c(result);
            }, function (error) {
                c(error);
            });
        });
    }

Friday, January 11, 2013

WinJS Event Binding with Parameters

This is actually a JavaScript topic but let's just exam it inside WinJS context. Suppose we have a simple home.html with two divs:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>homePage</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

    <link href="/css/default.css" rel="stylesheet" />
    <link href="/pages/home/home.css" rel="stylesheet" />
    <script src="/pages/home/home.js"></script>
</head>
<body>
    <!-- The content that will be loaded and displayed. -->
    <div class="fragment homepage">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">Welcome to Windows Store App!</span>
            </h1>
        </header>
        <section aria-label="Main content" role="main">
            <div id="book">This is book.</div>
            <div id="cd">This a CD.</div>
        </section>
    </div>
</body>
</html>
We want to show some message when those two divs are clicked and the showMessage is defined in home.js:
    function showMessage(value) {
        var msg = "You have clicked " + this.id + ". " + value;
        var popup = new Windows.UI.Popups.MessageDialog(msg);
        popup.showAsync();
    }
We can't bind the click handler directly to the method name because the the parameter will be missing and the execution context is wrong. In order to passing the parameter to the binding function and set the right "this" context, we could wrap the function call inside another function:
(function () {
    "use strict";

    function showMessage(value) {
        var msg = "You have clicked " + this.id + ". " + value;
        var popup = new Windows.UI.Popups.MessageDialog(msg);
        popup.showAsync();
    }

    WinJS.UI.Pages.define("/pages/home/home.html", {
        ready: function (element, options) {
            book.addEventListener("click", function () {
                showMessage.call(this, "Enjoy reading the book!");
            });
            cd.addEventListener("click", function () {
                showMessage.call(this, "Enjoy the music!");
            });
        }
    });
})();
Simple and it works. From the popup we can see the passed-in parameter and the right "this" object are referenced:



There's also other alternative to resolve the problem. John Resig posted an interesting article back in 2008 and later people refer it as JavaScript curry concept. It's basically to pre-fill arguments to a JavaScript function before it executed. Using John Resig's technique we can rewrite above binding and make it more generic and elegant:
(function () {
    "use strict";

    function showMessage(value) {
        var msg = "You have clicked " + this.id + ". " + value;
        var popup = new Windows.UI.Popups.MessageDialog(msg);
        popup.showAsync();
    }

    // Pre-fill arguments to a function. Example:
    //   function sum(a, b, c) { return a + b + c; }
    //   var sum_1_2 = curry(sum, this, 1, 2);
    //   var result = sum_1_2(3); // result = 1 + 2 + 3 = 6
    function curry(fn, self) {
        var self = self || window;
        var args = Array.prototype.slice.call(arguments, 2);
        return function () {
            fn.apply(self, args.concat(Array.prototype.slice.call(arguments)));
        };
    }

    WinJS.UI.Pages.define("/pages/home/home.html", {
        ready: function (element, options) {
            book.addEventListener("click", curry(showMessage, book, "Enjoy reading the book!"));
            cd.addEventListener("click", curry(showMessage, cd, "Enjoy the music!"));
        }
    });
})();

Friday, January 04, 2013

WinJS Promises Run in Sequence

Sometime we need to invoke a series of asynchronous calls (WinJS Promise objects) in sequence, i.e. wait for the first promise to complete and then start the second promise and so on. One way of doing that is by recursive calls as following demo code:

(function () {
    "use strict";
    
    // Download a link asychronously
    function downloadAsync(url) {
        console.log(url + " starting...");
        return WinJS.xhr({ url: url}).then(function () {
            console.log(url + " completed");
        }, function (err) {
            console.log(url + " error: " + err);
        });
    }
    
    // Recersivly download links asychronously
    function downloadRecursiveAsync(urls) {
        if (urls && urls.length > 0) {
            var url = urls[0];
            var remainUrls = urls.slice(1);
            return downloadAsync(url).then(function () {
                return downloadAsyncRecursive(remainUrls);
            });
        } else {
            return WinJS.Promise.as();
        }
    }

    WinJS.UI.Pages.define("/pages/home/home.html", {
        ready: function (element, options) {
            var testUrls = ["http://aa.com", "http://bb.com", "http://cc.com"];
            var promises = [];

            console.log("Start multiple downloads asynchronously without sequence.");
            for (var i = 0; i < testUrls.length; i++) {
                promises.push(downloadAsync(testUrls[i]));
            }

            WinJS.Promise.join(promises).then(function () {
                console.log("All asynchronous downloads completed without sequence.");
            }).then(function () {
                console.log("Start multiple download asynchronously in sequence.");
                downloadRecursiveAsync(testUrls).then(function () {
                    console.log("All asynchronous downloads completed in sequence.");
                });
            });
        }
    });
})();

Console log:

Start multiple downloads asynchronously without sequence.
http://aa.com starting...
http://bb.com starting...
http://cc.com starting...
http://bb.com completed
http://cc.com completed
http://aa.com completed
All asynchronous downloads completed without sequence.
Start multiple download asynchronously in sequence.
http://aa.com starting...
http://aa.com completed
http://bb.com starting...
http://bb.com completed
http://cc.com starting...
http://cc.com completed
All asynchronous downloads completed in sequence.

Thursday, December 20, 2012

WinJS Data Storage and Protection in Windows 8 Store Apps

In my previous post I discussed about how to protect JavaScript source in Windows Store apps. Today I will discuss how to store and secure WinJS app data inside the client machine.

Data Storage and Access


The first question is how and where to save the data. This MSDN documentation is a good reference. There're a lot of options out there but you may end up using one of following methods to store configurations or settings in a Windows 8 machine:

1. WinJS.Application.local.writeText and WinJS.Application.local.readText. Both methods return Promise and they are used to write/read the specified text to/from the specified file. The saved content will be plain text and the content is physically stored in C:\Users\{UserName}\AppData\Local\Packages\{AppID}\LocalState folder. Code example:
    var appLocal = WinJS.Application.local;
    var mySettings = [ {Locale : "EN-CA"}, { Theme : "Classic" }, { LastAccessDate: "2012-12-12" }]; 
    appLocal.writeText("app.config", JSON.stringify(mySettings)).then(function () {
        appLocal.readText("app.config").then(function (data) {
            try {
                var retrievedSettings = JSON.parse(data);
                // do stuff
            }
            catch (ex) {
                // Exception handling 
            }
        });
    });
2. Windows.Storage.ApplicationData.current.localFolder. Same as using "ms-appdata:///Local/" protocol. The data storage location of this localFolder is actually the same as above but the API is different:
    var localFolder = Windows.Storage.ApplicationData.current.localFolder;
    var saveOption = Windows.Storage.CreationCollisionOption;
    var mySettings = {Locale : "EN-CA", Theme : "Classic", LastAccessDate: "2012-12-12"}; 
    localFolder.createFileAsync("myapp.config", saveOption.replaceExisting).then(
        function (file) {
            return Windows.Storage.FileIO.writeTextAsync(file, JSON.stringify(mySettings));
        }).done(function () { 
            localFolder.getFileAsync("myapp.config").then(function (file) {
                return Windows.Storage.FileIO.readTextAsync(file);
            }).done(function (data) {
                var retrievedSettings = JSON.parse(data);
                // do stuff
            });
        });
    }
3. Windows.Storage.ApplicationData.current.localSettings. This localSettings, a key/value pair container, is a bit easier to use because it's not implemented by Prmoise like above two methods. You can set and get values directly in a traditional JavaScript way. The data is stored in C:\Users\{UserName}\AppData\Local\Packages\{AppID}\Settings folder. As name suggested localSettings is ideal for saving settings or small amount of data but not good for big size of content:
    var localSettings = Windows.Storage.ApplicationData.current.localSettings;
    var mySettings = { Locale: "EN-CA", Theme: "Classic", LastAccessDate: "2012-12-12" };
    localSettings.values["myAppSetting"] = JSON.stringify(mySettings); // Assign value
    var data = localSettings.values["myAppSetting"]; // Retrieve value
    if (data) {
        try {
            var retrievedSettings = JSON.parse(data);
            // do stuff
        }
        catch (ex) {
            localSettings.values.remove("myAppSetting");
        }
    }
4. Windows.Storage.ApplicationData.current.roamingSettings/roamingFolder.. The roamingSettings API is pretty much the same as localSettings, and roamingFolder just like localFolder. The difference are:
  • Roaming store would automatically sync the local data to the user's profile in the cloud when the user login as a Microsoft account.
  • Roaming store's physical location is C:\Users\{UserName}\AppData\Local\Packages\{AppID}\RoamingState.
  • Roaming store's URI is "ms-appdata:///Roaming/"
  • Roaming store can only save maximum of 100K data.

Data Encryption and Decryption


In all above methods the saved data are not secure and they can be easily retrieved in other place. So how to protect sensitive data stored in the client machine? Encryption is straightforward answer. Encrypt your data if you don't want to expose them directly to the end user.

The asymmetric encryption is hard and not applicable for distributed single alone application due to the complexity of PKI system. Symmetric encryption such as AES is used in most cases. But client side symmetric encryption is not safe in general because the same encryption key is used in the client machine. The encrypted data can be decrypted in any other machine running the same app, and it's not hard to get the decrypted value. It would be more secure if the encryption key is associated with login user's identity such as SID in Windows machine. In that case the encrypted data can't be decrypted easily in other machine or different user in the same machine.

The problem is that the user SID is not achievable in JavaScript or Runtime component. Fortunately Windows Runtime environment provides Windows.Security.Cryptography library which includes mechanism to encrypt/decrypt data using a key associated with the current user, and the library is accessible from WinJS:
    var localSettings = Windows.Storage.ApplicationData.current.localSettings;
    var cryptography = Windows.Security.Cryptography;
    var cryptoBuffer = cryptography.CryptographicBuffer;
    var cryptoProvider = new cryptography.DataProtection.DataProtectionProvider("LOCAL=user");
            
    var mySettings = { Locale: "EN-CA", Theme: "Classic", LastAccessDate: "2012-12-12" };
    var bufferData = cryptoBuffer.convertStringToBinary(JSON.stringify(mySettings), cryptography.BinaryStringEncoding.utf8);

    cryptoProvider.protectAsync(bufferData).then(function (encryptedData) {
        var dataToBeSaved = cryptoBuffer.encodeToHexString(encryptedData);
        localSettings.values["appSecuredSetting"] = dataToBeSaved;
    }).then(function () {
        var encryptedHexData = localSettings.values["appSecuredSetting"];
        if (encryptedHexData) {
            try {
                var bufferData = cryptoBuffer.decodeFromHexString(encryptedHexData);
                cryptoProvider.unprotectAsync(bufferData).then(
                    function (decryptedBuffer) {
                        var decryptedData =
                            cryptoBuffer.convertBinaryToString(cryptography.BinaryStringEncoding.utf8, decryptedBuffer);
                        var retrievedSettings = JSON.parse(decryptedData);
                        // do stuff
                    },
                    function (err) {
                        // Decryption error handling
                    });
            } catch (ex) {
                // DecodeFromHexString error handling
            }
        }
    });;
The descriptor parameter of "LOCAL=user" passed to DataProtectionProvider is important. I tested other available parameters and this "LOCAL=user" parameter is the only one that would prevent other user from decrypting the data. When data is encrypted with this parameter, they can't be decrypted by any other user in the same machine or other machine. I also tried to decrypt the data in another machine by the same user name but it failed.

The real encryption key used by DataProtectionProvider is invisible to end user or developer. That's great because it makes super hard for attackers to reproduce that key. Conclusion is that by using Windows.Security.Cryptography library and passing "LOCAL=user" to DataProtectionProvider we could secure the data saved to a Windows 8 box.

Tuesday, December 18, 2012

Secure JavaScript Source for Windows 8 Store Apps

I demonstrated how to import an installed Windows JavaScript/HTML application in my previous post. JavaScript is an interpreted language and theoretically you can't prevent it from being reverse engineering. But Windows 8 store apps' architecture makes super easy for a user with administration right to see apps' source file by simply a few clicks. This is not acceptable and I agree with Justin Angel's opinion: that's a fundamental design flaw.

Originally JavaScript is designed to run inside browser. It's mainly for presentation layer and the application flow is driven by the web server. This has been evolved a bit recently due to the popularity of JavaScript, for instance the node.js framework developed for server environment. Now JavaScript becomes one of the main stream languages for developing Windows 8 store apps and the JavaScript code plays more important role there. Those apps' source is so easy to get from physical file system that it would attract people to do so. No need to jailbreak or root the Windows 8 devices, now even casual attackers can have an easy starting point simply by a few clicks. The worst thing is that most developers may not aware of that potential security hole. They simply trust Microsoft and do little or nothing to protect their application. That's why Justin was so easy to break various popular games built by C++, C#/XAML and HTML/JavaScript.

As time goes by I believe more and more measures will be introduced to address the security issues of Windows 8 store apps. For developers, what we can do is to secure our code and data. Following three methods can be used to improve the code security for Window 8 applications:

1. Move critical logic and data back to server side. This is kind of hybrid approach. The Windows 8 app runs locally but some pages or sometimes it would just act like an embedded browser.

2. Split some code into Windows Runtime Component using C#/C++/VB. The Runtime Component is compiled as a dll and can be called directly from JavaScript. For further protection we can obfuscate the code in Runtime Component.

3. Minimize/obfuscate JavaScript code. That's an easy but effective way to protect your code and your logic. Scott Hanselman has a great post going through a couple of tools to do this job. For example following batch file will go through all JavaScript in a solution folder and make a minimized version of it with .min.js extension (original js file unchanged) using AjaxMin:

dir /s /b *.js | find /v ".min.js" | find /v "\bin\" > jsminlist.txt
for /f "usebackq delims=" %%x in (`findstr /V min.js jsminlist.txt`) do (AjaxMinifier.exe "%%x" -o "%%~dpx/%%~nx.min.js" -clobber)
Note that the AjaxMin's executable AjaxMinifier.exe needs to be copied to the solution's root folder before running above script. After run the batch command, we simply exclude the original javascripts not ending with *.min.js, and include those minimized version of JavaScript ending with *.min.js, then update all html files to reference the new JavaScript.

Another topic is secure the data we stored in client machine. I will discuss that in my next post.

Update 2013-01:
1. when publishing the app if you get app validation error of "xxx.js is not properly UTF-8 encoded. Re-save the file as UTF-8 (including Byte Order Mark).", you simple the open the JavaScript file in Visual Studio and click File => Save xxxx.js As... from the menu, select "Save with Encoding" (default is "Save") from the popup window, and select "Unicode (UTF-8 without signature) - Codepage 65001" encoding format then click OK to save the file with proper format for publishing.

2. if get the "Optimized Binding References" error during app validation, something like "WinJS.Binding.optimizeBindingReferences = true is not set and data-win-bind was found in xxxx.html on line number xxx.", to resolve the problem you have to add "WinJS.Binding.optimizeBindingReferences = true;" right on top of your JavaScript if you have data binding in your page. If you get this error only after JavaScript minification, that means the Windows app certification kit doesn't recognize the obfuscated JavaScript and you need to try another tool to do the job. Using JSMin from Douglas Crockford would work for sure since it only removes the space and comments without any other code scrambling.

Sunday, December 16, 2012

An Exercise of Reverse Engineering Windows 8 JavaScript/HTML App

What's the buzz this week? Quite a lot of attention was drawn from a Nokia engineer Justin Angel, a former Microsoft developer who posted online how to hack various popular Windows 8 games in detail. Justin Angel's blog is offline now and even its Google cached page is not available any more. This once more raises people's concern about the Windows 8 application security. Actually Justin had wrote a blog post talking about reverse engineering Windows 8 app more than a year ago. I believe Justin has notified Microsoft about security concerns in the past year but Microsoft just didn't paid enough attention on it. Justin looked frustrated and he wrote: "If Microsoft doesn’t take it upon itself to fix these security attack vectors it’s not because it couldn’t, it’s because it chooses not to."

Justin then hacked some Windows 8 games and rang alarm bells once again. Hope Microsoft won't ignore the security issues this time. As a follow-up on Justin's work I am going to do some testing to reverse engineering a WinJS app. The goal is to run and debug the app using Visual Studio 2010 locally. I am doing this only for study purpose by no means any other.

The test environment I have is a Windows 8 64-bit virtual machine with Visual Studio 2012 and BingMap SDK installed. The app to be reversely engineered is called USAToday, one of my favorite apps also one of the top free apps under News category in Windows store. After installed the app you can see the physical HTML, JavaScript and CSS files under C:\Program Files\WindowsApps\USATODAY.USATODAY_1.2.0.0_neutral__wy7mw3214mat8 folder (google it if you can't see or open this folder). Following are steps I did to import USAToday files into VS2012.

Step 1. Open up VS2012 and create a JavaScript Windows Store project named "USATodayMock" using Navigation App template. The VS2012 solution looks like:



Step 2. Copy highlighted files and folders in C:\Program Files\WindowsApps\USATODAY.USATODAY_1.2.0.0_neutral__wy7mw3214mat8 as below to the VS2012 project:



After files copied to the VS2010 solution:



Step 3. Add project references: Windows Library for JavaScript 1.0, BingMap for JavaScript, and two dlls from USAToday source files:



Now the VS2012 solution can be compiled. When you run it in debug mode in VS2012 it will prompt you a warning, just click yes to override the existing app:



Then you will get an exception thrown by getResourceString method inside Microsoft.PlayerFramework.Js/js/PlayerFramework.js:



It shows missing resource string for "Microsoft.PlayerFramework.Js/resources/TimeFormatterTemplate". Apparently the original USAToday solution includes Microsoft Player Framework which requires some resource strings. To make it simple I just reverse engineer the compiled PRI resource file (resources.pri) come with Windows 8 app package, and directly add those resource strings to the solution. See next step for detail.


Step 4. Open Developer Command Prompt, go to app installation folder C:\Program Files\WindowsApps\USATODAY.USATODAY_1.2.0.0_neutral__wy7mw3214mat8, run "makepri dump" to build the resources.pri.xml file:



The exported resources.pri.xml looks something like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PriInfo>
  <ResourceMap name="USATODAY.USATODAY" version="1.0" primary="true">
    <Qualifiers></Qualifiers>
    <ResourceMapSubtree name="Bing.Maps.Javascript"></ResourceMapSubtree>
    <ResourceMapSubtree name="Files">
    <ResourceMapSubtree name="Microsoft.PlayerFramework.Js">
      <ResourceMapSubtree name="resources">
         <NamedResource name="TimeElapsedTitle" 
          uri="ms-resource://USATODAY.USATODAY/Microsoft.PlayerFramework.Js/resources/TimeElapsedTitle">
            <Candidate qualifiers="Language-EN-US" isDefault="true" type="String">
              <Value>{hour.integer}:{minute.integer(2)}:{second.integer(2)}</Value>
            </Candidate>
         </NamedResource>
       </ResourceMapSubtree>
     </ResourceMapSubtree>
   </ResourceMap>
</PriInfo>
Copying those resource strings is tedious. I created a simple console app that convert those player framework resource strings in resources.pri.xml to WinJS recognizable JSON format:
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, string> resources = new Dictionary<string, string>();
            XDocument xdoc = XDocument.Load("c:\\temp\\resources.pri.xml");
            var frameworkResources = xdoc.Descendants("ResourceMapSubtree")
                    .Where(e => (string)e.Attribute("name") == "Microsoft.PlayerFramework.Js")
                    .Elements("ResourceMapSubtree")
                    .Where(e => (string)e.Attribute("name") == "resources")
                    .Elements();
            foreach (var item in frameworkResources)
            {
                var key = item.Attribute("name").Value;
                var value = item.Descendants("Value").First().Value;
                resources.Add(key, value);
            }
            using (TextWriter tw = new StreamWriter("C:\\temp\\resources.resjson"))
            {
                tw.WriteLine("{");
                int resourceCount = resources.Count, counter = 1;
                foreach (var item in resources)
                {
                    if (counter++ < resourceCount)
                        tw.WriteLine(string.Format("\"{0}\" : \"{1}\",", item.Key, item.Value));
                    else
                        tw.WriteLine(string.Format("\"{0}\" : \"{1}\"", item.Key, item.Value));
                }
                tw.WriteLine("}");
            }
        }
    }
}
Above console app will create a resources.resjson file in C:\temp folder. Copy it to USATodayMock solution.

Step 5. Modify getResourceString method in Microsoft.PlayerFramework.Js/js/PlayerFramework.js, remove the prefix of "Microsoft.PlayerFramework.Js/resources/" for the resource keys since they are included in the project directly:
    function getResourceString(id) {
        /// <summary>Returns the resource string with the specified ID.</summary>
        /// <param name="id" type="String">The resource identifier.</param>
        /// <returns type="String">The resource string.</returns>

        if (id.indexOf("resources/") > 0)
            id = id.substring(id.lastIndexOf("/") + 1);

        var string = WinJS.Resources.getString(id);

        if (string.empty) {
            throw invalidResourceId;
        }

        return string.value;
    }
Click F5 and bang the USATodayMock app can be run and debug now!

Step 6. This step is to do a little more of test by modifying the default page. USAToday Win8 app shows weather in the right top corner:



Let's just change weather text to red and show some dummy text when you click it. Search following text inside default.html page under the root of the USATodayMock solution:
    <div class="banner">
        <div class="snapped-back"></div>
        <div class="app-logo"></div>

        <div class="weather">
            <div class="location"></div>
            <div class="icon-wrapper hidden"><img class="icon" /></div>
            <div class="temp"></div>
        </div>
    </div>
Then replace it with following HTML:
    <div class="weather">
        <div class="location" style="color:red;" onclick="showAlert();"></div>
        <div class="icon-wrapper hidden"><img class="icon" /></div>
        <div class="temp"></div>
    </div>

    <div id="divAlert" style="display:none; position:fixed; margin-top: 200px; margin-left: 150px;">
            <h1>App has been modified!</h1> 
    </div>
    <script type="text/javascript">
        function showAlert() {
            event.cancelBubble = true;
            WinJS.Utilities.query("#divAlert").setStyle("display", "block");
            WinJS.Utilities.query("#app-body").setStyle("display", "none");
        }
    </script>
Run the application again you will see the red location text:



When you click that red text (New York) the page will show the custom content instead of going to the location setting page:



So we see how easy it's to import an installed Windows 8 application into Visual Studio and run it directly. In my next post I will discuss some methods to secure the code and make it harder to be reversely engineered.

Saturday, December 08, 2012

Notes About Android Threading

  • Main thread, or UI thread, starts Android application, processes UI rendering and handles user interactions. Do NOT execute any long-running or heavy tasks inside main thread otherwise the UI would appear lagging and not responsive.
  • A Looper holds a message queue and implements infinite loop. It takes tasks one by one from the queue and executes them in sequence. When message queue is empty, the Looper is blocking and waiting for processing next queued task. A thread can associate with a Looper (only one to one relationship), then it becomes a Looper thread being able to process messages and tasks continuously, vs. regular thread that will die when completes its job in the run() method.
  • Handler is a bridge between Looper message queue and thread(s). Current thread or other threads can push runnable jobs to a Looper message queue by method handler.post() and its scheduled variance (postDelayed, postAtTime, etc.), or send a message to the queue by handler.sendMessage() method and its scheduled variance (sendMessageDelayed, sendMessageAtTime, etc.). Handler can also process the message by the Handler's handleMessage(Message) callback.
  • You can setup a thread with a Looper manually. Androdi also provides a handy class called HandlerThread for starting a new thread that already has a Looper attached. Not sure why it's not called LooperThread. It's a bit confusing as there's no Handler in a HandlerThread object. You always need to create Handler for a HandlerThread:
        HandlerThread  handlerThread = new HandlerThread("Thread name");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());
    
  • Main thread is a Looper thread. All UI interactions are pushed to main thread's Looper message queue and then are processed one by one. Configuration change request, such as orientation rotation, is just a special type of message sent to main thread's message queue.
  • Only main thread can update UI elements safely. Worker thread or background thread can use following means to work on UI elements:
    • Implement main thread Handler, then you can update UI inside handler.handleMessage() callback, and post UI-related task to handler.Post() method or their variance.
    • Use View.Post(Runnable). Android View objects have tied to a default Handler inside UI thread so you can post UI-related runnable jobs to it directly.
    • Use Activity.runOnUiThread(Runnable) method. Android Activity class has this runOnUiThread helper method to update the UI.
    • Use AsyncTask. The AsyncTask implementation takes the advantage of Thread pooling concept and provides a simple, understandable interface. Simply run background task inside the doInbackground() method, and run UI-related work inside the onPreExecute(), onProgressUpdate() and onPostExcute() methods. But be aware of the performance penalty of using it, see next.
  • AsyncTask thread has low priority of Process.THREAD_PRIORITY_BACKGROUND. With such low priority the total CPU consumption of all AysncTasks together is less than 10 percent of overall CPU power. This may cause some performance issue. You can change this behavior by changing its priority:
        new AsyncTaskClass().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
        //...
        class AsyncTaskClass extends AsyncTask<...> {
            //...
            protected Void doInBackground() {
                Thread.currentThread().setPriority(Process.THREAD_PRIORITY_DEFAULT);
                //...
            }
        }
    
  • On the other hand the HandlerThread uses Process.THREAD_PRIORITY_DEFAULT priority by default, but you can specify HandlerThread's priority from its constructor, e.g. setting a low priority background:
    HandlerThread bgThread = new HandlerThread("Background thread", Process.THREAD_PRIORITY_BACKGROUND);
    
  • The ExecutorService is more powerful and can manage a pool of threads. Go for it if you want to have full control of your threads.
  • Usually worker thread is created from Activity. But it can also run in Service context (thread object created inside Service) without UI connection. Note that Service still runs in main UI thread by default if it starts from the Activity, but Service is not bound to Activity's life-cycle. IntentService simply extends Service and implements an internal HandlerThread object, so you can guarantee the job passed to IntentService's onHandleIntent callback runs in a separate worker thread.
  • There're a few ways to deal with recurring or repeating tasks. One common approach is keep posting to Activity's handler by using Handler.postDelayed() inside the runnable task:
        int recurInSeconds = 5;
        Handler handler = new Handler();
        handler.postDelayed(runnable, 0); // start the task
        Runnable runnable = new Runnable() {
           @Override
           public void run() {
              doTask(); 
              handler.postDelayed(this, recurInSeconds * 1000); // repeat the task
           }
        };
    Without Activity context we can use following methods to achieve the goal:
    • AlarmManager.setRepeating()/setInexactRepeating() to repeatedly start a Broadcast/Service in which the job is handled. For longer sleep intervals this is the preferred mechanism. It's handled by system and is only triggered at the time arrives thus consuming less power.
    • ScheduledThreadPoolExecutor.scheduleWithFixedDelay()/scheduleWithFixedDelay() to repeatedly run a task.
      ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
      executor.scheduleAtFixedRate(runnable, 0, recurInSeconds, TimeUnit.SECONDS);
    • Use Timer.scheduleAtFixedRate() to do the recurring TimerTask. This is not the recommended way described from Android development guild.
  • Anders Göransson's Efficient Android Threading slides for DroidCon are very informative. It's the best reference on the topic of Android threading I have found so far.