TOCPREVNEXTINDEX

 

Chapter 2: Key Concepts

This chapter describes the key concepts involved in programming with the HME SDK. Major areas of discussion include the following:

Application Life Cycle

All HME applications subclass the Application class, which is the main entry point for interactions with the SDK. The SDK creates a new instance of the application each time a Receiver creates a new connection. The application is responsible for painting the screen, handling events, and controlling media.

Every application is different, but the SDK will follow the same general pattern when managing the application’s life cycle. The methods called on your application will follow the pattern described here:

    1. Initialize
    2. Handle events
    3. Send commands from other threads
    4. Destroy

Initialize

After the Application is created by the Application Factory, its init() method is called. The init() method takes an IContext object and can throw a generic Exception. This exception allows you to check for required conditions and to close down the application if you detect a reason why it will be unable to run properly. If you override init(), be sure to call init() on the super class as well.

The init() method is where you create the user interface for your application (view hierarchy, associated resources, and event handlers, as described in the following section). Here is the complete code for the HelloWorld sample:

package com.tivo.hme.samples.hello; 
 
import java.awt.Color; 
 
import com.tivo.hme.interfaces.IContext; 
import com.tivo.hme.sdk.Application; 
 
public class HelloWorld extends Application 
{ 
    /** 
     * Create the app. 
     * @throws Exception 
     */ 
    public void init(IContext context) throws Exception 
    { 
        super.init(context); 
        getRoot().setResource(createText 
                              ("default-36-bold.font", 
                              Color.white, "Hello, world!")); 
    } 
} 

Handle Events

Once the application is constructed, the SDK will send it events from the Receiver. See “Events” for a description of events and to learn how events flow through your application.

void Application.dispatchEvent(HmeEvent event); 

All events are initially processed by dispatchEvent(), which sends the event to the appropriate object for handling:

HmeObject.handleEvent(HmeEvent event); 
Application.handleEvent(HmeEvent event); 

Your application can respond to key events easily by overriding one of the key event handlers (there are handlers for key press, key repeat, and key release for each key).

Unhandled events are sent to the parent and will eventually arrive in Application.handleEvent(). For certain kinds of events, handleEvent() will automatically send the event to a helper method.

Nonfatal errors are sent to your application via Application.handleApplicationError(). It could be an error loading a resource or a warning message from the Receiver. The Application.handleActive() method indicates that the application is either becoming active or deactivating on the client.

While the application is responding to an event, it may issue one or more commands. These commands are buffered and, after the event is processed, the accumulated commands are automatically flushed to the Receiver.

Send Commands from Other Threads

In addition to handling events, your application can choose to send commands to the Receiver at any time from other threads. A common example is creating a separate timer thread that updates the screen periodically. When commands are sent from other threads, don’t forget that you have to manually call flush().

Destroy

When the client disconnects, the hosting environment automatically calls the Application.close() method. (This method is final and cannot be overridden.) The close() method in turn does two thinigs:

You can override the destroy() method to free any held resources and terminate any additional threads started by your application. (At this point, the IContext object is still available to your application, so you can save persistent data, perform logging, and so on.) If you create your own threads, use the isApplicationClosing() method in the threads to determine whether the thread should shut down.

After calling destroy(), the close() method finishes shutting down the application. It sets the context to null and removes the application instance from its factory.

Views

One of the Receiver’s primary responsibilities is to paint HME applications onscreen. Views are the basic building blocks of HME applications and provide containment, a coordinate system, and transparency but are invisible unless they contain resources. Resources include visual elements, such as a color, image, or text, that are drawn inside of views. Sounds and animations are other examples of resources. Each view can contain a single resource. View coordinates are given in pixels.

HmeObject

HmeObject is the superclass for both the View and Resource classes, as shown in Figure 2-1. It contains a reference to the application and the object’s HME ID. Many of the methods in HmeObject call into the application to perform the actual work. Note that since Application is a subclass of Resource, all of the HmeObject helpers are also available inside your application class.

Figure 2-1. HmeObject and immediate subclasses

View Hierarchy

The layout of an application is controlled through a view hierarchy that defines what is shown onscreen. Each application contains a special 640x480 root view that is the top of the view hierarchy. Putting a blue color resource into the root view, for example, will turn the whole screen blue. Applications build up the view hierarchy starting at the root in order to lay out the screen.

