No, Flex & Flash Are Not Dead

Last week Adobe announced information about the company’s evolution and future plans of Flex. It was also announced that Adobe Flex would be contributed to an open source software foundation. The result of which, was mass speculation, fear, uncertainty, and doubt.   Rest assured, Flash is not dead, nor is Flex.

Andrew and Deepa from the Flex team have posted some questions and answers raised by these conversations. I highly recommend reading these in their entirety, as they will answer a lot of the questions about the future of Flex. Key takeaways:

Is Adobe still committed to Flash Builder?

Yes. Flash Builder will continue to be developed and Adobe will work to ensure Flex developers can use Flash Builder as their development tool with future releases of Flex SDK.

Will Adobe continue to support customers using Flex?

Yes. Adobe will continue to honor existing Flex support contracts.

What specifically is Adobe proposing?

We are preparing two proposals for incubating Flex SDK and BlazeDS at the Apache Software Foundation.

In addition to contributing the core Flex SDK (including automation and advanced data visualization components), Adobe also plans to donate the following:

  • Complete, but yet-to-be-released, Spark components, including ViewStack, Accordion, DateField, DateChooser and an enhanced DataGrid.
  • BlazeDS, the server-based Java remoting and web messaging technology that enables developers to easily connect to back-end distributed data and push data in real-time to Flex applications.
  • Falcon, the next-generation MXML and ActionScript compiler that is currently under development (this will be contributed when complete in 2012)
  • Falcon JS, an experimental cross-compiler from MXML and ActionScript to HTML and JavaScript.
  • Flex testing tools, as used previously by Adobe, so as to ensure successful continued development of Flex with high quality

Isn’t Adobe just abandoning Flex SDK and putting it out to Apache to die? 

Absolutely not – we are incredibly proud of what we’ve achieved with Flex and know that it will continue to provide significant value for many years to come. We expect active and on-going contributions from the Apache community. To be clear, Adobe plans on steadily contributing to the projects and we are working with the Flex community to make them contributors as well.

Flex has been open source since the release of Flex 3 SDK. What’s so different about what you are announcing now?

Since Flex 3, customers have primarily used the Flex source code to debug underlying issues in the Flex framework, rather than to actively develop new features or fix bugs and contribute them back to the SDK.

With Friday’s announcement, Adobe will no longer be the owner of the ongoing roadmap. Instead, the project will be in Apache and governed according to its well-established community rules. In this model, Apache community members will provide project leadership. We expect project management to include both Adobe engineers as well as key community leaders. Together, they will jointly operate in a meritocracy to define new features and enhancements for future versions of the Flex SDK. The Apache model has proven to foster a vibrant community, drive development forward, and allow for continuous commits from active developers.

What guarantees can Adobe make in relation to Flex applications continuing to run on Flash Player and Adobe AIR?

Adobe will continue to support applications built with Flex, as well as all future versions of the SDK running in PC browsers with Adobe Flash Player and as mobile apps with Adobe AIR indefinitely on Apple iOS, Google Android and RIM BlackBerry Tablet OS.

http://blogs.adobe.com/flex/2011/11/your-questions-about-flex.html

Adobe’s Mike Chambers has also posted information explaining a bit more detail, and providing insight into the future of Flash and AIR.   Key takeaways:

Adobe AIR

We are continuing to develop Adobe AIR for both the desktop and mobile devices. Indeed, we have seen wide adoption of Adobe AIR for creating mobile applications and there have been a number of blockbuster mobile applications created using Adobe AIR.

Flash Player for Desktop Browsers

We feel that Flash continues to play a vital role of enabling features and functionality on the web that are not otherwise possible. As such, we have a long term commitment to the Flash Player on desktops, and are actively working on the next Flash Player version.

http://www.mikechambers.com/blog/2011/11/11/clarifications-on-flash-player-for-mobile-browsers-the-flash-platform-and-the-future-of-flash/

AMF vs. JSON in AIR Mobile Applications

UPDATE 11/23/2011: Full source code now available at:
https://github.com/triceam/Flex-Mobile-Serialization-Tester


Recently, I’ve been asked more than once which is better: AMF or JSON for AIR mobile applications. This post is to highlight some performance comparisons, and a sample testing application that I put together. First, it is important to know what both AMF and JSON are.

