Chapter Project Overview

Let's move on to the bigger application to build your first killer Flash Communication application. The examples in this next exercise use some simple background graphics to highlight the Flash UI components on the Flash stage. Because you now have to address how the interfaces will be used, this exercise will walk through a simple user case. User case studies help to describe what you want the interface to achieve for the user.

The next two exercises in this chapter will collectively build the final project. The project involves building a controller application that will facilitate a synchronized presentation of slides. The project must also send a live video and audio feed of the presenter talking about the slides. Subscribers will have opportunities to talk with the presenter, at the presenter's discretion, of course. Figure 9.3 illustrates what the application will look like for the controller and the subscriber.

Figure 9.3. Exercise preview comparing the subscriber and controller interfaces.

graphics/09fig03.gif

The project is broken into two exercises. Exercise 9.2 steps through setting up your Flash movie to handle three different interfaces. Unlike the previous exercise, this project uses one SWF movie to handle the controller and the subscriber. Exercise 9.3 adds live streaming video from the controller to the subscribers.

The Flash UI components you will use through these three exercises include:

  • PresentationSWF

  • AudioConference

  • PeopleList

  • ConnectionLight

One of the best ways to understand what needs to be done is to look at how the applications will be used.

Controller User Case Study

The speaker accessing the controller application logs in with a special name that triggers the controller interface. This special name is decided at the server.

When the speaker arrives at the controller window, the web camera automatically begins recording, and the default presentation loads on all subscriber interfaces that are connected.

If the user wants to load a supporting movie, there is a loadSWF text field on the screen. The presentation must be available on the server to each subscribed client before it can be loaded.

The speaker can manage when the subscribers can start discussing an issue or topic. The speaker has the control to cut all audio conversations and continue with the presentation.

The speaker can see the name of every user monitoring the presentation, but the speaker cannot interact directly with a particular user.

Subscriber User Case Study

The subscriber logs in with her name. This is a public forum, so all names are accepted by the server. If the name entered is not the name associated with the speaker, the user accesses the subscriber interface.

The subscriber interface does not show the presentation and the video feed until the speaker has logged in. However, the participants are able to converse with their microphones using the Audio Conference tool on the interface.

Exercise 9.2: The Macromedia Flash MX Interface

This next exercise steps you through constructing the interface in Flash MX. It involves no coding.

There are three interfaces you have to construct in this exercise:

  • Login interface to register the user

  • Subscriber interface used by public viewers

  • Controller interface used by the speaker

These interfaces will be assembled strategically on the Flash Timeline. Later, in Exercise 9.3, ActionScript will move the play head, to change the interface presented to the user. The interface will be built to leverage common elements including ActionScript. After all, you have enough to debug, why make it harder at this stage?

Step 1: Prepare the Timeline

Before you set up your interfaces, you have to prepare your Timeline.

  1. Open Flash MX with a new document. Save the document as presentationApp.fla.

  2. Create a new layer (Insert, Layer) on the Timeline.

  3. Name the two layers Labels and Actions, as shown in Figure 9.4. Double-clicking the layer lets you rename it in place.

    Figure 9.4. The Timeline is used to display three different interfaces: login, subscriber, and controller.

    graphics/09fig04.gif

  4. Create keyframes on both layers in Frames 10 and 20. To create a keyframe, click the frame on a layer and press F7.

  5. On the Labels layer, add frame labels to the frames as follows:

    Frame 1: login

    Frame 10: subscriber

    Frame 20: controller

    To create the labels, select a frame and change the Frame field in the Properties panel.

Your Timeline should look exactly like Figure 9.4. You will be creating a series of new layers below the Actions layer. Always remember what frame you have selected before you drag components or graphical elements to the stage.

Tip

Keyframes are shown by hollow circles in the frame. When you have ActionScript on the frame, the letter "a" appears above the circle. When you have objects within the frame on the Flash stage, the circle becomes a solid color. Refer to Figure 9.5 to see the different keyframe renderings.

Figure 9.5. The login interface and Timeline setup. The background artwork is optional and is stored in the Timeline folder named BackgroundArt.