View paint order is well defined:

HelloWorld’s object hierarchy looks like this:

View: (0,0,640,480)

Resource: Text “Hello, world.”, 0xff000000

A view is totally invisible unless it contains a resource such as a color, image, or text

View Parameters

Table 2-1 lists the methods and parameters for the View class. For further details, see the Javadoc documentation.

Table 2-1 Methods and Parameters for the View class

Field
Description
View Method
parent
The parent view. This is null for the root view.
constructor
nchildren
children[]
Views can contain zero or more child views. Child views in turn can contain other views, thus defining the view hierarchy.
constructor
x
y
width
height
Each view has a bounding rectangle that defines the location and size of the view within its parent. The contents of a view are always clipped to its bounding rectangle. x/y can be negative, but width and height must be non-negative.
setBounds(x, y, width, height)
setLocation(x, y)
setSize(width, height)
[pixels]
tx
ty
Translation defines the origin of the view’s local coordinate system. Translation affects all of the view’s content as well as the child views. tx/ty can be positive or negative.
setTranslation(tx, ty)
[pixels]
sx
sy
Views can be scaled up or down. Scaling affects all of the view’s content as well as the child views. The scale must be greater than or equal to zero.
setScale(sx, sy)
[float]
transparency
Each view has a transparency value that controls how the view blends in with the background. Transparency is a number between 0 (opaque) and 1 (transparent). Transparency affects all of the view’s content as well as the child views.
setTransparency(transparency)
[float]
visible
Determines whether the view is visible. If false, the view and its content are not drawn. Also affects child views.
setVisible(visible)
[Boolean]
painting
If true, the view is painted as normal. If false, all changes to the view are suspended until painting is set to true again; this value is used to freeze the display onscreen while changes are made. Commands are buffered and executed in rapid succession when painting is set to true.
setPainting(painting)
[Boolean]
resource
Each view can contain a single resource such as an image, color, text, sound, or font. By default, a visible resource is centered in the view taking into account scaling, translation, clipping, transparency, etc.
setResource(resource)
clearResource()
resource flags
Resources are positioned within views according to the flags specified in setResource(). The flags determine where to place the resource within the view, how to scale it, and also control specific details such as text wrapping.
setResource(resource, flags)

Removing Views

To remove a view and all its children from the view hierarchy, call View.remove(). To conserve memory, remove unused views.

Painting Efficiently

Normally all changes to the view hierarchy have an immediate effect onscreen. This can cause undesired effects when a large number of changes need to be made, since some of the intermediate states will be visible onscreen.

There are several ways this can be counteracted. First, when creating a new View hierarchy, you can make the top-most view invisible. Once the view hierarchy is created, you can then make the top-most view visible in a single operation.

The SDK uses this technique automatically when calling Application.init(). The root view of an application is always created with visible set to false. This allows the application to create its initial view hierarchy and populate it with resources inside init() before the root view is made visible. This behavior is built into the SDK and requires no action on your part.

When making lots of changes to a view hierarchy, you can turn off painting of a view. This means that the initial state of the view hierarchy will remain visible while the changes are made. The new changes are queued up and are executed when painting is turned on again.

For example, the following code turns off painting, sets up a one-second cross fade, and then shows the results on the screen:

getRoot().setPainting(false); 
Resource anim = getResource("*1000"); 
fg.setTransparency(1.0f); 
fg.setTransparency(0.0f, anim); 
bg.setTransparency(0.0f); 
bg.setTransparency(1.0f, anim); 
getRoot().setPainting(true); 

Resources

Resources are elements that are used to draw onscreen or play media. To draw a resource on the screen, it must be assigned to a view. The view’s coordinate system will determine how the resource appears onscreen. Note that a resource can be assigned to more than one view. For example, each row in a list widget could contain the same background image resource.

Some resources generate events. For example, when an audio stream is played, the Receiver sends progress events to the HME application. The application can use these events to display a progress bar or automatically advance to the next track.

General Categories

There are three types of assets accessible to HME applications:

Two Techniques for Creating Resources

There are two different ways for an application to create resources:

Performance Tip for Creating Resources

