getting a shared object to expire after browser session in FLASH? - actionscript-3

I'm looking for a way to play flash animated content once ( even as the user navigates to different HTML pages with similar flash content) and expire after a set amount of time or after the browser is closed.
I'm aware I could use a shared object for this but can't seem to find info about how to clear them at browser session end.
I am open to using javascript or PHP to assist .
your help is appreciated -
thanks -MW

Instead of using a SharedObject, you could create two simple server-side services: one that maintains the session, and one that exposes the session through a generated XML file that your flash application could use.
The first service would set some session variables and should be called whenever the video is played. It could look like this:
<?php
// start-video.php
session_start();
$_SESSION['hasWatchedVideo'] = true;
$_SESSION['watchedVideoAt'] = time();
?>
The second service is the one that generates the XML response based on the session. It might look like this:
<?php
// flash-config.php
session_start();
// set expiration to 5 min
define(VIDEO_TIMEOUT, 300);
$playVideo = "true";
if($_SESSION['hasWatchedVideo']
&& (time() - $_SESSION['watchedVideoAt']) < VIDEO_TIMEOUT) {
$playVideo = "false";
}
header("Content-Type: text/xml");
echo "<config><playVideo>{$playVideo}</playVideo></config>";
?>
Then from your Flash application, you could do this:
/**
* Called whenever the app is loaded.
*/
protected function init():void {
var u:URLLoader = new URLLoader();
u.addEventListener(Event.COMPLETE, onComplete);
u.load(new URLRequest("http://example.com/flash-config.php"));
}
/**
* Determines whether or not the video should play based on the
* config service response.
*/
protected function onComplete(e:Event):void {
var x:XML = new XML(e.target.data);
if(x.playVideo == 'true') {
playVideo();
}
}
/**
* Should be called either when the video starts playing. I just tied
* it to a user click here.
*/
protected function playVideo():void {
// call the service to update the session
var u:URLLoader = new URLLoader();
u.load(new URLRequest("http://example.com/start-video.php"));
// ... play video code ...
}
I think this approach gives you a bit more flexibility than using a SharedObject. Hope that helps.
UPDATE:
You could use a session cookie in the browser as well. Basically set the expiry date to '0' and the cookie will expire when the user closes the browser. (Note: when I tested this in Firefox, closing the tab was not enough to kill the cookie. The entire browser had to be closed.)
You can use ExternalInterface, or a utility library like this. Using the library, you could have code like this in your flash application:
function playVideo():void {
if(!CookieUtil.getCookie('playvideo')) {
CookieUtil.setCookie('playvideo', 'true', 0);
// ... play video code ...
}
}
Whenever the user closes the browser, the cookie will be cleared. Next time they visit your site, the video will play again. Not sure if this is more inline with what you're look for, but hope it helps.

I modified your code a tad so it will be killed on sesion end ...
the PHP ...
<?php
// flash_php_session_cookie.php
$cookie= "false";
if (isset($_COOKIE["cookie"]))
$cookie= "true";
else
setcookie("cookie", "true", 0);
echo "<config><cookie>{$cookie}</cookie></config>";
?>
the FLASH ...
// the folowing functions handle call coresponding PHP files to handle cookies ...
// re-configure these to the location of the swf ...
var flashConfigURL:String ="flash_php_session_cookie.php";
//Called whenever the app is loaded ...
function initCookieFunc():void {
var u:URLLoader = new URLLoader();
u.addEventListener(Event.COMPLETE, onComplete);
u.load(new URLRequest(flashConfigURL));
}
// Determines whether or not the cookie exists / (assumes theres a text field named T on the stage) ...
function onComplete(e:Event):void {
var x:XML = new XML(e.target.data);
if (x.cookie == 'false') {
T.appendText("cookie doesn't exist yet");
} else {
// cookie exists ...
T.appendText("cookie exists");
}
}
initCookieFunc();
I am going to keep a loose version of the "TIMEOUT"
version as well.
It's great to have an answer to this
Thanks again RJ for the invaluable code
-MW

