Flex: Pass data from preloader to app? - apache-flex

I would like to track the customer experience in downloading and initializing my flex app.
(a) Is there a way to pass data from preloader to the application? I would like to pass the time it takes to download and the time it takes to initialize.
(b)Alternatively:
Is there an event at the application level that corresponds to the preloader events:
1. Download complete
2. Initialization complete (same as Application creationComplete)

The "Showing the download progress of an application" article in the livedocs should help.
Based on that documentation, I would do something like this:
create a simple subclass of the DownloadProgressBar,
override the event listeners to track the amount of time that has elapsed during download/initialisation,
store the time values as static properties so you can access them from your Application once it's completed initialisation.
Here is an example of what I'm thinking (I've not compiled this code, it's more to give an idea of what I'm talking about).
package
{
public class TimedProgressBar extends mx.preloaders.DownloadProgressBar
{
public static var startTime:Number = 0;
public static var downloadCompleteTime:Number = 0;
public static var RSLCompleteTime:Number = 0;
public function TimedProgressBar()
{
super();
startTime = getTimer();
}
override protected function completeHandler(event:Event):void
{
super();
downloadCompleteTime = getTimer();
}
override protected function rslCompleteHandler(event:RSLEvent):void
{
super();
RSLCompleteTime = getTimer();
}
}
}
Set that as your preloader in your Application.mxml and listen for the APPLICATION_COMPLETE event:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
preloader="TimedProgressBar"
applicationComplete="applicationCompleteHandler(event)">
private function applicationCompleteHandler(event:FlexEvent):void
{
var completeTime:Number = getTimer();
var downloadTime:Number = TimedProgressBar.downloadCompleteTime - TimedProgressBar.startTime;
var rslDownloadTime:Number = TimedProgressBar.RSLCompleteTime - TimedProgressBar.downloadCompleteTime;
var totalInitTime:Number = completeTime - TimedProgressBar.startTime;
// Do whatever logging you want with this information.
}

Related

Test case for fragment in android

In my application, I have multiple fragments on a single activity. Now I want to write a test case to check if these fragments are loading properly. To begin with, I passed some touch event to scroll to a particular fragment and then I am trying to fetch the name of this fragment. Below is my code for the test case:-
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity>
{
MainActivity mMainActivity;
ActionBar tactionbar;
Fragment tFragment;
public static final int TEST_POSITION = 2;
private static String mSelection ;
private int mPos = 0;
public MainActivityTest()
{
super(MainActivity.class);
}
protected void setUp() throws Exception
{
super.setUp();
mMainActivity = (MainActivity) getActivity();
tactionbar = mfoneclay.getActionBar();
}
public void testPreConditions()
{
assertNotNull(mMainActivity);
assertNotNull(tactionbar);
}
public void testFragmentUI()
{
mMainActivity.runOnUiThread(
new Runnable(){
public void run()
{
mMainActivity.getCurrentFocus();
}
});
for (int i = 1; i <= TEST_POSITION; i++)
{
this.sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
mPos = tactionbar.getSelectedNavigationIndex();
}
this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
mSelection = (String)tactionbar.getTabAt(mPos).getText();
String resultText = "Exclusive";
assertEquals(resultText,mSelection);
}
}
Here, "Exclusive" is the name of one of my tab to which I am navigating to via the touch event. Now, while running the test case, I can see that it is properly navigating to the "Exclusive" fragment, but the result shows the value of the msection variable as the name of the activity and not the fragments name. What am I doing wrong?
Got the solution. It was so stupid of me to use the wrong components to fetch the fragment. It turns out that I have to use "ViewPager" to fetch the fragments.

Structuring a MonoTouch.Dialog application