There is another important difference between these two mechanisms. Resources created with getResource() are cached by the Receiver for later reuse. Calling this method twice with the same string will return the same resource. Prefer getResource() over create- to avoid accidentally sending resources over the network more than once.

Cleaning Up Resources

Applications that use many resources should call remove() on them when they are no longer needed. If you do not call remove(), you may run out of memory in your Application and on the Receiver (see Chapter 3, “Limits”).

Assigning Resources to Views

Resources that draw onscreen, such as colors, images, and text, as well as streaming sounds, must be assigned to views. Resources are assigned to views using the View.setResource() method. The view can also be assigned a set of resource flags that provide additional control over how that resource is painted within the view. Table 2-2 describes the resource flags. Note that some flags are valid only for specific kinds of resources.

Table 2-2  Resource Flags

Flag
Used With
Description
RSRC_HALIGN_LEFT
image, text
Horizontally left aligned
RSRC_HALIGN_CENTER
image, text
Horizontally center aligned (default)
RSRC_HALIGN_RIGHT
image, text
Horizontally right aligned
RSRC_VALIGN_TOP
image, text
Vertically top aligned
RSRC_VALIGN_CENTER
image, text
Vertically center aligned (default)
RSRC_VALIGN_BOTTOM
image, text
Vertically bottom aligned
RSRC_TEXT_WRAP
text
Wrap long text strings
RSRC_IMAGE_HFIT
image
Fit horizontally and maintain aspect ratio
RSRC_IMAGE_VFIT
image
Fit vertically and maintain aspect ratio
RSRC_IMAGE_BESTFIT
image
Fit using the larger dimension

System Fonts and Sounds

All receivers have a set of system resources for use by developers. Note that system resources cannot be created with create- since they exist implicitly. They are accessed using getResource(). Table 2-3 lists the system resources.

Table 2-3 System Fonts and Sounds

Resource Type
getResource() parameter
System fonts
"default.ttf"
"system.ttf"
System sounds
"alert.snd"
"bonk.snd"
"deselect.snd"
"error.snd"
"left.snd"
"pagedown.snd"
"pageup.snd"
"right.snd"
"select.snd"
"slowdown1.snd"
"speedup1.snd"
"speedup2.snd"
"speedup3.snd"
"thumbsdown.snd"
"thumbsup.snd"
"tivo.snd"
"updown.snd"

Inlined vs. Streamed Resources

Remember that the Receiver communicates with the application over a single TCP/IP socket using the HME protocol. Created resources can be sent inline through that socket. For example, if an application creates an image resource that is 512kb, no other HME commands can be read by the Receiver until after the entire image resource has been read. This slight delay might become apparent to the end user when HME is running over a wireless network.

For stream resources, the URI string is the only information that is communicated inline, and a separate HTTP request is made by the Receiver to fetch the data referred to by the URI. The HME command stream is not blocked while the resource is loading. For this reason, prefer createStream() over createImage() if you want to load a large number of images without stalling the application.

Types of Resources

This section lists the various types of resources available in the HME SDK. It includes information on the create- method for each resource type. At the end, Table 2-4 summarizes how to specify the strings for each resource type to the getResource() method.

Resources include the following:

Colors

A color resource fills a view with a specific RGB value.

The syntax for createColor() is

public Resource createColor(java.awt.Color color) 

For example:

createColor(java.awt.Color) // use awt 

Fonts

Receivers draw text using TrueType fonts. Each Receiver includes two built-in fonts, default.ttf and system.ttf. Applications can also upload custom fonts to the Receiver. Custom font files are located using the methodology described in Chapter 3, “Finding Assets.” A custom font is uploaded to the device as soon as it is created.

A font resource is the combination of a TrueType resource, style information (plain/bold/italic), and a point size.

The createFont() method has the following forms:

public Resource createFont(java.lang.String family,
                           int style,
                           int size) 
 
public Resource createFont(java.lang.String family, 
                           int style, 
                           int size, 
                           int flags) 

The second form of createFont() also includes font metric measurement flags. See Chapter 4 for more information on font metrics.

For example:

createFont("default.ttf", FONT_BOLD, 12) 
createFont( "default.ttf", 
           FONT_BOLD,  
           36, 
           FONT_METRICS_BASIC|FONT_METRICS_GLYPH ); 

