Showing posts with label Flash. Show all posts
Showing posts with label Flash. Show all posts

Wednesday, December 4, 2013

Automatically determining mobile or computer-based delivery


Moving into a world of mobile versus pc accessed content doesn’t always mean dramatic rewrites of web-content.  Responsive web design helps the content scale to meet the device’s abilities.  However, when working with rapid eLearning development tools like Adobe Captivate and Articulate Storyline, a one size fits all approach isn’t always possible. 


The Challenge
Currently, there is no standard for delivering multimedia across platforms.  Flash still dominates the pc environment, but is absent on mobile devices.  HTML5 is progressing, but isn’t persistent (especially if you are delivering to a corporate environment using browsers from a few years ago).  Rapid development tools are beginning to introduce HTML5 output, but they lack many of the features that are present if those same files are published as Flash.

There’s a need to deliver the best possible learning experience based on the device that the learner wants to use to access the material.  If a user is sitting at a pc with Flash and looking for interactive eLearning, it can be delivered via Flash.  If the user is on the road and looking to access the same content from their mobile phone, it can’t.  In those cases, the device sets up a few more constraints, but they shouldn’t keep the learner from getting to the information they need in the manner she wants to access it. 

A Solution
Clearly not the solution as there are untold other options out there, but this is one possible solution that leverages some simple javascript similar to what we used earlier to determine the appropriate Captivate output size.  Essentially, you’ll provide two versions of the material – one for pc-based users and one for mobile.  You’ll then create a launch file that will determine how they are accessing the material and point them to the appropriate version.

Decision File
In the body of an HTML launch file, you can place the following javascript code that will determine how the learner is accessing the page:
<script>
if(navigator.appVersion.indexOf(“Mobile”)!=-1){
  window.location=”mobile.htm”;
} else {
  window.location=”pc.htm”;
}
</script>

Essentially, navigator.appVersion pulls a string of information about the browser.  The .indexOf portion of the line searches for the word Mobile in the string.  If it is present, it will return the place in the string where it begins (not -1), so we will open the mobile version of the content.  If it is not present, we will open the pc version of the content.

PC File
If you are using a rapid development tool, this one is the simplest – you merely identify the page that is created by the tool.  This will allow you to launch the Flash version with no problems (assuming the pc has Flash on it). 

Mobile File
This one is a bit trickier as you likely have to make concessions about the delivery from what you would have created in Captivate.  There are a couple of options that can make this simple for the developer, but the focus should instead be on creating what is best for the learner on their mobile device, which can often mean duplicative development effort.

Potential output formats include rendering Captivate files as mp4 videos (once you’re removed all interactivity), providing content in an HTML5 output version from the rapid development tool, providing alternate content in pdf form, or even leveraging standard HTML development.  The mobile version doesn’t have to be a lessened experience, it just has to be one tailored to the device.  For example, a PDF with buttons to navigate works brilliantly on an iPad because it feels so natural, but on a PC, it seems a bit lackluster. 

Overall
Unfortunately, at this point, there is still a bit of duplicative effort that is required for the developer (and designer).  However, it provides a much more ideal situation for the learner by allowing them to access the content in which they are interested through the device they desire without knowing there is another possibility.

Wednesday, January 30, 2013

Interacting with Captivate 5.5 using Flash AS3


