HDML provides the concept of activities to help you structure your HDML service. Specifically, activities help you do the following:
Activities correspond to tasks the user wants to carry out; each activity consists of one or more steps. An HDML service can provide multiple activities to its users. For example, an email service could provide activities such as "Create a New Message" and "View Inbox". The "Create a New Message" activity might include steps to choose an e-mail address, enter a subject, and enter the message body.
Figure 2-1 provides a conceptual overview of services and activities.
FIGURE 2-1. Services and activities
The HDML examples in Chapter 1 demonstrate simple individual activities, each of which consists of a single linear series of steps (cards). However, many services need to provide more complex activities, which contain nested activities or "subactivities." For example, suppose you are implementing the inbox portion of an email service. The overall activity that a user accomplishes with an email inbox is viewing mail. A nested activity is reading an individual piece of mail. In some cases, activities are nested several levels deep. For example, responding to a message could be a nested activity within the viewing mail activity.
When you design an HDML service, you should use an approach similar to the "top-down" design approach popularized by structured programming languages. Start with general activities and then divide them into progressively smaller subactivities. Continue until you have divided the subactivities into steps. Structuring your service this way yields a tremendous advantage: when you code it in HDML, many of the trickier aspects of HDML, such as navigation and variable scoping, virtually take care of themselves. If you use activities correctly, your application's user interface will be clean and intuitive.
Chapter 1 provides examples that use the GO and PREV task types to navigate from card to card within an activity. To navigate between activities, you use the following task types.
There are some special options that you can use with these task types. These options are discussed later in this chapter.
GO and GOSUB appears subtle, it is important. When you request a card (URL) with GO, you are including it in the current activity. When you request a card with GOSUB, you are effectively starting a new, nested activity.
To implement an activity in HDML follow these general guidelines:
GO and PREV tasks.
GOSUB task to request the first card in the nested activity.
RETURN or CANCEL tasks. The differences between RETURN and CANCEL tasks are discussed later in this chapter.
Suppose you want to provide a simple stock market data service that allows the user to get either market indices or individual stock quotes. Figure 2-2 graphically represents the activities and tasks that the service provides.
FIGURE 2-2. Activities and steps for a simple stock market data service
Figure 2-3 depicts how you could implement the "view quote" activity.
FIGURE 2-3. Implementation of a stock market "view quote" activity
The navigation model in the example operates as follows. When the user chooses the Quotes item, this invokes a GOSUB task, which starts a nested activity and displays the entersymbol card. If the user decides not to enter a stock symbol, he or she can simply press the PREV key, which exits the activity and returns back to the menu. By default, pressing PREV in the first card of an activity returns to the calling activity.
After viewing a quote, the user can press the ACCEPT key to pop out of the activity and return to the menu. Alternatively, the user can press the PREV key to return to the entersymbol card and enter another stock symbol.
Note that there is no particular correspondence between activities and HDML decks. A deck can contain multiple activities and an activity can span multiple decks. For example, in Figure 2-3, the menu.hdml deck contains cards in two different activities.
Chapter 1 describes how the phone maintains a history stack of the cards the user visits. That is a simplification of what the phone does: the phone actually maintains a history stack of visited activities and the cards the user visits within those activities. Each time the user navigates to a nested activity, the phone pushes the current activity onto the history stack. When the phone pushes an activity on to the stack, it maintains the entire user interface state of the activity, including its variable values and the cards the user has visited in it. When the user navigates from a nested activity back to the activity that called it, the phone pops the nested activity off the stack, restoring the calling activity (and its user interface state) to the top of the stack.
Figure 2-4 illustrates the state of the history stack at each stage of the interface described in the previous section.
FIGURE 2-4. History stack for sample stock market service
Note that when the user returns to the top level card, the item he or she chooses is still chosen. This is because the phone has preserved the state of the activity, including the value of the choice key (the ch variable in this example).
Of course, after the user has backed up and the phone has popped an activity off the stack, the activity's user interface state is lost. For example, if the user returns the menu (the first card in the figure) and subsequently navigates forward to the "Enter Symbol" card, the variable in the "Enter Symbol" card will no longer be set.
Figure 2-5 illustrates an example email service that nests activities several levels deep. The service is implemented with separate HDML decks (shown on the left). Note that the card that displays the email message does not need to specify a RETURN task. The default task for the ACCEPT key is to execute a PREV task (request the previous card in the history). Because the previous card is in the calling activity, the phone returns to the calling activity.
FIGURE 2-5. Email service with activities nested to several levels
The scope of an HDML variable is an activity. An activity can include many decks; any deck in an activity can reference variables in any other deck in the activity. Until the user invokes a destination that uses the GOSUB task, the variables remain in scope.
For example, the following HDML defines two decks within the same activity, the first deck defines an action that uses the GO task to request the second deck. Note how the second deck uses variables set in the first deck (both explicitly and by user input). The interface is depicted in Figure 2-6.
FIGURE 2-6. Variables used in an activity that spans multiple decks
The phone maintains the variables for each activity separately. If an activity and the activity that invokes it have a variable with the same name, the variables do not collide--they are completely independent of each other. For example, in the following code, Activity A initialize the value of var to 10 and then displays it. It allows the user to navigate to Activity B, which also displays a variable named var. Because var is not set in Activity B, it appears blank.
FIGURE 2-7. Activities containing a variable with the same name
There are ways to pass variable values between activities. For more information see Passing information between activities.
There are several ways a nested activity can allow a user to return to a calling activity:
CANCEL task
RETURN task
By default each of these returns the user to the card in the calling activity that invoked the nested activity. For example, the following HDML defines an activity that calls a nested activity. As shown in Figure 2-8, if the user presses ACCEPT, PREV, or SOFT1, the phone returns to the invoking card in the calling activity.
FIGURE 2-8. Navigation between activities
In some cases, you might not want to return the user to the card in the calling activity that invoked a nested activity. For example, if the card provides a list which the nested activity modifies, returning directly to it would be undesirable--because the list would be obsolete.
Fortunately, HDML allows you to change the default navigation from a nested activity back to the activity that called it. You can change the navigation by specifying the following task options for the GOSUB task that navigates to the nested activity:
NEXT option, which specifies the card to go to if the nested activity uses the RETURN task to return
CANCEL option, which specifies the card to go to if the nested activity uses the CANCEL task to return
For example, you could rewrite the example above so that when the user presses SOFT1 (the key labelled DEL), the phone returns to a "inbox empty" card instead of the same inbox card.
FIGURE 2-9.
Navigation using the NEXT option of the GOSUB task
Note that the NEXT option effectively performs a GO task; the card it specifies is in the same activity as the card that contains the NEXT option.
You can override a calling activity's NEXT and CANCEL options by specifying the DEST option for the RETURN and CANCEL tasks of the nested activity. However, the FRIEND option in the GOSUB task of the calling activity must be set to TRUE for it to work. This is a security precaution that prevents an "unfriendly" subactivity from directly accessing a card in the calling activity.
HDML provides several mechanisms for passing information between activities:
VARS option (with the GOSUB task) to set variable values for a nested activity
RECEIVE option to specify variables that accept return values from the nested activity; the nested activity uses the RETVALS option to return values to these variables
CLEAR option to unset all the variables of the activity that called it.
The following sections describe these methods of passing variable information in more detail.
The VARS option passes variables by name to nested activities. The syntax for the VARS option is:
The nested activity uses the variable names to reference variables that the calling activity specifies in the VARS option; it must know the names of the variables in order to use them. For example, the following HDML deck includes an activity that passes several variables to a nested activity. The nested activity displays the variable values in a simple display card.
FIGURE 2-10. Variables passed to a subactivity
Note that when you set a variable's value, you must escape the value using URL escaping conventions. For example, the code above uses the plus sign (+) to specify a space in the var2 variable value.
To accept return values from a nested activity, you specify the RECEIVE task option in the calling activity and the RETVALS task option in the nested activity. The RECEIVE option specifies a semicolon-delimited list of variables to receive values; the RETVALS option specifies a semicolon-delimited list of values. When the nested activity returns, the phone uses the values in the RETVALS list to set the RECEIVE variables.
The phone assigns the return values by position. That is, it assigns each value in the RETVALS list to the variable that occupies the corresponding position in the RECEIVE list. If the RETVALS list doesn't provide a corresponding value for a variable in the RECEIVE list, the phone clears the variable, so it has no value. Note that this contrasts with the way the phone handles variables in the VARS option--which it passes by name.
The table below provides some examples of how the phone sets variables in the RECEIVE list.
The following HDML deck includes an activity that calls two nested activities to change the variables it displays. The interface (depicted in Figure 2-11) is similar to that used in many common HDML services (such as UP.Mail and UP.Organizer) to provide lists of settings that the user can change. It is the recommended method for implementing "forms" in HDML.
FIGURE 2-11. Nested activities that return values
You can use a nested activity to clear all the variables in an activity. To do this, you must write the nested and the calling activities so that:
FRIEND option in the GOSUB task of the calling activity is set to TRUE
CLEAR option in the RETURN or CANCEL task in the nested activity is set to TRUE
The default for the FRIEND task option is FALSE. This is a security precaution that prevents malicious activities from resetting all of a calling activity's variables. Always make sure an activity is calling a "friendly" activity when you set the FRIEND option to TRUE.
Suppose you want to modify the interface depicted in Figure 2-11 so that the user can clear the both the Name and Alias fields by pressing SOFT1. To do this, you add a no-display card that uses the clear option. The added code is shown in bold below. The interface is shown in Figure 2-12.
FIGURE 2-12. Clearing variables in a calling activity
You can specify both the CLEAR option and the RETVALS option for the CANCEL or RETURN tasks of a nested activity: the phone first clears the variables of the calling activity and then sets any of the variables specified in the calling activity's RECEIVE option.