AMF

Action Message Format (AMF) is a compact binary format that is used to serialize
ActionScript object graphs. Once serialized an AMF encoded object graph may be used
to persist and retrieve the public state of an application across sessions or allow two
endpoints to communicate through the exchange of strongly typed data.

-from the AMF3 Specficiation

JSON

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition – December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others.

-from www.json.org/

Both AMF and JSON are compact serialization formats and provide efficient data transport.  The main differences between the two formats are as follows:

  • AMF is a binary format that is not easily readable by humans, JSON is a text-based format that is easily readable.
  • AMF allows for serialization of strongly typed objects in transactions between the client and server, JSON only supports generic or loosely-typed objects.

Former Adobe Evangelist James Ward put together a suite of benchmarks comparing JSON, SOAP, and AMF that show comparable performance between AMF and JSON.   Recently, AIR 3.0 and Flash Player 11 brought native JSON support, which greatly improves JSON parsing in Flash & AIR runtimes.   This is a huge boost, especially for mobile applications that consume JSON data.

I put together a very basic test case where a mobile application makes requests of simple data objects from a ColdFusion CFC.  In each test iteration, a request is made for 1, 10, 100, 1000, and 10000 value objects, in both AMF and JSON formats. The total round trip time from request to deserialization is measured and compared for each case, for a total of 5 iterations through each cycle.   My findings are that AMF and JSON have comparable performance in smaller record sets.  However, AMF seems to have better performance as data sets grow.  In my test cases, the 1000+ record results were consistently faster using AMF.  However, in smaller data sets, JSON was often faster (however not consistently, or by much of a margin). I tested these times on both an iPhone 4 and Motorolla Atrix, both running on the carrier networks (not over wifi).

Below is a video of the serialization testing application at work.

Here are a few screenshots of the application.

The Tests

For these tests I created two basic CFCs (ColdFusion Components). One is a simple data value object. The other CFC is a gateway to expose a remote service that returns the value objects to the client. I chose a ColdFusion CFC for this case b/c it can easily be serialized as AMF or JSON just by changing the endpoint used to consume the service.

Here is the basic value object CFC:

[as3]component {

property name="itemId";
property name="value1";
property name="value2";
property name="value3";

this.itemId = 0;
this.value1 = CreateUUID();
this.value2 = CreateUUID();
this.value3 = CreateUUID()
}[/as3]

Here is the service CFC used to return data to the client:

[as3]component {

remote array function getRecords(numeric records=1) {
var result = [];

for (var x = 0; x < records; x=x+1) {
var item = new SampleVO();
item.itemId = x;
ArrayAppend( result, item );
}

return result;
}
}[/as3]

Obviously, this is a fictional data object with randomly generated values. However, it still represents a reasonable service payload for data serialization. By accessing the data via the ColdFusion Flex/Remoting gateway, you access the remote services via AMF3.

[as3]
remoteObject = new RemoteObject("ColdFusion");
remoteObject.source = "com.tricedesigns.mobileTest.Services";
remoteObject.endpoint = "http://tricedesigns.com/flex2gateway/";

var token : AsyncToken = remoteObject.getRecords( RECORD_COUNT[ recordCountIndex ] );
token.addResponder( new mx.rpc.Responder( onAMFResult, onFault ) );[/as3]

By accessing the data via an http endpoint, with returnformat=josn, you will invoke the same CFC remote method exposed as JSON.

[as3]httpService = new HTTPService();

httpService.url = "http://tricedesigns.com/com/tricedesigns/mobileTest/Services.cfc?method=getrecords&records=" + RECORD_COUNT[ recordCountIndex ] + "&returnformat=json";
var token : AsyncToken = httpService.send();
token.addResponder( new mx.rpc.Responder( onJSONResult, onFault ) );[/as3]

The JSON-formatted data will look something like this:

[js][{"ITEMID":0,"VALUE3":"FA817ED6-EB7C-0677-097452161BCB6689","VALUE2":"FA817ED5-0FC4-BD8B-6515B283E5426AAC","VALUE1":"FA817ED4-0B3A-71B4-D45559FBB0AE5BEE"},
{"ITEMID":1.0,"VALUE3":"FA817ED9-FBBE-B9A2-9C01390B65B65DDB","VALUE2":"FA817ED8-A5EF-EE8E-72692303F9C5CFCB","VALUE1":"FA817ED7-D569-2008-A5BFB9F6E1154FE6"},
{"ITEMID":2.0,"VALUE3":"FA817EDC-FC4E-3473-6FC7910831CB293A","VALUE2":"FA817EDB-DF92-71D5-B6B5C67EC93816DD","VALUE1":"FA817EDA-90A3-1566-96FC2524628DCB56"},
{"ITEMID":3.0,"VALUE3":"FA817EDF-923A-DC19-07128DF719212B97","VALUE2":"FA817EDE-E59F-40F0-FE3A9267DE952E8E","VALUE1":"FA817EDD-B0F0-5B20-675E0B0A61D4DA46"},
{"ITEMID":4.0,"VALUE3":"FA817EE2-CDCA-5C3D-88D3B72EEF11AA60","VALUE2":"FA817EE1-99D3-741D-58F5BA5DC00C035F","VALUE1":"FA817EE0-F1AB-0AEF-2FC57BA2104FB365"},
{"ITEMID":5.0,"VALUE3":"FA817EE5-0750-E4A5-18914030A5EC4BF2","VALUE2":"FA817EE4-07A4-D025-16BF02A7452F3EC2","VALUE1":"FA817EE3-E72B-B8CA-F22607314115CACF"},
{"ITEMID":6.0,"VALUE3":"FA817EE8-AC14-79D7-6F2BF568CE172823","VALUE2":"FA817EE7-95B8-9BA8-9265B6BAFF927D48","VALUE1":"FA817EE6-FCD4-2998-965667E97F515AB5"},
{"ITEMID":7.0,"VALUE3":"FA817EEB-E5F2-F2AB-FE68B85311E126B0","VALUE2":"FA817EEA-C93B-65FF-C7867A6A097BA1FC","VALUE1":"FA817EE9-B009-EC7E-BA063963F0E905E9"},
{"ITEMID":8.0,"VALUE3":"FA817EEE-C060-FD2E-B611E38AC454A789","VALUE2":"FA817EED-BED5-79F3-E6F72A823B92B5D9","VALUE1":"FA817EEC-F930-7069-52DB96A08D828F6B"},
{"ITEMID":9.0,"VALUE3":"FA817EF1-AABB-8AE0-0D1141E449A99A4F","VALUE2":"FA817EF0-0E1C-C0AD-2446C0706A87C9DF","VALUE1":"FA817EEF-0899-5737-A53E353397F401CF"}][/js]

In the mobile client application, I have a SerializationTestController class that handles all of the test logic and communications back and forth with the server. The time for each test is measured from immediately before the the request is made to the server, until after the data has been deserialized to an ArrayCollection. You can view the SerializationTestController class below:

[as3]package control
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.utils.getTimer;

import model.TestSummaryVO;
import model.TestVO;

import mx.collections.ArrayCollection;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
import mx.rpc.remoting.RemoteObject;

import views.SummaryView;

