/* Copyright (c) 2007 Adobe Systems Incorporated Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //class that provides a queue to upload geo information to flickr based on //a photo id package com.adobe.onair { import air.net.SocketMonitor; import com.adobe.onair.events.GeoencoderErrorEvent; import com.adobe.onair.events.GeoencoderEvent; import com.adobe.onair.flickr.FlickrGeoencoder; import com.adobe.onair.flickr.errors.GeoSetLocationErrors; import com.adobe.utils.ArrayUtil; import com.adobe.utils.StringUtil; import com.adobe.onair.airsnapshot.settings.AIRSnapshotSettings; import flash.events.ErrorEvent; import flash.events.EventDispatcher; import flash.events.StatusEvent; import flash.filesystem.File; import flash.filesystem.FileMode; import flash.filesystem.FileStream; import mx.logging.Log; public class GeoencoderQueue extends EventDispatcher { //whether a file is currently being uploaded private var _running:Boolean = false; //path to serialize queue private const STORAGE_URL:String = "app-storage:/geoqueue.db"; //app settings private var _settings:AIRSnapshotSettings; //array that stores the geo data to upload private var queue:Array = new Array(); //instance of FlickrGeoencoder, which does the actual communicaiton //with flickr private var encoder:FlickrGeoencoder; //monitor connectivity private var _monitor:SocketMonitor; //constructor. Takes a settings object which contains the auth //settings for communicating with flickr public function GeoencoderQueue(settings:AIRSnapshotSettings) { _settings = settings; //create the FlickrGeoencoder instance, pass the auth info from the settings encoder = new FlickrGeoencoder(settings.flickrAPIKey, settings.authToken, settings.flickrAPISecret); //when image has succesfully been geoencoded encoder.addEventListener(GeoencoderEvent.ON_GEOENCODE, onGeoEncode); //error communicating with server encoder.addEventListener(ErrorEvent.ERROR, onError); //error response from flickr encoder.addEventListener(GeoencoderErrorEvent.ON_GEOENCODING_ERROR, onGeoEncodingError); //initialize queue initQueue(); } /************ Initialization functions *******************/ //initialize / update flickr settings private function initFlickr(settings:AIRSnapshotSettings):void { //note, we dont instantiate flickr instance here, so we wont //have to create a new instance every time the settings are //updated encoder.token = settings.authToken; encoder.secret = settings.flickrAPISecret; encoder.api_key = settings.flickrAPIKey; } //initialize queue / Array private function initQueue():void { //get a reference to serialized queue var f:File = new File(STORAGE_URL); //see if it exists if(!f.exists) { //if not, initialize a new Array and return queue = new Array(); return; } //if file does exist, de-serialize it, and load it. var fs:FileStream = new FileStream(); fs.open(f, FileMode.READ); queue = (fs.readObject() as Array); fs.close(); } /************ Public vars / getter / setters ***************/ //setting for the socket monitor public function set monitor(value:SocketMonitor):void { //check and see if we are already listening on a monitor if(_monitor != null) { //if so, remove the listener _monitor.removeEventListener(StatusEvent.STATUS,onStatusChange); } //store an instance of the monitor _monitor = value; //register for the status event so we know when connection status //changes _monitor.addEventListener(StatusEvent.STATUS, onStatusChange); } //settings to be used by application public function set settings(value:AIRSnapshotSettings):void { //store new settings _settings = value; //update flickr API with new setting info initFlickr(value); } /***************** Event Handlers ******************/ //called when the status of the connectivity to the flickr upload //server changes private function onStatusChange(e:StatusEvent):void { if(_monitor.available) { //if we just came online, then send the next image sendNextItem(); } } //event handler called when geo info has sucessfully been sent to flickr private function onGeoEncode(e:GeoencoderEvent):void { Log.getLogger(AIRSnapshot.LOG_NAME).info("GeoencoderQueue : Image Geo Encoded : " + e.flickrGeoItem.photoID); //encoding sucessful, remove it from the upload queue removeFromQueue(e.flickrGeoItem); //set to false to indicate that no upload in progress _running = false; //encode next item sendNextItem(); } //called when an error occurs communicting with the server private function onError(e:ErrorEvent):void { Log.getLogger(AIRSnapshot.LOG_NAME).error("GeoencoderQueue : Error Sending Geo Info : " + e.text); _running = false; } //called when an error is returned from flickr private function onGeoEncodingError(e:GeoencoderErrorEvent):void { Log.getLogger(AIRSnapshot.LOG_NAME).error("GeoencoderQueue : Flickr GeoEncoding Error : " + e.text); var del:Boolean = false; //if any of these errors are returned from the server, it means //that we have invalid or old data, so we will delete the item switch(e.errorCode) { case GeoSetLocationErrors.PHOTO_NOT_FOUND: { del = true; break; } case GeoSetLocationErrors.REQUIRED_ARGUMENTS_MISSING: { del = true; break; } case GeoSetLocationErrors.INVALID_LATITUDE: { del = true; break; } case GeoSetLocationErrors.INVALID_LONGITUDE: { del = true; break; } case GeoSetLocationErrors.INVALID_ACCURACY: { del = true; break; } } _running = false; if(del) { //bad data, so delete item from queue Log.getLogger(AIRSnapshot.LOG_NAME).error("GeoencoderQueue : Removing Item from Queue : Bad data : " + e.flickrGeoItem.photoID); removeFromQueue(e.flickrGeoItem); sendNextItem(); } //we only send the next item if error was due to bad data). //this is so we don't get in a loop while there is an issue with //the server, or auth. } /************ General functions and APIS ****************/ //serialize the queue Array to the file system private function saveQueue():void { var f:File = new File(STORAGE_URL); var fs:FileStream = new FileStream(); fs.open(f, FileMode.WRITE); fs.writeObject(queue); fs.close(); } //add an item to the queue to be sent to flickr public function addItem(g:FlickrGeoItem):void { //if data isnt set, ignore it if(!StringUtil.stringHasValue(g.latitude) || !StringUtil.stringHasValue(g.longitude) || !StringUtil.stringHasValue(g.photoID)) { Log.getLogger(AIRSnapshot.LOG_NAME).error("GeoencoderQueue : Invalid GeoPhotoItem : ignoring"); return; } //add it to the queue addToQueue(g); //send the next item sendNextItem(); } //adds the specified item to the queue private function addToQueue(g:FlickrGeoItem):void { //add it to array queue.push(g); //save it (in case app closes or crashes) saveQueue(); } //sends the next item in the queue private function sendNextItem():void { //make sure that all of the flickr apis and keys have been set if(!StringUtil.stringHasValue(_settings.authToken) || !StringUtil.stringHasValue(_settings.flickrAPIKey) || !StringUtil.stringHasValue(_settings.flickrAPISecret)) { //if not, dont try to upload //todo: throw an event here. Log.getLogger(AIRSnapshot.LOG_NAME).error("GeoencoderQueue : Skipping upload : settings not set"); return; } //check to see if we are offline, or in the process of uploading //another file if(_running || !_monitor.available) { //if we are offline, or uploading a file, then dont upload new file Log.getLogger(AIRSnapshot.LOG_NAME).info("GeoencoderQueue : not sending geo data"); return; } //see if there is anything to upload if(queue.length == 0) { //if not, return Log.getLogger(AIRSnapshot.LOG_NAME).info("GeoencoderQueue : no geo data to upload"); return; } _running = true; var item:FlickrGeoItem = FlickrGeoItem(queue[0]); //upload file item in queue Log.getLogger(AIRSnapshot.LOG_NAME).info("GeoencoderQueue : Sending Item" + item.photoID); encoder.setLocation(item); } //remove the specified file from the upload queue private function removeFromQueue(g:FlickrGeoItem):void { //remove from queue ArrayUtil.removeValueFromArray(queue, g); //save queue (in case app crashes or closes) saveQueue(); } } }