Text

Text resources display a text string within a view and consist of a Font resource, a Color resource for the foreground, and the text string itself.

The syntax for createText() is:

public Resource createText(java.lang.Object font, 
                           java.lang.Object color, 
                           java.lang.String text) 

For example:

createText("default-12.ttf", Color.red, "Hello, world!")
createText("default-12.ttf", "0xff0000", "Hello, world!") 
createText(font, Color.black, "Hello, world!") 

Images

Image resources are PNG, JPEG or GIF files that are sent to the Receiver for display. Image files can be in the classpath, or can be located using the methodology described in Chapter 3, “Finding Assets.” Also see Chapter 4, “Rendering Constraints,” for information on hardware limits related to image display.

The createImage() method has the following forms:

public ImageResource createImage(java.lang.String name) 
// creates an image resource. name should be a PNG or JPG 
// image file. 
 
public ImageResource createImage(java.awt.Image image) 
// creates an image resource from an AWT image 
 
public ImageResource createImage(byte[] buf) 
// creates an image from an entire byte buffer 
 
public ImageResource createImage(byte[] buf, 
                                 int off, 
                                 int len) 
// creates an image from a byte buffer containing a 
// PNG or JPG image 

For example:

createImage("my-image.png") 

The HME SDK supports the dynamic creation of images at runtime using the AWT image framework. A dynamic image will automatically be PNG encoded and sent over the network. For example:

Image img = new BufferedImage(...); 
Graphics2D g = img.getGraphics(); 
g.fillRect(10, 10, 20, 20); 
getRoot().setResource(img); 

Sounds

A sound resource is a short audio clip, usually only a few seconds long. In addition to the built-in sounds listed in “System Fonts and Sounds,” applications can upload custom sounds. Custom sounds are located using the methodology described in Chapter 3, “Finding Assets.” The sounds must be in 8,000 Hz signed 16-bit little endian mono PCM format.

The syntax for createSound() is:

public SoundResource createSound(java.lang.String name) 

For example:

createSound("dogBark.snd") 

A shortcut method in HmeObject makes it easy to play sounds:

play("bonk.snd") 
play("dogBark.snd") 

Streams

Stream resources are images or audio streams that are asynchronously streamed from the network to the Receiver. The Receiver sends events to the application to communicate stream properties and progress. At most, there are three parameters used to create stream resources:

image/jpeg 
image/jpg 
image/png 
image/gif 
audio/mpeg3 

The createStream() method has the following forms:

public StreamResource createStream(java.lang.String uri) 
// creates a new stream with the given URI 
 
public StreamResource createStream(java.lang.String uri, 
                                    java.lang.String contentType, 
                                 boolean play) 
// creates a new stream; allows you to specify to pause the 
// stream at first 
 
public StreamResource createStream(java.lang.String uristr, 
                                   java.util.Map args) 
// (Advanced) creates a new HME application stream resource.  
// This method is used to embed one application within  
// another. The arguments will be appended to the uri. 

For example:

// create an image stream  
createStream("http://.../myPicture.png")  
 
// create an mp3 stream
createStream("http://.../mySong.mp3")  
 
// create a paused stream
createStream(".../mySong.mp3", null, false)  

Streams are controlled as follows:

See the Music Playback Tech Note for more information on playing music streams.

Animation

Many of the commands used to manipulate views and resources can be animated instead of taking effect instantly. Animation is a good way to hide network latency. The command will be applied over a period of time as described by an animation resource. For example:

createAnimation(1000)   // run linearly over 1 second 

An animation resource consists of a duration in milliseconds and an ease in/out value. A negative ease (ease in) will gradually accelerate before achieving a linear speed. A positive ease (ease out) will move linearly and then decelerate. An ease of zero is entirely linear. Experiment with easing to improve the look of your animations.

Type
Value
Description
Ease in
-1..0
Acceleration, then linear speed
Linear
0
Linear speed
Ease out
0..1
Linear speed, then deceleration

For example, to fade in some text, change the transparency of the view from 1 (entirely transparent) to 0 (not transparent) over a period of 1000 milliseconds with an ease of zero. The effect will be that the view fades in gradually over 1 second.