Adobe Captivate is a great eLearning development program, but there are some things it can’t do.  In the past few years, they have made substantial progress in expanding the possibilities by including widgets.  Widgets can be purchased (sometimes free), downloaded and used within Captivate to do something more powerful or interactive.  However, if you are working in a corporate or restricted environment, downloading and installing widgets can sometimes range from a policy violation to being totally blocked.  In those situations, it is helpful to know a bit about Flash and write your own Flash files that can be included in Captivate (Insert >> Animation).  While they don’t have the same flexibility as true widgets, these mini-swfs can do things Captivate can’t on it’s own (and get you started with widget development, if that’s the route you want to go. 

Nested?
A swf that you include in a Captivate module as an animation works similarly to a nested movie clip.  Think of the Captivate timeline as the United States.  The state is a holder movieclip, and the city is the swf you have created in Flash.  As such, interacting with the Captivate timeline requires going two levels into the parent – Moveclip(parent.parent).

What can I do there?
Short answer – anything.  Captivate outputs Flash modules.  As such, anything you can do in Flash, you can pretty much do in Captivate.  If you want to do typical Captivate commands like advancing slides or pausing the playhead, check out this post from CPGuru that details all the system variables you can manipulate (like rdcmdPause and rdcmdNextSlide).

Those commands are actually variables, so from your Flash timeline, you need to move two levels up and set them to 1:
Movieclip(parent.parent).rdcmdNextSlide = 1;

***Update***
For Captivate 5.5 and Captivate 6, the following code works, as opposed to the parent.parent method: MovieClip(this.parent.root).rdcmndNextSlide = 1;

In addition to setting the Captivate system variables and triggers, you can also manipulate variables that were originally established in Captivate.  For example, if you have a Flash swf on one page, but want to display different subsequent slides based on the score earned, you could do the following:
Create a passedScore variable in Captivate
In Flash, at the conclusion of your interaction, set:
Movieclip(parent.parent).passedScore = Math.round(intCorrect*100/intTotal);

Captivate 5.5/6: MovieClip(this.parent.root).passedScore = Math.round(intCorrect*100/intTotal);
In Captivate, work with the variable through actions or display it using $$passedScore$$.

Quick example
Recently, I leverages Flash AS3 commands to manipulate a Captivate module when a colleague with no Flash experience needed to create a module that included a drag and drop interaction.  I build the Flash interaction to her design specifications.  Once the learner completes the interaction, the score is passed back to the Captivate module and a button is shown in the Flash module.  Clicking on the button triggers the nextSlide command in Captivate and advances the module that the learner can’t otherwise do.

While it’s a simple use, it allowed for easy division of work and enabled us to provide the module desired to our customer.  We were able to include interactions that aren’t available in Captivate off the shelf.  Drag and drop interactions are fairly straightforward to build in Flash, but creating the remainder of the 30-page module is a task much more rapidly completed in Captivate.  Leveraging the strengths of the two applications allowed us to create the module as quickly as possible.

Wednesday, November 28, 2012

Printing from Flash


It seems like an oxymoron, but there can be times you need to print documents from within Flash.  Whether you need to print certificates of completion, supplemental materials, or something that the user works to create within the Flash environment, there can be legitimate reasons you need to kill trees and use toner.

The simplest way to offer something printable through your module is to create a PDF that is linked from the Flash (AS3) document with the following code:
var url:URLRequest = new URLRequest(“myDocument.pdf”);
navigateToURL(url,”blank”);
However, that only works if you want your learner to print a document that is previously created and static.  If you want a custom document that includes material the user helps to create while using your Flash module, the work needs to be done in Flash.

At a high level, you will create a single movieclip (mc) in Flash that will later be printed out.  When printing, the mc will scale to fit on the page, so it is helpful to set up your mc using the aspect ratio of the anticipated printout.  For example, if you are expecting a printout on an 8.5” x 11” paper, you can set your printable mc to 850 x 1100. 

As the data displayed on the screen is most likely formatted differently than your printout, you should position your printable mc well off the stage.  It will not be visible to the user, but it will be available for printing.

In the example below, we are going to print out a certificate of completion.  We have created a mc named print1_mc.  On that mc, we have two text fields that will be populated with content from the module: name and score.  To set the text fields on the print1_mc, you execute the code as you would for a mc on the stage, except specify the mc first: print1_mc.txtName = strName; print1_mc.txtScore = intScore;

The following code should be executed with some trigger event (either a button click, a frame access, etc).

// set up the print job to communicate with the printer
var printJob:PrintJob = new PrintJob();
if(printJob.start())
{
     // scale your mc to match the width of the printout
     print1_mc.width = printJob.pageWidth;
     // adjust the height to the same scale
     print1_mc.scaleY = print1_mc.scaleX;
     if(print1_mc.height > printJob.pageHeight)
     {
          // if the mc is taller than the printout allows, scale to the height instead
          print1_mc.height = printJob.pageHeight;
          print1_mc.scaleX = print1_mc.scaleY;
     }
     // add your page to the print job
     printJob.addPage(print1_mc);
     // send the printout to the printer (actually opens the print dialogue)
     printJob.send();
}

If you want to print multiple pages, you should create an additional mc for each page.  Assuming you set all of their sizes the same, you can use the following code for each of them after you have scaled the first page:
print2_mc.scaleX = print2_mc.scaleY = print1_mc.scaleX;
printJob.addPage(print2_mc);
You can add that code for each subsequent page prior to the printJob.send() command in order to send them as a single job to the printer.

Wednesday, November 14, 2012

Making SCORM Calls from Flash (AS3)


If you are using a learning management system (LMS), chances are you can communicate with it using SCORM commands.  While many rapid development tools like Captivate and Articulate will automatically generate SCORM calls, they don’t provide you the flexibility and precision that can be necessary with eLearning module development.  In those situations, Flash is (still) a viable option.

If you are comfortable building a module in Flash, doing some light coding in ActionScript 3 (AS3), then the SCORM commands will simply be plug n play.  In this situation, it is more important to know when and how you should be making your SCORM calls as opposed to what the code will be. 

While there are a variety of calls that can be made (outlined in the ADL’s SCORM 1.2 Run-Time Environment), they fall into three primary categories: set, get, commit.  Some basic examples are detailed below, but the link above provides the full range of calls that can be made as well as specific keywords and values that are required for each type of variable used.

Set
To set a SCORM variable on the LMS:
fscommand(“LMSSetValue”, “cmi.core.lesson_status,completed”);
All set commands follow the same pattern.  The only thing that changes is the API call (cmi.core.lesson_status) and the value that you are looking to set (completed).  The fscommand has two variables.  The first string lets the LMS know you are interested in setting a variable.  The second string combines the API call and the value you are looking to set separated by a comma.  This can be done with a string (as defined above) or with a variable (intScore is the variable):
fscommand(“LMSSetValue”, “cmi.core.score.raw,”+intScore);
You can see in the example above that it is still two strings that are included in the fscommand, but the second string is concatenated between a literal and the variable intScore.

In addition to setting variables at the conclusion of a module, you should also potentially set the completion status at the beginning of the module.  However, rather than simply setting the variable, you will need to first get the completion status to ensure you aren’t overwriting a status of completed.  A module is initially ‘not attempted’, but once the learner begins the module, it should be modified to read ‘incomplete’.  Rather than making this SCORM call upon entry to the module, you would first get the current status to ensure it wasn’t previously completed before you reset it to incomplete. 

Another variable that is set throughout the module and retrieved at the beginning manages the bookmark.
fscommand(“LMSSetValue”, “cmi.suspend_data,”+stringBookmark);
The bookmark is a (maximum) 4096-byte string.  As such, you can include a variety of information about previous interactions with the module.  Leveraging a character within the string such as the pipe character (|), you can include a variety of information that can later be separated into an array and read in a logical way to return the module to the previous state. 

Get
A get API call works very similarly to the set call:
fscommand(“LMSGetValue”, “cmi.core.lesson_status,returnedStatus”);
In the case above, the LMS will return the status previously stored and set it as the value in the returnedStatus variable. 

While set variables are the simplest, get variables are the most powerful differentiator between a Flash module and a Captivate module.  While Captivate can set variables, it is very difficult to determine if the variable has successfully been set and to what value.  For example, at the conclusion of a module, you indicate that the completion status has been recorded.  However, in Captivate, you are making that statement based on an assumption.  Using Flash, you can set the completion status, then use a timed interval to check to ensure that you can get the same status that you believe should have been set by the LMS.  This will help to ensure you don’t have latency issues as you can refrain from communicating with the learner until you have confirmed (with a get call) that the LMS has the correct value stored. 

In addition to the fscommand, you also have to set up what happens when the variable is returned.  As such, you’ll need to add the following code:
ExternalInterface.addCallback(“SetVariable2”,mySetVariable);
function mySetVariable(varname:String, varvalue:String):void {
this[varname] = varvalue;

}
Here, the variable name that you set in the original fscommand is passed back with varvalue set to the value that is returned. 

this[varname] differs from this.varname in that whatever is the value of varname is the name of the variable name that is set.  For example:
varname = “returnedStatus”;
this[varname] = “completed”
// is the same as saying
this.returnedStatus = “completed”;

Here is a full example:
var returnedStatus:String;
// get the status at the beginning of the module
fscommand(“LMSGetValue”, “cmi.core.lesson_status,returnedStatus”);
// process the returned information from the javascript
ExternalInterface.addCallback(“SetVariable2”,mySetVariable);
function mySetVariable(varname:String, varvalue:String):void {
this[varname] = varvalue;
if(varname== “returnedStatus”) {
// only execute the code if we are getting the status
if(varvalue == “completed”) {
// already completed, so do nothing with the stauts
} else {
// set to incomplete since haven’t completed yet
fscommand(“LMSSetValue”, “cmi.core.lesson_status,incomplete”);
}
// we’ve successfully gotten something back, so show the next button
nextButton_btn.visible = true;
}
}

Commitment
When setting variables, depending on the LMS, they may or may not immediately perform an API call to the LMS.  The advantage of the call happening immediately is that the transaction has occurred.  The downside is that setting a handful of variables requires frequent calls between the module and the system.  Instead, many LMSs require a commitment statement to push all of the set variables to the LMS.
fscommand(“LMSCommit”, “”);
That simple statement tells the module to send everything that has been recently set to the LMS.  If you limit the amount of set calls, you can execute this command after each to ensure the variables aren’t waiting in the module in case of an unexpected termination of the program. 

How to Publish
While the fscommands are simple, there are a couple of other steps to ensure the javascript is properly placed in the html files associated with the module.  The most crucial part of publication is to tell Flash that you wish to publish the HTML as:
Flash with SCORM 1.2 Tracking
This will add the proper javascript functions to the HTML page to ensure that the fscommands are passing the information to something that will then interact with the LMS. 

The more difficult change is one that is required for the ability to get variables.  Flash, unfortunately, needs one of the HTML functions renamed in order for the return calls to function correctly.  By default, line 322 of the HTML page states
If ( cmd == “LMSGetValue”) {
SCORM_demoObj.SetVariable(arg2, SCOGetValue(arg1));
Here, you can see how the arguments passed through fscommand get placed into the function to make the actual call.  However, SetVariable is a function that is used twice.  As such, it should be renamed to SetVariable2(arg2… resulting in:
If ( cmd == “LMSGetValue”) {
SCORM_demoObj.SetVariable2(arg2, SCOGetValue(arg1));

Once you have mastered setting and getting SCORM variables, you will find that you can create valuable tracking that allow you to not only record how learners are interacting with your module, but also provide them a more positive, consistent experience where they know what is happening with the system and aren’t surprised that things didn’t get set as planned.