[Event(name="testStatusChange", type="control.TestUpdateEvent")]
[Event(name="testUpdate", type="control.TestUpdateEvent")]
public class SerializationTestController extends EventDispatcher
{
private var remoteObject : RemoteObject;
private var httpService : HTTPService;

private var _testing : Boolean = false;

private var testIndex : int = 0;
private var iterationIndex : int = 0;
private var recordCountIndex : int = 0;
private var testInstanceIndex : int = 0;

private var _results : ArrayCollection;
private var currentTest : TestVO;

public static const ITERATIONS : int = 5;
public static const RECORD_COUNT : Array = [1,10,100,1000,10000];
public static const TESTS : Array = [ TestVO.TYPE_AMF, TestVO.TYPE_JSON ];

public function SerializationTestController(target:IEventDispatcher=null)
{
super(target);

remoteObject = new RemoteObject("ColdFusion");
remoteObject.source = "com.tricedesigns.mobileTest.Services";
remoteObject.endpoint = "http://tricedesigns.com/flex2gateway/";

httpService = new HTTPService();

_results = new ArrayCollection();
}

[Bindable(event="testStatusChange")]
public function get testing ():Boolean
{
return _testing;
}

public function get results():ArrayCollection
{
return _results;
}

public function get chartResults() : ArrayCollection
{
var result : ArrayCollection = new ArrayCollection();

for ( var index : int = 0; index < ITERATIONS; index++ ) { var summaryVO : TestSummaryVO = new TestSummaryVO(); summaryVO.iteration = index+1; result.addItem( summaryVO ); } for each ( var vo : TestVO in results ) { summaryVO = result.getItemAt( vo.iteration ) as TestSummaryVO; if ( vo.type == TestVO.TYPE_AMF ) summaryVO[ "amfDuration" + SerializationTestController.RECORD_COUNT[ vo.recordIndex ] ] = vo.endTime – vo.startTime; else summaryVO[ "jsonDuration" + SerializationTestController.RECORD_COUNT[ vo.recordIndex ] ] = vo.endTime – vo.startTime; } return result; } public function startTest() : void { if ( _testing ) return; _testing = true; testIndex = 0; iterationIndex = 0; recordCountIndex = 0; testInstanceIndex = 0; updateTest(); dispatchEvent( new TestUpdateEvent( TestUpdateEvent.TEST_STATUS ) ); dispatchEvent( new TestProgressEvent( "STARTING TEST…" ) ); } private function completeTest() : void { _testing = false; dispatchEvent( new TestUpdateEvent( TestUpdateEvent.TEST_STATUS ) ); dispatchEvent( new TestProgressEvent( "TEST COMPLETE" ) ); } private function createTestVO() : void { currentTest = new TestVO(); currentTest.startTime = getTimer(); currentTest.index = testInstanceIndex; currentTest.iteration = iterationIndex; currentTest.recordIndex = recordCountIndex; currentTest.type = TESTS[ testIndex ]; } private function finalizeTestVO(error : Boolean = false) : void { if ( error ) currentTest.endTime = -1 else currentTest.endTime = getTimer(); _results.addItem( currentTest ); dispatchEvent( new TestUpdateEvent( TestUpdateEvent.TEST_UPDATE, currentTest ) ); dispatchEvent( new TestProgressEvent( "task completed in " + (currentTest.endTime – currentTest.startTime) + " milliseconds" ) ); currentTest = null; } private function updateTest() : void { if ( iterationIndex >= ITERATIONS )
return completeTest();

createTestVO();

if ( TESTS[ testIndex ] == TestVO.TYPE_AMF )
{
doAMFTest();
recordCountIndex ++;

if ( recordCountIndex >= RECORD_COUNT.length )
{
recordCountIndex = 0;
testIndex++;
}
}

else if ( TESTS[ testIndex ] == TestVO.TYPE_JSON )
{
doJSONTest();
recordCountIndex ++;

if ( recordCountIndex >= RECORD_COUNT.length )
{
recordCountIndex = 0;
testIndex = 0;
iterationIndex ++;
}
}

testInstanceIndex++;
}

private function doAMFTest() : void
{
dispatchEvent( new TestProgressEvent( "AMF Requesting " + RECORD_COUNT[ recordCountIndex ] ) );
var token : AsyncToken = remoteObject.getRecords( RECORD_COUNT[ recordCountIndex ] );
token.addResponder( new mx.rpc.Responder( onAMFResult, onFault ) );
}

protected function onAMFResult( event : ResultEvent ) : void
{
var result : ArrayCollection = event.result as ArrayCollection;
finalizeTestVO();
updateTest();
}

private function doJSONTest() : void
{
dispatchEvent( new TestProgressEvent( "JSON Requesting " + RECORD_COUNT[ recordCountIndex ] ) );
httpService.url = "http://tricedesigns.com/com/tricedesigns/mobileTest/Services.cfc?method=getrecords&records=" + RECORD_COUNT[ recordCountIndex ] + "&returnformat=json";
var token : AsyncToken = httpService.send();
token.addResponder( new mx.rpc.Responder( onJSONResult, onFault ) );
}

protected function onJSONResult( event : ResultEvent ) : void
{
var resultString : String = event.result as String;
var result : ArrayCollection = new ArrayCollection( JSON.parse( resultString ) as Array );

finalizeTestVO();
updateTest();

}

protected function onFault( event : FaultEvent ) : void
{
trace( event.fault.toString() );
finalizeTestVO(true);
updateTest();
}

}
}[/as3]

