Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: [1]
Print
Author Topic: Setting alwaysInFront within a simple command (Flex / AIR)  (Read 11504 times)
neodigm
Newbie
*
Posts: 2


View Profile Email
« on: September 26, 2010, 09:22:43 »


Hello,

I need help setting the alwaysInFront property from within a simple command.  Any suggestions about how to access this property?

   override public function execute( notef:INotification ) :void {

   var app:wisdomatica = wisdomatica( notef.getBody() );
   app.

   app.stage.nativeWindow.alwaysInFront = true;
}

Also would appreciate some advice on executing the Adobe update framework from within a command.

override public function execute( note:INotification ) :void{
   //    this doesnt work, just run from applicaton

   include "../../../../application_updater/application_updater.as";
   checkUpdate();   
}

Thanks in advance.
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #1 on: September 26, 2010, 12:38:48 »

Your handling of alwaysInFront is ok, but unless you're performing a complex update, usually the thing to do is Mediate the app and or the stage. This cuts down on the number of Commands involved if you do lots of trivial updates to the same component.

When we use a Mediator as the single touchpoint in the codebase for important components, then we limit the coupling between the component and the app.

That said, if this is the only time you manipulate the App or the Stage, your single Command solution would be fine. The final exception is if you do that one thing *a lot*, then you still might want to Mediate the component (i.e. the stage in this case) so as not to have the runtime burden of repeated Command instantiation which does take time.

If you communicate with the stage a bit in your app, you should have a StageMediator (a long-lived actor) that tends the Stage itself. You send a note (SET_ALWAYS_IN_FRONT) that it responds to by setting its view component's property like:

:
protected function get stage():Stage
{
     return viewComponent as Stage;
}

override public function handleNotification(note:INotification):void
{
     switch (note.getName()) {
          case SET_ALWAYS_IN_FRONT:    
               stage.alwaysInFront=Boolean(note.getBody());
          .
          .
          .
     }
}

As for your handling of the AirUpdater, even though it is not a view component in the hierarchy, it definitely has to be communicated with, so mediation is a much better choice than a command, because there is a conversation that needs to take place. It could be done with scads of Commands, but it's much easier to handle (and understand) with a Mediator

Below are two classes and the updater version file I used successfully in a recent AIR application, the names have been changed of course. Also, you'll note that the StateMachine was used in the example. You don't have to use the StateMachine, but you'll find it super useful when your app goes through multiple steps (that can fail and possibly be retried) at startup such as checking for the net, checking for updates, checking for license info to run the app, checking for credentials to a service the app uses, etc...

AIRUpdateView is a panel that is shown when we get to the point in the app where we're checking for an update. It shows whether the update is required or not (an extension to the update config file that I added), a description of the update (release notes), and buttons for skipping or accepting the update. If the update is required, the user has only the option to continue, otherwise they see the skip button.

:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">

<mx:Script>
<![CDATA[
import com.myapp.common.constants.MyAppIcons;
import com.myapp.common.constants.MyAppSays;
import com.myapp.common.events.MyAppEvent;
import air.update.events.StatusUpdateEvent;

[Bindable] public var newVersion:String;
[Bindable] public var releaseNotes:String;
[Bindable] public var required:Boolean;

private function sendEvent( name:String, data:*=null ):void
{
dispatchEvent( new MyAppEvent( name, data ) );
}

]]>
</mx:Script>

<!-- AIR UPDATE PANEL-->
<mx:Panel title="{(required)?'Required':'Optional'} Update Available" status="New Version: {newVersion}"
titleIcon="{MyAppIcons.EXCLAMATION}"
horizontalAlign="center" verticalAlign="middle"
width="400" height="250" right="20" top="20"
backgroundAlpha="1" backgroundColor="#FFFFFF"
themeColor="#ffff00">

<!-- FORM -->
<mx:Form height="156" width="353" paddingBottom="0" paddingTop="0" horizontalScrollPolicy="off" verticalScrollPolicy="off">

<!-- RELEASE NOTES -->
<mx:Label text="Release Notes" fontWeight="bold"/>
<mx:TextArea height="123" width="330" editable="false" wordWrap="true"
id="taReleaseNotes" htmlText="{releaseNotes}" textAlign="left"
cornerRadius="0" borderStyle="none" paddingRight="10"
/>