Smooth scrolling is achieved the same way. Rather than setting the translation of a view to its new value immediately, use an animation parameter to set the translation gradually. The result is a smooth scrolling effect until the new translation is achieved.

The SDK provides the following types of animations:

View Animations:

Bounds

Scale

Translate

Transparency

Visible

Remove

Resource Animations:

Event animation

Animation Examples

Here are some examples of different types of animation:

// set bounds linearly 2 seconds 
view.setBounds(0, 0, 100, 100, "*2000"); 
 
// set bounds over 2 seconds, with some deceleration 
view.setBounds(0, 0, 100, 100, "*2000,.5"); 
 
// set bounds over 2 seconds, with full acceleration 
view.setBounds(0, 0, 100, 100, "*2000,-1"); 
 

Animation Chaining with Send Event

Sometimes it is desirable to chain several animated commands together so that they occur sequentially. This can be a complicated task because the HME application that issues the commands is separate from the Receiver that obeys them. One approach is to use sleep. Unfortunately, this is unlikely to look good because the HME application and Receiver aren’t synchronized:

view.setBounds(0, 0, 100, 100, "*1000"); // move over 1 second 
Thread.sleep(1000);                      // wait for 1 second 
view.setBounds(0, 0, 500, 500, "*2000"); // now move again 

A better approach is to send a custom event to the Receiver with an animation. Just as a View.setBounds() can be animated, a Resource.sendEvent() can be animated as well. If both commands are issued at the same time and specify the same animation, the custom event will be sent to the application roughly when the setBounds() is complete. See the SDK samples for more details.

Syntax for the getResource() Method

Table 2-4 lists the format for strings supplied to the getResource() method.

Table 2-4 Syntax for creating resources with the getResource() method 
Resource
Syntax for getResource()
Examples
Color
A hexadecimal string specifying an RGB or ARGB color
getResource("0xff0000") // an opaque color
getResource("0x80ff0000")// a translucent color 
 
Font
A string specifying the name of the font, optionally followed by style information (plain/bold/italic) and point size, separated by hyphens and ending in the suffix .ttf.
getResource("default-12.ttf")     // plain font
getResource("default-bold-12.ttf")
Image
A string ending in one of the following suffixes: .gif, .jpg, .jpeg, .mpg, .png
getResource("statue.png")
Sound
A string ending in the suffix .snd.
getResource("carHorn.snd")
Stream
A string that specifies a URI.
getResource("http://myWebSite/mySong.mp3") 
Animation
 
A string with an asterisk (*) and a float value that indicates the number of milliseconds for the animation.
getResource("*1000")  // run linearly over 
                      // 1 second 
  
getResource("*1000,.5") // run over 1 second,
                        //with deceleration
 
// cross fade two views over one second 
Resource anim = getResource("*1000"); 
fg.setTransparency(1.0f) 
fg.setTransparency(0.0f, anim) 
bg.setTransparency(0.0f) 
bg.setTransparency(1.0f, anim) 
 

Flushing Pending Commands

Applications communicate with the Receiver using HME protocol commands. Many of the methods in View, Resource, and Application translate directly to a specific protocol command. For efficiency, these commands are buffered by the SDK before being sent over the network.

Under most circumstances, the SDK will automatically flush the buffered commands to the Receiver. For example, the SDK will flush pending commands after the application is finished handling each key event. If an application uses extra threads, it may be necessary to manually flush pending commands by calling HmeObject.flush().

Events

The Receiver generates events and sends them to the application. There are seven types of events:

Key Event

Key events (event type is EVT_KEY) are sent to the application whenever a key is pressed, released, or held down. Each key event contains three parameters: action, code, and raw code, as shown in Table 2-5. Table 2-6 lists the key codes for standard keys, which are found on all TiVo remote controls. Key codes for standard keys begin with KEY_. Table 2-7 lists the key codes for nonstandard keys, which are found only on select TiVo remote controls and therefore are not guaranteed to be generated by the Receiver. Key codes for nonstandard keys begin with KEY_OPT_.

Table 2-5 Parameters for Key Events
Field
Description
action
KEY_PRESS, KEY_REPEAT, or KEY_RELEASE.
code
The key being pressed, as one of the KEY_XXX or KEY_OPT_XXX constants. See Tables 2-6 and 2-7 for a complete list of keys.
rawcode
The “raw” IR code from the remote. This might be used to support keys that are present on the remote but not officially supported in the HME protocol.