Also, here is the TestVO value object that shows the information captured for each test:

[as3]package model
{
public class TestVO
{
public static const TYPE_JSON : String = "json";
public static const TYPE_AMF : String = "amf";

public var index : int;
public var iteration : int = 0;
public var startTime : int;
public var endTime : int;
public var type : String;
public var recordIndex : int;

public function TestVO()
{
}
}
}[/as3]

Summary

Both JSON and AMF are acceptable serialization formats for mobile applications built with AIR.   Both are compact serialization formats that minimize packet size.  Both have native parsing/decoding by the AIR runtime.   AMF will generally provide better performance for larger data sets.  JSON *may* provide marginally better performance for small data sets.  AMF also allows for strongly typed object serialization & deserialization, where JSON does not.

The answer to the question of “should I use AMF or JSON” is subjective… What kind of data are you returning, and how much data is it? Do you already have AMF services built?  Do you already have JSON services built?   Are the services consumed by multiple endpoints, with multiple technologies?  Do you rely upon strongly typed objects in you development and maintenance processes?  Both AMF and JSON are viable solutions for mobile applications.


UPDATE 11/23/2011: Full source code now available at:
https://github.com/triceam/Flex-Mobile-Serialization-Tester


Thanks also to fellow Adobe evangelist Raymond Camden for the CF tips & guidance.

Upcoming Events in 2011

Hi Everyone! Here are a few events that I’ll be speaking at/attending in the remainder of 2011. Come check out Adobe’s cross-platform tooling, and feel free to bring your questions for me. I hope to see you at any one of these events!

MoDevDC Meetup

Tonight! 11/02/2011
This month’s MoDevDC (Mobile Developers DC) tech meetup is all about cross-platform development tools. Stop by to see a high level overview of Adobe’s cross-platform mobile development offerings, including AIR and PhoneGap. This will include the basics of “what are these tools”, as well as some demo applications.
http://www.meetup.com/modevdc/events/38825722/

DC Droids Meetup

11/15/2011
Here is a chance for you to learn about another method to develop Android applications (and make them compatible across platforms) from an expert from Adobe. You will also have the chance to win a copy of Flash Builder 4.5. I will walk through the processes of building Android & cross-platform applications using both Adobe AIR and PhoneGap (a cross-platform mobile application development framework). This session will cover demo applications, as well as real-world coding and best practices.
http://www.meetup.com/DC-Droids/events/38630182/

MoDevEast Conference & Hackathon

12/02/2011 – 12/03/2011
MoDevEast is where mobile developers and marketers gain the upper edge. If you are developing apps or mobile websites, targeting phones or tablets, staying ahead is paramount in this fast moving industry. MoDevEast 2011 will offer five tracks that hone development skills and sharpen mobile business strategy. I’ll be speaking on cross-platform mobile development, and I’ll definitely be there for the event and hackathon!
http://www.modeveast.com/


Building PhoneGap Applications With Dreamweaver

Update (3/28/2012): 

Hi Everyone, I know there have been lots of questions about PhoneGap + Android SDK, and lots of headaches because Google’s Android SDK keeps changing and breaking the Dreamweaver integration. Fret not! There is an easier way — Adobe has released a plugin to integrate Dreamweaver with PhoneGap Build. With this plugin, you build your experience in Dreamweaver, then push to the PhoneGap Build service for cloud-based compilation of device-specific binaries. Read more about this plugin here: http://blogs.adobe.com/dreamweaver/2012/04/phonegap-build-extension-for-dreamweaver-cs5-5.html


Original Post:

PhoneGap apps are built with HTML and JavaScript, and can be created with any IDE or text editor. You can build them in xCode or Eclipse. Did you also know that you can build PhoneGap apps within Dreamweaver, and you can even launch and debug on the iOS Simulator and Android Emulator all from within Dreamweaver?

Here’s a video of this in action from Adobe TV. After the video, we’ll walk through this process step by step.