From the examples at Xamarin.com you can build basic M.T. Dialog apps, but how do you build a real life application?
Do you:
1) Create a single DialogViewController and tree every view/RootElement from there or,
2) Create a DialogViewController for every view and use the UINavigationController and push it on as needed?
Depending on your answer, the better response is how? I've built the example task app, so I understand adding elements to a table, click it to go to the 'next' view for editing, but how to click for non-editing? How to click a button, go next view if answer is number 1?
Revised:
There is probably no one right answer, but what I've come up with seems to work for us. Number 2 from above is what was chosen, below is an example of the code as it currently exists. What we did was create a navigation controller in AppDelegate and give access to it throughout the whole application like this:
public partial class AppDelegate : UIApplicationDelegate
{
public UIWindow window { get; private set; }
//< There's a Window property/field which we chose not to bother with
public static AppDelegate Current { get; private set; }
public UINavigationController NavController { get; private set; }
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
Current = this;
window = new UIWindow (UIScreen.MainScreen.Bounds);
NavController = new UINavigationController();
// See About Controller below
DialogViewController about = new AboutController();
NavController.PushViewController(about, true);
window.RootViewController = NavController;
window.MakeKeyAndVisible ();
return true;
}
}
Then every Dialog has a structure like this:
public class AboutController : DialogViewController
{
public delegate void D(AboutController dvc);
public event D ViewLoaded = delegate { };
static About about;
public AboutController()
: base(about = new About())
{
Autorotate = true;
about.SetDialogViewController(this);
}
public override void LoadView()
{
base.LoadView();
ViewLoaded(this);
}
}
public class About : RootElement
{
static AboutModel about = AboutVM.About;
public About()
: base(about.Title)
{
string[] message = about.Text.Split(...);
Add(new Section(){
new AboutMessage(message[0]),
new About_Image(about),
new AboutMessage(message[1]),
});
}
internal void SetDialogViewController(AboutController dvc)
{
var next = new UIBarButtonItem(UIBarButtonSystemItem.Play);
dvc.NavigationItem.RightBarButtonItem = next;
dvc.ViewLoaded += new AboutController.D(dvc_ViewLoaded);
next.Clicked += new System.EventHandler(next_Clicked);
}
void next_Clicked(object sender, System.EventArgs e)
{
// Load next controller
AppDelegate.Current.NavController.PushViewController(new IssuesController(), true);
}
void dvc_ViewLoaded(AboutController dvc)
{
// Swipe location: https://gist.github.com/2884348
dvc.View.Swipe(UISwipeGestureRecognizerDirection.Left).Event +=
delegate { next_Clicked(null, null); };
}
}
Create a sub-class of elements as needed:
public class About_Image : Element, IElementSizing
{
static NSString skey = new NSString("About_Image");
AboutModel about;
UIImage image;
public About_Image(AboutModel about)
: base(string.Empty)
{
this.about = about;
FileInfo imageFile = App.LibraryFile(about.Image ?? "filler.png");
if (imageFile.Exists)
{
float size = 240;
image = UIImage.FromFile(imageFile.FullName);
var resizer = new ImageResizer(image);
resizer.Resize(size, size);
image = resizer.ModifiedImage;
}
}
public override UITableViewCell GetCell(UITableView tv)
{
var cell = tv.DequeueReusableCell(skey);
if (cell == null)
{
cell = new UITableViewCell(UITableViewCellStyle.Default, skey)
{
SelectionStyle = UITableViewCellSelectionStyle.None,
Accessory = UITableViewCellAccessory.None,
};
}
if (null != image)
{
cell.ImageView.ContentMode = UIViewContentMode.Center;
cell.ImageView.Image = image;
}
return cell;
}
public float GetHeight(UITableView tableView, NSIndexPath indexPath)
{
float height = 100;
if (null != image)
height = image.Size.Height;
return height;
}
public override void Selected(DialogViewController dvc, UITableView tableView, NSIndexPath indexPath)
{
//base.Selected(dvc, tableView, path);
tableView.DeselectRow(indexPath, true);
}
}
#miquel
The current idea of a workflow is an app that starts with a jpg of the Default.png that fades into the first view, with a flow control button(s) that would move to the main app. This view, which I had working previous to M.T.D. (MonoTouch.Dialog), which is a table of text rows with an image. When each row is clicked, it moves to another view that has the row/text in more detail.
The app also supports in-app-purchasing, so if the client wishes to purchase more of the product, then switch to another view to transact the purchase(s). This part was the main reason for switching to M.T.D., as I thought M.T.D. would be perfect for it.
Lastly there would be a settings view to re-enable purchases, etc.
PS How does one know when the app is un-minimized? We would like to show the fade in image again.
I have been asking myself the same questions. I've used the Funq Dependency Injection framework and I create a new DialogViewController for each view. It's effectively the same approach I've used previously developing ASP.NET MVC applications and means I can keep the controller logic nicely separated. I subclass DialogViewController for each view which allows me to pass in to the controller any application data required for that particular controller. I'm not sure if this is the recommended approach but so far it's working for me.
I too have looked at the TweetStation application and I find it a useful reference but the associated documentation specifically says that it isn't trying to be an example of how to structure a MonoTouch application.
I use option 2 that you stated as well, it works pretty nicely as you're able to edit the toolbar options on a per-root-view basis and such.
Option 2 is more feasible, as it also gives you more control on each DialogViewController. It can also helps if you want to conditionally load the view.

