Screen to small to show this interactive demo

Main Screen

The main display shows trending topics along with Search and Nearby Tweets tools. KPS provides a default header and footer; the app may modify both.

Note: In this annotated code listing, click any element in the screenshot to highlight the code that creates it.

 

Main Handler & Viewer

Apps built with KPS are driven by data, whereas many other frameworks start with events. Rather than starting by building the user interface, a KPS app starts by gathering its data. So, first this app loads current trends using the Twitter Search API. With that data, it displays the top five trends, along with tools for Search and Nearby.

                    
Why XML?
XML is a great tool for describing data, for declaring the structure of objects- and everyone knows XML. KPS uses XML for the structure of apps. This common structure makes the code easier to read, and inherently modular. This in turn encourages more code sharing and easier code maintenance. It allows developers to isolate parts of the code and manage them independently. It also enables tools to easily operate on KPS applications, such as the outline view navigator in KDT (as shown at right). What XML does not do well is describe logic: for that KPS embeds ECMAScript (aka JavaScript).
<?xml version="1.0" encoding="utf-8"?> <application xmlns="http://www.kinoma.com/kps/1" id="com.kinoma.whatshappening" version="1.0">
Interacting with Kinoma Play's Homescreen
The Service element defines the entry point into the app. The Title and Icon given here will determine how the app is listed on the Kinoma Play homescreen. The Action parameter selects which Handler will be invoked when the user launches the app.
<service title="What's Happening?" icon="locate" action="/main"/>
Icons
Icons are loaded from icon strips and made available throughout the app. They are referenced by name in Viewers to render the icon and to bind data records to interface elements (as explained below). Developers can provide icon strips at small, medium, and large resolutions. Kinoma Play automatically chooses the icon size that is most appropriate for the user's display.
<icons small="small.png" medium="medium.png" large="large.png" names="trend,locate" />
Data Retrieval and Processing in Handlers
KPS handlers are roughly analogous to web server handlers, such as an Apache plug-in or a PHP script: they are triggered by a URI, their behavior may be modified by query parameters, they operate asynchronously, and they usually return some data or redirect to another URI. KPS handlers retrieve and parse data from various services and manipulate it into a format that KPS uses to construct the user interface. The Handler creates and returns an object, usually a List, that represents the items to be displayed on screen. To do this, the Handler can redirect requests to other Handlers within the app, services provided by KPS, or external web services.
<handler id="main"> <script><![CDATA[ function onQuery(query){ this.loadHTTP("http://api.twitter.com/1/trends/1.json"); }
Asynchronous Loading
Like web services, KPS Handlers often operate asynchronously. KPS even provides a default progress indicator while retrieving data asynchronously. A handler is initially invoked through its onQuery() function. onQuery() can directly return data to be displayed by a Viewer, or it may initiate an asynchronous request for data. When the response from the data retrieval request is received, KPS invokes the handler’s onResponse() function which processes the data for display by a Viewer.
function onResponse(response, query, link){ var reply = response.parseJSON(); var trends = reply[0].trends; var data = this.createList();
record = this.createRecord("", "label", "Trending Topics"); data.addRecord(record);
Interface Elements are List Records
Many KPS Handlers return a simple list of records that represent what should be displayed. List records are used to specify both data-driven UI elements and user interface tools, such as separators, buttons, and icons. This example builds a list that contains one record for each of the first five Twitter trends, visual separator records, and records that function as buttons to move to other features of the app.
for (var i = 0; i < trends.length && i < 5; i++){ var trend = trends[i]; var record = this.createRecord("/search?search_term=" + trend.query, "trend", trend.name); data.addRecord(record); }
record = this.createRecord("", "separator"); data.addRecord(record);
record = this.createRecord("/SEARCH", "search", "Search Twitter"); data.addRecord(record);
record = this.createRecord("", "separator"); data.addRecord(record);
record = this.createRecord("/nearby", "near", "Show Nearby Tweets"); data.addRecord(record);
record = this.createRecord("", "separator"); data.addRecord(record); return data; } ]]></script> </handler>
Building the UI in Viewers
Viewers transform the records built by handlers into a user interface by binding records to display elements. Viewers are optional – KPS provides a simple default viewer. A common way to build a KPS application is first to implement the data flow using only Handlers with the default Viewer, and then add Viewers to refine the user interface. App developers have access to a wide range of user interface tools built into KPS, and can customize all of them. In addition, developers can build entirely new controls in KPS.
<viewer id="main"> <layout> <table>
Mapping Records to UI Elements
Each record in a List specifies an icon type. The icon property has two roles: it specifies the binding between record types and display types, and it provides the default icon for the Viewer to display for the record. KPS uses the icon to map each record in a List created by the Handler to a Cell in the Viewer. Cells describe how to render their associated Records. Many Cells, such as the cells in this Viewer, describe their rendering using a simple XML layout language. When more precise control is required, Cells use scripts to describe the rendering of records, as is done in the Search Viewer below.
<cell icon="tweet"> <thumbnail style="middle"/> <column> <text id="description" style="medium,bold,top" /> <text id="title" style="medium,bottom" lines="all" /> </column> </cell>
<cell icon="trend"> <icon style="large,left" /> <text id="title" style="medium,middle" /> </cell>
<cell icon="separator" select="false"> <separator /> </cell>
<cell icon="label" select="false"> <text id="title" style="large,bold,center" /> </cell>
<cell> <text id="title" style="black,left" /> </cell> </table> </layout> </viewer>