</mx:Form>

<!-- CONTROL BAR -->
<mx:ControlBar paddingTop="0" paddingBottom="0" height="42">
<mx:ViewStack selectedIndex="{(required)?0:1}" width="100%" height="28">
<mx:HBox width="100%" horizontalAlign="right" verticalAlign="middle" paddingTop="0"  paddingBottom="0">
<mx:Button label="Install Required Update" click="sendEvent(MyAppSays.DOWNLOAD_UPDATE)" />
</mx:HBox>
<mx:HBox width="100%" horizontalAlign="right" verticalAlign="middle" paddingTop="0"  paddingBottom="0">
<mx:Button label="Install" click="sendEvent(MyAppSays.DOWNLOAD_UPDATE)" />
<mx:Button label="Skip" click="sendEvent(MyAppSays.SKIP_UPDATE)" />
</mx:HBox>
</mx:ViewStack>
</mx:ControlBar>
</mx:Panel>
</mx:Canvas>


The AIRUpdateMediator has the AIRUpdateView as its view component, but it also communicates with the AIR updater to initiate the check, listen for results and tell the updater to update if the user accepts it. Note that the AIRUpdateView component doesn't actually exist when this Mediator is registered, it first communicates with the updater to find out if we even need to show it. If so, the StateMachine is notified. Another Mediator is listening for the state change, switches its view component's ViewStack, causing the AIRUpdateView to be shown. That Mediator retrieves the AIRUpdateMediator and sets its view component, giving it a way to listen to the user's choice.

:
package net.myapp.view
{
import air.update.ApplicationUpdater;
import air.update.events.DownloadErrorEvent;
import air.update.events.StatusUpdateErrorEvent;
import air.update.events.StatusUpdateEvent;
import air.update.events.UpdateEvent;

import flash.events.*;
import flash.filesystem.File;
import flash.utils.*;

import mx.controls.Alert;

import com.myapp.common.constants.MyAppSays;
import com.myapp.view.components.AIRUpdateView;

import org.puremvc.as3.multicore.patterns.mediator.Mediator;
import org.puremvc.as3.multicore.utilities.statemachine.StateMachine;

public class AIRUpdateMediator extends Mediator
{

public static const NAME:String = "AIRUpdateMediator";

public function AIRUpdateMediator( )
{
super( NAME );
}

override public function onRegister():void
{
updater.configurationFile = new File("app:/myapp-update-config.xml");
updater.addEventListener( UpdateEvent.INITIALIZED, onInitialized );
updater.addEventListener( UpdateEvent.DOWNLOAD_COMPLETE, onDownloadComplete );
updater.addEventListener( StatusUpdateEvent.UPDATE_STATUS, onStatusUpdate );
updater.addEventListener( StatusUpdateErrorEvent.UPDATE_ERROR, onStatusUpdateError );
updater.addEventListener( DownloadErrorEvent.DOWNLOAD_ERROR, onDownloadError );
updater.addEventListener( ErrorEvent.ERROR, onError);
updater.initialize();
}

protected function onInitialized( event:UpdateEvent ):void
{
updater.checkNow();
}

protected function onStatusUpdate( event:StatusUpdateEvent ):void
{
event.preventDefault();
if ( event.available ) {
status = event;
sendNotification( StateMachine.ACTION, null, MyAppSays.UPDATE_PENDING );
} else {
sendNotification( StateMachine.ACTION, null, MyAppSays.NO_UPDATE_PENDING );
facade.removeMediator(NAME);
}
}

protected function onError( event:ErrorEvent ):void
{
Alert.show( event.text, "Unexpected Update Error" );
sendNotification( StateMachine.ACTION, null, MyAppSays.UPDATE_ERROR );
}

protected function onStatusUpdateError( event:StatusUpdateErrorEvent ):void
{
Alert.show( event.text, "Update Installation Failed" );
sendNotification( StateMachine.ACTION, null, MyAppSays.UPDATE_ERROR );
}

protected function onDownloadError( event:DownloadErrorEvent ):void
{
Alert.show( event.text, "Error Downloading Update" );
sendNotification( StateMachine.ACTION, null, MyAppSays.UPDATE_ERROR );
}

protected function onDownloadComplete( event:UpdateEvent ):void
{
event.preventDefault();
event.stopImmediatePropagation();
updater.installUpdate();
}

protected function onDownloadUpdate( event:Event ):void
{
sendNotification( StateMachine.ACTION, null, MyAppSays.DOWNLOAD_UPDATE );
updater.downloadUpdate();
}

protected function onSkipUpdate( event:Event ):void
{
sendNotification( StateMachine.ACTION, null, MyAppSays.SKIP_UPDATE );
facade.removeMediator(NAME);
}

override public function setViewComponent( viewComponent:Object ):void
{
super.setViewComponent(viewComponent);
updateView.newVersion = status.version;
updateView.releaseNotes = status.details[0][1]; // not localized

   var updateDescriptor:XML = (status.target as ApplicationUpdater).updateDescriptor;
   var fs:Namespace = updateDescriptor.namespace("fs");
   updateView.required = (updateDescriptor.fs::required == "true");
updateView.addEventListener( MyAppSays.DOWNLOAD_UPDATE, onDownloadUpdate );
updateView.addEventListener( MyAppSays.SKIP_UPDATE, onSkipUpdate );

}

override public function onRemove():void
{
// unhook from updater
updater.removeEventListener( UpdateEvent.INITIALIZED, onInitialized );
updater.removeEventListener( UpdateEvent.DOWNLOAD_COMPLETE, onDownloadComplete );
updater.removeEventListener( StatusUpdateEvent.UPDATE_STATUS, onStatusUpdate );
updater.removeEventListener( StatusUpdateErrorEvent.UPDATE_ERROR, onStatusUpdateError );
updater.removeEventListener( ErrorEvent.ERROR, onError );
updater=null;

// unhook from view if connected
if (updateView != null) {
updateView.removeEventListener( MyAppSays.DOWNLOAD_UPDATE, onDownloadUpdate );
updateView.removeEventListener( MyAppSays.SKIP_UPDATE, onSkipUpdate );
}
}

protected function get updateView():AIRUpdateView
{
return viewComponent as AIRUpdateView;
}

private var updater:ApplicationUpdater = new ApplicationUpdater();
private var status:StatusUpdateEvent;

}
}