In order to use the iOS simulator and Android emulator, you’ll need to download and install xCode and the Android SDK.   Once you’ve downloaded those, let’s focus on setting up your project within Dreamweaver.

The first thing that you need to do is create a new “site” within Dreamweaver for your PhoneGap application.  Go to the “Site” menu, and select “New Site…”.

The site setup/details dialog will be displayed.   Go ahead and give it a name and directory to contain project files and resources.

Next, we need to create the main application file.  Within Dreamweaver, go to “File”, and select “New…”.  Then select the “Page from Sample” option, sample folder “Mobile Starters”, then select the page template for “jQuery Mobile (PhoneGap)” and click “Create”.

This will create a new HTML file for the mobile project.  Go ahead and save the file you just created.   The first time this file is saved, you will be prompted to copy dependent files.  Click “Copy” to copy the dependent files into your application “site”.

Once in Dreamweaver, you can edit the HTML and JavaScript to your heart’s content.   You can take full advantage of Dreamweaver’s code view or design view, live previews, and any other features.

Once you are ready to build and deploy to the android and iOS simulators, you’ll need to setup the mobile development configuration.   First, we’ll need to configure the application frameworks.   Within Dreamweaver, go to the “Site” menu, select “Mobile Applications”, then select “Configure Application Framework”.

The “Configure Application Framework” dialog will be displayed.   Here, you’ll need to enter the full path to your Android SDK, and the path to the iOS Developer tools (xCode).

Once you have the application frameworks configured, you’ll need to configure your application’s settings.    Go to the “Site” menu, select “Mobile Applications”, then select “Application Settings…”.

Within the “Native Application Settings” dialog, you’ll need to specify your application bundle id (the unique id for the application), an application name, the author, version, and application icons.   You can also select iOS SDK versions, and which Android emulator to use.

Once you’ve configured the application settings and application frameworks, you are ready to build your app and run in the emulators.   Just go to the “Site” menu, select “Mobile Applications”, select “Build and Emulate”, then choose a device or platform.

Once you chose a platform or device, Dreamweaver will go ahead and launch the appropriate emulator or simulator, and launch the application.

If you run into issues with the Android SDK, first make sure that you are using the latest Dreamweaver SDK following the instructions at http://blog.assortedgarbage.com/2011/05/resolving-android-sdk-failed-to-install-with-dreamweaver-cs5-5/

If you still have issues with deploying to Android and see the error message below in the build log, then you are probably using the latest Android SDK that was recently released in October.

[code]Install file not specified.

‘ant install’ now requires the build target to be specified as well.

ant debug install
ant release install
ant instrument install
This will build the given package and install it.

Alternatively, you can use
ant installd
ant installr
ant installi
ant installt
to only install an existing package (this will not rebuild the package.)[/code]

The latest Android SDK introduced an additional parameter that is not yet supported by the PhoneGap integration kit within Dreamweaver. You can fix this by updating the build.xml file for the application instance to override the “install” target and add the required dependencies which make this error go away.

Go to you application build director and open the “build.xml” file. This will be inside a folder named after the bundle_ID within your target directory. You can find the target directory within the “Application Settings” dialog, as shown below:

In my case, the build directory is /Users.triceam/Destkop/com.company.phonegapsample_Android

Find the “import” node below (at the end of the file):

[xml]<import file="${sdk.dir}/tools/ant/build.xml" />[/xml]

Add to this line the attribute ‘as=”imported”‘ and a new “install” target that will override the existing “install” target as shown below. This build target will utilize the existing “install” target, and add necessary debug file dependencies to fix the build error shown above.

[xml]
<import file="${sdk.dir}/tools/ant/build.xml" as="imported" />
<!– Override the target to add the dependency –>
<target name="install"
depends="-set-debug-files,imported.install" />
[/xml]

Not only is Dreamweaver CS5.5 a best-of-breed solution for building web content, with PhoneGap support, Dreamweaver is now a best-of-breed solution for building cross-platform mobile applications as well. Using the PhoneGap integration within Dreamweaver allows you to use familiar tools, familiar development processes, and your current web development skills to build exciting new mobile applications. This enables you, as the developer or designer to focus on what matters – the application or content within your mobile scenarios.