Connecting flex and weborb?

im a newbie in flex. Im have a question :)
I have
[Bindable]
private var model:AlgorithmModel = new AlgorithmModel();
private var serviceProxy:Algorithm = new Algorithm( model );
In MXML
private function Show():void
{
// now model.Solve_SendResult = null
while(i<model.Solve_SendResult.length) //
{
Draw(); //draw cube
}
}
private function Solve_Click():void
{
//request is a array
Request[0] = 2;
Request[1] = 2;
Request[2] = 3;
serviceProxy.Solve_Send(request);
Show();
}
<s:Button x="386" y="477" label="Solve" click="Solve_Click();"/>
And when i call serviceProxy.Solve_Send(request); with request is array and i want use model.Solve_SendResult in my code flex to draw many cubes use papervison3d but in the first time i received model.Solve_SendResult = null . But when I click again then everything OK.
Anyone help me? Thanks?
The model.Solve_SendResult object contains a result of the executed serviceProxy.Solve_Send(request) method. The Solve_Send will be executed asynchronously and as a result, at the moment when you fire the show method the Solve_SendResult object may be still null.
As a solution, you can use the following:
Create a custom event
package foo
{
import flash.events.Event;
public class DrawEvent extends Event
{
public static const DATA_CHANGED:String = "dataChanged";
public function DrawEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}
In your Algorithm class define the following:
[Event(name=DrawEvent.DATA_CHANGED, type="foo.DrawEvent")]
public class Algorithm extends EventDispatcher{
//your code
In the Solve_SendHandler method of the Algorithm class add the following
public virtual function Solve_SendHandler(event:ResultEvent):void
{
dispatchEvent(new DrawEvent(DrawEvent.DATA_CHANGED));
//your code
}
In your MXML class create onLoad method and add an event listener to an instance of the Algorithm class as it shown below:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="onLoad()">
public function onLoad():void
{
serviceProxy.addEventListener(DrawEvent.DATA_CHANGED, onDataChanged);
}
private function onDataChanged(event:DrawEvent):void{
while(i<model.Solve_SendResult.length) //
{
Draw(); //draw cube
}
}
make the following changes in the Solve_Click() method:
private function Solve_Click():void
{
//request is a array
Request[0] = 2;
Request[1] = 2;
Request[2] = 3;
serviceProxy.Solve_Send(request);
}
That is it! So, basically the code above do the following: you added a listener to your service (algorithm class), and the listener is listening for the DrawEvent.DATA_CHANGED event. The DrawEvent.DATA_CHANGED will be dispatched when your client receive a result of the Solve_Send invocation. Thus, the onDataChanged will draw your cube or do whatever you want :)
The approach above is basic and you have to know how events work in flex and how you can deal with it. Additional information is available here:
http://livedocs.adobe.com/flex/3/html/help.html?content=createevents_3.html
http://livedocs.adobe.com/flex/3/html/help.html?content=events_07.html
Regards,
Cyril

Flash UI blocked during flex rpc.soap.Operation::send

I've got a Flash UI that does a periodic server call to get some updated information. The call uses the flex sdk's rpc.soap.Operation class. It looks something like this:
var wsOperation:Operation = Operation(webService.getOperation(SomeOperation));
wsOperation.addEventListener("fault", wsError);
wsOperation.addEventListener("result", wsResult);
wsOperation.send(...some params);
This call gets some data from a SQL database. I have timed the call from right before the send to the start of the wsResult function at ~4 seconds. During this time, my UI is not updated. It is frozen/unresponsive.
Now, I know Flash is single-threaded/asynchronous, so I'm not sure why this is happening. I see that the send(..) function returns an AsyncToken which I am not using. Could this have something to do with it?
Any other ideas as to why this is happening are appreciated. Thanks.
I still haven't found an acceptable solution to this. It seems ridiculous that I would have to Pseudo thread to get flash to update the UI during a 4 second call. I'm wondering if maybe the parsing of the soap response could be taking up a lot of time. If there is a lot of processing to do, will Flash delay updating the UI indefinitely?
You will get a UI freeze in Flash because it is single threaded. However, you can do pseudo threading with something like this:
package
{
import flash.display.DisplayObjectContainer;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.utils.getTimer;
import mx.core.UIComponent;
import mx.managers.ISystemManager;
public class PseudoThread extends EventDispatcher
{
public function PseudoThread(sm:ISystemManager, threadFunction:Function, threadObject:Object)
{
fn = threadFunction;
obj = threadObject;
// add high priority listener for ENTER_FRAME
sm.stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 100);
sm.stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
sm.stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
thread = new UIComponent();
sm.addChild(thread);
thread.addEventListener(Event.RENDER, renderHandler);
}
// number of milliseconds we think it takes to render the screen
public var RENDER_DEDUCTION:int = 10;
private var fn:Function;
private var obj:Object;
private var thread:UIComponent;
private var start:Number;
private var due:Number;
private var mouseEvent:Boolean;
private var keyEvent:Boolean;
private function enterFrameHandler(event:Event):void
{
start = getTimer();
var fr:Number = Math.floor(1000 / thread.systemManager.stage.frameRate);
due = start + fr;
thread.systemManager.stage.invalidate();
thread.graphics.clear();
thread.graphics.moveTo(0, 0);
thread.graphics.lineTo(0, 0);
}
private function renderHandler(event:Event):void
{
if (mouseEvent || keyEvent)
due -= RENDER_DEDUCTION;
while (getTimer() < due)
{
if (!fn(obj))
{
if (!thread.parent)
return;
var sm:ISystemManager = thread.systemManager;
sm.stage.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
sm.stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
sm.stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
sm.removeChild(thread);
thread.removeEventListener(Event.RENDER, renderHandler);
dispatchEvent(new Event("threadComplete"));
}
}
mouseEvent = false;
keyEvent = false;
}
private function mouseMoveHandler(event:Event):void
{
mouseEvent = true;
}
private function keyDownHandler(event:Event):void
{
keyEvent = true;
}
}
}
This will enable you to do a process without the UI Freeze. It basically uses the stage's RENDER event to defer processing. This code performs as much Actionscript computation as possible limited by the time needed to maintain the frame rate. For more information see: http://blogs.adobe.com/aharui/2008/01/threads_in_actionscript_3.html