graphics/09fig05.gif


Step 2: The Login Interface (Frame: 1)

With your Timeline ready, start constructing the interfaces, beginning with the Login screen.

  1. Create two new layers below Actions. Rename them Login and connectionLight. Note that the extra artwork you see in this presentation is stuffed into the BackgroundArt folder on the Timeline. The artwork has no relationship to the components on the screen. See Figure 9.5 for reference.

  2. Move your Timeline cursor to Frame 1 on the Login layer. Place a Text input box with the instance name login_txt on the Flash stage here.

  3. On the same frame and layer, place a PushButton from the standard Flash UI components. In the Properties window, set Label to Login and Click Handler to appLogin.

  4. Place your cursor on Frame 10 on the Login layer, and create an empty keyframe (F7). This will stop the login from displaying across the other interfaces.

  5. Place your Timeline cursor at Frame 1 on the connectionLight layer. With this frame selected, add a second PushButton from the standard Flash UI components. In the Properties window, set Label to Logout and Click Handler to appClose.

  6. On the same frame and layer, place a ConnectionLight component from the Flash Communication UI components. Give it the instance name connectionLight_mc.

The ConnectionLight and Logout buttons will be available across all interfaces, so there is no blank keyframe required. You also do not need to assign the Logout button an instance name, because ActionScript will not be changing any properties of the button.

Step 3: The Subscriber Interface (Frame: 10)

With the login screen completed, move on to the interface that subscribers will use to view the presentation.

  1. Create three new Timeline layers below the connectionLight layer. Note that the extra artwork you see in Figure 9.6 is stuffed into the BackgroundArt folder on the Timeline. The artwork has no relationship to the components on the screen. Name the new layers PresentationSWF, PeopleList, and AudioConference, respectively.

    Figure 9.6. The subscriber interface and Timeline setup. Use this as a layout guide for the interface. Make sure you know where your Timeline cursor is each time you place an object on the Flash stage. This screenshot also shows you the contents of the BackgroundArt folder.

    graphics/09fig06.gif

  2. Create a keyframe at Frame 10 for each new layer.

  3. Place your Timeline cursor at Frame 10 on the PresentationSWF layer. From the Communication UI Components panel, drag a PresentationSWF component to the Flash stage.

  4. Set the properties of the PresentationSWF component in the Properties panel as shown in Figure 9.7:

    Instance name: presentation_mc

    Width: 529

    Height: 308.9

    X: 10.8

    Y: 12.4

    Figure 9.7. The Properties panel for the PresenationSWF component. The component will be larger than your Flash stage when you drag it on to the stage. Resize it to the values in this Properties window.

    graphics/09fig07.gif

    The Presentation component is a handy tool to synchronize the playback of prebuilt presentation slides. The Properties panel in Figure 9.7 is displaying the name of the SWF file that will be loaded when the presenter comes online. Creating SWF movies for this component will be described at the end of the chapter.

  5. On the same Frame, add a new embedded Video object: Open the Library panel (F11) and select New Video from the panel's drop-down menu. This will insert a new embedded Video object in the Library. Drag that to the Flash stage.

  6. Give the Video object the following properties:

    Instance name: speaker_video

    Width: 130

    Height: 100

    X: 410

    Y: 12

  7. Place the Timeline cursor at Frame 10 on the AudioConference layer.

  8. Drag the AudioConference UI component from the Communications UI Components panel, and give the AudioConference object the following properties:

    Instance name: audioConf_mc

    Width: 134

    Height: 130

    X: 406

    Y: 118

  9. Place the Timeline cursor at Frame 10 on the peopleList layer.

  10. Drag the PeopleList UI component from the Communications UI Components panel, and give the PeopleList object the following properties:

    Instance name: peopleList_mc

    Width: 134

    Height: 114

    X: 407

    Y: 135

That's it for the subscriber interface. Of course, you are welcome to place these elements where you want, but it is helpful keeping them separated onto their own layers.

Step 4: The Controller Interface (Frame: 20)

