-
[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
001
package
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
289
* Based on Jens Krause blog post: http://www.websector.de/blog/2009/09/09/custom-applicationupdaterui-for-using-air-updater-framework-in-flex-4/
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
303
* Based on Jens Krause blog post: http://www.websector.de/blog/2009/09/09/custom-applicationupdaterui-for-using-air-updater-framework-in-flex-4/
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
318
* Also based on Jens Krause blog post: http://www.websector.de/blog/2009/09/09/custom-applicationupdaterui-for-using-air-updater-framework-in-flex-4/
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
351
* Also based on Jens Krause blog post: http://www.websector.de/blog/2009/09/09/custom-applicationupdaterui-for-using-air-updater-framework-in-flex-4/
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(
true
,
false
);
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
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):
01
private
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
'공부방 > Flex' 카테고리의 다른 글
FlexBuilder(FlashBuilder)에서 Profiler(프로파일링) 사용 (0) 2016.10.06 [Flex] ApplicationUpdater 클래스로 Adobe AIR 자동 업데이트 (0) 2012.07.20 [Flex] 대용량 파일 전송 (0) 2012.07.18 Communicating between Flash Player and Adobe AIR with sockets (0) 2012.07.13 [Flex] Combobox 텍스트 입력 자동 완성 (0) 2012.04.09