Table 2-6 Key Codes for Standard Keys on the Remote Control 
Code
Type
Description
KEY_UNKNOWN
0
key not known
KEY_TIVO*
1
TiVoŽ or equivalent
KEY_UP
2
arrow up
KEY_DOWN
3
arrow down
KEY_LEFT
4
arrow left
KEY_RIGHT
5
arrow right
KEY_SELECT
6
select
KEY_PLAY
7
play
KEY_PAUSE
8
pause
KEY_SLOW
9
play slowly
KEY_REVERSE
10
reverse
KEY_FORWARD
11
fast forward
KEY_REPLAY
12
instant replay
KEY_ADVANCE
13
advance to next marker
KEY_THUMBSUP
14
thumbs up
KEY_THUMBSDOWN
15
thumbs down
KEY_VOLUMEUP
16
volume up
KEY_VOLUMEDOWN
17
volume down
KEY_CHANNELUP
18
channel up
KEY_CHANNELDOWN
19
channel down
KEY_MUTE
20
mute
KEY_RECORD
21
record
KEY_LIVETV*
23
back to live TV
KEY_INFO
25
info
KEY_DISPLAY
KEY_INFO
(same code as KEY_INFO)
KEY_CLEAR
28
clear
KEY_ENTER
29
enter
KEY_NUM0
40
0
KEY_NUM1
41
1
KEY_NUM2
42
2
KEY_NUM3
43
3
KEY_NUM4
44
4
KEY_NUM5
45
5
KEY_NUM6
46
6
KEY_NUM7
47
7
KEY_NUM8
48
8
KEY_NUM9
49
9

*Keys reserved for internal TiVo use.
Table 2-7 Key Codes for Nonstandard Keys on the Remote Control
Code
Type
Description
KEY_OPT_WINDOW
22
window
KEY_OPT_ASPECT
KEY_OPT_WINDOW
aspect (same code as window)
KEY_OPT_PIP
KEY_OPT_WINDOW
picture-in-picture (same code as window)
KEY_OPT_EXIT*
24
exit
KEY_OPT_LIST*
26
list now playing
KEY_OPT_GUIDE*
27
guide
KEY_OPT_STOP
51
stop
KEY_OPT_MENU
52
DVD menu
KEY_OPT_TOP_MENU
53
top menu on DVD box
KEY_OPT_ANGLE
54
angle
KEY_OPT_DVD*
55
DVD
*Keys reserved for internal TiVo use.

ResourceInfo Event

Streaming resources send events (event type is EVT_RSRC_INFO) as the stream is processed. Each event consists of a status code and a map of key/value pairs that contain additional information about the resource. For example, an audio stream might send a ResourceInfo event with RSRC_STATUS_PLAYING and "pos=10000/60000" to indicate that the Receiver has played 10 seconds of the 60 second stream.

Resource Status Event

When the SDK receives a ResourceInfo event that contains a status change, the system automatically generates a Resource Status event (event type is EVT_RSRC_STATUS).Your application can monitor for a Resource Status event as a shortcut to replace handling all ResourceInfo events and unpacking them to see if they contain a status change.

ApplicationInfo Event

The ApplicationInfo event (EVT_APP_INFO) is used to communicate warnings and changes to the active resource. It contains a map of key/value pairs. An application receives this event when it is set active (active=true) or inactive (active=false).

DeviceInfo Event

The Receiver sends a DeviceInfo event (EVT_DEVICE_INFO) immediately after an HME connection is successfully established. It contains a map of key/value pairs that describe the underlying device on which HME is running, including the device’s version and capabilities. Because this event is sent early in the application life cycle, your application can make configuration decisions based on this event.

FontInfo Event

The FontInfo event class (EVT_FONT_INFO) returns two types of information about the requested font:

See Chapter 4, “Programming Techniques,” for an example of processing a FontInfo event.

Idle Event

An Idle event is sent by the Receiver to the application when the user has been inactive for 15 minutes. After this time, the Receiver times out to live TV. The purpose of this event is to prevent burn-in on expensive screens. If you do not want your application to time out, it should send an acknowledgment within 15 seconds of receiving the Idle event by implementing handleIdle() and calling application.acknowledgeIdle(true).