The Controller interface is very simple. You only need to add the LoadBox and the AudioChat toggle button. So let's finish it up so we can get to the code.

  1. Below the Audio Conference layer, create a new layer called controller-only.

  2. Create a keyframe (F7) on the Timeline at Frame 20 on the new controller-only layer.

  3. All that's left is to assemble three elements on the Flash stage. Use Figure 9.8 as your guide for placement, and start by adding a text input box with the instance name SWFtoLoad_txt to the Flash stage. This will act as the input box for the user to enter the name of a new SWF movie to use as the presentation. Ensure you have selected the text box as input.

    Figure 9.8. The Controller interface contains two additional elements: the load and the enable AudioChat button.

    graphics/09fig08.gif

  4. From the standard Flash UI components, place a PushButton next to the SWFtoLoad_txt and set the following properties in the Properties window:

    Instance name: null

    Label: load

    ClickHandler: appLogin

  5. Place a second PushButton at the bottom of your stage. Set its properties as follows:

    Instance name: chatControl_pb

    Label: Enable Audio Chat

    ClickHandler: appLogin

That's it; the three interfaces are now assembled. You can test your design skills with this interface, or you can download the templates from the book's web site.

Now, the scripting begins. Don't forget to save your work before you move on.

Exercise 9.3: Scripting

The scripting side of this project requires a series of four scripts. The efficiency will be revealed when you realize that you don't have to set up the NetConnection for the three different interfaces.

Step 1: Initialization (Frame 1)

The bulk of the ActionScript for this project exists at the login interface. When the login button is pressed and the server authenticates, the play head moves to the appropriate place on the Timeline. Taking this approach saves time and is prone to fewer bugs than having duplicated scripts. Taking this approach also lets you quickly add elements to both the controller and the subscriber applications.