google maps flex API: handle both double and single clicks?

how do you tell a single mouse click from a double click? my single
click listener seems to be trapping all the double clicks as well:
tz locator
it's a known issue for the flash/flex API but the js workaround doesn't seem to handle both either: code.google.com
Might need a bit of clarification, but make sure you are using the Google Map's MapMouseEvent, not the Flash API's click events (please assume this code in inside a Map subclass):
public class GoogleMap extends Map
{
import com.google.maps.LatLng;
import com.google.maps.Map;
import com.google.maps.MapEvent;
import com.google.maps.MapMouseEvent;
public function GoogleMap():void
{
super();
this.key = "YOUR_API_KEY";
addEventListener(MapEvent.MAP_READY, _onMapReady);
addEventListener(MapMouseEvent.CLICK, _onMapClick);
addEventListener(MapMouseEvent.DOUBLE_CLICK, _onMapDoubleClick);
}
protected function _onMapClick(event:MapMouseEvent):void
{
trace("single!");
var mousePoint:Point = new Point(mouseX, mouseY);
var mousePointLocal:Point = globalToLocal(mousePoint);
var mouseLatLng:LatLng = this.fromViewportToLatLng(mousePointLocal);
}
protected function _onMapDoubleClick(event:MapMouseEvent):void
{
trace("double!");
}
protected function _onMapReady(event:MapEvent):void
{
trace("ready!")
}
}

Resources