You'll just have to expire the SharedObject yourself. It's not complicated.
This way your .swf will be completely self contained, not relying on anything external which IMO is a good thing.
package {
import flash.display.Sprite;
import flash.net.SharedObject;
import flash.net.SharedObjectFlushStatus;
public class SharedObjectExample extends Sprite {
private var _so:SharedObject;
private var _now:Date;
private var _last_played:Number;
private static const EXPIRE_TIME:Number = 1000 * 60 * 60 * 24; // 24hrs in msec
public function SharedObjectExample() {
// create a new date for the current time to compare against
_now = new Date;
// create a new shared object
_so = SharedObject.getLocal("application-name", "/");
// try read from the shared object
if (_so.data.last_played) _last_played = _now;
// if no value is set we play the video and set the current time
if (!_last_played) {
// play video here
_last_played = _now.time;
// check if the "cookie" has expired and it's time to play again
} else if ( _now.time - _last_played > EXPIRE_TIME) {
// play video here
_last_played = _now.time;
} else {
// do nothing
}
// and finally, save
saveValue();
}
private function saveValue(event:MouseEvent):void {
// i've removed the code that asks the user for permission if the request for storage is denied
_so.data.last_played = _last_played;
var flushStatus:String = null;
try {
flushStatus = _so.flush(1000);
} catch (error:Error) {
trace("Could not write SharedObject to disk");
}
}
}
}

Related

how to load dynamic text sharedObject when flash is reopened in Actionscript 3?

I created a simple username page, in frame 1, there is a button and input text
like the code below
stop();
var SOlastFrame: SharedObject = SharedObject.getLocal("save_frame2");
var shared_data: String
next.addEventListener(MouseEvent.CLICK, gotomyNextFrame);
function gotomyNextFrame(e: MouseEvent): void {
shared_data = inputName.text
nextFrame()
SOlastFrame.data.lastframe = currentFrame;
SOlastFrame.flush();
}
if (SOlastFrame.data.lastframe != null) {
gotoAndStop(SOlastFrame.data.lastframe);
}
i saved the last frame by adding this code
SOlastFrame.data.lastframe = currentFrame;
SOlastFrame.flush();
so that I can jump to the last frame I opened, I added this code
if (SOlastFrame.data.lastframe != null) {
gotoAndStop(SOlastFrame.data.lastframe);
}
at frame 2, I put dynamic text
with code like this
var SOnameUser: SharedObject = SharedObject.getLocal("saveName");
SOnameUser.data.yourName = shared_data;
SOnameUser.flush();
trace(SOnameUser.data.yourName);
userName.text = "Hello " + shared_data;
if (shared_data != null) {
userName.text = shared_data;
SOnameUser.data.yourName = shared_data;
}
I think it works, dynamic text is saved successfully.
If I reopen it, it will go directly to frame 2 because of the execution result SOlastFrame.data.lastframe.
the problem is that the username that I saved earlier turns to null.
how can i load dynamic text on shareobject when SOlastFrame.data.lastframe is executed.
or
how to load sharedObject when flash is reopened
You get it like that because when you start 2-nd time you go straight to the frame 2 where the shared_data is empty. I think you should re-organize... well, everything.
// Frame 1: Splash (you don't have it).
// First of all, use a single SO rather than two.
var SO:SharedObject = SharedObject.getLocal("my.save");
// Check if there are saved fields.
if (SO.data.userName && SO.data.lastFrame)
{
// If there are saved credentials, the user
// won't even see the Login Frame.
gotoAndStop(SO.data.lastFrame);
}
else
{
// If there are no saved credentials — proceed to the Login Frame.
nextFrame();
}
Then, this is your frame 1.
// Frame 2: Login.
stop();
// You don't actually need to SO.flush() every time,
// it's a (feeble) measure against sudden crashes.
SO.data.lastFrame = currentFrame;
// Your code, for the most part, but simpler.
next.addEventListener(MouseEvent.CLICK, gotoNext);
function gotoNext(e:MouseEvent):void
{
// You don't need any additional variables
// to temporarily store the user name.
SO.data.userName = inputName.text;
nextFrame();
}
Then, here we go.
// Frame 3: only logged (via Login Frame or via SO data)
// users reach this point.
stop();
// In case we moved from the Login Frame.
SO.data.lastFrame = currentFrame;
// The only thing left to do.
userName.text = "Hello " + SO.data.userName;

Remote shared object not firing onSync method after update