Search Dialog

A simple text input Dialog that allows a user to input a search term. The dialog Fields are specified declaratively in XML.

Search Dialog & Handler

Dialogs are a special kind of Handler that come pre-packaged with built-in Viewers. They retrieve responses from the user. The user input is sent to another Handler for further processing. The Handler triggered by the Dialog is specified in the dialog’s action attribute.

Below the dialog are the Handlers for the Search and Nearby functions. The Search Handler takes the user input from the dialog and uses it to search Twitter.  The Nearby Handler gathers location data from the device and then triggers the Search Handler.

  <dialog id="SEARCH" action="/search" title="Search..." cancel="Cancel" ok="Search">
    <script><![CDATA[
        function canOK(){
          return this.items.search_term.value ? true : false;
        }
      ]]></script>
    <field id="search_term" />
  </dialog>

  <handler id="search">
    <script><![CDATA[
        function onQuery(query){
          var URI = "http://search.twitter.com/search.json?";
          if (query.search_term != undefined) URI += "q=" + encodeURIComponent(query.search_term);
          if (query.location != undefined) URI += "&geocode=" + encodeURIComponent(query.location);
          this.loadHTTP(URI);   			
        }

        function onResponse(response, query, link){
          var reply = response.parseJSON();
          var results = reply.results;   				
   				
          var data = this.createList();
          for (var i = 0; i < results.length; i++){
            var r = results[i];
            var record = this.createRecord("", "tweet", KPS.stripHTML(r.text),
			                   r.profile_image_url, r.from_user);
            data.addRecord(record);
            if (i != results.length - 1){
              record = this.createRecord("", "separator");
              data.addRecord(record);
            }
          }   				
          return data;
        }
      ]]></script>
  </handler>
    
  <handler id="nearby">
    <script><![CDATA[   			
        function onQuery(query){
          if (this.application.lat != undefined && this.application.long != undefined){
            this.trigger("/search?location=" + encodeURIComponent(this.application.lat + 
			 "," + this.application.long + ",1mi"));
          }else{
          
Location Services
KPS provides convenient access to many device capabilities, all modeled using the same asynchronous query/response model used for outside service providers. In this example, locate() determines the location of the device. Once a location is determined, KPS invokes the Handler’s onReponse() function.
this.locate(); } } function onResponse(response, query, link){ this.application.lat = response.latitude; this.application.long = response.longitude; this.trigger("/search?location=" + encodeURIComponent(this.application.lat + "," + this.application.long + ",1mi")); } ]]></script> </handler>

Search Results

A Viewer that shows Twitter search results. The same Viewer is used to display results from the text-based search and from the location-based search. The separators between tweets are drawn with code to show how developers can programmatically render the contents of a Cell.

 

Search Results Viewer

This Viewer renders the List created by the Search Handlers (which is, itself, called by the Nearby Handler). This Viewer demonstrates custom cell drawing -- the separators between Tweets are rendered using script, rather than specified declaratively (as in the Main Viewer above).

  <viewer id="search">
  
Layout Inheritence
A Viewer can inherit from other Viewers to simplify code development and allow for code reuse. In this instance, the Viewer inherits the declaration of how to display records with type "tweet" from the "main" Viewer.
<layout like="main"> <table>
<cell icon="separator"> <canvas> <script><![CDATA[ function measure(){ return {height: 5} }
Custom Drawing
Some apps need display capabilities beyond the default user interface library or default layout options provided by KPS. The developer can draw arbitrary graphics using the draw() function. This functionality is shown here rendering a non-standard separator for the Tweets.
function draw(record, x, y, width, height){ var dashes = Math.floor((width-10) / 20) + 1; var onX = x + ((width - (10 + ((dashes - 1) * 20))) / 2); for (var i = 0; i < dashes; i++){ this.fillColor("black", onX, y, 10, height); onX += 20; } } ]]></script> </canvas> </cell> </table> </layout> </viewer> </application>