If your application does not time out to live TV, it is a good idea to run your own screen saver in response to an Idle event. This technique will prevent screen burn in on plasma and other expensive televisions.

Event Flow

The Receiver sends events over the network to the application. The Application superclass is responsible for deciding where to post the events inside dispatchEvent().

An application can ask the SDK to deliver keypress events to a specific View. This is called setting the focus. If the focus isn’t set, keypress events will be sent to the application. For example, an application might set the focus to a View subclass that implements a list widget. The list view can handle the arrow keys itself and propagate other keys to its parent.

The default behavior is:

Event
Routing
Key
Key events are posted to the focused view.
ResourceInfo
Resource info events are posted to the stream itself.
ApplicationInfo
Application info events are posted to the application.
DeviceInfo
Device info events are posted to the application.
FontInfo
Font info events are posted to the resource that created the font.
Idle
Idle events are posted to the application.

As events propagate through views and resources, each participant can either handle the event itself or ask its parent to handle the event. For example, a view might handle KEY_RIGHT but allow other keys to pass to its parent. Events that are posted to a resource are automatically propagated to the views that contain the resource.

In addition to sending the event to the containing views, each resource also maintains a separate list of handlers. Events that are posted to a resource will be sent to the handlers if the resource doesn’t handle the event. For example, an application can create an image stream without putting it into a view. The application can wait until a ResourceInfo event arrives indicating that the image has been fully loaded before placing the resource in a view.

Send Event

HME includes a facility for sending custom events to a specific resource. The Resource.sendEvent() method handles only key events and can be called only on applications. See “Animation Chaining with Send Event” for an example of how this is useful.

Processing Streams (ResourceInfo Event)

The Receiver will send ResourceInfo events to keep the application informed as the stream is created and processed. Each ResourceInfo event contains a status code and a map of key/value pairs that contain additional information about the stream:

Status
Description
RSRC_STATUS_UNKNOWN
Not initialized
RSRC_STATUS_CONNECTING
Waiting to connect
RSRC_STATUS_CONNECTED
A connection was made
RSRC_STATUS_LOADING
Loading first frame
RSRC_STATUS_READY
First frame loaded successfully
RSRC_STATUS_PLAYING
Media is playing
RSRC_STATUS_PAUSED
Media is paused
RSRC_STATUS_SEEKING
Media is seeking
RSRC_STATUS_CLOSED
Stream closed
RSRC_STATUS_COMPLETE
Download complete
RSRC_STATUS_ERROR
Some error occurred

Image Streams

The width and height of the image will be sent along with the RSRC_STATUS_COMPLETE event.

Audio Streams

The current position of the audio stream will be sent along with each RSRC_STATUS_PLAYING event.

Note that it is not possible to reliably determine the duration of an MP3 stream on the receiving end. To work around this difficultly, HME receivers look for an optional X-TiVo-Accurate-Duration MIME header in the HTTP response. When this header is present, the Receiver can include the duration in the RSRC_STATUS_PLAYING event. The SDK contains a sample music player that shows how to calculate the duration of an MP3 and include it in the HTTP response. See the Music Playback Tech Note for more information on how to change the playback speed of MP3 audio files.

Advanced: Active Resource

In HME terms, an application is considered a stream resource and is handled similarly to an audio stream. The application is usually responsible for handling key events, but this is not always the case. Other stream resources are also capable of handling events. For example, an audio stream can handle KEY_PAUSE and KEY_PLAY itself without any intervention from the application.

One resource is always designated as the active resource within the Receiver, and key events will be sent to this resource. If the resource chooses not to handle a particular key, the event will propagate over the network to the application that contains the resource.

On high latency networks, you can set an audio stream to be the active resource. Since the audio stream handles the pause and play keys, the stream is guaranteed to pause and play instantly when the user presses a key even if the application itself is running far away.

Sample HME Hosting Environment

The SDK sample hosting environment is separate from the HME SDK, as shown in Figure 2-2. You can use this out-of-the-box environment during initial development of your application. If you later choose to implement a custom hosting environment—with a server-based host, for example—you can replace the sample hosting environment with your own implementation.