I have game server, FMS 4.5 on Windows, already working prefect, and client apps were created in old CS4, and all is perfect.
Now I want to create a mobile app in AS3, and have a problem with remote shared object, which is working perfect in old flash program.
When users are logged into app, I'm receiving an update with onSync method. But whenever remote shared object is updated, I'm not receiving updates.
for example, on client, where I have main_nc as netConnection object:
var ncu_so:SharedObject = SharedObject.getRemote("Zusers", main_nc.uri, false);
ncu_so.addEventListener(SyncEvent.SYNC, syncNCU);
ncu_so.client=this;
ncu_so.connect(main_nc);
private function syncNCU(e:SyncEvent):void {
........
//here I receive new info....
}
and sample on server...
application.onAppStart = function(){
this.Zusers_so = SharedObject.get( "Zusers", false );
...........
}
function sampleUserEnter(client) {
var t=new Object();
t.username=client.username;
application.Zusers_so.setProperty(client.id,t);
//this one call is synced with app
}
function sampleChangeName(client,newName) {
var t=application.Zusers_so.getProperty(client.id);
t.username=newName;
application.Zusers_so.setProperty(client.id,t);
//this IS NOT syncing with app
}
As I said, this code is working with old flash software, but wont update when using AS3. Any idea?
I found an easy solution. Not sure why it works but it works....
var ncu_so:SharedObject = SharedObject.getRemote("Zusers", main_nc.uri, false);
ncu_so.addEventListener(SyncEvent.SYNC, syncNCU);
//I add the listener for checking status
ncu_so.addEventListener(NetStatusEvent.NET_STATUS, statusNCU);
ncu_so.client=this;
ncu_so.connect(main_nc);
private function syncNCU(e:SyncEvent):void {
........
//here I receive new info....
}
//In function for NetStatus event, I just set a simple property
//which I do not use in the app..
//and sunchronization start working as usual after initial sync
private function statusNCU(ev:NetStatusEvent):void {
if (ev.info.code == "NetConnection.Connect.Success") {
ncu_so.setProperty("anyPropertyName",new Date());
}
}

Run an AS3 function only once

I'm making an AIR app and I would like to know if it's possible to run a function only once ?
If the user reboot his device and launch the app, the function won't be trigger again.
Thank you for your answers,
Since you're using adobe AIR, you have a few options.
Write a file that contains your save data.
Using AIR you can write a data file to the user's machine. This is more permanent than SharedObject's, which for various reasons can not work or persist long term.
You can actually serialize entire classes for easy retrieval of complex objects.
Here is an example of a class that managers a save file (for preferences or a game save etc):
package
{
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.utils.ByteArray;
import flash.utils.Dictionary;
import flash.net.registerClassAlias;
public class SaveData
{
//a list of any vars you want to save.
public var firstRun:Boolean = true;
public var someVar:int = 1;
public var anotherVar:Dictionary;
//the file for the user save data
public static function getFile():File {
return File.applicationStorageDirectory.resolvePath("saveFile.data");
}
public static function loadFile():SaveData {
registerClassAlias("flash.utils.Dictionary", Dictionary); //you need to register any non-native classes you want to save
registerClassAlias("saveData", SaveData);
var data:SaveData
//check for a default save file in the application directory (optional, but sometimes it's nice to bundle a default with your application before the user saves their own data
var f:File = new File("app:/saveFile.data");
if(f.exists){
data = loadFromFile(f);
}else {
trace("Default File Doesn't Exist");
}
//check the user storage directory for a save file, and see if it's newer than the one in the application directory
var userFile:File = getFile();
if (userFile.exists) {
if (f.exists && f.modificationDate.time < userFile.modificationDate.time) {
trace("Local File is newer, load it");
data = loadFromFile(userFile);
}else {
trace("Default File is newer");
}
}else {
trace("Save File DOESN'T EXIST YET");
}
if (!data) {
//data didn't load, let's create a new save file
data = new SaveData();
saveToFile(data);
}
saveData = data; //assing to main static accessible var
return data;
}
private static function loadFromFile(f:File):SaveData {
var d:SaveData;
try{
var stream:FileStream = new FileStream();
stream.open(f, FileMode.READ);
d = stream.readObject() as SaveData;
stream.close();
trace("DATA LOADED: " + f.nativePath);
return d;
}catch (err:Error) {
trace("Error Loading Save Data: " + f.nativePath);
}
return null;
}
public static function saveToFile(saveData:SaveData):void {
var fileStream:FileStream = new FileStream();
fileStream.open(getFile(), FileMode.WRITE);
fileStream.writeObject(saveData);
fileStream.close();
}
}
}
Then to use it:
var saveData:SaveData = SaveData.loadFile();
if(saveData.firstRun){
//do you first run code
//now set first run to false
saveData.firstRun = false;
SaveData.saveFile(saveData);
}
use a SharedObject. There is another answer covering this, but since no example was given I'll give one:
var mySO:SharedObject = SharedObject.getLocal("yourAppNameHere");
if(mySO.data.hasHadFirstRun){
//the cookie exists
}else{
//the cookie hasn't indicated there has been a first run yet, so do whatever you need to do
//some code
mySO.data.hasHadFirstRun = true; //now that you've done whatever on the first run, let's save the cookie
mySO.data.flush(); //save (though it will save automatically I believe when you close the application
}
use SQLite.
AIR can create and read SQLite databases, if your application has a need to store other data that would make sense to live in a table structure, it might make sense for you to add on a table for storing user preferences and things like a first run flag - if you wouldn't use the database for anything else though, it would definitely be over-kill.
Find out more about SQLite in AIR here:
http://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118666ade46-7d49.html