myapp-version.xml is the standard updater file that is downloaded that tells the app about the update. Note I've added my own namespace to include the required field.

:
<?xml version="1.0" encoding="utf-8"?>
<update xmlns="http://ns.adobe.com/air/framework/update/description/1.0"
        xmlns:fs="http://schemas.futurescale.com/air/framework/update/description/1.0">
<version>1.0.1 Maintenance Release</version>
<url>http://myapp.com/air/MyApp.air</url>
<description>
<![CDATA[<UL><LI>This release includes self-serve trials.</LI></UL>]]></description>
<fs:required>true</fs:required>
</update>
« Last Edit: September 26, 2010, 12:47:32 by puremvc » Logged
neodigm
Newbie
*
Posts: 2


View Profile Email
« Reply #2 on: September 28, 2010, 04:27:06 »

Thanks for the feedback.  I did implement a stage Mediator.  This mediator now handles Always In Front, Launch at Startup, Full Screen, and Display on Taskbar.  These options are managed by the user and persisted into local xml file.

I am looking at the updater logic now.  Currently this just involves installing the framework and one line of code.  I am considering the mediator that you suggested because it will give me better control over how often it is run.  I don't think that I want it to run every time the app runs, maybe every other time.

Thanks again.
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #3 on: September 28, 2010, 08:14:47 »

The mediator and panel shown are used each time the app runs. You can see when the mediator is registered, it sets the event listeners on the updater and initializes it. When it is initialized, it will unconditionally check for updates and if there are any, we'll end up showing the panel and notifying the user.

This really doesn't take long, but if you don't want to check for update each time, you could have the onRegister method 'flip a coin' or 'roll a 12-sided die' to see if it will check or not. Just check a random (weighted) value that insures you won't check more than x% of the time.

If you'll be checking then set the event listeners and initialize the updater. Otherwise send out the MyAppSays.NO_UPDATE_PENDING notification (or StateMachine action if you decide to use it).

And of course if you're not using the StateMachine, you can convert all the StateMachine.ACTION notifications to normal notifications named similar to the type parameters in the StateMachine.ACTION notes. Just handle them with other Mediators and/or Commands as need be.

-=Cliff>
Logged
Pages: [1]
Print