ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Flex] Custom AIR updater interface using ApplicationUpdater
    공부방/Flex 2012. 7. 20. 15:39

    Update

    I have updated the project to work with Flash Builder 4, you can get the code at this post.

    Original Post:

    To tell you the truth, I never gave the ApplicationUpdaterUI much thought in terms of memory use before this post. I just thought it was a great way to add a complete update system to your AIR applications. I had not thought it could be so memory intensive until a recent conversation with Jesse Warden on Twitter:



    * Please note that Jesse is dude that looks about 17 and I am the older more distinguished gentleman.

    Problem with ApplicationUpdaterUI

    Indeed, that “mofo eats mad RAM” is putting it lightly. It turns out that the ApplicationUpdaterUI consumes about 14MB of RAM within the application. Furthermore, for the ApplicationUpdaterUI to even check for available updates, it must loads the entire UI in memory even if not displayed. So just by using the framework, you end adding 14MB to your application that won’t be garbage collected. Granted, in most cases 14MB of RAM is not that big of deal. However, for smaller applications you might want to consider an alternative approach.

    There happens to another class file called ApplicationUpdater. This class basically gives you all the benefits of the ApplicationUpdaterUI framework without any of the visible elements. This means you have to build your own user interface. This might actually be a benefit in the long run as you may want to customize your application updater to match your application. The other benefit is that you can use the ApplicationUpdater class to check for updates without loading any user interface. The interface only needs to be loaded if there is an actual update available.

    Custom Updater Interface with ApplicationUpdater

    In building my own custom updater interface, I thought it might be nice to have the same look and feel of the ApplicationUpdaterUI along with some of the more polite features (like postpone update until restart). Building your own updater using the ApplicationUpdater turns out to be a little more involved than I had originally thought. However, when you start attacking a problem you persevere until the end, no matter what the pain.

    I did find some available information in the Adobe AIR 1.5 Cookbook, which has a basic custom updater example. This helped with some of the basic stuff, but I still needed a little more detail to make something similar to the ApplicationUpdaterUI. I found another good example by Jens Krause, but this example is for Flex 4. I needed something that would work for Flex 3. So here is what I built based on these examples:



    The application consists of one class file and one MXML dialog for displaying all the user interface elements. I borrowed some of the icons from the ApplicationUpdaterUI which is available in the AIR SDK (I hope Adobe doesn’t mind). The update dialog UI is only loaded when needed, all checks for application updates happen using the ApplicationUpdater. The update dialog is only displayed when the user wants to manually update or the periodic update check finds a new application update.

    The Good and the Bad

    The good news is the updater only consumes about 2MB of RAM when you load the update dialog box, part of that is the ApplicationUpdater itself running in the background. The other good news is that you can yank out or customize any part of the this example to meet your own needs. The bad news is that even though I tried my best to fully remove the update dialog and have garbage collection clean it up, it doesn’t fully unload from memory (go figure). I don’t feel this is a huge issue as the application is most likely restarted after you install an update. Below is the code followed by a zip file containing the full application.

    UpdateManager.as

    001package com.thanksmister
    002{
    003    import air.update.ApplicationUpdater;
    004    import air.update.events.DownloadErrorEvent;
    005    import air.update.events.StatusFileUpdateErrorEvent;
    006    import air.update.events.StatusFileUpdateEvent;
    007    import air.update.events.StatusUpdateErrorEvent;
    008    import air.update.events.StatusUpdateEvent;
    009    import air.update.events.UpdateEvent;
    010 
    011    import flash.desktop.NativeApplication;
    012    import flash.events.ErrorEvent;
    013    import flash.events.Event;
    014    import flash.events.ProgressEvent;
    015    import flash.filesystem.File;
    016 
    017    public class UpdateManager
    018    {
    019        private var appUpdater:ApplicationUpdater;
    020        private var appVersion:String;
    021        private var baseURL:String;
    022        private var updaterDialog:UpdaterDialog;
    023        private var configurationFile:File;
    024        private var isFirstRun:String;
    025        private var upateVersion:String;
    026        private var applicationName:String;
    027        private var installedVersion:String;
    028        private var description:String;
    029 
    030        private var initializeCheckNow:Boolean false;
    031        private var isInstallPostponed:Boolean false;
    032        private var showCheckState:Boolean true;
    033 
    034        /**
    035         * Constructer for UpdateManager Class
    036         *
    037         * @param showCheckState Boolean value to show the Check Now dialog box
    038         * @param initializeCheckNow Boolean value to initialize application and run check on instantiation of the Class
    039         * */
    040        public function UpdateManager(showCheckState:Boolean true, initializeCheckNow:Boolean =false)
    041        {
    042            this.showCheckState = showCheckState;
    043            this.configurationFile = new File("app:/config/update.xml");
    044            this.initializeCheckNow = initializeCheckNow;
    045            initialize();
    046        }
    047 
    048        public function checkNow():void
    049        {
    050            //trace("checkNow");
    051            isInstallPostponed = false;
    052            if(showCheckState) {
    053                createDialog(UpdaterDialog.CHECK_UPDATE);
    054            else {
    055                appUpdater.checkNow();
    056            }
    057        }
    058 
    059        //----------  ApplicationUpdater ----------------//
    060 
    061        private function initialize():void
    062        {
    063            //trace("initialize");
    064            if(!appUpdater){
    065                appUpdater = new ApplicationUpdater();
    066                appUpdater.configurationFile = configurationFile;
    067                appUpdater.addEventListener(UpdateEvent.INITIALIZED, updaterInitialized);
    068                appUpdater.addEventListener(StatusUpdateEvent.UPDATE_STATUS, statusUpdate);
    069                appUpdater.addEventListener(UpdateEvent.BEFORE_INSTALL, beforeInstall);
    070                appUpdater.addEventListener(StatusUpdateErrorEvent.UPDATE_ERROR, statusUpdateError);
    071                appUpdater.addEventListener(UpdateEvent.DOWNLOAD_START, downloadStarted);
    072                appUpdater.addEventListener(ProgressEvent.PROGRESS, downloadProgress);
    073                appUpdater.addEventListener(UpdateEvent.DOWNLOAD_COMPLETE, downloadComplete);
    074                appUpdater.addEventListener(DownloadErrorEvent.DOWNLOAD_ERROR, downloadError);
    075                appUpdater.addEventListener(ErrorEvent.ERROR, updaterError);
    076                appUpdater.initialize();
    077            }
    078        }
    079 
    080        private function beforeInstall(event:UpdateEvent):void
    081        {
    082            //trace("beforeInstall");
    083            if (isInstallPostponed) {
    084                event.preventDefault();
    085                isInstallPostponed = false;
    086            }
    087        }
    088 
    089        private function updaterInitialized(event:UpdateEvent):void
    090        {
    091            //trace("updaterInitialized");
    092            this.isFirstRun = event.target.isFirstRun;
    093            this.applicationName = getApplicationName();
    094            this.installedVersion = getApplicationVersion();
    095 
    096            if(showCheckState && initializeCheckNow) {
    097                createDialog(UpdaterDialog.CHECK_UPDATE);
    098            else if (initializeCheckNow) {
    099                appUpdater.checkNow();
    100            }
    101        }
    102 
    103        private function statusUpdate(event:StatusUpdateEvent):void
    104        {
    105            //trace("statusUpdate");
    106             event.preventDefault();
    107             if(event.available){
    108                this.description = getUpdateDescription(event.details);
    109                this.upateVersion = event.version;
    110 
    111                if(!showCheckState) {
    112                    createDialog(UpdaterDialog.UPDATE_AVAILABLE);
    113                else if (updaterDialog) {
    114                    updaterDialog.applicationName = this.applicationName;
    115                    updaterDialog.installedVersion = this.installedVersion;
    116                    updaterDialog.upateVersion = this.upateVersion;
    117                    updaterDialog.description = this.description
    118                    updaterDialog.updateState = UpdaterDialog.UPDATE_AVAILABLE;
    119                }
    120             else {
    121                if (showCheckState) createDialog(UpdaterDialog.NO_UPDATE);
    122             }
    123        }
    124 
    125        private function statusUpdateError(event:StatusUpdateErrorEvent):void
    126        {
    127            event.preventDefault();
    128            if(!updaterDialog){
    129                createDialog(UpdaterDialog.UPDATE_ERROR);
    130            else {
    131                updaterDialog.updateState = UpdaterDialog.UPDATE_ERROR;
    132            }
    133        }
    134 
    135        private function statusFileUpdate(event:StatusFileUpdateEvent):void
    136        {
    137            event.preventDefault();
    138            if(event.available) {
    139                updaterDialog.updateState = UpdaterDialog.UPDATE_DOWNLOADING;
    140                appUpdater.downloadUpdate();
    141            else {
    142                updaterDialog.updateState = UpdaterDialog.UPDATE_ERROR;
    143            }
    144        }
    145 
    146        private function statusFileUpdateError(event:StatusFileUpdateErrorEvent):void
    147        {
    148            event.preventDefault();
    149            updaterDialog.updateState = UpdaterDialog.UPDATE_ERROR;;
    150        }
    151 
    152        private function downloadStarted(event:UpdateEvent):void
    153        {
    154            updaterDialog.updateState = UpdaterDialog.UPDATE_DOWNLOADING;
    155        }
    156 
    157        private function downloadProgress(event:ProgressEvent):void
    158        {
    159            updaterDialog.updateState = UpdaterDialog.UPDATE_DOWNLOADING;
    160            var num:Number = (event.bytesLoaded/event.bytesTotal)*100;
    161            updaterDialog.downloadProgress(num);
    162        }
    163 
    164        private function downloadComplete(event:UpdateEvent):void
    165        {
    166            event.preventDefault(); // prevent default install
    167            updaterDialog.updateState = UpdaterDialog.INSTALL_UPDATE;
    168        }
    169 
    170        private function downloadError(event:DownloadErrorEvent):void
    171        {
    172            event.preventDefault();
    173            updaterDialog.updateState = UpdaterDialog.UPDATE_ERROR;
    174        }
    175 
    176        private function updaterError(event:ErrorEvent):void
    177        {
    178            updaterDialog.errorText = event.text;
    179            updaterDialog.updateState = UpdaterDialog.UPDATE_ERROR;
    180        }
    181 
    182        //----------  UpdaterDialog Events ----------------//
    183 
    184        private function createDialog(state:String):void
    185        {
    186            if(!updaterDialog) {
    187                updaterDialog = new UpdaterDialog();
    188                updaterDialog.isFirstRun = this.isFirstRun;
    189                updaterDialog.applicationName = this.applicationName;
    190                updaterDialog.installedVersion = this.installedVersion;
    191                updaterDialog.upateVersion = this.upateVersion;
    192                updaterDialog.updateState = state;
    193                updaterDialog.description = this.description;
    194                updaterDialog.addEventListener(UpdaterDialog.EVENT_CHECK_UPDATE, checkUpdate);
    195                updaterDialog.addEventListener(UpdaterDialog.EVENT_INSTALL_UPDATE, installUpdate);
    196                updaterDialog.addEventListener(UpdaterDialog.EVENT_CANCEL_UPDATE, cancelUpdate);
    197                updaterDialog.addEventListener(UpdaterDialog.EVENT_DOWNLOAD_UPDATE, downloadUpdate);
    198                updaterDialog.addEventListener(UpdaterDialog.EVENT_INSTALL_LATER, installLater);
    199                updaterDialog.open();
    200            }
    201        }
    202 
    203        /**
    204         * Check for update.
    205         * */
    206        private function checkUpdate(event:Event):void
    207        {
    208            //trace("checkUpdate");
    209            appUpdater.checkNow();
    210        }
    211 
    212        /**
    213         * Install the update.
    214         * */
    215        private function installUpdate(event:Event):void
    216        {
    217            appUpdater.installUpdate();
    218        }
    219 
    220        /**
    221         * Install the update.
    222         * */
    223        private function installLater(event:Event):void
    224        {
    225            isInstallPostponed = true;
    226            appUpdater.installUpdate();
    227            destoryUpdater();
    228        }
    229 
    230        /**
    231         * Download the update.
    232         * */
    233        private function downloadUpdate(event:Event):void
    234        {
    235            appUpdater.downloadUpdate();
    236        }
    237 
    238        /**
    239         * Cancel the update.
    240         * */
    241        private function cancelUpdate(event:Event):void
    242        {
    243            appUpdater.cancelUpdate();
    244            destoryUpdater();
    245        }
    246 
    247        //----------  Destroy All ----------------//
    248 
    249        private function destroy():void
    250        {
    251            if (appUpdater) {
    252                appUpdater.configurationFile = configurationFile;
    253                appUpdater.removeEventListener(UpdateEvent.INITIALIZED, updaterInitialized);
    254                appUpdater.removeEventListener(StatusUpdateEvent.UPDATE_STATUS, statusUpdate);
    255                appUpdater.removeEventListener(StatusUpdateErrorEvent.UPDATE_ERROR, statusUpdateError);
    256                appUpdater.removeEventListener(UpdateEvent.DOWNLOAD_START, downloadStarted);
    257                appUpdater.removeEventListener(ProgressEvent.PROGRESS, downloadProgress);
    258                appUpdater.removeEventListener(UpdateEvent.DOWNLOAD_COMPLETE, downloadComplete);
    259                appUpdater.removeEventListener(DownloadErrorEvent.DOWNLOAD_ERROR, downloadError);
    260                appUpdater.removeEventListener(UpdateEvent.BEFORE_INSTALL, beforeInstall);
    261                appUpdater.removeEventListener(ErrorEvent.ERROR, updaterError);
    262 
    263 
    264                appUpdater = null;
    265            }
    266 
    267            destoryUpdater();
    268        }
    269 
    270        private function destoryUpdater():void
    271        {
    272            if(updaterDialog) {
    273                updaterDialog.destroy();
    274                updaterDialog.removeEventListener(UpdaterDialog.EVENT_CHECK_UPDATE, checkUpdate);
    275                updaterDialog.removeEventListener(UpdaterDialog.EVENT_INSTALL_UPDATE, installUpdate);
    276                updaterDialog.removeEventListener(UpdaterDialog.EVENT_CANCEL_UPDATE, cancelUpdate);
    277                updaterDialog.removeEventListener(UpdaterDialog.EVENT_DOWNLOAD_UPDATE, downloadUpdate);
    278                updaterDialog.removeEventListener(UpdaterDialog.EVENT_INSTALL_LATER, installLater);
    279                updaterDialog.close();
    280                updaterDialog = null;
    281            }
    282            isInstallPostponed = false;
    283        }
    284 
    285        //----------  Utilities ----------------//
    286 
    287        /**
    288         * Getter method to get the version of the application
    290         *
    291         * @return String Version of application
    292         *
    293         */
    294        private function getApplicationVersion():String
    295        {
    296            var appXML:XML = NativeApplication.nativeApplication.applicationDescriptor;
    297            var ns:Namespace = appXML.namespace();
    298            return appXML.ns::version;
    299        }
    300 
    301        /**
    302         * Getter method to get the name of the application file
    304         *
    305         * @return String name of application
    306         *
    307         */
    308        private function getApplicationFileName():String
    309        {
    310            var appXML:XML = NativeApplication.nativeApplication.applicationDescriptor;
    311            var ns:Namespace = appXML.namespace();
    312            return appXML.ns::filename;
    313        }
    314 
    315        /**
    316         * Getter method to get the name of the application, this does not support multi-language.
    317         * Based on a method from Adobes ApplicationUpdaterDialogs.mxml, which is part of Adobes AIR Updater Framework
    319         *
    320         * @return String name of application
    321         *
    322         */
    323        private function getApplicationName():String
    324        {
    325            var applicationName:String;
    326            var xmlNS:Namespace=new Namespace("http://www.w3.org/XML/1998/namespace");
    327            var appXML:XML=NativeApplication.nativeApplication.applicationDescriptor;
    328            var ns:Namespace=appXML.namespace();
    329 
    330            // filename is mandatory
    331            var elem:XMLList=appXML.ns::filename;
    332 
    333            // use name is if it exists in the application descriptor
    334            if ((appXML.ns::name).length() != 0)
    335            {
    336                elem=appXML.ns::name;
    337            }
    338 
    339            // See if element contains simple content
    340            if (elem.hasSimpleContent())
    341            {
    342                applicationName=elem.toString();
    343            }
    344 
    345            return applicationName;
    346        }
    347 
    348        /**
    349         * Helper method to get release notes, this does not support multi-language.
    350         * Based on a method from Adobes ApplicationUpdaterDialogs.mxml, which is part of Adobes AIR Updater Framework
    352         *
    353         * @param detail Array of details
    354         * @return String Release notes depending on locale chain
    355         *
    356         */
    357        protected function getUpdateDescription(details:Array):String
    358        {
    359            var text:String="";
    360 
    361            if (details.length == 1)
    362            {
    363                text=details[0][1];
    364            }
    365            return text;
    366        }
    367    }
    368}

    UpdateDialog.mxml

    001<?xml version="1.0" encoding="utf-8"?>
    002<mx:Window xmlns="*" xmlns:mx="http://www.adobe.com/2006/mxml" styleName="updateDialogWindow"
    003    layout="absolute" maximizable="false" resizable="false" currentState="{_updateState}"
    004    clipContent="false" showStatusBar="false" width="530" height="180">
    005 
    006    <mx:Metadata>
    007        [Event(name="checkUpdate", type="flash.events.Event")]
    008        [Event(name="downloadUpdate", type="flash.events.Event")]
    009        [Event(name="downloadUpdate", type="flash.events.Event")]
    010        [Event(name="cancelUpdate", type="flash.events.Event")]
    011    </mx:Metadata>
    012 
    013    <mx:Script>
    014        <![CDATA[
    015            public static var EVENT_CHECK_UPDATE:String "checkUpdate";
    016            public static var EVENT_INSTALL_UPDATE:String "installUpdate";
    017            public static var EVENT_DOWNLOAD_UPDATE:String "downloadUpdate";
    018            public static var EVENT_CANCEL_UPDATE:String "cancelUpdate";
    019            public static var EVENT_INSTALL_LATER:String "installLater";
    020 
    021            [Bindable] public static var UPDATE_DOWNLOADING:String "updateDownloading";
    022            [Bindable] public static var INSTALL_UPDATE:String "installUpdate";
    023            [Bindable] public static var UPDATE_AVAILABLE:String "updateAvailable";
    024            [Bindable] public static var NO_UPDATE:String "noUpdate";
    025            [Bindable] public static var CHECK_UPDATE:String "checkUpdate";
    026            [Bindable] public static var UPDATE_ERROR:String "updateError";
    027 
    028            [Bindable] private var _isFirstRun:String;
    029            [Bindable] private var _installedVersion:String;
    030            [Bindable] private var _updateVersion:String;
    031            [Bindable] private var _updateDescription:String;
    032            [Bindable] private var _applicationName:String;
    033            [Bindable] private var _updateState:String;
    034            [Bindable] private var _errorText:String "There was an error checking for updates.";
    035 
    036            public function set isFirstRun(value:String):void
    037            {
    038                _isFirstRun = value;
    039            }
    040 
    041            public function set installedVersion(value:String):void
    042            {
    043                _installedVersion = value;
    044            }
    045 
    046            public function set upateVersion(value:String):void
    047            {
    048                _updateVersion = value;
    049            }
    050 
    051            public function set updateState(value:String):void
    052            {
    053                _updateState = value;
    054            }
    055 
    056            public function set applicationName(value:String):void
    057            {
    058                _applicationName = value;
    059            }
    060 
    061            public function set description(value:String):void
    062            {
    063                _updateDescription = value;
    064            }
    065 
    066            public function set errorText(value:String):void
    067            {
    068                _errorText = value;
    069            }
    070 
    071            public function downloadProgress(value:Number):void
    072            {
    073                if(progressBar) progressBar.setProgress(value, 100);
    074            }
    075 
    076            private function continueUpdate():void
    077            {
    078                if (this.currentState == UpdaterDialog.CHECK_UPDATE){
    079                    this.dispatchEvent(new Event(EVENT_CHECK_UPDATE));
    080                else if (this.currentState == UPDATE_AVAILABLE) {
    081                    this.dispatchEvent(new Event(EVENT_DOWNLOAD_UPDATE));
    082                }else if (this.currentState == INSTALL_UPDATE) {
    083                    this.dispatchEvent(new Event(EVENT_INSTALL_UPDATE));
    084                }
    085            }
    086 
    087            private function cancelUpdate():void
    088            {
    089                if (this.currentState == INSTALL_UPDATE) {
    090                    this.dispatchEvent(new Event(EVENT_INSTALL_LATER));
    091                    return;
    092                }
    093                this.dispatchEvent(new Event(EVENT_CANCEL_UPDATE));
    094            }
    095 
    096            public function destroy():void
    097            {
    098                iconImage.unloadAndStop(true);
    099                iconImage.source = null;
    100 
    101                continueButton.removeEventListener(MouseEvent.CLICK, continueUpdate);
    102                cancelButton.removeEventListener(MouseEvent.CLICK, cancelUpdate);
    103 
    104                // becaause we used skins, we have to clear them for garbage collection
    106                continueButton.styleName = null;
    107                cancelButton.styleName = null;
    108 
    109                while(this.numChildren > 0){
    110                    this.removeChildAt(0);
    111                }
    112            }
    113        ]]>
    114    </mx:Script>
    115 
    116    <mx:states>
    117        <mx:State name="{CHECK_UPDATE}">
    118            <mx:AddChild position="lastChild">
    119                <mx:Label x="152" y="86" text="Application:" styleName="updateDialogLabel"/>
    120            </mx:AddChild>
    121            <mx:AddChild position="lastChild">
    122                <mx:Text x="230" y="86" text="{this._applicationName}"styleName="updateDialogText"/>
    123            </mx:AddChild>
    124            <mx:AddChild position="lastChild">
    125                <mx:Label x="107" y="19" text="Check for updates" styleName="updateTitle" />
    126            </mx:AddChild>
    127            <mx:AddChild position="lastChild">
    128                <mx:Text x="107" y="50" text="Allow the application to check for updates?" styleName="updateDialogText"/>
    129            </mx:AddChild>
    130            <mx:SetProperty name="height" value="180"/>
    131        </mx:State>
    132        <mx:State name="{UPDATE_AVAILABLE}">
    133            <mx:SetProperty name="height" value="360"/>
    134            <mx:AddChild position="lastChild">
    135                <mx:Text  text="{this._installedVersion}" x="230" y="114"styleName="updateDialogText"/>
    136            </mx:AddChild>
    137            <mx:AddChild position="lastChild">
    138                <mx:Text  text="{this._updateVersion}" x="230" y="134"styleName="updateDialogText"/>
    139            </mx:AddChild>
    140            <mx:AddChild position="lastChild">
    141                <mx:Label x="118" y="114" text="Installed Version:"styleName="updateDialogLabel"/>
    142            </mx:AddChild>
    143            <mx:AddChild position="lastChild">
    144                <mx:Label x="127" y="134" text="Update Version:" styleName="updateDialogLabel"/>
    145            </mx:AddChild>
    146            <mx:AddChild position="lastChild">
    147                <mx:Label x="152" y="96" text="Application:"  styleName="updateDialogLabel"/>
    148            </mx:AddChild>
    149            <mx:AddChild position="lastChild">
    150                <mx:Text x="230" y="96" text="{this._applicationName}"styleName="updateDialogText"/>
    151            </mx:AddChild>
    152            <mx:AddChild position="lastChild">
    153                <mx:Label x="107" y="19" text="Update Available" styleName="updateTitle"/>
    154            </mx:AddChild>
    155            <mx:AddChild position="lastChild">
    156                <mx:Text x="107" y="50" text="An updated version of the application is available for download." styleName="updateDialogText"/>
    157            </mx:AddChild>
    158 
    159            <mx:AddChild position="lastChild">
    160                <mx:Label id="releaseLabel" x="10" y="222" text="Release notes"styleName="updateDialogLabel"/>
    161            </mx:AddChild>
    162            <mx:AddChild position="lastChild">
    163                <mx:TextArea text="{_updateDescription}" x="10" y="248" width="508" height="100"styleName="updateDialogTextArea"/>
    164            </mx:AddChild>
    165 
    166            <mx:AddChild position="lastChild">
    167                <mx:HRule x="10" y="214" width="508" styleName="updateDialogHRule"/>
    168            </mx:AddChild>
    169            <mx:SetProperty target="{cancelButton}" name="y" value="164"/>
    170            <mx:SetProperty target="{continueButton}" name="y" value="164"/>
    171            <mx:SetProperty target="{cancelButton}" name="label" value="Download later"/>
    172            <mx:SetProperty target="{continueButton}" name="x" value="269"/>
    173            <mx:SetProperty target="{continueButton}" name="label" value="Download now"/>
    174        </mx:State>
    175        <mx:State name="{NO_UPDATE}">
    176            <mx:AddChild position="lastChild">
    177                <mx:Label x="122" y="86" text="Application:" styleName="updateDialogLabel"/>
    178            </mx:AddChild>
    179            <mx:AddChild position="lastChild">
    180                <mx:Text x="200" y="86" text="{this._applicationName}"styleName="updateDialogText"/>
    181            </mx:AddChild>
    182            <mx:AddChild position="lastChild">
    183                <mx:Label x="107" y="19" text="No Updates" styleName="updateTitle"/>
    184            </mx:AddChild>
    185            <mx:AddChild position="lastChild">
    186                <mx:Text x="107" y="50" text="There is no application update available at this time." styleName="updateDialogText"/>
    187            </mx:AddChild>
    188            <mx:RemoveChild target="{continueButton}"/>
    189            <mx:SetProperty target="{cancelButton}" name="label" value="Close"/>
    190            <mx:SetProperty name="height" value="180"/>
    191        </mx:State>
    192        <mx:State name="{UPDATE_DOWNLOADING}">
    193            <mx:AddChild position="lastChild">
    194                <mx:ProgressBar x="107" y="84" width="411" label=" " id="progressBar"mode="manual" height="15" styleName="updateDiallogProgress"/>
    195            </mx:AddChild>
    196            <mx:AddChild position="lastChild">
    197                <mx:Text x="107" y="20" text="Downloading Update" styleName="updateTitle"/>
    198            </mx:AddChild>
    199            <mx:RemoveChild target="{continueButton}"/>
    200            <mx:AddChild position="lastChild">
    201                <mx:Label x="107" y="53" text="Download progress..." styleName="updateDialogLabel"/>
    202            </mx:AddChild>
    203            <mx:SetProperty name="height" value="180"/>
    204        </mx:State>
    205        <mx:State name="{UPDATE_ERROR}">
    206            <mx:AddChild position="lastChild">
    207                <mx:Label x="107" y="19" text="Unexpected error" styleName="updateTitle"/>
    208            </mx:AddChild>
    209            <mx:AddChild position="lastChild">
    210                <mx:Label x="107" y="50" text="{_errorText}" styleName="updateDialogLabel"/>
    211            </mx:AddChild>
    212            <mx:RemoveChild target="{continueButton}"/>
    213            <mx:SetProperty target="{cancelButton}" name="label" value="Close"/>
    214            <mx:SetProperty name="height" value="180"/>
    215        </mx:State>
    216        <mx:State name="{INSTALL_UPDATE}">
    217            <mx:AddChild position="lastChild">
    218                <mx:Text  text="{this._installedVersion}" x="230" y="139"styleName="updateDialogText"/>
    219            </mx:AddChild>
    220            <mx:AddChild position="lastChild">
    221                <mx:Text  text="{this._updateVersion}" x="230" y="159"styleName="updateDialogText"/>
    222            </mx:AddChild>
    223            <mx:AddChild position="lastChild">
    224                <mx:Label x="118" y="139" text="Installed Version:"styleName="updateDialogLabel"/>
    225            </mx:AddChild>
    226            <mx:AddChild position="lastChild">
    227                <mx:Label x="127" y="159" text="Update Version:"  styleName="updateDialogLabel"/>
    228            </mx:AddChild>
    229            <mx:AddChild position="lastChild">
    230                <mx:Label x="152" y="121" text="Application:" styleName="updateDialogLabel"/>
    231            </mx:AddChild>
    232            <mx:AddChild position="lastChild">
    233                <mx:Text x="230" y="121" text="{this._applicationName}"styleName="updateDialogText"/>
    234            </mx:AddChild>
    235            <mx:AddChild position="lastChild">
    236                <mx:Label x="107" y="19" text="Install update" id="windowTitle4"styleName="updateTitle"/>
    237            </mx:AddChild>
    238            <mx:AddChild position="lastChild">
    239                <mx:Text x="107" y="50" text="The update for the application is downloaded and ready to be installed." styleName="updateDialogText"/>
    240            </mx:AddChild>
    241 
    242            <mx:AddChild position="lastChild">
    243                <mx:Label x="10" y="262" text="Release notes" styleName="updateDialogLabel"/>
    244            </mx:AddChild>
    245            <mx:AddChild position="lastChild">
    246                <mx:TextArea id="relaeseNotesTextArea0" text="{_updateDescription}" x="10"y="288" width="508" height="100" styleName="updateDialogTextArea"/>
    247            </mx:AddChild>
    248            <mx:SetProperty name="height" value="402"/>
    249            <mx:AddChild position="lastChild">
    250                <mx:ProgressBar id="installProgressBar" x="107" y="84" width="411" label=" "mode="manual" height="15" styleName="installDiallogProgress"creationComplete="installProgressBar.setProgress(100,100)"/>
    251            </mx:AddChild>
    252            <mx:AddChild position="lastChild">
    253                <mx:HRule x="10" y="249" width="508" styleName="updateDialogHRule"/>
    254            </mx:AddChild>
    255            <mx:SetProperty target="{cancelButton}" name="label" value="Postpone until restart"/>
    256            <mx:SetProperty target="{cancelButton}" name="y" value="198"/>
    257            <mx:SetProperty target="{continueButton}" name="y" value="198"/>
    258            <mx:SetProperty target="{continueButton}" name="x" value="320"/>
    259            <mx:SetProperty target="{continueButton}" name="label" value="Install update"/>
    260        </mx:State>
    261    </mx:states>
    262 
    263    <mx:Button x="107" y="129" label="Cancel" id="cancelButton" click="cancelUpdate()"height="34" styleName="updateDialogButton"/>
    264    <mx:Button x="202" y="129" label="Check for Updates" id="continueButton"click="continueUpdate()" height="34" styleName="updateDialogButton"/>
    265    <mx:Image source="@Embed('/assets/images/UpdateIcon.png')" x="15" y="25" width="81"height="74" id="iconImage"/>
    266 
    267</mx:Window>

    Main.mxml

    01<?xml version="1.0" encoding="utf-8"?>
    02<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
    03    layout="vertical" width="428" height="280" creationComplete="init()">
    04 
    05    <mx:Style source="assets/styles.css"/>
    06 
    07    <mx:Script>
    08        <![CDATA[
    09            import com.thanksmister.UpdateManager;
    10            import mx.rpc.events.ResultEvent;
    11 
    12            private var updater:UpdateManager;
    13            [Bindable] private var baseURL:String;
    14            [Bindable] private var updates:String;
    15 
    16            private function init():void
    17            {
    18                configService.send();
    19            }
    20 
    21            private function handleResult(event:ResultEvent):void
    22            {
    23                var xml:XML = event.result as XML;
    24                baseURL = xml..baseurl.toString();
    25                updates = xml..updates.toString();
    26 
    27                if(updates)
    28                    updater = new UpdateManager(truefalse);
    29            }
    30        ]]>
    31    </mx:Script>
    32 
    33    <mx:HTTPService id="configService" method="GET" resultFormat="e4x"url="config/configuration.xml" result="handleResult(event)" />
    34 
    35    <mx:Text fontSize="14" color="0xFFFFFFF" text="Update tester.  Please click the button below to begin update checking or wait for the dealy time (3 min). Once the application is successfully updated, the application version will be updated from v1 to v2." x="10" y="10" width="342"height="104"/>
    36 
    37    <mx:Button label="Begin update process" color="0xFFFFFFF"  x="125" y="104"click="updater.checkNow()"/>
    38 
    39    <mx:Label id="versionText" fontSize="14" color="0xFFFFFFF"/>
    40    <mx:Label text="{'Base URL: ' + baseURL}" color="0xFFFFFFF"/>
    41    <mx:Label text="{'Updates: ' + updates}" color="0xFFFFFFF"/>
    42</mx:WindowedApplication>

    I packed up the Flex project. You would use this basically the same as you would use the ApplicationUpdaterUI framework. You need to create an update version of your AIR file and place it and update.xm file on a server. You can test it from the local IDE if you replace the server url with “app:/”. Just remember you can not actually update an AIR application from the IDE, it has to be installed and running on your machine to update, and the update files have to be accessible just like when you use the ApplicationUpdaterUI.

    The code is free to use, hose, rip apart, just post back any fixes or enhancements you make.

    Source Files

    UpdateTester.zip

    Update

    Don’t forget to add a closing event handler to the dialog box that calls the destory() function. This was not in the original code and there needs to be a way to handle users clicking on the close box of the Window for the update dialog user interface. I also added a change that if you don’t want to show the check for update now option, the “no update available” dialog does not appear either, here is the updated code for statusUpdate function in UpdateManager.as (not in the downloaded zip):

    01private function statusUpdate(event:StatusUpdateEvent):void
    02        {
    03            //trace("statusUpdate");
    04             event.preventDefault();
    05             if(event.available){
    06                this.description = getUpdateDescription(event.details);
    07                this.upateVersion = event.version;
    08 
    09                if(!showCheckState) {
    10                    createDialog(UpdaterDialog.UPDATE_AVAILABLE);
    11                else if (updaterDialog) {
    12                    updaterDialog.applicationName = this.applicationName;
    13                    updaterDialog.installedVersion = this.installedVersion;
    14                    updaterDialog.upateVersion = this.upateVersion;
    15                    updaterDialog.description = this.description
    16                    updaterDialog.updateState = UpdaterDialog.UPDATE_AVAILABLE;
    17                }
    18             else {
    19                if (showCheckState) createDialog(UpdaterDialog.NO_UPDATE);
    20             }
    21        }

    -Mister

Designed by Tistory.