AS3 add callbacks for listeners

lately I've been asked to change a small flash app i made to work with external callbacks instead of using the embedded buttons.
I tried to automate the process and came up with this function:
/**
* Add an External callback for all PUBLIC methods that listen to 'listenerType' in 'listenersHolder'.
* #param listenersHolder - the class that holds the listeners we want to expose.
* #param listenerType - a String with the name of the event type we want to replace with the external callback.
*/
public static function addCallbacksForListeners(listenersHolder:*, listenerType:String):void
{
// get the holder description
var description:XML = describeType(listenersHolder);
// go over the methods
for each(var methodXML:XML in description..method)
{
// go over the methods parameters
for each(var parameterXML:XML in methodXML..parameter)
{
// look for the requested listener type
var parameterType:String = parameterXML.#type;
if (parameterType.indexOf(listenerType) > -1)
{
var methodName:String = methodXML.#name;
trace("Found: " + methodName);
// get the actual method
var method:Function = listenersHolder[methodName];
// add the callback
try
{
ExternalInterface.addCallback(methodName, method);
trace("A new callback was added for " + methodName);
}
catch (err:Error)
{
trace("Error adding callback for " + methodName);
trace(err.message);
}
}
}
}
before using this function I had to change the listener's function to Public, add null default parameter and of course remove/hide the visuals.
for example, from :
private function onB1Click(e:MouseEvent):void
to :
public function onB1Click(e:MouseEvent = null):void
add this line to the init/onAddedToStage function:
addCallbacksForListeners(this, "MouseEvent");
and remove the button from stage or just comment the line that adds it.
My question is: can you find a better/ more efficient way to do that ?
any feedback is welcomed..
Maybe you should make the javascript to call a single method with different functionName param. Then you only need to add one callback and you don't need to make all those functions public.
ExternalInterface.addCallback('callback', onCallback);
public function onCallback(res:Object)
{
var functionName:String = res.functionName;
this[functionName]();
}

Web Database - tx.executeSql callback not running every time

I have an HTML5 website built using jQuery Mobile.
On my index.htm page I have an ahref. When I click on that link I run a function which does a tx.executeSql and the callback method is run which then navigates to the new page.
The works fine the first time.
If I navigate to more pages and then come back to the index.htm page, the functions are run when the link is clicked, however the callback on the tx.executeSql isn't ever run.
Any ideas would be greatly appreciated. I have used all different mechanisms for calling the functions from javascript to jquery, but it makes no difference.
To be clear - the first function called is setFeaturedRecruiter() - you can see the code below. The second time I come back here the "renderResults" callback function isn't run.
// when we click on the actual featured recruiter link we copy from this table to the featured recruiter table to overwrite its contents
function setFeaturedRecruiter() {
alert('setFeaturedRecruiter()');
retrieveActualFeaturedRecruiter();
return true;
}
function retrieveActualFeaturedRecruiter() {
alert('retrieveActualFeaturedRecruiter()');
db.transaction(function (tx) {
alert('select * from featuredRecruiterActual...');
tx.executeSql('SELECT * FROM featuredRecruiterActual', [], renderResults, pnetOnError);
});
}
pnetOnError = function (tx, e) {
alert('Something unexpected happened: ' + e.message);
}
function renderResults(tx, rs) {
alert('renderResults()');
var idNo;
var name;
var logo;
var totalAds;
for (var i = 0; i < rs.rows.length; i++) {
r = rs.rows.item(i);
idNo = r.idNo * 1;
name = r.name;
logo = r.logo;
totalAds = r.totalAds;
}
writeToFeaturedRecruiter(idNo, name, logo, totalAds);
}
I've worked around this problem by disabling ajax when navigating between pages. This was done by adding to the ahref tag: data-ajax="false". This caused the page to load correctly and overcomes the problem.