You will see a messaging technique used in this script between the server and the client. In this technique, the server will authenticate the user and control the play head on the client based on the value sent at the login prompt. Let's review the code in depth.

  1. Open the ActionScript panel (F9) in Expert mode (Ctrl+Shift+E)

  2. Include the required Libraries (the details of the FlashcomCustom.as file are discussed later):

    /* NetConnection Debugger enabling, remove when development is complete */ 
    #include "NetDebug.as"
    /* Custom Status Handler Scripts */
    #include "FlashcomCustom.as"
    
  3. Set up the initial application objects. We will use a new global object called session to store data across the application. This script will also set up the NetConnection, the ConnectionLight, and disable the logout button (until it's been connected). Add the following code:

    // 
    // INITIALIZE THE APPLICATION
    //
    _global.session = new Object();
    myConnection_nc = new NetConnection();
    connectionLight_mc.connect(myConnection_nc);
    logout_pb.setEnabled(false);
    
  4. Define the appLogin method that will be invoked by the Login button:

    // 
    // LOCAL APPLICATION FUNCTION HANDLERS
    //
    appLogin = function () {
          // Login Button was clicked, make the connection with the server
          myConnection_nc.connect("rtmp://localhost/chapter09/myInstance");
          // setup a session object that will contain information about this user
          session = ({username:login_txt.text});
    };
    

    When executed, this function will request a connection from the server and store the username in a username property of the session object.

  5. Define the appClose method that will be invoked by the Logout button and when the NetConnection status receives a closed code:

    appClose = function () { 
          if (myConnection_nc.isConnected) {
                myConnection_nc.close();
          }
          _root.gotoAndStop(1);
          logout_pb.setEnabled(false);
    };
    

    When executed, this function closes the active connection, disables the Logout button, and returns the user to the login screen.

  6. Define the appInit method that will be invoked when the NetConnection Status returns a success code. When executed, the connection has been accepted by the server, but hasn't been authenticated. This function will call a handler, authenticateUser, on the server to determine access privileges. The server will decide if the user is a controller or a subscriber. Add the following code:

    // 
    // appInit RUNS WHEN THE SERVER RETURNS "NetConnection.Connect.Success" CODE
    //
    appInit = function () {
          trace(" ** Connected :-)");
          trace(" ** User set to: "+Session.username+".  Authenticating user...");
          // CALL THE AUTHENTICATE USER() FUNCTION ON THE SERVER
          myConnection_nc.call("authenticateUser", new serverRet(), Session.username);
          logout_pb.setEnabled(true);
    };
    

    Let's look at the call method a little closer. It has three parameters: method, Return object, and the username. The method is defined in SSAS in the client scope. This exposes the method to the Flash player.

    The Return object is invoked when the server responds. NetConnection functions are the only remote functions that are able to return a result. Because you are requesting processes on a remote computer, the results will not be immediately available. To deal with this, an object is created with two events: onStatus to handle errors and onResult to handle result sets. The final parameter is a required argument of the remote function.

    When the server process is complete, it returns the role of the user (subscriber or controller) to the object. So let's build the Return object.

  7. This object will be instantiated as a listener to handle the server's response. When the server responds, the internal function, onResult, is called. If an error occurs, the onStatus function is called, and it returns the user to the login screen by invoking the appClose() function. Add the following code:

    // GLOBAL SERVER METHOD HANDLER 
    //
    serverRet = function () {
          // HANDLE SERVER RETURNS
          this.onResult = function(returnVar) {
                trace(" ** :-) Server returned: "+returnVar);
                                  session.userRole = returnVar;
                      root.gotoAndPlay(returnVar);
          };
    
          // HANDLE SERVER ERRORS
          this.onStatus = function() {
                trace(" !! Server method, failed, refer to the App Inspector for trace data, 
    graphics/ccc.gifclosing the connection");
                appClose();
          };
    };
    

    When the server responds successfully, it sends back a variable containing the role the user will play. This role is used to move the play head to the correct interface referencing the frame label using gotoAndStop

  8. Your final step is to stop the play head from moving when the movie is loaded. Use a stop() command at the bottom of the first frame:

    stop(); 
    

The first frame and the bulk of this project are now behind you. Before moving on to the subscriber code, let's review the FlashCustom.as file mentioned earlier.

FlashComCustom.as

Earlier in this script, the custom script, FlashComCustom.as, was included. It contains a special onStatus catch-all template presented in Appendix E,"ActionScript Templates Quick Reference." This file is also available for download from the book's web site (http://flashcom.pangaeanewmedia.ca/). Placing the script into an external file makes it easier to quickly reuse across multiple applications. Because there is a lot of trace activity, the code is fairly dense to include when you are developing and debugging in this exercise.

Within the FlashComCustom.as file, two NetConnection status changes are monitored: success and closed. These two events are key in the operation of your application. When the code is success, the function appInit() is called. When the code is closed, the function appClose() is called. Review the following script section; it is included in the FlashComCustom.as script:

</BEGIN EXERPT FUNCTION: onStatus..> 
      case "NetConnection" :
            if (infoMessage_array[2] == "Success") {
              trace("    |::* Connection was Accepted by the Server , continuing to 
graphics/ccc.gifload!");
              // SCRIPTS TO RUN WHEN THE CONNECTION HAS SUCCEEDED
              appInit();
              }

            if (infoMessage_array[2] == "Closed") {
              // SCRIPTS TO RUN WHEN THE CONNECTION HAS FAILED
              trace("    |::* Connection was Closed, returning the user!");
              appClose();
              }
            break;
</END EXERPT..>

NetConnection.prototype.onStatus = onStatusTemplate;
NetStream.prototype.onStatus = onStatusTemplate;

Note

If you are not comfortable using this generic onStatus handler, or if you would rather use your own onStatus handlers, you can use the following script in place of the #include "FlashComCustom.as" line:

onStatusTemplate = function (info) { 
      trace("StatusChange: "+info.code);
      if (info.code == "NetConnection.Connect.Success") {
            appInit();
      } else if (info.code == "NetConnection.Connect.Closed") {
            appClose();
      }
};

NetConnection.prototype.onStatus = onStatusTemplate;
NetStream.prototype.onStatus = onStatusTemplate;

This script overwrites the onStatus prototypes for the NetConnection and the NetStream objects. Overwriting the prototype means that you never have to create an onStatus handler once this has been declared. Prototyping will be discussed in detail in Chapters 10, "STEP 10: Lobby Applications," and 12, "Client-Side (Flash) ActionScript."

The initial code is now complete. Now we'll look at the scripts specific to the subscriber and controller interfaces.

Step 2: Subscriber Scripts (Frame 10)

The Subscriber script controls the connection of the UI components to the NetConnection, and also establishes a receiving (subscription) NetStream from the server.

  1. Click keyframe 10 on the Actions layer to move your play head.

  2. Open the ActionScript panel (F9) in Expert mode (Ctrl+Shift+E). You should see an empty ActionScript Editor. If you do not, then you haven't created the keyframe (represented by a hollow circle in the Timeline). Click the frame, and press (F7) to create the keyframe. Ensure you have a keyframe on Frame 10.

  3. Set the global Speaker mode to false (for subscriber):

    // SET THE SPEAKER MODE FOR THE PRESENTATIONSWF COMPONENT 
    _global.speakerMode = false;
    

    This variable is monitored by the PresentationSWF UI component. When the variable is set to true, the PresentationSWF component enters into Presenter mode and allows the user to advance the presentation slides on all subscribers. When it is set to false, the component does not allow the user to advance the slides.

  4. Connect the UI components to the NetConnection:

    // 
    // CONNECT THE OTHER UI COMPONENTS TO THE NETCONNECTION OBJECT
    peopleList_mc.connect(myConnection_nc);
    presentation_mc.connect(myConnection_nc);
    audioConf_mc.connect(myConnection_nc);
    
  5. Turn off the AudioConference UI component's visibility:

    audioConf_mc["_visible"] = false; 
    PeopleList_mc["_visible"] = false;
    

    This status will be toggled by the controller, similar to the ball you controlled in Exercise 9.1. The PeopleList component will also be hidden, because it is not used by the participants.

    Note

    You cannot connect a UI component to the NetConnection until it is available on the Flash stage. Users connected will not be shown unless the PeopleList component is active. Turning the visibility off lets the controller or speaker use the component, but the user cannot access it.

  6. Subscribe to the NetStream speakerStream. This will be a one-way stream originating from the controller movie. Attach it to the speaker_video object. If the controller has not connected, the stream will open and wait for a feed. Use the following code:

    // 
    // INSTANSIATE THE NETSTREAM OBJECT TO "speaker_ns"
    speaker_ns = new NetStream(myConnection_nc);
    speaker_video.attachVideo(speaker_ns);
    speaker_ns.play("speakerStream");
    
  7. Create handlers in the NetStream variable that will respond to broadcast messages sent by anyone connected to the stream:

    // 
    // NETSTREAM MESSAGE HANDLERS
    speaker_ns.audioConfControl = function(isAudioConfOK) {
          audioConf_mc._visible = isAudioConfOK;
    };
    
    speaker_ns.loadSWF = function(SWFtoLoad) {
          presentation_mc.loadSWF = SWFtoLoad;
    };
    

    The only movie that has the broadcast script is the controller. audioConfControl toggles the visibility property of the AudioConference UI component. The loadSWF handler loads any presentations cued by the controller.

  8. Stop the play head from moving:

    stop(); 
    

That's the subscriber script. This script will not be executed until the play head enters Frame 10. It will retain all methods and connections described in Frame 1. Next, let's review the controller script.

Step 3: Controller Script (Frame 20)

The controller script is very similar to the subscriber script, in that it must connect the UI components. When the play head enters Frame 20, it will have missed the subscriber frame (10), and the connection method for the UI components will not have been called. You could optimize this code, but for this demonstration it is easier to present them separately. This script also connects the presenter's microphone and camera to the speakerStream.

  1. Set the global speaker mode to false, and connect the UI components:

    // SET THE SPEAKER MODE FOR THE PRESENTATIONSWF COMPONENT 
    _global.speakerMode = true;
    //
    // CONNECT THE OTHER UI COMPONENTS TO THE NETCONNECTION OBJECT
    peopleList_mc.connect(myConnection_nc);
    presentation_mc.connect(myConnection_nc);
    audioConf_mc.connect(myConnection_nc);
    audioConf_mc["_visible"] = false;
    

    The audioConference component must be connected, but will not be displayed to the speaker because the speaker stream is broadcasting the microphone. The speaker will be able to hear the audio conversation.

  2. Connect and start publishing the speakerStream sending the camera and microphone feeds to all subscribers:

    // CONNECT THE PUBLISHING SPEAKER STREAM 
    speaker_video.attachVideo(Camera.get());
    speaker_ns = new NetStream(myConnection_nc);
    speaker_ns.attachVideo(Camera.get());
    speaker_ns.attachAudio(Microphone.get());
    speaker_ns.publish("speakerStream");
    
  3. Set two functions that will be used by the enable audio PushButton:

    // AUDIO CONFERENCE MESSAGE BROADCAST FUNCTIONS 
    disableChat = function () {
          chatControl_pb.setLabel("Enable Audio Chat");
          chatControl_pb.setClickHandler("enableChat");
          speaker_ns.send("audioConfControl", false);
    };
    
    enableChat = function () {
          chatControl_pb.setLabel("Enable Audio Chat");
          chatControl_pb.setClickHandler("disableChat");
          speaker_ns.send("audioConfControl", true);
    };
    

    This technique is exactly the same as the technique you used in Exercise 9.1. It toggles the visibility of the AudioConference UI component on each subscriber. It invokes the function, audioConfControl, that you scripted in the Subscriber interface.

  4. Stop the play head from moving forward:

    // playhead control 
    stop();
    

That's it! The interface and the Flash ActionScript is now complete. Save your movie, but don't test it yet. You still have to set up the main.asc script on the Flash Communication Server.

Step 4: main.asc Scripts

The server-side ActionScript is very simple. There are three operations required: loading the UI component framework, accepting the connection, and authenticating the user.

  1. Create a folder on the FlashCom Applications folder called chapter09.

  2. Locate the file simple_preso.swf in the Flash Communication Server samples folder named sample_broadcast.

  3. Copy the file into the chapter09 folder. This file contains the presentation that will be dynamically loaded by the PresentationSWF component.

  4. Create a new main.asc file in the chapter09 folder.

  5. To set up the Flash UI component framework, load the component.asc ActionScript Library:

    load("components.asc"); 
    
  6. Handle connection requests with application.onConnect, and accept all connections made to the application:

    application.onConnect = function(theClient) { 
          // ACCEPT ALL CONNECTION REQUESTS
          application.acceptConnection(theClient);
    };
    
  7. Create a new prototype method for the Client object to handle authentication and register the user with the UI component framework:

    // 
    // ADD A NEW PROTOTYPE METHOD (authenticateUser) TO THE CLIENT OBJECT
    Client.prototype.authenticateUser = function(username) {
          trace("Called from Client");
    
          if (username == "Kevin") {
                clientRole = "controller";
          } else {
                clientRole = "subscriber";
          }
          trace("Role: "+clientRole);
    
          // SET THE USERNAME IN THE UI COMPONENT FRAMEWORK
          gFrameworkFC.getClientGlobals(this).username = username+"_"+clientRole;
    
          // SEND THE VALUE OF "CLIENTROLE" TO THE FLASH MOVIE
          return clientRole;
    };
    

"What's this?" you might ask. This is the first time you have ever had to interact with the Object prototypes. Don't worry, you will be doing more of that in Chapter 10. This approach will allow all clients to invoke the authenticateUser method. The function will return an ActionScript object to the caller containing the decided role of the user.

Why do this here and not on the client? Authentication should, where practical, be processed on the server. This is to ensure that users cannot hack the SWF file and find the authentication string that will give them control of the application. It is also a good idea to perform this on the server, so you can change the authentication without worrying about users using the wrong version of the Flash movie.

The project is now complete and ready for testing.



    Part I: 10 Quick Steps for Getting Started