The sample hosting environment performs the following tasks:

Main Class

The Main class provides the basis for the sample HME hosting environment. To launch an HME application using the sample environment, you must run Main and then tell it which applications to run (see Chapter 1).

The Main class takes the following options:

Option
Description
-d
Turn on debugging.
--jar <jarfile>
Start the factory for the given jar.
--jars <dir>
Scan the directory for HME application jar files. Start multiple applications.
--intf <interface>
Listen on a specific network interface.
--nomdns <interface>
Turn off multicast DNS discovery. Use the specified network interface.
--port <port>
Listen on a specific port. If not specified, defaults to 7288. If the default port is already in use, a random port will be used instead.
--launcher <file>
Used to start multiple applications in a single JVM. The launcher configuration file contains a list of all the applications to launch and their arguments. See the samples for more details.

Figure 2-2. Separation of HME hosting environment from the SDK application objects

Application Factory

The sample host environment handles the connection between the TiVo box and the Application Factory. Based on the URI sent by the TiVo box, the host environment maps the HTTP request to the appropriate Application Factory. The Application Factory then creates an instance of its corresponding application, as shown in Figure 2-2. (An application can change the URI it listens on, as described in Chapter 3, “Application URI.”)

Context

The hosting environment creates an application context, which implements the IContext interface, and passes this object to the application when it is initialized. The application can use this context for logging and storing persistent data, and for communicating with the HME infrastructure.

The following subsections highlight key methods provided by the IContext interface.

Persistent Data

There are two forms of the setPersistentData() method, which allows you to store small amounts of data for use by your application. The simpler form of setPersistentData() allows you to store a key/value pair associated with a particular Receiver and application. It could be used, for example, to store a personal high score for a specific user and a particular game or to store the last song played by a particular user of an MP3 music application. The syntax is

void setPersistentData(java.lang.String key, 
                      java.lang.String value) 

The second form of setPersistentData() allows you to store global data for an application.

void 	setPersistentData(java.lang.String key, 
                       java.lang.String value, 
                       java.lang.String applicationId, 
                       boolean applicationGlobal)  

Examples of using global persistent data would be when data is shared by all users of a particular application—perhaps to publish high scores for all players of a certain game. Another use for persistent data is when multiple applications share the same data so that the user doesn’t have to enter it twice (for example, to store the user’s zipcode for use by a suite of related applications). (In this case applicationGlobal would be false.) The Java documentation provides detailed examples of using this method.

In the sample hosting environment, persistent data is stored in a text file on the local computer, so space is limited by the hard drive capacity. It is recommended that you store only small amounts of data, on the order of 5kb or less, so that performance is not affected.

Logging

The getLogger() method returns an instance of ILogger, which the application can use to write messages to a centralized log. In the sample hosting environment, the logger sends its output to stdout.

Identifying a Specific Receiver

The getReceiverGUID() method returns the ID for a specific Receiver. This method is useful for identifying a specific user of the application.

Obtaining Connection Attributes

The getConnectionAttribute(string key) method returns a specific HTTP header.

The getConnectionAttributes() method returns a map of all headers so that you can iterate over the entire set.

Application URL

The getBaseURI() method returns the URL of the application.

Application Assets

The getAssetURI() method returns the location of the assets for the application, such as a collection of images or music.

Custom Factories

Custom factories are used for several purposes:

Naming a Factory

You can continue to use the generic Factory’s command line facilities even if your application has a custom factory, as long as you honor a specific naming convention. If an application is named App, then its corresponding factory should be a public inner class named AppFactory. Following this convention makes it possible for HME to automatically find the custom factory class, for example:

package com.foo; 
 
public class MP3Player extends Application 
{ 
    public static class MP3PlayerFactory extends Factory 
    {  
    } 
} 

getAppFactory() Method

The static Application.getAppFactory() method can be overridden to return an appropriate factory that doesn’t use the naming pattern described above:

static com.tivo.hme.interfaces.IFactory 
     getAppFactory(java.lang.String appClassName, 
                   java.lang.ClassLoader loader, 
                   com.tivo.hme.interfaces.IArgumentList args) 

Be sure to initialize the custom factory by calling the initFactory() method before returning from the getAppFactory() method.

TOCPREVNEXTINDEX