Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - Cross-Platform Mobile Development

96 Articles
article-image-cross-platform-solution-xamarinforms-and-mvvm-architecture
Packt
22 Feb 2016
9 min read
Save for later

A cross-platform solution with Xamarin.Forms and MVVM architecture

Packt
22 Feb 2016
9 min read
In this article by George Taskos, the author of the book, Xamarin Cross Platform Development Cookbook, we will discuss a cross-platform solution with Xamarin.Forms and MVVM architecture. Creating a cross-platform solution correctly requires a lot of things to be taken under consideration. In this article, we will quickly provide you with a starter MVVM architecture showing data retrieved over the network in a ListView control. (For more resources related to this topic, see here.) How to do it... In Xamarin Studio, click on File | New | Xamarin.Forms App. Provide the name XamFormsMVVM. Add the NuGet dependencies by right-clicking on each project in the solution and choosing Add | Add NuGet Packages…. Search for the packages XLabs.Forms and modernhttpclient, and install them. Repeat step 2 for the XamFormsMVVM portable class library and add the packages Microsoft.Net.Http and Newtonsoft.Json. In the XamFormsMVVM portable class library, create the following folders: Models, ViewModels, and Views. To create a folder, right-click on the project and select Add | New Folder. Right-click on the Models folder and select Add | New File…, choose the General | Empty Interface template, name it IDataService, and click on New, and add the following code: public interface IDataService { Task<IEnumerable<OrderModel>> GetOrdersAsync (); } Right-click on the Models folder again and select Add | New File…, choose the General | Empty Class template, name it DataService, and click on New, and add the following code: [assembly: Xamarin.Forms.Dependency (typeof (DataService))] namespace XamFormsMVVM{ public class DataService : IDataService { protected const string BaseUrlAddress = @"https://api.parse.com/1/classes"; protected virtual HttpClient GetHttpClient() { HttpClient httpClient = new HttpClient(new NativeMessageHandler()); httpClient.BaseAddress = new Uri(BaseUrlAddress); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue ("application/json")); return httpClient; } public async Task<IEnumerable<OrderModel>> GetOrdersAsync () { using (HttpClient client = GetHttpClient ()) { HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, client.BaseAddress + "/Order"); requestMessage.Headers.Add("X-Parse- Application-Id", "fwpMhK1Ot1hM9ZA4iVRj49VFz DePwILBPjY7wVFy"); requestMessage.Headers.Add("X-Parse-REST- API-Key", "egeLQVTC7IsQJGd8GtRj3ttJV RECIZgFgR2uvmsr"); HttpResponseMessage response = await client.SendAsync(requestMessage); response.EnsureSuccessStatusCode (); string ordersJson = await response.Content.ReadAsStringAsync(); JObject jsonObj = JObject.Parse (ordersJson); JArray ordersResults = (JArray)jsonObj ["results"]; return JsonConvert.DeserializeObject <List<OrderModel>> (ordersResults.ToString ()); } } } } Right-click on the Models folder and select Add | New File…, choose the General | Empty Interface template, name it IDataRepository, and click on New, and add the following code: public interface IDataRepository { Task<IEnumerable<OrderViewModel>> GetOrdersAsync (); } Right-click on the Models folder and select Add | New File…, choose the General | Empty Class template, name it DataRepository, and click on New, and add the following code in that file: [assembly: Xamarin.Forms.Dependency (typeof (DataRepository))] namespace XamFormsMVVM { public class DataRepository : IDataRepository { private IDataService DataService { get; set; } public DataRepository () : this(DependencyService.Get<IDataService> ()) { } public DataRepository (IDataService dataService) { DataService = dataService; } public async Task<IEnumerable<OrderViewModel>> GetOrdersAsync () { IEnumerable<OrderModel> orders = await DataService.GetOrdersAsync ().ConfigureAwait (false); return orders.Select (o => new OrderViewModel (o)); } } } In the ViewModels folder, right-click on Add | New File… and name it OrderViewModel. Add the following code in that file: public class OrderViewModel : XLabs.Forms.Mvvm.ViewModel { string _orderNumber; public string OrderNumber { get { return _orderNumber; } set { SetProperty (ref _orderNumber, value); } } public OrderViewModel (OrderModel order) { OrderNumber = order.OrderNumber; } public override string ToString () { return string.Format ("[{0}]", OrderNumber); } } Repeat step 5 and create a class named OrderListViewModel.cs: public class OrderListViewModel : XLabs.Forms.Mvvm.ViewModel{ protected IDataRepository DataRepository { get; set; } ObservableCollection<OrderViewModel> _orders; public ObservableCollection<OrderViewModel> Orders { get { return _orders; } set { SetProperty (ref _orders, value); } } public OrderListViewModel () : this(DependencyService.Get<IDataRepository> ()) { } public OrderListViewModel (IDataRepository dataRepository) { DataRepository = dataRepository; DataRepository.GetOrdersAsync ().ContinueWith (antecedent => { if (antecedent.Status == TaskStatus.RanToCompletion) { Orders = new ObservableCollection<OrderViewModel> (antecedent.Result); } }, TaskScheduler. FromCurrentSynchronizationContext ()); } } Right-click on the Views folder and choose Add | New File…, select the Forms | Forms Content Page Xaml, name it OrderListView, and click on New: <?xml version="1.0" encoding="UTF-8"?> <ContentPage x_Class="XamFormsMVVM.OrderListView" Title="Orders"> <ContentPage.Content> <ListView ItemsSource="{Binding Orders}"/> </ContentPage.Content> </ContentPage> Go to XmaFormsMVVM.cs and replace the contents with the following code: public App() { if (!Resolver.IsSet) { SetIoc (); } RegisterViews(); MainPage = new NavigationPage((Page)ViewFactory. CreatePage<OrderListViewModel, OrderListView>()); } private void SetIoc() { var resolverContainer = new SimpleContainer(); Resolver.SetResolver (resolverContainer.GetResolver()); } private void RegisterViews() { ViewFactory.Register<OrderListView, OrderListViewModel>(); } Run the application, and you will get results like the following screenshots: For Android: For iOS: How it works… A cross-platform solution should share as much logic and common operations as possible, such as retrieving and/or updating data in a local database or over the network, having your logic centralized, and coordinating components. With Xamarin.Forms, you even have a cross-platform UI, but this shouldn't stop you from separating the concerns correctly; the more abstracted you are from the user interface and programming against interfaces, the easier it is to adapt to changes and remove or add components. Starting with models and creating a DataService implementation class with its equivalent interface, IDataService retrieves raw JSON data over the network from the Parse API and converts it to a list of OrderModel, which are POCO classes with just one property. Every time you invoke the GetOrdersAsync method, you get the same 100 orders from the server. Notice how we used the Dependency attribute declaration above the namespace to instruct DependencyService that we want to register this implementation class for the interface. We took a step to improve the performance of the REST client API; although we do use the HTTPClient package, we pass a delegate handler, NativeMessageHandler, when constructing in the GetClient() method. This handler is part of the modernhttpclient NuGet package and it manages undercover to use a native REST API for each platform: NSURLSession in iOS and OkHttp in Android. The IDataService interface is used by the DataRepository implementation, which acts as a simple intermediate repository layer converting the POCO OrderModel received from the server in OrderViewModel instances. Any model that is meant to be used on a view is a ViewModel, the view's model, and also, when retrieving and updating data, you don't carry business logic. Only data logic that is known should be included as data transfer objects. Dependencies, such as in our case, where we have a dependency of IDataService for the DataRepository to work, should be clear to classes that will use the component, which is why we create a default empty constructor required from the XLabs ViewFactory class, but in reality, we always invoke the constructor that accepts an IDataService instance; this way, when we unit test this unit, we can pass our mock IDataService class and test the functionality of the methods. We are using the DependencyService class to register the implementation to its equivalent IDataRepository interface here as well. OrderViewModel inherits XLabs.Forms.ViewModel; it is a simple ViewModel class with one property raising property change notifications and accepting an OrderModel instance as a dependency in the default constructor. We override the ToString() method too for a default string representation of the object, which simplifies the ListView control without requiring us, in our example, to use a custom cell with DataTemplate. The second ViewModel in our architecture is the OrderListViewModel, which inherits XLabs.Forms.ViewModel too and has a dependency of IDataRepository, following the same pattern with a default constructor and a constructor with the dependency argument. This ViewModel is responsible for retrieving a list of OrderViewModel and holding it to an ObservableCollection<OrderViewModel> instance that raises collection change notifications. In the constructor, we invoke the GetOrdersAsync() method and register an action delegate handler to be invoked on the main thread when the task has finished passing the orders received in a new ObservableCollection<OrderViewModel> instance set to the Orders property. The view of this recipe is super simple: in XAML, we set the title property which is used in the navigation bar for each platform and we leverage the built-in data-binding mechanism of Xamarin.Forms to bind the Orders property in the ListView ItemsSource property. This is how we abstract the ViewModel from the view. But we need to provide a BindingContext class to the view while still not coupling the ViewModel to the view, and Xamarin Forms Labs is a great framework for filling the gap. XLabs has a ViewFactory class; with this API, we can register the mapping between a view and a ViewModel, and the framework will take care of injecting our ViewModel into the BindingContext class of the view. When a page is required in our application, we use the ViewFactory.CreatePage class, which will construct and provide us with the desired instance. Xamarin Forms Labs uses a dependency resolver internally; this has to be set up early in the application startup entry point, so it is handled in the App.cs constructor. Run the iOS application in the simulator or device and in your preferred Android emulator or device; the result is the same with the equivalent native themes for each platform. Summary Xamarin.Forms is a great cross-platform UI framework that you can use to describe your user interface code declaratives in XAML, and it will be translated into the equivalent native views and pages with the ability of customizing each native application layer. Xamarin.Forms and MVVM are made for each other; the pattern fits naturally into the design of native cross-platform mobile applications and abstracts the view from the data easy using the built-in data-binding mechanism. Resources for Article: Further resources on this subject: Code Sharing Between iOS and Android [Article] Working with Xamarin.Android [Article] Sharing with MvvmCross [Article]
Read more
  • 0
  • 0
  • 7483

article-image-developing-your-first-cordova-application
Packt
16 Feb 2016
24 min read
Save for later

Developing Your First Cordova Application

Packt
16 Feb 2016
24 min read
In this article, you will develop, build, and deploy your first Apache Cordova application from scratch. The application you will develop is a Sound Recorder utility that you can use to record your voice or any sound and play it back. In this chapter, you will learn about the following topics: Generating your initial Apache Cordova project artifacts by utilizing the Apache Cordova Command-line Interface (CLI) Developing and building your mobile application from the initial Cordova generated code Deploying your developed mobile application to a real Android mobile device to see your application in action (For more resources related to this topic, see here.) An introduction to Cordova CLI In order to create, develop, build, and test a Cordova application, you first need to use the Cordova CLI. Using this, you can create new Apache Cordova project(s), build them on mobile platforms such as iOS, Android, Windows Phone, and so on, and run them on real devices or within emulators. Note that in this chapter, we will focus on deploying our Sound Recorder application in Android devices only. In the next chapter, we will learn how to deploy our Sound Recorder application in iOS and Windows Phone devices. Installing Apache Cordova Before installing Apache Cordova CLI, you need to make sure that you install the following software: Target platform SDK: For Android, you can download its SDK from http://developer.android.com/sdk/index.html (for other platforms, you need to download and install their corresponding SDKs) Node.js: This is accessible at http://nodejs.org and can be downloaded and installed from http://nodejs.org/download/ After installing Node.js, you should be able to run Node.js or node package manager (npm) from the command line. In order to install Apache Cordova using npm, run the following command (you can omit sudo if you are working in a Windows environment): > sudo npm install -g cordova It's worth mentioning that npm is the official package manager for Node.js and it is written completely in JavaScript. npm is a tool that allows users to install Node.js modules, which are available in the npm registry. The sudo command allows a privileged Unix user to execute a command as the super user, or as any other user, according to the sudoers file. The sudo command, by default, requires you to authenticate with a password. Once you are authenticated, you can use the command without a password, by default, for 5 minutes. After successfully installing Apache Cordova (Version 3.4.0), you should be able to execute Apache Cordova commands from the command line, for example, the following command will show you the current installed version of Apache Cordova: > cordova -version In order to execute the Cordova commands without any problem, you also need to have Apache Ant installed and configured in your operating system. You can download Apache Ant from http://ant.apache.org. The complete instructions on how to install Ant are mentioned at https://ant.apache.org/manual/install.html. Generating our Sound Recorder's initial code After installing Apache Cordova, we can start creating our Sound Recorder project by executing the following command: > cordova create soundRecorder com.jsmobile.soundrecorder SoundRecorder After successfully executing this command, you will find a message similar to the following one (note that the location path will be different on your machine): Creating a new cordova project with name "SoundRecorder" and id "com.jsmobile.soundrecorder" at location "/Users/xyz/projects/soundRecorder" If we analyze the cordova create command, we will find that its first parameter represents the path of your project. In this command, a soundRecorder directory will be generated for your project under the directory from which the cordova create command is executed. The second and third parameters are optional. The second parameter, com.jsmobile.soundrecorder, provides your project's namespace (it should be noted that in Android projects, this namespace will be translated to a Java package with this name), and the last parameter, SoundRecorder, provides the application's display text. You can edit both these values in the config.xml configuration file later, which will be illustrated soon. The following screenshot shows our SoundRecorder project's generated artifacts:   The Sound Recorder's initial structure As shown in the preceding screenshot, the generated Apache Cordova project contains the following main directories: www: This directory includes your application's HTML, JavaScript, and CSS code. You will also find the application's starting page (index.html), along with various subdirectories, which are as follows: css: This directory includes the default Apache Cordova application's CSS file (index.css) js: This directory includes the default Apache Cordova application's JavaScript file (index.js) img: This directory includes the default Apache Cordova application's logo file (logo.png) config.xml: This file contains the application configuration. The following code snippet shows the initial code of the config.xml file: <?xml version='1.0' encoding='utf-8'?> <widget id="com.jsmobile.soundrecorder" version="0.0.1" > <name>SoundRecorder</name> <description> A sample Apache Cordova application that responds to the deviceready event. </description> <author email="dev@cordova.apache.org" href="http://cordova.io"> Apache Cordova Team </author> <content src="index.html" /> <access origin="*" /> </widget> As shown in the preceding config.xml file, config.xml contains the following elements that are available on all the supported Apache Cordova platforms: The <widget> element's id attribute represents the application's namespace identifier as specified in our cordova create command, and the <widget> element's version attribute represents its full version number in the form of major.minor.patch. The <name> element specifies the application's name. The <description> and <author> elements specify the application's description and author, respectively. The <content> element (which is optional) specifies the application's starting page that is placed directly under the www directory. The default value is index.html. The <access> element(s) defines the set of external domains that the application is allowed to access. The default value is *, which means that the application is allowed to access any external server(s). Specifying the <access> element's origin to * is fine during application development, but it is considered a bad practice in production due to security concerns. Note that before moving your application to production, you should review its whitelist and declare its access to specific network domains and subdomains. There is another element that is not included in the default config.xml, and this is the <preference> element. The <preference> element(s) can be used to set the different preferences of the Cordova application and can work on all or a subset of the Apache Cordova-supported platforms. Take the example of the following code: <preference name="Fullscreen" value="true" /> If the Fullscreen preference is set to true, it means that the application will be in fullscreen mode on all Cordova-supported platforms (by default, this option is set to false). It is important to note that not all preferences work on all Cordova-supported platforms. Consider the following example: <preference name="HideKeyboardFormAccessoryBar" value="true"/> If the HideKeyboardFormAccessoryBar preference is set to true, then the additional helper toolbar, which appears above the device keyboard, will be hidden. This preference works only on iOS and BlackBerry platforms. platforms: This directory includes the application's supported platforms. After adding a new platform using Apache Cordova CLI, you will find a newly created directory that contains the platform-specific generated code under the platforms directory. The platforms directory is initially empty because we have not added any platforms yet. We will add support to the Android platform in the next step. plugins: This directory includes your application's used plugins. If you aren't already aware, a plugin is the mechanism to access the device's native functions in Apache Cordova. After adding a plugin (such as the Media plugin) to the project, you will find a newly created directory under the plugins directory, which contains the plugin code. Note that we will add three plugins in our Sound Recorder application example. merges: This directory can be used to override the common resources under the www directory. The files placed under the merges/[platform] directory will override the matching files (or add new files) under the www directory for the specified platform (the [platform] value can be iOS, Android, or any other valid supported platform). hooks: This directory contains scripts that can be used to customize Apache Cordova commands. A hook is a piece of code that executes before and/or after the Apache Cordova command runs. An insight into the www files If we look in the www directory, we will find that it contains the following three files: index.html: This file is placed under the application's www directory, and it contains the HTML content of the application page index.js: This file is placed under the www/js directory, and it contains a simple JavaScript logic that we will illustrate soon index.css: This file is placed under the www/css directory, and it contains the style classes of the HTML elements The following code snippet includes the most important part of the index.html page: <div class="app"> <h1>Apache Cordova</h1> <div id="deviceready" class="blink"> <p class="event listening">Connecting to Device</p> <p class="event received">Device is Ready</p> </div> </div> <script type="text/javascript" src="cordova.js"></script> <script type="text/javascript" src="js/index.js"></script> <script type="text/javascript"> app.initialize(); </script> The index.html page has a single div "app", which contains a child div "deviceready". The "deviceready" div has two paragraph elements, the "event listening" and "event received" paragraphs. The "event received" paragraph is initially hidden as indicated by index.css: .event.received { background-color:#4B946A; display:none; } In the index.html page, there are two main JavaScript-included files, as follows: cordova.js: This file contains Apache Cordova JavaScript APIs index.js: This file contains the application's simple logic Finally, the index.html page calls the initialize() method of the app object. Let's see the details of the app object in index.js: var app = { initialize: function() { this.bindEvents(); }, bindEvents: function() { document.addEventListener('deviceready', this.onDeviceReady, false); }, onDeviceReady: function() { app.receivedEvent('deviceready'); }, receivedEvent: function(id) { var parentElement = document.getElementById(id); var listeningElement = parentElement.querySelector('.listening'); var receivedElement = parentElement.querySelector('.received'); listeningElement.setAttribute('style', 'display:none;'); receivedElement.setAttribute('style', 'display:block;'); console.log('Received Event: ' + id); } }; The initialize() method calls the bindEvents() method, which adds an event listener for the 'deviceready' event. When the device is ready, the onDeviceReady() method is called, and this in turn calls the receivedEvent() method of the app object. In the receivedEvent() method, the "event listening" paragraph is hidden and the "event received" paragraph is shown to the user. This is to display the Device is Ready message to the user once Apache Cordova is fully loaded. It is important to note that you must not call any Apache Cordova API before the 'deviceready' event fires. This is because the 'deviceready' event fires only once Apache Cordova is fully loaded. Now you have an Apache Cordova project that has common cross-platform code, so we need to generate a platform-specific code in order to deploy our code on a real device. To generate Android platform code, you need to add the Android platform as follows: > cd soundRecorder > cordova platform add android In order to add any platform, you need to execute the cordova platform command from the application directory. Note that in order to execute the cordova platform command without problems, you need to perform the following instructions: Have Apache Ant installed and configured in your operating system as described in the Installing Apache Cordova section Make sure that the path to your Android SDK platform tools and the tools directory are added to your operating system's PATH environment variable After executing the cordova platform add command, you will find a new subdirectory Android added under the soundRecorder/platforms directory, which is added by Android. In order to build the project, use the following command: > cordova build Finally, you can run and test the generated Android project in the emulator by executing the following command: > cordova emulate android You might see the ERROR: No emulator images (avds) found message flash if no Android AVDs are available in your operating system. So, make sure you create one! The following screenshot shows our Sound Recorder application's initial screen:   It is recommended that you make your code changes in the root www directory, and not in the platforms/android/assets/www directory (especially if you are targeting multiple platforms) as the platforms directory will be overridden every time you execute the cordova build command, unless you are willing to use Apache Cordova CLI to initialize the project for a single platform only. Developing Sound Recorder application After generating the initial application code, it's time to understand what to do next. Sound Recorder functionality The following screenshot shows our Sound Recorder page:   When the user clicks on the Record Sound button, they will be able to record their voices; they can stop recording their voices by clicking on the Stop Recording button. You can see this in the following screenshot:   As shown in the following screenshot, when the user clicks on the Playback button, the recorded voice will be played back:   Sound Recorder preparation In order to implement this functionality using Apache Cordova, we need to add the following plugins using the indicated commands, which should be executed from the application directory: media: This plugin is used to record and play back sound files: > cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-media.git device: This plugin is required to access the device information: > cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-device.git file: This plugin is used to access the device's filesystem: > cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-file.git In order to apply these plugins to our Apache Cordova project, we need to run the cordova build command again from the project directory, as follows: > cordova build Sound Recorder details Now we are done with the preparation of our Sound Recorder application. Before moving to the code details, let's see the hierarchy of our Sound Recorder application, as shown in the following screenshot: The application's www directory contains the following directories: css: This directory contains the custom application CSS file(s) img: This directory contains the custom application image file(s) js: This directory contains the custom application JavaScript code jqueryMobile: This directory (which is a newly added one) contains jQuery mobile framework files Finally, the index.html file contains the application's single page whose functionality was illustrated earlier in this section. It is important to note that Apache Cordova does not require you to use a JavaScript mobile User Interface (UI) framework. However, it is recommended that you use a JavaScript mobile UI framework in addition to Apache Cordova. This is in order to facilitate building the application UI and speed up the application development process. Let's see the details of the index.html page of our Sound Recorder application. The following code snippet shows the included files in the page: <link rel="stylesheet" type="text/css" href="css/app.css" /> <link rel="stylesheet" href="jqueryMobile/jquery.mobile-1.4.0.min.css"> <script src="jqueryMobile/jquery-1.10.2.min.js"></script> <script src="jqueryMobile/jquery.mobile-1.4.0.min.js"></script> ... <script type="text/javascript" src="cordova.js"></script> <script type="text/javascript" src="js/app.js"></script> In the preceding code, the following files are included: app.css: This is the custom style file of our Sound Recorder application The files required by the jQuery mobile framework, which are: jquery.mobile-1.4.0.min.css jquery-1.10.2.min.js jquery.mobile-1.4.0.min.js cordova.js: This is the Apache Cordova JavaScript API's file app.js: This is the custom JavaScript file of our Sound Recorder application It is important to know that you can download the jQuery mobile framework files from http://jquerymobile.com/download/. The following code snippet shows the HTML content of our application's single page, whose id is "main": <div data-role="page" id="main"> <div data-role="header"> <h1>Sound Recorder</h1> </div> <div data-role="content"> <div data-role="fieldcontain"> <h1>Welcome to the Sound Recorder Application</h1> <p>Click 'Record Sound' button in order to start recording. You will be able to see the playback button once the sound recording finishes.<br/><br/></p> <input type="hidden" id="location"/> <div class="center-wrapper"> <input type="button" id="recordSound" data- icon="audio" value="Record Sound" class="center-button" data- inline="true"/> <input type="button" id="playSound" data- icon="refresh" value="Playback" class="center-button" data- inline="true"/><br/> </div> <div data-role="popup" id="recordSoundDialog" data- dismissible="false" style="width:250px"> <div data-role="header"> <h1>Recording</h1> </div> <div data-role="content"> <div class="center-wrapper"> <div id="soundDuration"></div> <input type="button" id="stopRecordingSound" value="Stop Recording" class="center-button" data- inline="true"/> </div> </div> </div> </div> </div> <div data-role="footer" data-position="fixed"> <h1>Powered by Apache Cordova</h1> </div> </div> Looking at the preceding code, our Sound Recording page ("main") is defined by setting a div's data-role attribute to "page". It has a header defined by setting a div's data-role to "header". It has content defined by setting a div's data-role to "content", which contains the recording and playback buttons. The content also contains a "recordSoundDialog" pop up, which is defined by setting a div's data-role to "popup". The "recordSoundDialog" pop up has a header and content. The pop-up content displays the recorded audio duration in the "soundDuration" div, and it has a "stopRecordingSound" button that stops recording the sound. Finally, the page has a footer defined by setting a div's data-role to "footer", which contains a statement about the application. Now, it's time to learn how we can define event handlers on the the page HTML elements and use the Apache Cordova API inside our defined event handlers to implement the application's functionality. The following code snippet shows the page initialization code: (function() { $(document).on("pageinit", "#main", function(e) { e.preventDefault(); function onDeviceReady() { $("#recordSound").on("tap", function(e) { // Action is defined here ... }); $("#recordSoundDialog").on("popupafterclose", function(event, ui) { // Action is defined here ... }); $("#stopRecordingSound").on("tap", function(e) { // Action is defined here ... }); $("#playSound").on("tap", function(e) { // Action is defined here ... }); } $(document).on('deviceready', onDeviceReady); initPage(); }); // Code is omitted here for simplicity function initPage() { $("#playSound").closest('.ui-btn').hide(); } })(); In jQuery mobile, the "pageinit" event is called once during page initialization. In this event, the event handlers are defined and the page is initialized. Note that all of the event handlers are defined after the 'deviceready' event fires. The event handlers are defined for the following: Tapping the "recordSound" button Closing the "recordSoundDailog" dialog Tapping the "stopRecordingSound" button Tapping the "playSound" button In initPage(), the "playSound" button is hidden as no voice has been recorded yet. As you noticed, in order to hide an element in jQuery mobile, you just need to call its hide() method. We can now see the details of each event handler; the next code snippet shows the "recordSound" tap event handler: var recInterval; $("#recordSound").on("tap", function(e) { e.preventDefault(); var recordingCallback = {}; recordingCallback.recordSuccess = handleRecordSuccess; recordingCallback.recordError = handleRecordError; startRecordingSound(recordingCallback); var recTime = 0; $("#soundDuration").html("Duration: " + recTime + " seconds"); $("#recordSoundDialog").popup("open"); recInterval = setInterval(function() { recTime = recTime + 1; $("#soundDuration").html("Duration: " + recTime + " seconds"); }, 1000); }); The following actions are performed in the "recordSound" tap event handler: A call to the startRecordingSound(recordingCallback) function is performed. The startRecordingSound(recordingCallback) function is a helper function that starts the sound recording process using the Apache Cordova Media API. Its recordingCallback parameter represents a JSON object, which has the recordSuccess and recordError callback attributes. The recordSuccess callback will be called if the recording operation is a success, and the recordError callback will be called if the recording operation is a failure. Then, the "recordSoundDialog" dialog is opened and its "soundDuration" div is updated every second with the duration of the recorded sound. The following code snippet shows the startRecordingSound(recordingCallback), stopRecordingSound(), and requestApplicationDirectory(callback) functions: var BASE_DIRECTORY = "CS_Recorder"; var recordingMedia; function startRecordingSound(recordingCallback) { var recordVoice = function(dirPath) { var basePath = ""; if (dirPath) { basePath = dirPath + "/"; } var mediaFilePath = basePath + (new Date()).getTime() + ".wav"; var recordingSuccess = function() { recordingCallback.recordSuccess(mediaFilePath); }; recordingMedia = new Media(mediaFilePath, recordingSuccess, recordingCallback.recordError); // Record audio recordingMedia.startRecord(); }; if (device.platform === "Android") { var callback = {}; callback.requestSuccess = recordVoice; callback.requestError = recordingCallback.recordError; requestApplicationDirectory(callback); } else { recordVoice(); } } function stopRecordingSound() { recordingMedia.stopRecord(); recordingMedia.release(); } function requestApplicationDirectory(callback) { var directoryReady = function (dirEntry) { callback.requestSuccess(dirEntry.toURL()); }; var fileSystemReady = function(fileSystem) { fileSystem.root.getDirectory(BASE_DIRECTORY, {create: true}, directoryReady); }; window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, fileSystemReady, callback.requestError); } The next section illustrates the preceding code snippet. Recording and playing the audio files back In order to record the audio files using Apache Cordova, we need to create a Media object, as follows: recordingMedia = new Media(src, mediaSuccess, mediaError); The Media object constructor has the following parameters: src: This refers to the URI of the media file mediaSuccess: This refers to the callback that will be invoked if the media operation (play/record or stop function) succeeds mediaError: This refers to the callback that will be invoked if the media operation (again a play/record or stop function) fails In order to start recording an audio file, a call to the startRecord() method of the Media object must be performed. When the recording is over, a call to stopRecord() of the Media object method must be performed. In startRecordingSound(recordingCallback), the function gets the current device platform by using device.platform, as follows: If the current platform is Android, then a call to requestApplicationDirectory(callback) is performed in order to create an application directory (if it is not already created) called "CS_Recorder" under the device's SD card root directory using the Apache Cordova File API. If the directory creation operation succeeds, recordVoice() will be called by passing the application directory path as a parameter. The recordVoice() function starts recording the sound and saves the resulting audio file under the application directory. Note that if there is no SD card in your Android device, then the application directory will be created under the app's private data directory (/data/data/[app_directory]), and the audio file will be saved under it. In the else block which refers to the other supported platforms (Windows Phone 8 and iOS), recordVoice() is called without creating an application-specific directory. At the time of writing this article, in iOS and Windows Phone 8, every application has a private directory, and applications cannot store their files in any place other than this directory, using the Apache Cordova APIs. In the case of iOS, the audio files will be stored under the tmp directory of the application's sandbox directory (the application's private directory). In the case of Windows Phone 8, the audio files will be stored under the application's local directory. Note that using the native Windows Phone 8 API (Window.Storage), you can read and write files in an SD card with some restrictions. However, until the moment you cannot do this using Apache Cordova; hopefully this capability will soon be supported by Cordova (http://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn611857.aspx). In recordVoice(), it starts creating a media file using the Media object's startRecord() function. After calling the media file's stopRecord() function and after the success of the recording operation, recordingCallback.recordSuccess will be called by recordingSuccess. The recordingCallback.recordSuccess function calls handleRecordSuccess, passing the audio file's full path mediaFilePath as a parameter. The following code snippet shows the handleRecordSuccess function: function handleRecordSuccess(currentFilePath) { $("#location").val(currentFilePath); $("#playSound").closest('.ui-btn').show(); } The handleRecordSuccess function stores the recorded audio filepath in the "location" hidden field, which is used later by the playback button, and shows the "playSound" button. In requestApplicationDirectory(callback), which is called in case of Android, it does the following: Calls window.requestFileSystem in order to request the device filesystem before performing any file operation(s) Calls fileSystem.root.getDirectory when the filesystem is ready in order to create our custom application directory When our custom application directory is created successfully, the path of the created directory, or the existing directory, is passed to recordVoice() that was illustrated earlier In the other application actions, the following code snippet shows the "stopRecordingSound" tapping and "recordSoundDialog" closing event handlers: $("#recordSoundDialog").on("popupafterclose", function(event, ui) { clearInterval(recInterval); stopRecordingSound(); }); $("#stopRecordingSound").on("tap", function(e) { $("#recordSoundDialog").popup("close"); }); function stopRecordingSound(recordingCallback) { recordingMedia.stopRecord(); recordingMedia.release(); } In the "stopRecordingSound" tapping event handler, it closes the open "recordSoundDialog" pop up. Generally, if "recordSoundDialog" is closed by the "stopRecordingSound" button's tapping action or by pressing special device keys, such as the back button in Android devices, then the recording timer stops as a result of calling clearInterval(recInterval), and then it calls the stopRecordingSound() function to stop recording the sound. The stopRecordingSound() function calls the Media object's stopRecord() method, and then releases it by calling the Media object's release() method. The following code snippet shows the "playSound" tap event handler: var audioMedia; var recordingMedia; $("#playSound").on("tap", function(e) { e.preventDefault(); var playCallback = {}; playCallback.playSuccess = handlePlaySuccess; playCallback.playError = handlePlayError; playSound($("#location").val(), playCallback); }); function playSound(filePath, playCallback) { if (filePath) { cleanUpResources(); audioMedia = new Media(filePath, playCallback.playSuccess, playCallback.playError); // Play audio audioMedia.play(); } } function cleanUpResources() { if (audioMedia) { audioMedia.stop(); audioMedia.release(); audioMedia = null; } if (recordingMedia) { recordingMedia.stop(); recordingMedia.release(); recordingMedia = null; } } In the "playSound" tap event handler, it calls the playSound(filePath, playCallback) function by passing the audio file location, which is stored in the "location" hidden field and playCallback. The playSound(filePath, playCallback) function uses the Media object's play() method to play back the saved audio file after releasing used Media objects. Note that this is a requirement to avoid running out of system audio resources. Building and running Sound Recorder application Now, after developing our application code, we can start building our application using the following cordova build command: > cordova build In order to run the application in your Android mobile or tablet, just make sure you enable USB debugging in your Android device. Then, plug your Android device into your development machine and execute the following command from the application directory: > cordova run android Congratulations! After running this command, you will see the Sound Recorder application deployed in your Android device; you can now start testing it on your real device. Summary In this article, you developed your first Apache Cordova application. You now know how to use the Apache Cordova Device API at a basic level. You also know how to use the Media and File APIs along with jQuery mobile to develop the Sound Recorder application. You now understand how to use Apache Cordova CLI in order to manage your Cordova mobile application. In addition, you know how to create a Cordova project, add a new platform (in our case, Android), build your own Cordova mobile application, and deploy your Cordova mobile application to the emulator, and most importantly, to a real device! To learn more, refer to these books: Creating Mobile Apps with jQuery Mobile (https://www.packtpub.com/web-development/creating-mobile-apps-jquery-mobile) Building Mobile Applications Using Kendo UI Mobile and ASP. NET Web API (https://www.packtpub.com/application-development/building-mobile-applications-using-kendo-ui-mobile-and-aspnet-web-api) jQuery Mobile First Look (https://www.packtpub.com/web-development/jquery-mobile-first-look) jQuery Mobile Web Development Essentials (https://www.packtpub.com/web-development/jquery-mobile-web-development-essentials-second-edition) Resources for Article: Further resources on this subject: Understanding mutability and immutability in Python, C#, and JavaScript[article] Object Detection Using Image Features in JavaScript[article] Learning Node.js for Mobile Application Development[article]
Read more
  • 0
  • 0
  • 5115

article-image-creating-our-first-app-ionic
Packt
16 Feb 2016
20 min read
Save for later

Creating Our First App with Ionic

Packt
16 Feb 2016
20 min read
There are many options for developing mobile applications today. Native applications require a unique implementation for each platform, such as iOS, Android, and Windows Phone. It's required for some use cases such as high-performance CPU and GPU processing with lots of memory consumption. Any application that does not need over-the-top graphics and intensive CPU processing could benefit greatly from a cost-effective, write once, and run everywhere HTML5 mobile implementation. In this article, we will cover: Setting up a development environment Creating a HelloWorld app via CLI Creating a HelloWorld app via Ionic Creator Copying examples from Ionic Codepen Demos Viewing the app using your web browser Viewing the app using iOS Simulator Viewing the app using Xcode for iOS Viewing the app using Genymotion for Android Viewing the app using Ionic View Customizing the app folder structure (For more resources related to this topic, see here.) For those who choose the HTML5 route, there are many great choices in this active market. Some options may be very easy to start but could be very hard to scale or could face performance problems. Commercial options are generally expensive for small developers to discover product and market fit. It's a best practice to think of the users first. There are instances where a simple responsive design website is a better choice; for example, the business has mainly fixed content with minimal updating required or the content is better off on the web for SEO purposes. Ionic has several advantages over its competitors: It's written on top of AngularJS UI performance is strong because of its use of the requestAnimationFrame() technique It offers a beautiful and comprehensive set of default styles, similar to a mobile-focused Twitter Bootstrap Sass is available for quick, easy, and effective theme customization You will go through several HelloWorld examples to bootstrap your Ionic app. This process will give you a quick skeleton to start building more comprehensive apps. The majority of apps have similar user experience flows such as tabs and a side menu. Setting up a development environment Before you create the first app, your environment must have the required components ready. Those components ensure a smooth process of development, build, and test. The default Ionic project folder is based on Cordova's. Therefore you will need the Ionic CLI to automatically add the correct platform (that is, iOS, Android, or Windows Phone) and build the project. This will ensure all Cordova plugins are included properly. The tool has many options to run your app in the browser or simulator with live reload. Getting ready You need to install Ionic and its dependencies to get started. Ionic itself is just a collection of CSS styles and AngularJS Directives and Services. It also has a command-line tool to help manage all of the technologies such as Cordova and Bower. The installation process will give you a command line to generate initial code and build the app. Ionic uses npm as the installer, which is included when installing Node.js. Please install the latest version of Node.js from http://nodejs.org/download/. You will need Cordova, ios-sim (iOS Simulator), and Ionic: $ npm install -g cordova ionic ios-sim This single command line will install all three components instead of issuing three command lines separately. The -g parameter is to install the package globally (not just in the current directory). For Linux and Mac, you may need to use the sudo command to allow system access: $ sudo npm install -g cordova ionic ios-sim There are a few common options for an integrated development environment: Xcode for iOS Eclipse or Android Studio for Android Microsoft Visual Studio Express or Visual Studio for Windows Phone Sublime Text (http://www.sublimetext.com/) for web development All of those have a free license. Sublime Text is free for non-commercial use only but you have to purchase a license if you are a commercial developer. Most frontend developers would prefer to use Sublime Text for coding HTML and JavaScript because it's very lightweight and comes with a well-supported developer community. You could code directly in Xcode, Eclipse, or Visual Studio Express, but those are somewhat heavy duty for web apps, especially when you have a lot of windows open and just need something simple to code. How to do it… If you decide to use Sublime Text, you will need Package Control (https://packagecontrol.io/installation), which is similar to a Plugin Manager. Since Ionic uses Sass, it's optional to install the Sass Syntax Highlighting package: Select Sublime Text | Preferences | Package Control: Select Package Control: Install Package. You could also just type the commands partially (that is, inst) and it will automatically select the right option. Type Sass and the search results will show one option for TextMate & Sublime Text. Select that item to install. See also There are tons of packages that you may want to use, such as Haml, JSHint, JSLint, Tag, ColorPicker, and so on. You can browse around this website: https://sublime.wbond.net/browse/popular, for more information. Creating a HelloWorld app via CLI It's quickest to start your app using existing templates. Ionic gives you three standard templates out of the box via the command line: Blank: This template has a simple one page with minimal JavaScript code. Tabs: This template has multiple pages with routes. A route URL goes to one tab or tabs. Sidemenu: This is template with left and/or right menu and with center content area. There are two other additional templates: maps and salesforce. But these are very specific to apps using Google Maps or for integration with the Salesforce.com API. How to do it… To set up the app with a blank template from Ionic, use this command: $ ionic start HelloWorld_Blank blank If you don't have an account in http://ionic.io/, the command line will ask for it. You could either press y or n to continue. It's not required to have an account at this step. If you replace blank with tabs, it will create a tab template: $ ionic start HelloWorld_Tabs tabs Similarly, this command will create an app with a sidemenu: $ ionic start HelloWorld_Sidemenu sidemenu The sidemenu template is the most common template as it provides a very nice routing example with different pages in the templates folder under /www. Additional guidance for the Ionic CLI is available on the GitHub page: https://github.com/driftyco/ionic-cli How it works… This article will show you how to quickly start your codebase and visually see the result. However, the following are the core concepts: Controller: Manage variables and models in the scope and trigger others, such as services or states. Directive: Where you manipulate the DOM, since the directive is bound to a DOM object. Service: Abstraction to manage models or collections of complex logic beside get/set required. Filter: Mainly used to process an expression in the template and return some data (that is, rounding number, add currency) by using the format {{ expression | filter }}. For example, {{amount | currency}} will return $100 if the amount variable is 100. The project folder structure will look like the following:   You will spend most of your time in the /www folder, because that's where your application logic and views will be placed. By default from the Ionic template, the AngularJS module name is called starter. You will see something like this in app.js, which is the bootstrap file for the entire app: angular.module('starter', ['ionic', 'ngCordova', 'starter.controllers', 'starter.services', 'starter.directives', 'starter.filters']) This basically declares starter to be included in ng-app="starter" of index.html. We would always have ionic and ngCordova. The other modules are required and listed in the array of string [...] as well. They can be defined in separate files. Note that if you double click on the index.html file to open in the browser, it will show a blank page. This doesn't mean the app isn't working. The reason is that the AngularJS component of Ionic dynamically loads all the .js files and this behavior requires server access via a http protocol (http://). If you open a file locally, the browser automatically treats it as a file protocol (file://) and therefore AngularJS will not have the ability to load additional .js modules to run the app properly. There are several methods of running the app that will be discussed. Creating a HelloWorld app via Ionic Creator Another way to start your app codebase is to use Ionic Creator. This is a great interface builder to accelerate your app development with a drag-and-drop style. You can quickly take existing components and position them to visualize how it should look in the app via a web-based interface. Most common components like buttons, images, checkboxes, and so on are available. Ionic Creator allows the user to export everything as a project with all .html, .css, and .js files. You should be able edit content in the /www folder to build on top of the interface. Getting ready Ionic Creator requires registration for a free account at https://creator.ionic.io/ to get started. How to do it… Create a new project called myApp:   You will see this simple screen:   The center area is your app interface. The left side gives you a list of pages. Each page is a single route. You also have access to a number of UI components that you would normally have to code by hand in an HTML file. The right panel shows the properties of any selected component. You're free to do whatever you need to do here by dropping components to the center screen. If you need to create a new page, you have to click the plus sign in the Pages panel. Each page is represented as a link, which is basically a route in AngularJS UI Router's definition. To navigate to another page (for example, after clicking a button), you can just change the Link property and point to that page. There is an Edit button on top where you can toggle back and forth between Edit Mode and Preview Mode. It's very useful to see how your app will look and behave. Once completed, click on the Export button on the top navigation. You have three options: Use the Ionic CLI tool to get the code Download the project as a zip file Review the raw HTML The best way to learn Ionic Creator is to play with it. You can add a new page and pick out any existing templates. This example shows a Login page template:   Here is how it should look out of the box:   There's more... To switch to Preview Mode where you can see the UI in a device simulator, click the switch button on the top right to enable Test:   In this mode, you should be able to interact with the components in the web browser as if it's actually deployed on the device. If you break something, it's very simple to start a new project. It's a great tool to use for "prototyping" and to get initial template or project scaffolding. You should continue to code in your regular IDE for the rest of the app. Ionic Creator doesn't do everything for you, yet. For example, if you want to access specific Cordova plugin features, you have to write that code separately. Also, if you want to tweak the interface outside of what is allowed within Ionic Creator, it will also require specific modifications to the .html and .css files. Copying examples from Ionic Codepen Demos Sometimes it's easier to just get snippets of code from the example library. Ionic Codepen Demos (http://codepen.io/ionic/public-list/) is a great website to visit. Codepen.io is a playground (or sandbox) to demonstrate and learn web development. There are other alternatives such as http://plnkr.com or http://jsfiddle.com. It's just a developer's personal preference which one to choose. However, all Ionic's demos are already available on Codepen, where you can experiment and clone to your own account. http://plnkr.com has an existing AngularJS boilerplate and could be used to just practice specific AngularJS areas because you can copy the link of sample code and post on http://stackoverflow.com/ if you have questions. How to do it… There are several tags of interest to browse through if you want specific UI component examples: You don't need a Codepen account to view. However, if there is a need to save a custom pen and share with others, free registration will be required. The Ionic Codepen Demos site has more collections of demos comparing to the CLI. Some are based on a nightly build of the platform so they could be unstable to use. There's more... You can find the same side menu example on this site: Navigate to http://codepen.io/ionic/public-list/ from your browser. Select Tag: menus and then click on Side Menu and Navigation: Nightly. Change the layout to fit a proper mobile screen by clicking on the first icon of the layout icons row on the bottom right of the screen. Viewing the app using your web browser In order to "run" the web app, you need to turn your /www folder into a web server. Again there are many methods to do this and people tend to stick with one or two ways to keep things simple. A few other options are unreliable such as Sublime Text's live watch package or static page generator (for example, Jekyll, Middleman App, and so on). They are slow to detect changes and may freeze your IDE so these won't be mentioned here. Getting ready The recommended method is to use the ionic serve command line. It basically launches an HTTP server so you can open your app in a desktop browser. How to do it… First you need to be in the project folder. Let's assume it is the Side Menu HelloWorld: $ cd HelloWorld_Sidemenu From there, just issue the simple command line: $ ionic serve  That's it! There is no need to go into the /www folder or figure out which port to use. The command line will provide these options while the web server is running: The most common option to use here is r to restart or q to quit when you are done. There is an additional step to view the app with the correct device resolution: Install Google Chrome if it's not already on your computer. Open the link (for example, http://localhost:8100/#/app/playlists) from ionic serve in Google Chrome. Turn on Developer Tools. For example, in Mac's Google Chrome, select View | Developer | Developer Tools: Click on the small mobile icon in the Chrome Developer Tools area: There will be a long list of devices to pick from: After selecting a device, you need to refresh the page to ensure the UI is updated. Chrome should give you the exact view resolution of the device. Most developers would prefer to use this method to code as you can debug the app using Chrome Developer Tools. It works exactly like any web application. You can create breakpoints or output variables to the console. How it works... Note that ionic serve is actually watching everything under the /www folder except the JavaScript modules in the /lib folder. This makes sense because there is no need for the system to scan through every single file when the probability for it to change is very small. People don't code directly in the /lib folder but only update when there is a new version of Ionic. However, there is some flexibility to change this. You can specify a watchPatterns property in the ionic.project file located in your project root to watch (or not watch) for specific changes: { "name": "myApp", "app_id": "", "watchPatterns": [ "www/**/*", "!www/css/**/*", "your_folder_here/**/*" ] } While the web server is running, you can go back to the IDE and continue coding. For example, let's open the playlists.html file under /www/templates and change the first line to this: <ion-view view-title="Updated Playlists"> Go back to the web browser where Ionic opened the new page; the app interface will change the title bar right away without requiring you to refresh the browser. This is a very nice feature when there is a lot of back and between code changes and allows checking on how it works or looks in the app instantly. Viewing the app using iOS Simulator So far you have been testing the web-app portion of Ionic. In order to view the app in the simulator, follow the next steps. How to do it... Add the specific platform using: $ ionic platform add ios Note that you need to do the "platform add" before building the app. The last step is to emulate the app: $ ionic emulate ios Viewing the app using Xcode for iOS Depending on personal preference, you may find it more convenient to just deploy the app using ionic ios --device on a regular basis. This command line will push the app to your physical device connected via USB without ever running Xcode. However, you could run the app using Xcode (in Mac), too. How to do it... Go to the /platforms/ios folder. Look for the folder with .xcodeproj and open in Xcode. Click on the iOS Device icon and select your choice of iOS Simulator. Click on the Run button and you should be able to see the app running in the simulator. There's more... You can connect a physical device via a USB port and it will show up in the iOS Device list for you to pick. Then you can deploy the app directly on your device. Note that iOS Developer Membership is required for this. This method is more complex than just viewing the app via a web browser. However, it's a must when you want to test your code related to device features such as camera or maps. If you change code in the /www folder and want to run it again in Xcode, you have to do ionic build ios first, because the running code is in the Staging folder of your Xcode project: For debugging, the Xcode Console can output JavaScript logs as well. However, you could use the more advanced features of Safari's Web Inspector (which is similar to Google Chrome's Developer Tools) to debug your app. Note that only Safari can debug a web app running on a connected physical iOS device because Chrome does not support this on a Mac. It's simple to enable this capability: Allow remote debugging for an iOS device by going to Settings | Safari | Advanced and enable Web Inspector. Connect the physical iOS device to your Mac via USB and run the app. Open the Safari browser. Select Develop, click on your device's name (or iOS Simulator), and click on index.html. Note: If you don't see the Develop menu in Safari, you need to navigate to menu Preferences | Advanced and check on Show Develop menu in menu bar. Safari will open a new console just for that specific device just as it's running within the computer's Safari. Viewing the app using Genymotion for Android Although it's possible to install the Google Android simulator, many developers have inconsistent experiences on a Mac computer. There are many commercial and free alternatives that offer more convenience and a wide range of device support. Genymotion provides some unique advantages such as allowing users to switch Android model and version, supporting networking from within the app, and allowing SD card simulation. You will learn how to set up an Android developer environment (on a Mac in this case) first. Then you will install and configure Genymotion for mobile app development. How to do it... The first step is to set up the Android environment properly for development. Download and install Android Studio from https://developer.android.com/sdk/index.html. Run Android Studio. You need to install all required packages such as the Android SDK. Just click on Next twice at the Setup Wizard screen and select the Finish button to start packages installation. After installation is complete, you need to install additional packages and other SDK versions. At the Quick Start screen, select Configure: Then select SDK Manager: It's a good practice to install a previous version such as Android 5.0.1 and 5.1.1. You may also want to install all Tools and Extras for later use. Select the Install packages... button. Check the box on Accept License and click on Install. The SDK Manager will give you SDK Path on the top. Make a copy of this path because you need to modify the environment path. Go to Terminal and type: $ touch ~/.bash_profile; open ~/.bash_profile It will open a text editor to edit your bash profile file. Insert the following line where /YOUR_PATH_TO/android-sdk should be the SDK Path that you copied earlier: export ANDROID_HOME=/YOUR_PATH_TO/android-sdk export PATH=$ANDROID_HOME/platform-tools:$PATH export PATH=$ANDROID_HOME/tools:$PATH Save and close that text editor. Go back to Terminal and type: $ source ~/.bash_profile $ echo $ANDROID_HOME You should see the output as your SDK Path. This verifies that you have correctly configured the Android developer environment. The second step is to install and configure Genymotion. Download and install Genymotion and Genymotion Shell from http://Genymotion.com. Run Genymotion. Select the Add button to start adding a new Android device. Select a device you want to simulate. In this case, let's select Samsung Galaxy S5: You will see the device being added to "Your virtual devices". Click on that device: Then click on Start. The simulator will take a few seconds to start and will show another window. This is just a blank simulator without your app running inside yet. Run Genymotion Shell. From Genymotion Shell, you need to get a device list and keep the IP address of the device attached, which is Samsung Galaxy S5. Type devices list: Type adb connect 192.168.56.101 (or whatever the IP address was you saw earlier from the devices list command line). Type adb devices to confirm that it is connected. Type ionic platform add android to add Android as a platform for your app. Finally, type ionic run android. You should be able to see the Genymotion window showing your app. Although there are many steps to get this working, it's a lot less likely that you will have to go through the same process again. Once your environment is set up, all you need to do is to leave Genymotion running while writing code. If there is a need to test the app in different Android devices, it's simple just to add another virtual device in Genymotion and connect to it. Summary In this article, we learned how to create your first ionic App. We also covered various ways in which we can view the App on Various platforms, that is, web browser, iOS Simulator, Xcode, Genymotion . You can also refer the following books on the similar topics: Learning Ionic: https://www.packtpub.com/application-development/learning-ionic Getting Started with Ionic: https://www.packtpub.com/application-development/getting-started-ionic Ionic Framework By Example: https://www.packtpub.com/application-development/ionic-framework-example Resources for Article: Further resources on this subject: Directives and Services of Ionic [article] First Look at Ionic [article] Ionic JS Components [article]
Read more
  • 0
  • 0
  • 9009

article-image-creating-grids-panels-and-other-widgets
Packt
10 Feb 2016
6 min read
Save for later

Creating Grids, Panels, and other Widgets

Packt
10 Feb 2016
6 min read
In this article by Raymond Camden, author of the book jQuery Mobile Web Development Essentials – Third Edition, we will look at dialogs, grids, and other widgets. While jQuery mobile provides great support for them, you get even more UI controls within the framework. In this article, we will see how to layout content with grids and make responsive grids. (For more resources related to this topic, see here.) Laying out content with grids Grids are one of the few features of jQuery mobile that do not make use of particular data attributes. Instead, you work with grids simply by specifying CSS classes for your content. Grids come in four flavors: two-column, three-column, four-column, and five-column grids. You will probably not want to use the five-column one on a phone device. Save that for a tablet instead. You begin a grid with a div block that makes use of the class ui-grid-X, where X will either be a, b, c, or d. ui-grid-a represents a two-column grid. The ui-grid-b class is a three-column grid. You can probably guess what c and d create. So, to begin a two-column grid, you would wrap your content with the following: <div class="ui-grid-a">   Content </div> Within the div tag, you then use div for each cell of the content. The class for grid calls begins with ui-block-X, where X goes from a to d. The ui-block-a class would be used for the first cell, ui-block-b for the next, and so on. This works much like the HTML tables. Putting it together, the following code snippet demonstrates a simple two-column grid with two cells of content: <div class="ui-grid-a">   <div class="ui-block-a">Left</div>   <div class="ui-block-b">Right</div> </div> The text within a cell will automatically wrap. Listing 7-1 demonstrates a simple grid with a large amount of text in one of the columns:   In the mobile browser, you can clearly see the two columns: If the text in these divs seems a bit close together, there is a simple fix for that. In order to add a bit more space between the content of the grid cells, you can add a class to your main div that specifies ui-content. This tells jQuery mobile to pad the content a bit. For example: <div class="ui-grid-a ui-content"> This small change modifies the previous screenshot like the following: Listing 7-1: test1.html <div data-role="page" id="first">       <div data-role="header">         <h1>Grid Test</h1>       </div>       <div role="main" class="ui-content">         <div class="ui-grid-a">           <div class="ui-block-a">           <p>           This is my left hand content. There won't be a lot of           it.           </p>           </div>           <div class="ui-block-b">             <p>               This is my right hand content. I'm going to fill it               with some dummy text.             </p>             <p>               Bacon ipsum dolor sit amet andouille capicola spare               ribs, short loin venison sausage prosciutto               turkey flank frankfurter pork belly short ribs.               chop, pancetta turkey bacon short ribs ham flank               pork belly. Tongue strip steak short ribs tail           </p>           </div>         </div>       </div>     </div> Working with other types of grids then is simply a matter of switching to the other classes. For example, a four-column grid would be set up similar to the following code snippet: <div class="ui-grid-c">   <div class="ui-block-a">1st cell</div>   <div class="ui-block-b">2nd cell</div>   <div class="ui-block-c">3rd cell</div> </div> Again, keep in mind your target audience. Anything over two columns may be too thin on a mobile phone. To create multiple rows in a grid, you need to simply repeat the blocks. The following code snippet demonstrates a simple example of a grid with two rows of cells: <div class="ui-grid-a">   <div class="ui-block-a">Left Top</div>   <div class="ui-block-b">Right Top</div>   <div class="ui-block-a">Left Bottom</div>   <div class="ui-block-b">Right Bottom</div> </div> Notice that there isn't any concept of a row. jQuery mobile handles knowing that it should create a new row when the block starts over with the one marked ui-block-a. The following code snippet, Listing 7-2, is a simple example: Listing 7-2:test2.html <div data-role="page" id="first">       <div data-role="header">         <h1>Grid Test</h1>       </div>       <div role="main" class="ui-content">         <div class="ui-grid-a">           <div class="ui-block-a">             <p>               <img src="ray.png">             </p>           </div>           <div class="ui-block-b">           <p>           This is Raymond Camden. Here is some text about him. It           may wrap or it may not but jQuery Mobile will make it          look good. Unlike Ray!           </p>           </div>           <div class="ui-block-a">             <p>               This is Scott Stroz. Scott Stroz is a guy who plays               golf and is really good at FPS video games.             </p>           </div>           <div class="ui-block-b">             <p>               <img src="scott.png">             </p>           </div>         </div>       </div>     </div> The following screenshot shows the result: Making responsive grids Earlier we mentioned that complex grids may not work depending on the size or your targeted devices. A simple two-column grid is fine, but the larger grids would render well on tablets only. Luckily, there's a simple solution for it. jQuery mobile's latest updates include much better support for responsive design. Let's consider a simple example. Here is a screenshot of a web page using a four-column grid: It is readable for sure, but it is a bit dense. By making use of responsive design, we could handle the different sizes intelligently using the same basic HTML. jQuery mobile enables a simple solution for this by adding the class ui-responsive to an existing grid. Here is an example: <div class="ui-grid-c ui-responsive"> By making this one small change, look how the phone version of our page changes: The four-column layout now is a one-column layout instead. If viewed in a tablet, the original four-column design will be preserved. Summary In this article, you learned more about how jQuery mobile enhances basic HTML to provide additional layout controls to our mobile pages. With grids, you learned a new way to easily layout content in columns. Resources for Article:   Further resources on this subject: Classes and Instances of Ember Object Model [article] Introduction to Akka [article] CoreOS Networking and Flannel Internals [article]
Read more
  • 0
  • 0
  • 2196

article-image-creating-adaptive-application-layout
Packt
14 Jan 2016
17 min read
Save for later

Creating an Adaptive Application Layout

Packt
14 Jan 2016
17 min read
In this article by Jim Wilson, the author of the book, Creating Dynamic UI with Android Fragments - Second Edition, we will see how to create an adaptive application layout. (For more resources related to this topic, see here.) In our application, we'll leave the wide-display aspect of the program alone because static layout management is working fine there. We work on the portrait-oriented handset aspect of the application. For these devices, we'll update the application's main activity to dynamically switch between displaying the fragment containing the list of books and the fragment displaying the selected book's description. Updating the layout to support dynamic fragments Before we write any code to dynamically manage the fragments within our application, we first need to modify the activity layout resource for portrait-oriented handset devices. This resource is contained in the activity_main.xml layout resource file that is not followed by (land) or (600dp). The layout resource currently appears as shown here: <LinearLayout android_orientation="vertical" android_layout_width="match_parent" android_layout_height="match_parent" > <!-- List of Book Titles --> <fragment android_layout_width="match_parent" android_layout_height="0dp" android_layout_weight="1" android_name="com.jwhh.fragments.BookListFragment2" android_id="@+id/fragmentTitles" tools_layout="@layout/fragment_book_list"/> </LinearLayout> We need to make two changes to the layout resource. The first is to add an id attribute to the LinearLayout view group so that we can easily locate it in code. The other change is to completely remove the fragment element. The updated layout resource now contains only the LinearLayout view group, which includes an id attribute value of @+id/layoutRoot. The layout resource now appears as shown here: <LinearLayout android_id="@+id/layoutRoot" android_orientation="vertical" android_layout_width="match_parent" android_layout_height="match_parent" > </LinearLayout> We still want our application to initially display the book list fragment, so removing the fragment element may seem like a strange change, but doing so is essential as we move our application to dynamically manage the fragments. We will eventually need to remove the book list fragment to replace it with the book description fragment. If we were to leave the book list fragment in the layout resource, our attempt to dynamically remove it later would silently fail. Only dynamically added fragments can be dynamically removed. Attempting to dynamically remove a fragment that was statically added with the fragment element in a layout resource will silently fail. Adapting to device differences When our application is running on a portrait-oriented handset device, the activity needs to programmatically load the fragment containing the book list. This is the same Fragment class, BookListFragment2, we were previously loading with the fragment element in the activity_main.xml layout resource file. Before we load the book list fragment, we first need to determine whether we're running on a device that requires dynamic fragment management. Remember that, for the wide-display devices, we're going to leave the static fragment management in place. There'll be a couple of places in our code where we'll need to take different logic paths depending on which layout we're using. So we'll need to add a boolean class-level field to the MainActivity class in which we can store whether we're using dynamic or static fragment management: boolean mIsDynamic; We can interrogate the device for its specific characteristics such as screen size and orientation. However, remember that much of our previous work was to configure our application to take the advantage of the Android resource system to automatically load the appropriate layout resources based on the device characteristics. Rather than repeating those characteristics checks in code, we can instead simply include the code to determine which layout resource was loaded. The layout resource for wide-display devices we created earlier, activity_main_wide.xml, statically loads both the book list fragment and the book description fragment. We can include in our activity's onCreate method code to determine whether the loaded layout resource includes one of those fragments as shown here: public class MainActivity extends Activity implements BookListFragment.OnSelectedBookChangeListener { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main_dynamic); // Get the book description fragment FragmentManager fm = getFragmentManager(); Fragment bookDescFragment = fm.findFragmentById(R.id.fragmentDescription); // If not found than we're doing dynamic mgmt mIsDynamic = bookDescFragment == null || !bookDescFragment.isInLayout(); } // Other members elided for clarity } When the call to the setContentView method returns, we know that the appropriate layout resource for the current device has been loaded. We then use the FragmentManager instance to search for the fragment with an id value of R.id.fragmentDescription that is included in the layout resource for wide-display devices, but not the layout resource for portrait-oriented handsets. A return value of null indicates that the fragment was not loaded and we are, therefore, on a device that requires us to dynamically manage the fragments. In addition to the test for null, we also include the call to the isInLayout method to protect against one special case scenario. In the scenario where the device is in a landscape layout and then rotated to portrait, a cached instance to the fragment identified by R.id.fragmentDescription may still exist even though in the current orientation, the activity is not using the fragment. By calling the isInLayout method, we're able to determine whether the returned reference is part of the currently loaded layout. With this, our test to set the mIsDynamic member variable effectively says that we'll set mIsDynamic to true when the R.id.fragmentDescription fragment is not found (equals null), or it's found but is not a part of the currently loaded layout (!bookDescFragment.isInLayout). Dynamically loading a fragment at startup Now that we're able to determine whether or not dynamically loading the book list fragment is necessary, we add the code to do so to our onCreate method as shown here: protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main_dynamic); // Get the book description fragment FragmentManager fm = getFragmentManager(); Fragment bookDescFragment = fm.findFragmentById(R.id.fragmentDescription); // If not found than we're doing dynamic mgmt mIsDynamic = bookDescFragment == null || !bookDescFragment.isInLayout(); // Load the list fragment if necessary if (mIsDynamic) { // Begin transaction FragmentTransaction ft = fm.beginTransaction(); // Create the Fragment and add BookListFragment2 listFragment = new BookListFragment2(); ft.add(R.id.layoutRoot, listFragment, "bookList"); // Commit the changes ft.commit(); } } Following the check to determine whether we're on a device that requires dynamic fragment management, we include FragmentTransaction to add an instance of the BookListFragment2 class to the activity as a child of the LinearLayout view group identified by the id value R.id.layoutRoot. This code capitalizes on the changes we made to the activity_main.xml resource file by removing the fragment element and including an id value on the LinearLayout view group. Now that we're dynamically loading the book list, we're ready to get rid of that other activity. Transitioning between fragments As you'll recall, whenever the user selects a book title within the BookListFragment2 class, the fragment notifies the main activity by calling the MainActivity.onSelectedBookChanged method passing the index of the selected book. The onSelectedBookChanged method currently appears as follows: public void onSelectedBookChanged(int bookIndex) { FragmentManager fm = getFragmentManager(); // Get the book description fragment BookDescFragment bookDescFragment = (BookDescFragment) fm.findFragmentById(R.id.fragmentDescription); // Check validity of fragment reference if(bookDescFragment == null || !bookDescFragment.isVisible()){ // Use activity to display description Intent intent = new Intent(this, BookDescActivity.class); intent.putExtra("bookIndex", bookIndex); startActivity(intent); } else { // Use contained fragment to display description bookDescFragment.setBook(bookIndex); } } In the current implementation, we use a technique similar to what we did in the onCreate method to determine which layout is loaded; we try to find the book description fragment within the currently loaded layout. If we find it, we know the current layout includes the fragment, so we go ahead and set the book description directly on the fragment. If we don't find it, we call the startActivity method to display the activity that contains the book description fragment. Starting a separate activity to handle the interaction with the BookListFragment2 class unnecessarily adds complexity to our program. Doing so requires that we pass data from one activity to another, which can sometimes be complex, especially if there are a large number of values or some of those values are object types that require additional coding to be passed in an Intent instance. More importantly, using a separate activity to manage the interaction with the BookListFragment2 class results in redundant work due to the fact that we already have all the code necessary to interact with the BookListFragment2 class in the MainActivity class. We'd prefer to handle the interaction with the BookListFragment2 class consistently in all cases. Eliminating redundant handling To eliminate this redundant handling, we start by stripping any code in the current implementation that deals with starting an activity. We can also avoid repeating the check for the book description fragment because we performed that check earlier in the onCreate method. Instead, we can now check the mIsDynamic class-level field to determine the proper handling. With that in mind, now we can initially modify the onSelectedBookChanged method to look like the following code: public void onSelectedBookChanged(int bookIndex) { BookDescFragment bookDescFragment; FragmentManager fm = getFragmentManager(); // Check validity of fragment reference if(mIsDynamic) { // Handle dynamic switch to description fragment } else { // Use the already visible description fragment bookDescFragment = (BookDescFragment) fm.findFragmentById(R.id.fragmentDescription); bookDescFragment.setBook(bookIndex); } } We now check the mIsDynamic member field to determine the appropriate code path. We still have some work to do if it turns out to be true, but in the case of it being false, we can simply get a reference to the book description fragment that we know is contained within the current layout and set the book index on it much like we were doing before. Creating the fragment on the fly In the case of the mIsDynamic field being true, we can display the book description fragment by simply replacing the book list fragment we added in the onCreate method with the book description fragment using the code shown here: FragmentTransaction ft = fm.beginTransaction(); bookDescFragment = new BookDescFragment(); ft.replace(R.id.layoutRoot, bookDescFragment, "bookDescription"); ft.addToBackStack(null); ft.setCustomAnimations( android.R.animator.fade_in, android.R.animator.fade_out); ft.commit(); Within FragmentTransaction, we create an instance of the BookDescFragment class and call the replace method passing the ID of the same view group that contains the BookListFragment2 instance that we added in the onCreate method. We include a call to the addToBackStack method so that the back button functions correctly, allowing the user to tap the back button to return to the book list. The code includes a call to the FragmentTransaction class' setCustomAnimations method that creates a fade effect when the user switches from one fragment to another. Managing asynchronous creation We have one final challenge that is to set the book index on the dynamically added book description fragment. Our initial thought might be to simply call the BookDescFragment class' setBook method after we create the BookDescFragment instance, but let's first take a look at the current implementation of the setBook method. The method currently appears as follows: public void setBook(int bookIndex) { // Lookup the book description String bookDescription = mBookDescriptions[bookIndex]; // Display it mBookDescriptionTextView.setText(bookDescription); } The last line of the method attempts to set the value of mBookDescriptionTextView within the fragment, which is a problem. Remember that the work we do within a FragmentTransaction class is not immediately applied to the user interface. Instead, as we discussed earlier in this chapter, in the deferred execution of transaction changes section, the work within the transaction is performed sometime after the completion of the call to the commit method. Therefore, the BookDescFragment instance's onCreate and onCreateView methods have not yet been called. As a result, any views associated with the BookDescFragment instance have not yet been created. An attempt to call the setText method on the BookDescriptionTextView instance will result in a null reference exception. One possible solution is to modify the setBook method to be aware of the current state of the fragment. In this scenario, the setBook method checks whether the BookDescFragment instance has been fully created. If not, it will store the book index value in the class-level field and later automatically set the BookDescriptionTextView value as part of the creation process. Although there may be some scenarios that warrant such a complicated solution, fragments give us an easier one. The Fragment base class includes a method called setArguments. With the setArguments method, we can attach data values, otherwise known as arguments, to the fragment that can then be accessed later in the fragment lifecycle using the getArguments method. Much like we do when associating extras with an Intent instance, a good practice is to define constants on the target class to name the argument values. It is also a good programming practice to provide a constant for an argument default value in the case of non-nullable types such as integers as shown here: public class BookDescFragment extends Fragment { // Book index argument name public static final String BOOK_INDEX = "book index"; // Book index default value private static final int BOOK_INDEX_NOT_SET = -1; // Other members elided for clarity } If you used Android Studio to generate the BookDescFragment class, you'll find that the ARG_PARAM1 and ARG_PARAM2 constants are included in the class. Android Studio includes these constants to provide examples of how to pass values to fragments just as we're discussing now. Since we're adding our own constant declarations, you can delete the ARG_PARAM1 and ARG_PARAM2 constants from the BookDescFragment class and also the lines in the generated BookDescFragment.onCreate and BookDescFragment.newInstance methods that reference them. We'll use the BOOK_INDEX constant to get and set the book index value and the BOOK_INDEX_NOT_SET constant to indicate whether the book index argument has been set. To simplify the process of creating the BookDescFragment instance and passing it the book index value, we'll add a static factory method named newInstance to the BookDescFragment class that appears as follows: public static BookDescFragment newInstance(int bookIndex) { BookDescFragment fragment = new BookDescFragment(); Bundle args = new Bundle(); args.putInt(BOOK_INDEX, bookIndex); fragment.setArguments(args); return fragment; } The newInstance methods start by creating an instance of the BookDescFragment class. It then creates an instance of the Bundle class, stores the book index in the Bundle instance, and then uses the setArguments method to attach it to the BookDescFragment instance. Finally, the newInstance method returns the BookDescFragment instance. We'll use this method shortly within the MainActivity class to create our BookDescFragment instance. If you used Android Studio to generate the BookDescFragment class, you'll find that most of the newInstance method is already in place. The only change you'll have to make is replace the two lines that referenced the ARG_PARAM1 and ARG_PARAM2 constants you deleted with the call to the args.putInt method shown in the preceding code. We can now update the BookDescFragment class' onCreateView method to look for arguments that might be attached to the fragment. Before we make any changes to the onCreateView method, let's look at the current implementation that appears as follows: public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View viewHierarchy = inflater.inflate( R.layout.fragment_book_desc, container, false); // Load array of book descriptions mBookDescriptions = getResources().getStringArray(R.array.bookDescriptions); // Get reference to book description text view mBookDescriptionTextView = (TextView) viewHierarchy.findViewById(R.id.bookDescription); return viewHierarchy; } As the onCreateView method is currently implemented, it simply inflates the layout resource, loads the array containing the book descriptions, and caches a reference to the TextView instance where the book description is loaded. We can now update the method to look for and use a book index that might be attached as an argument. The updated method appears as follows: public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View viewHierarchy = inflater.inflate( R.layout.fragment_book_desc, container, false); // Load array of book descriptions mBookDescriptions = getResources().getStringArray(R.array.bookDescriptions); // Get reference to book description text view mBookDescriptionTextView = (TextView) viewHierarchy.findViewById(R.id.bookDescription); // Retrieve the book index if attached Bundle args = getArguments(); int bookIndex = args != null ? args.getInt(BOOK_INDEX, BOOK_INDEX_NOT_SET) : BOOK_INDEX_NOT_SET; // If we find the book index, use it if (bookIndex != BOOK_INDEX_NOT_SET) setBook(bookIndex); return viewHierarchy; } Just before we return the fragment's view hierarchy, we call the getArguments method to retrieve any arguments that might be attached. The arguments are returned as an instance of the Bundle class. If the Bundle instance is non-null, we call the Bundle class' getInt method to retrieve the book index and assign it to the bookIndex local variable. The second parameter of the getInt method, BOOK_INDEX_NOT_SET, is returned if the fragment happens to have arguments attached that do not include the book index. Although this should not normally be the case, being prepared for any such an unexpected circumstance is a good idea. Finally, we check the value of the bookIndex variable. If it contains a book index, we call the fragment's setBook method to display it. Putting it all together With the BookDescFragment class now including support for attaching the book index as an argument, we're ready to fully implement the main activity's onSelectedBookChanged method to include switching to the BookDescFragment instance and attaching the book index as an argument. The method now appears as follows: public void onSelectedBookChanged(int bookIndex) { BookDescFragment bookDescFragment; FragmentManager fm = getFragmentManager(); // Check validity of fragment reference if(mIsDynamic){ // Handle dynamic switch to description fragment FragmentTransaction ft = fm.beginTransaction(); // Create the fragment and pass the book index bookDescFragment = BookDescFragment.newInstance(bookIndex); // Replace the book list with the description ft.replace(R.id.layoutRoot, bookDescFragment, "bookDescription"); ft.addToBackStack(null); ft.setCustomAnimations( android.R.animator.fade_in, android.R.animator.fade_out); ft.commit(); } else { // Use the already visible description fragment bookDescFragment = (BookDescFragment) fm.findFragmentById(R.id.fragmentDescription); bookDescFragment.setBook(bookIndex); } } Just as before, we start with the check to see whether we're doing dynamic fragment management. Once we determine that we are, we start the FragmentTransaction instance and create the BookDescFragment instance. We then create a new Bundle instance, store the book index into it, and then attach the Bundle instance to the BookDescFragment instance with the setArguments method. Finally, we put the BookDescFragment instance into place as the current fragment, take care of the back stack, enable animation, and complete the transaction. Everything is now complete. When the user selects a book title from the list, the onSelectedBookChanged method gets called. The onSelectedBookChanged method then creates and displays the BookDescFragment instance with the appropriate book index attached as an argument. When the BookDescFragment instance is ultimately created, its onCreateView method will then retrieve the book index from the arguments and display the appropriate description. Summary In this article, we saw that using the FragmentTransaction class, we're able to dynamically switch between individual fragments within an activity, eliminating the need to create a separate activity class for each screen in our application. This helps to prevent the proliferation of unnecessary activity classes, better organize our applications, and avoid the associated increase in complexity. Resources for Article: Further resources on this subject: UI elements and their implementation[article] Creating Dynamic UI with Android Fragments[article] Android Fragmentation Management[article]
Read more
  • 0
  • 0
  • 2998

article-image-first-look-ionic
Packt
13 Jan 2016
9 min read
Save for later

First Look at Ionic

Packt
13 Jan 2016
9 min read
In this article, Sani Yusuf the author of the book Ionic Framework By Example, explains how Ionic framework lets you build Hybrid mobile applications with web technologies such as HTML5, CSS, and JavaScript. But that is not where it stops. Ionic provides you with components that you use to build native-like features for your mobile applications. Think of Ionic as the SDK for making your Hybrid mobile application. Most of the feature you have on native apps such as Modals, Gestures, and POP-UPs etc are all provided to you by Ionic and can be easily extended for new features or customized to suit your needs. Ionic itself does not grant you the ability to communicate with device features such as GPS and Camera, instead it works side by side with Cordova to achieve this. Another great feature of Ionic is how loosely coupled all its components are. You can decide to use only some of Ionic on an already existing hybrid application if you wish to do so. The Ionic framework is built with AngularJS, which is arguably the most well tested and widely used JavaScript framework out there. This feature is particularly powerful as it gives you all the goodness of angular as a part of any Ionic app you develop. In the past, architecting hybrid application proved to be difficult, but with angular, we can create our mobile application using the Single Page Application (SPA) technique. Angular also makes it really easy to organize your application for development and working across teams whiles providing you the possibility of easily adding custom features or libraries. (For more resources related to this topic, see here.) Features of Ionic Ionic provides you with a lot of cool neat features and tricks that help you create beautiful and well-functional hybrid apps in no time. The features of Ionic come under three categories: CSS features JavaScript features Ionic CLI CSS features To start off with, Ionic comes in stock with a great CSS library that provides you with some boilerplate styles. This Ionic CSS styles are generated with SASS, a CSS pre-processor for more advanced CSS style manipulation. Some of the cool CSS features that come built-in with Ionic include: Buttons Cards Header and Footers Lists Forms Elements Grid System All these features and many more come already provided for you and are easily customizable. They also have the same look and feel that native equivalents have, so you will not have to do any editing to make them look like native components. The JavaScript features The JavaScript features are at the heart of the Ionic framework and are essential for building Ionic apps. They also consist of other features that let you do things from under hood like customize your application or even provide you with helper functions that you can use to make developing your app more pleasant. A lot of these JavaScript features actually exist as HTML custom elements that make it easy to declaratively use these features. Some of these features include: Modal Slide-Box Action Sheet Side Menu Tabs Complex Lists Collection Repeat All the JavaScript features of Ionic are built with Angular, and most can be easily plugged in as angular directives. Each of them also perform different actions that help you achieve specific functions and are all documented in the Ionic website. The Ionic CLI This is the final part that makes up the three major arms of the Ionic framework. The Ionic CLI is a very important tool that lets you use the issue Ionic commands via the command line/terminal. It is also with the Ionic CLI that we get access to some Ionic features that make our app development process more streamlined. It is arguably most important part of Ionic and it is also the feature you will use to do most actions. The features of the Ionic CLI include: Creating Ionic projects Issuing Cordova commands Developing and testing Ionic splash/Icon generator Ionic labs SASS Uploading the app to Ionic view Accessing Ionic.IO tools The Ionic CLI is a very powerful tool, and for the most part it is the tool we will be using throughout this book to perform specific actions. This is why the first thing we are going to do is to set up the Ionic CLI. Setting up Ionic The following steps will give a brief of how to setup Ionic: Install Node JS: To set up Ionic, the first thing you will need to do is to install Node JS on your computer so that you can have access to Node Package Manager (NPM). If you already have node installed on your computer, you can skip this step and got to step 2. Go to www.nodejs.org and click on the Install button. That should download the latest version of Node JS on your computer. Don't worry if you are on a Mac, PC, or Linux, the correct one for your operating system will be automatically downloaded. After the download is finished, install the downloaded software on your computer. You may need to restart your computer if you are running windows. Open up the terminal if you are on Mac/Linux or the Windows command line if you are on a Windows machine. Type the following command node –v and press Enter. You should see the version number of your current installation of Node JS. If you do not see a version number, this might mean that you have not correctly installed Node JS and should try running step 1 again. Install Ionic CLI: The next step is to use NPM to install the Ionic CLI. Open a new Terminal (OSX and Linux) or command line (Windows) window and run the npm install Ionic –g command. If you are on Linux/OSX, you might need to run sudo install Ionic –g. This command will aim to install Ionic globally. After this has finished running, run the Ionic –v command on your Terminal/command line and press Enter. You should have seen a version number of your Ionic CLI. This means that you have Ionic installed correctly and are good to go. If you do not see a version number, then you have not installed Ionic correctly on your machine and should perform step 2 again. The Ionic workflow When you create a new Ionic project, there are a couple of folders and files that come in stock. You directory should look similar to the following screenshot: The structure you see is pretty much the same as in every Cordova project with the exception of a few files and folders. For example, there is an scss folder. This contains a file that lets us customize the look and feel of our application. You will also note that in your www/lib folder, there is a folder called ionic, which contains all the required files to run Ionic. There is a CSS, fonts, JS, and SCSS folder: CSS: This folder contains all the default CSS that come with an Ionic app. Fonts: Ionic comes with its own font and Icon library called Ionicons. This Ionicons library contains hundreds of icons, which are all available for use in your app. JS: This contains all the code for the core Ionic library. Since Ionic is built with angular, there is a version of Angular here with a bunch of other files that make up the ionic framework. SCSS: This is a folder that contains SASS files used to build the beautiful Ionic framework CSS styles. Everything here can be overwritten easily. If you take a look at the root folder, you will see a lot of other files that are generated for you as part of the Ionic workflow. These files are not overly important now, but let's take a look at the more important ones: Bower.json: This is the file that contains some of the dependencies gotten from the bower package manager. The bower dependencies are resolved in the lib folder by as specified in the bowerrc file. This is a great place to specify other third party dependencies that your project might need. Config.xml: This is the standard config file that comes along with any Phonegap/Cordova project. This is where you request permissions for device features and also specify universal and platform-specific configurations for you app. Gulpfile: Ionic uses the gulp build tool, and this file contains some code that is provided by Ionic that enables you do some amazing things. Ionic.project: This is a file specific for Ionic services. It is the file used by the Ionic CLI and the Ionic.IO services as a place to specify some of your Ionic specific configuration. Package.json: This is a file used by node to specify some node dependencies. When you create a project with the Ionic CLI, Ionic uses both the Node and Bowe Package manager to resolve some of your dependencies. If you require a node module when you are developing ionic apps, you can specify these dependencies here. These files are some of the more important files that come stock with a project created with the Ionic CLI. At the moment, you do not need to worry too much about them but it's always good to know that they exist and have an idea about what they actually represent. Summary In this article, we discussed exactly what Ionic means and what problems it aims to solve. We also got to discuss the CSS, JS, and Ionic CLI features of the Ionic framework lightly. Resources for Article: Further resources on this subject: Ionic JS Components [article] Creating Instagram Clone Layout using Ionic framework [article] A Virtual Machine for a Virtual World [article]
Read more
  • 0
  • 0
  • 5007
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-project-structure
Packt
08 Jan 2016
14 min read
Save for later

The Project Structure

Packt
08 Jan 2016
14 min read
In this article written by Nathanael Anderson, author of the book Getting Started with NativeScript, we will see how to navigate through your new project and its full structure. We will explain each of the files that are automatically created and where you create your own files. Then, we will proceed to gain a deeper understanding of some of the basic components of your application, and we will finally see how to change screens. In this article, we will cover the following topics: An overview of the project directory The root directory The application components (For more resources related to this topic, see here.) Project directory overview By running the nativescript create crossCommunicator command, the command creates a nice structure of files and folders for us to explore. First, we will do a high level overview of the different folders and their purposes and touch on the important files in those folders. Then, we will finish the overview by going into depth about the App directory, which is where you will spend pretty much your entire time in developing an application. To give you a good visual overview, here is what the project hierarchy structure looks like: The root directory In your Root folder, you will see only a couple of directories. The package.json file will look something like this: {   "nativescript": {     "id": "org.nativescript.crossCommunicator",     "tns-android": {       "version": "1.5.0"     },     "tns-ios": {       "version": "1.5.0"     },   },   "dependencies": {     "tns-core-modules": "1.5.0"   } } This is the NativeScript master project configuration file for your entire application. It basically outlines the basic information and all platform requirements of the project. It will also be modified by the nativescript tool when you add and remove any plugins or platforms. So, in the preceding package.json file, you can see that I have installed the Android (tns-android) and iOS (tns-ios) platforms, using the nativscript platform add command, and they are both currently at version 1.5.0. The tns-core-modules dependency was added by the nativescript command when we created the project and it is the core modules. Changing the app ID Now, if you want this to be your company's name instead of the default ID of org.nativescript.yourProjectName, there are two ways to set the app ID. The first way is to set it up when you create the project; executing a nativescript create myProjName --appid com.myCompany.myProjName command will automatically set the ID value. If you forget to run the create command with a --appid option; you can change it here. However, any time you change the option here, you will also have to remove all the installed platforms and then re-add the platforms you are using. This must be done as when each platform is added because it uses this configuration id while building all the platform folders and all of the needed platform files. The package.json file This file contains basic information about the current template that is installed. When you create a new application via the nativescript create command, by default, it copies everything from a template project called tns-template-hello-world. In the future, NativeScript will allow you to choose the template, but currently this is the only template that is available and working. This template contains the folders we discussed earlier and all the files that we will discuss further. The package.json file is from this template, and it basically tells you about the template and its specific version that is installed. Le's take a look at the following code snippet: {   "name": "tns-template-hello-world",   "main": "app.js",   "version": "1.5.0", ... more json documentation fields... } Feel free to modify the package.json file so that it matches your project's name and details. This file currently does not currently serve much purpose beyond template documentation and the link to the main application file. License The License file that is present in the the App folder is the license that the tns-template-hello-world is under. In your case, your app will probably be distributed under a different license. You can either update this file to match your application's license or delete it. App.js Awesome! We have finally made it to the first of the JavaScript files that make up the code that we will change to make this our application. This file is the bootstrap file of the entire application, and this is where the fun begins. In the preceding package.json file, you can see that the package.json file references this file (app.js) under the main key. This key in the package.json file is what NativeScript uses to make this file the entry point for the entire application. Looking at this file, we can see that it currently has four simple lines of code: var application = require("application"); application.mainModule = "main-page"; application.cssFile = "./app.css"; application.start(); The file seems simple enough, so let's work through the code as we see it. The first line loads the NativeScript common application component class, and the application component wraps the initialization and life cycle of the entire application. The require() function is the what we use to reference another file in your project. It will look in your current directory by default and then it will use some additional logic to look into a couple places in the tns_core_modules folder to check whether it can find the name as a common component. Now that we have the application component loaded, the next lines are used to configure what the application class does when your program starts and runs. The second line tells which one of the files is the main page. The third line tells the application which CSS file is the main CSS file for the entire application. And finally, we tell the application component to actually start. Now, in a sense, the application has actually started running, we are already running our JavaScript code. This start() function actually starts the code that manages the application life cycle and the application events, loads and applies your main CSS file, and finally loads your main page files. If you want to add any additional code to this file, you will need to put it before the application.start() command. On some platforms, nothing below the application.start() command will be run in the app.js file. The main-page.js file The JavaScript portion of the page is probably where you will spend the majority of your time developing, as it contains all of the logic of your page. To create a page, you typically have a JavaScript file as you can do everything in JavaScript, and this is where all your logic for the page resides. In our application, the main page currently has only six lines of code: var vmModule = require("./main-view-model"); function pageLoaded(args) {   var page = args.object;   page.bindingContext = vmModule.mainViewModel; } exports.pageLoaded = pageLoaded; The first line loads the main-view-model.js file. If you haven't guessed yet, in our new application, this is used as the model file for this page. We will check out the optional model file in a few minutes after we are done exploring the rest of the main-page files. An app page does not have to have a model, they are totally optional. Furthermore, you can actually combine your model JavaScript into the page's JavaScript file. Some people find it easier to keep their model separate, so when Telerik designed this example, they built this application using the MVVM pattern, which uses a separate view model file. For more information on MVVM, you can take a look at the Wikipedia entry at https://en.wikipedia.org/wiki/Model_View_ViewModel. This file also has a function called pageLoaded, which is what sets the model object as the model for this page. The third line assigns the page variable to the page component object that is passed as part of the event handler. The fourth line assigns the model to the current page's bindingContext attribute. Then, we export the pageLoaded function as a function called pageLoaded. Using exports and module.exports is the way in which we publish something to other files that use require() to load it. Each file is its own independent blackbox, nothing that is not exported can be seen by any of the other files. Using exports, you can create the interface of your code to the rest of the world. This is part of the CommonJS standard, and you can read more about it at the NodeJS site. The main-page.xml file The final file of our application folder is also named main-page; it is the page layout file. As you can see, the main-page.xml layout consists of seven simple lines of XML code, which actually does quite a lot, as you can see: <Page loaded="pageLoaded">   <StackLayout>     <Label text="Tap the button" cssClass="title"/>     <Button text="TAP" tap="{{ tapAction }}" />     <Label text="{{ message }}" cssClass="message"     textWrap="true"/>   </StackLayout> </Page> Each of the XML layout files are actually a simplified way to load your visual components that you want on your page. In this case, it is what made your app look like this: The main-view-model.js file The final file in our tour of the App folder is the model file. This file has about 30 lines of code. By looking at the first couple of lines, you might have figured out that this file was transpiled from TypeScript. Since this file actually has a lot of boilerplate and unneeded code from the TypeScript conversion, we will rewrite the code in plain JavaScript to help you easily understand what each of the parts are used for. This rewrite will be as close to what as I can make it. So without further ado, here is the original transpiled code to compare our new code with: var observable = require("data/observable"); var HelloWorldModel = (function (_super) {   __extends(HelloWorldModel, _super);   function HelloWorldModel() {     _super.call(this);     this.counter = 42;     this.set("message", this.counter + " taps left");   }   HelloWorldModel.prototype.tapAction = function () {     this.counter--;     if (this.counter <= 0) {       this.set("message", "Hoorraaay! You unlocked the       NativeScript clicker achievement!");     }     else {       this.set("message", this.counter + " taps left");     }   };   return HelloWorldModel; })(observable.Observable); exports.HelloWorldModel = HelloWorldModel; exports.mainViewModel = new HelloWorldModel(); The Rewrite of the main-view-model.js file The rewrite of the main-view-model.js file is very straightforward. The first thing we need for a working model to also require the Observable class is the primary class that handles data binding events in NativeScript. We then create a new instance of the Observable class named mainViewModel. Next, we need to assign the two default values to the mainViewModel instance. Then, we create the same tapAction() function, which is the code that is executed each time when the user taps on the button. Finally, we export the mainViewModel model we created so that it is available to any other files that require this file. This is what the new JavaScript version looks like: // Require the Observable class and create a new Model from it var Observable = require("data/observable").Observable; var mainViewModel = new Observable(); // Setup our default values mainViewModel.counter = 42; mainViewModel.set("message", mainViewModel.counter + " taps left"); // Setup the function that runs when a tap is detected. mainViewModel.tapAction = function() {   this.counter--;   if (this.counter <= 0) {     this.set("message", "Hoorraaay! You unlocked the NativeScript     clicker achievement!");   } else {     this.set("message", this.counter + " taps left");   } }; // Export our already instantiated model class as the variable name that the main-page.js is expecting on line 4. exports.mainViewModel = mainViewModel; The set() command is the only thing that is not totally self-explanatory or explained in this code. What is probably fairly obvious is that this command sets the variable specified to the value specified. However, what is not obvious is when a value is set on an instance of the Observable class, it will automatically send a change event to anyone who has asked to be notified of any changes to that specific variable. If you recall, in the main-page.xml file, the: <Label text="{{ message }}" ….> line will automatically register the label component as a listener for all change events on the message variable when the layout system creates the label. Now, every time the message variable is changed, the text on this label changes. The application component If you recall, earlier in the article, we discussed the app.js file. It basically contains only the code to set up the properties of your application, and then finally, it starts the application component. So, you probably have guessed that this is the primary component for your entire application life cycle. A part of the features that this component provides us is access to all the application-wide events. Frequently in an app, you will want to know when your app is no longer the foreground application or when it finally returns to being the foreground application. To get this information, you can attach  the code to two of the events that it provides like this: application.on("suspend", function(event) {   console.log("Hey, I was suspended – I thought I was your   favorite app!"); }); application.on("resume", function(event) {   console.log("Awesome, we are back!"); }); Some of the other events that you can watch from the application component are launch, exit, lowMemory and uncaughtError. These events allow you to handle different application wide issues that your application might need to know about. Creating settings.js In our application, we will need a settings page; so, we will create the framework for our application's setting page now. We will just get our feet a little wet and explore how to build it purely in JavaScript. As you can see, the following code is fairly straightforward. First, we require all the components that we will be using: Frame, Page, StackLayout, Button, and finally, the Label component. Then, we have to export a createPage function, which is what NativeScript will be running to generate the page if you do not have an XML layout file to go along with the page's JavaScript file. At the beginning of our createPage function, we create each of the four components that we will need. Then, we assign some values and properties to make them have some sort of visual capability that we will be able to see. Next, we create the parent-child relationships and add our label and button to the Layout component, and then we assign that layout to the Page component. Finally, we return the page component: // Add our Requires for the components we need on our page var frame = require("ui/frame"); var Page = require("ui/page").Page; var StackLayout = require("ui/layouts/stack-layout").StackLayout; var Label = require("ui/label").Label; var Button = require("ui/button").Button;   // Create our required function which NativeScript uses // to build the page. exports.createPage = function() {   // Create our components for this page   var page = new Page();   var layout = new StackLayout();   var welcomeLabel = new Label();   var backButton = new Button();     // Assign our page title   page.actionBar.title = "Settings";   // Setup our welcome label   welcomeLabel.text = "You are now in Settings!";   welcomeLabel.cssClass = "message";     // Setup our Go Back button   backButton.text = "Go Back";   backButton.on("tap", function () {     frame.topmost().goBack();   });     // Add our layout items to our StackLayout   layout.addChild(welcomeLabel);   layout.addChild(backButton);     // Assign our layout to the page.   page.content = layout;   // Return our created page   return page; }; One thing I did want to mention here is that if you are creating a page totally programmatically without the use of a Declarative XML file, the createPage function must return the page component. The frame component is expected to have a Page component. Summary We have covered a large amount of foundational information in this article. We also covered which files are used for your application and where to find and make any changes to the project control files. In addition to all this, we also covered several foundational components such as the Application, Frame, and Page components. Resources for Article: Further resources on this subject: Overview of TDD [article] Understanding outside-in [article] Understanding TDD [article]
Read more
  • 0
  • 0
  • 5306

article-image-task-automation
Packt
05 Nov 2015
33 min read
Save for later

Task Automation

Packt
05 Nov 2015
33 min read
In this article by Kerri Shotts, author of the Mastering PhoneGap Mobile Application Development, you will learn the following topics: Logology, our demonstration app Why Gulp for Task Automation Setting up your app's directory structure Installing Gulp Creating your first Gulp configuration file Performing substitutions Executing Cordova tasks Managing version numbers Supporting ES2015 Linting your code Minifying/uglifying your code (For more resources related to this topic, see here.) Before we begin Before you continue with this article, ensure that you have the following tools installed. The version that was used in this article is listed as well, for your reference: Git (http://git-scm.com, v2.8.3) Node.js (http://nodejs.org, v0.12.2) npm, short for Node Package Manager (typically installed with Node.js, v2.7.4) Cordova 5.x (http://cordova.apache.org, v5.2.0) or PhoneGap 5.x (http://www.phonegap.com, v5.2.2) You'll need to execute the following in each directory in order to build the projects: # On Linux / Mac OS X $ npm install && gulp init % On Windows >npm install > gulp init If you're not intending to build the sample application in the code bundle, be sure to create a new directory that can serve as a container for all the work you'll be doing in this article.. Just remember, each time you create a new directory and copy the prior version to it, you'll need to execute npm install and gulp init to set things up. About Logology I'm calling it Logology—and if you're familiar with any Greek words, you might have already guessed what the app will be: a dictionary. Now, I understand that this is not necessarily the coolest app, but it is sufficient for our purposes. It will help you learn how advanced mobile development is done. By the time we're done, the app will have the following features: Search: The user will be able to search for a term Browse: The user will be able to browse the dictionary Responsive design: The app will size itself appropriately to any display size Accessibility: The app will be usable even if the user has visual difficulties Persistent storage: The app will persist settings and other user-generated information File downloads: The app will be able to download new content Although the app sounds relatively simple, it's complex enough to benefit from task automation. Since it is useful to have task automation in place from the very beginning, we'll install Gulp and verify that it is working with some simple files first, before we really get to the meat of implementing Logology. As such, the app we build in this article is very simple: it exists to verify that our tasks are working correctly. Once we have verified our workflow, we can go on to the more complicated project at hand. You may think that working through this is very time-consuming, but it pays off in the long run. Once you have a workflow that you like, you can take that workflow and apply it to the other apps you may build in the future. This means that future apps can be started almost immediately (just copy the configuration from a previous app). Even if you don't write other apps, the time you saved from having a task runner outweigh the initial setup time. Why Gulpfor task automation? Gulp (http://gulpjs.com) is a task automation utility using the Node.js platform. Unlike some other task runners, one configures Gulp by writing the JavaScript code. The configuration for Gulp is just like any other JavaScript file, which means that if you know JavaScript, you can start defining tasks quickly. Gulp also uses the concept of "streams" (again, from Node.js). This makes Gulp very efficient. Plugins can be inserted within these steams to perform many different transformations, including beautification or uglification, transpilation (for example, ECMAScript 6 to ECMAScript 2015), concatenation, packaging, and much more. If you've performed any sort of piping on the command line, Gulp should feel familiar, because it operates on a similar concept. The output from one process is piped to the next process, which performs any number of transformations, and so on, until the final output is written to another location. Gulp also tries to run as many dependent tasks in parallel as possible. Ideally, this makes running Gulp tasks faster, although it really depends on how your tasks are structured. Other task runners such as Grunt will perform their task steps in sequence, which may result in slower output, although tracing the steps from input to output may be easier to follow when the steps are performed sequentially. That's not to say that Gulp is the best task runner—there are many that are quite good, and you may find that you prefer one of them over Gulp. The skills you learn in this article can easily be transferred to other task running and build systems. Here are some other task runners that are useful: Grunt (http://www.gruntjs.com): This configuration is specified through settings, not code. Tasks are performed sequentially. Cake (http://coffeescript.org/documentation/docs/cake.html): This uses CoffeeScript and the configuration is specified via code, such as Gulp. If you like using CoffeeScript, you might prefer this over Gulp. Broccoli (https://github.com/broccolijs/broccoli): This also uses the configuration through code. Installing Gulp Installing Gulp is easy, but is actually a two-step process. The first step is to install Gulp globally. This installs the command-line utility, but Gulp actually won't work without also being installed locally within our project. If you aren't familiar with Node.js, packages can be installed locally and/or globally. A locally installed package is local to the project's root directory, while a globally installed package is specific to the developer's machine. Project dependencies are tracked in package.json, which makes it easy to replicate your development setup on another machine. Assuming you have Node.js installed and package.json created in your project directory, the installation of Gulp will go very easily. Be sure to be positioned in your project's root directory and then execute the following: $ npm install -g gulp $ npm install --save-dev gulp If you receive an error while running these commands on OS X, you may need to run them with sudo. For example: sudo install -g gulp. You can usually ignore any WARN messages. It's a good idea to be positioned in your project's root directory any time you execute an npm or gulp command. On Linux and OS X, these commands generally will locate the project's root directory automatically, but this isn't guaranteed on all platforms, so it's better to be safe than sorry. That's it! Gulp itself is very easy to install, but most workflows will require additional plugins that work with Gulp. In addition, we'll also install Cordova dependencies for this project. First, let's install the Cordova dependencies: $ npm install --save-dev cordova-lib cordova-ios cordova-android cordova-lib allows us to programmatically interact with Cordova. We can create projects, build them, and emulate them—everything we can do with the Cordova command line we can do with cordova-lib. cordova-ios and cordova-android refer to the iOS and Android platforms that cordova platform add ios android would add. We've made them dependencies for our project, so we can easily control the version we build with. While starting a new project, it's wise to start with the most recent version of Cordova and the requisite platforms. Once you begin, it's usually a good practice to stick with a specific platform version unless there are serious bugs or the like. Next, let's install the Gulp plugins we'll need: $ npm install --save-dev babel-eslint cordova-android cordova-ios cordova-lib cordova-tasks gulp gulp-babel gulp-bump gulp-concat gulp-eslint gulp-jscs gulp-notify gulp-rename gulp-replace-task gulp-sourcemaps gulp-uglify gulp-util               merge-stream rimraf These will take a few moments to install; but when you're done, take a look in package.json. Notice that all the dependencies we added were also added to the devDependencies. This makes it easy to install all the project's dependencies at a later date (say, on a new machine) simply by executing npm install. Before we go on, let's quickly go over what each of the above utility does. We'll go over them in more detail as we progress through the remainder of this article. gulp-babel: Converts ES2015 JavaScript into ES5. If you aren't familiar with ES2015, it has several new features and an improved syntax that makes writing mobile apps that much easier. Unfortunately, because most browsers don't yet natively support the ES2015 features and syntax, it must be transpiled to ES5 syntax. Of course, if you prefer other languages that can be compiled to ES5 JavaScript, you could use those as well (these would include CoffeeScript and similar). gulp-bump: This small utility manages version numbers in package.json. gulp-concat: Concatenates streams together. We can use this to bundle files together. gulp-jscs: Performs the JavaScript code style checks against your code. Supports ES2015. gulp-eslint: Lints your JavaScript code. Supports ES2015. babel-eslint: Provides ES2015 support to gulp-eslint. gulp-notify: This is an optional plugin, but it is handy especially when some of your tasks take a few seconds to run. This plugin will send a notification to your computer's notification panel when something of import occurs. If the plugin can't send it to your notification panel, it logs to the console. gulp-rename: Renames streams. gulp-replace-task: Performs search and replace within streams. gulp-sourcemaps: When transpiling ES2015 to ES5, it can be helpful to have a map between the original source and the transpiled source. This plugin creates them as a part of the workflow. gulp-uglify: Uglifies/minifies code. While useful for code obfuscation, it also reduces the size of your code. gulp-util: Additional utilities for Gulp, such as logging. merge-stream: Merges multiple tasks. rimraf: Easy file deletion. Akin to rm on the command line. Creating your first Gulp configuration file Gulp tasks are defined by the contents of the project's gulpfile.js file. This is a JavaScript program, so the same skills you have with JavaScript will apply here. Furthermore, it's executed by Node.js, so if you have any Node.js knowledge, you can use it to your advantage. This file should be placed in the root directory of your project, and must be named gulpfile.js. The first few lines of your Gulp configuration file will require the Gulp plugins that you'll need in order to complete your tasks. The following lines then specify how to perform various tasks. For example, a very simple configuration might look like this: var gulp = require("gulp"); gulp.task("copy-files", function () { gulp.src(["./src/**/*"])       .pipe(gulp.dest("./build")); }); This configuration only performs one task: it moves all the files contained within src/ to build/. In many ways, this is the simplest form of a build workflow, but it's a bit too simple for our purposes. Note the pattern we use to match all the files. If you need to see the documentation on what patterns are supported, see https://www.npmjs.com/package/glob. To execute the task, one can execute gulp copy-files. Gulp would then execute the task and copy all the files from src/ to build/. What makes Gulp so powerful is the concept of task composition. Tasks can depend on any number of other tasks, and those tasks can depend on yet more tasks. This makes it easy to create complex workflows out of simpler pieces. Furthermore, each task is asynchronous, so it is possible for many tasks with no shared dependencies to operate in parallel. Each task, as you can see in the prior code, is comprised of selecting a series of source files (src()), optionally performing some additional processing on each file (via pipe()), and then writing those files to a destination path (dest()). If no additional processing is specified (as in the prior example), Gulp will simply copy the files that match the wildcard pattern. The beauty of streams, however, is that one can execute any number of transformations before the final data is saved to storage, and so workflows can become very complex. Now that you've seen a simple task, let's get into some more complicated tasks in the next section. How to execute Cordova tasks It's tempting to use the Cordova command-line interface directly, but there's a problem with this: there's no great way to ensure that what you write will work across multiple platforms. If you are certain you'll only work with a specific platform, you can go ahead and execute shell commands instead; but what we're going to do is a bit more flexible. The code in this section is inspired by https://github.com/kamrik/CordovaGulpTemplate. The Cordova CLI is really just a thin wrapper around the cordova-lib project. Everything the Cordova CLI can do, cordova-lib can do as well. Because the Cordova project will be a build artifact, we need to be able to create a Cordova project in addition to building the project. We'll also need to emulate and run the app. To do this, we first require cordova-lib at the top of our Gulp configuration file (following the other require statements): var cordovaLib = require("cordova-lib"); var cordova = cordovaLib.cordova.raw; var rimraf = require("rimraf"); Next, let's create the code to create a new Cordova project in the build directory: var cordovaTasks = {     // CLI: cordova create ./build com.example.app app_name     //              --copy-from template_path create: function create() { return cordova.create(BUILD_DIR, pkg.cordova.id,                               pkg.cordova.name,                 { lib: { www: { url: path.join(__dirname,                                     pkg.cordova.template), link: false                     }                   }                 });     } } Although it's a bit more complicated than cordova create is on the command line, you should be able to see the parallels. The lib object that is passed is simply to provide a template for the project (equivalent to --copy-from on the command line). In our case, package.json specifies that this should come from the blank/ directory. If we don't do this, all our apps would be created with the sample Hello World app that Cordova installs by default. Our blank project template resides in ../blank, relative from the project root. Yours may reside elsewhere (since you're apt to reuse the same template), so package.json can use whatever path you need. Or, you might want the template to be within your project's root; in which case, package.json should use a path inside your project's root directory. We won't create a task to use this just yet — we need to define several other methods to build and emulate Cordova apps: var gutil = require("gulp-util"); var PLATFORM = gutil.env.platform ? gutil.env.platform :"ios";                                                   // or android var BUILD_MODE = gutil.env.mode ? gutil.env.mode :"debug";                                                   // or release var BUILD_PLATFORMS = (gutil.env.for ? gutil.env.for                                     : "ios,android").split(","); var TARGET_DEVICE = gutil.env.target ? "--target=" + gutil.env.target :""; var cordovaTasks = { create: function create() {/* as above */}, cdProject: function cdProject() { process.chdir(path.join(BUILD_DIR, "www"));     }, cdUp: function cdUp() { process.chdir("..");     }, copyConfig: function copyConfig() { return gulp.src([path.join([SOURCE_DIR], "config.xml")])                 .pipe(performSubstitutions())                 .pipe(gulp.dest(BUILD_DIR"));     },     // cordova plugin add ... addPlugins: function addPlugins() { cordovaTasks.cdProject(); return cordova.plugins("add", pkg.cordova.plugins)             .then(cordovaTasks.cdUp);     },     // cordova platform add ... addPlatforms: function addPlatforms() { cordovaTasks.cdProject(); function transformPlatform(platform) { return path.join(__dirname, "node_modules", "cordova-" + platform);         } return cordova.platforms("add", pkg.cordova.platforms.map(transformPlatform))               .then(cordovaTasks.cdUp);     },     // cordova build <platforms> --release|debug     //                           --target=...|--device build: function build() { var target = TARGET_DEVICE; cordovaTasks.cdProject(); if (!target || target === "" || target === "--target=device") { target = "--device";       } return cordova.build({platforms:BUILD_PLATFORMS, options: ["--" + BUILD_MODE, target] })             .then(cordovaTasks.cdUp);     },     // cordova emulate ios|android --release|debug emulate: function emulate() { cordovaTasks.cdProject(); return cordova.emulate({ platforms: [PLATFORM], options: ["--" + BUILD_MODE,                                         TARGET_DEVICE] })             .then(cordovaTasks.cdUp);     },     // cordova run ios|android --release|debug run: function run() { cordovaTasks.cdProject(); return cordova.run({ platforms: [PLATFORM], options: ["--" + BUILD_MODE, "--device",                                     TARGET_DEVICE] })             .then(cordovaTasks.cdUp);     }, init: function() { return this.create()             .then(cordovaTasks.copyConfig)             .then(cordovaTasks.addPlugins)             .then(cordovaTasks.addPlatforms);     } }; Place cordovaTasks prior to projectTasks in your Gulp configuration. If you aren't familiar with promises, you might want to learn more about them. http://www.html5rocks.com/en/tutorials/es6/promises/ is a fantastic resource. Before we explain the preceding code, there's another change you need to make, and that's to projectTasks.copyConfig, because we move copyConfig to cordovaTasks: var projectTasks = { …, copyConfig: function() { return cordovaTasks.copyConfig();     }, ... } Most of the earlier mentioned tasks should be fairly self-explanatory — they correspond directly with their Cordova CLI counterparts. A few, however, need a little more explanation. cdProject / cdUp: These change the current working directory. All the cordova-lib commands after create need to be executed from within the Cordova project directory, not our project's root directory. You should notice them in several of the tasks. addPlatforms: The platforms are added directly from our project's dependencies, rather than from the Cordova CLI. This allows us to control the platform versions we are using. As such, addPlatforms has to do a little more work to specify the actual directory name of each platform. build: This executes the cordova build command. By default, the CLI will build every platform, but it's possible that we might want to control the platforms that are built, hence the use of BUILD_PLATFORMS. On iOS, the build for an emulator is different than the build for a physical device, so we also need a way to specify that, which is what TARGET_DEVICE is for. This will look for emulators with the name specified for the TARGET_DEVICE, but we might want to build for a physical device; in which case, we will look for device (or no target specified at all) and switch over to the --device flag, which forces Cordova to build for a physical device. init: This does the hard work of creating the Cordova project, copying the configuration file (and performing substitutions), adding plugins to the Cordova project, and then adding the platforms. Now is also a good time to mention that we can specify various settings with switches on the Gulp command line. In the earlier snippet, we're supporting the use of --platform to specify the platform to emulate or run, --mode to specify the build mode (debug or release), --for to determine what platforms Cordova will build for, and --target for specifying the target device. The code specifies sane defaults if these switches aren't specified, but they also allow the developer extra control over the workflow, which is very useful. For example, we'll be able to use commands like these: $ gulp build --for ios,android --target device $ gulp emulate --platform ios --target iPhone-6s $ gulp run --platform ios --mode release Next, let's write the code to actually perform various Cordova tasks — it's pretty simple: var projectTasks = {     ..., init: function init() { return cordovaTasks.init();     }, emulateCordova: function emulateCordova() { return cordovaTasks.emulate();     }, runCordova: function runCordova() { return cordovaTasks.run();     }, buildCordova: function buildCordova() { return cordovaTasks.build();     }, clean: function clean(cb) { rimraf(BUILD_DIR, cb);     },     ... }   … gulp.task("clean", projectTasks.clean); gulp.task("init", ["clean"], projectTasks.init); gulp.task("build", ["copy"], projectTasks.buildCordova); gulp.task("emulate", ["copy"], projectTasks.emulateCordova); gulp.task("run", ["copy"], projectTasks.runCordova); There's a catch with the cordovaTasks.create method — it will fail if anything is already in the build/ directory. As you can guess, this could easily happen, so we also created a projectTasks.clean method. This clean method uses rimraf to delete a specified directory. This is equivalent to using rm -rf build. We then build a Gulp task named init that depends on clean. So, whenever we execute gulp init, the old Cordova project will be removed and a new one will be created for us. Finally, note that the build (and other) tasks all depend on copy. This means that all our files in src/ will be copied (and transformed, if necessary) to build/ prior to executing the desired Cordova command. As you can see, our tasks are already becoming very complex, while also remaining graspable when taken singularly. This means we can now use the following tasks in Gulp: $ gulp init                   # create the cordova project;                               # cleaning first if needed $ gulp clean                  # remove the cordova project $ gulp build                  # copy src to build; apply                               # transformations; cordova build $ gulp build --mode release   # do the above, but build in                               # release mode $ gulp build --for ios        # only build for iOS $ gulp build --target=device  # build device versions instead of                               # emulator versions $ gulp emulate --platform ios # copy src to build; apply                               # transformations;                               # cordova emulate ios $ gulp emulate --platform ios --target iPhone-6                               # same as above, but open the                               # iPhone 6 emulator $ gulp run --platform ios     # copy src to build;                               # apply transformations;                               # cordova run ios --device Now, you're welcome to use the earlier code as it is, or you can use an NPM package that takes care of the cordovaTasks portion for you. This has the benefit of drastically shortening your Gulp configuration. We've already included this package in our package.json file — it's named cordova-tasks, and was created by the author, and shares a lot of similarities to the earlier code. To use it, the following needs to go at the top of our configuration file below all the other require statements: var cordova = require("cordova-tasks"); var cordovaTasks = new cordova.CordovaTasks(     {pkg: pkg, basePath: __dirname, buildDir: "build", sourceDir: "src", gulp: gulp, replace: replace}); Then, you can remove the entire cordovaTasks object from your configuration file as well. The projectTasks section needs to change only slightly: var projectTasks = { init: function init() { return cordovaTasks.init();     }, emulateCordova: function emulateCordova() { return cordovaTasks.emulate({buildMode: BUILD_MODE, platform: PLATFORM, options: [TARGET_DEVICE]});     }, runCordova: function runCordova() { return cordovaTasks.run({buildMode: BUILD_MODE, platform: PLATFORM, options: [TARGET_DEVICE]});     }, buildCordova: function buildCordova() { var target = TARGET_DEVICE; if (!target || target === "" || target === "--target=device") { target = "--device";       } return cordovaTasks.build({buildMode: BUILD_MODE, platforms: BUILD_PLATFORMS, options: [target]});     },... } There's one last thing to do: in copyCode change .pipe(performSubstitutions()) to .pipe(cordovaTasks.performSubstitutions()). This is because the cordova-tasks package automatically takes care of all of the substitutions that we need, including version numbers, plugins, platforms, and more. This was one of the more complex sections, so if you've come this far, take a coffee break. Next, we'll worry about managing version numbers. Supporting ES2015 We've already mentioned ES2015 (or EcmaScript 2015) in this article. Now is the moment we actually get to start using it. First, though, we need to modify our copy-code task to transpile from ES2015 to ES5, or our code wouldn't run on any browser that doesn't support the new syntax (that is still quite a few mobile platforms). There are several transpilers available: I prefer Babel (https://babeljs.io). There is a Gulp plugin that we can use that makes this transpilation transformation extremely simple. To do this, we need to add the following to the top of our Gulp configuration: var babel = require("gulp-babel"); var sourcemaps = require("gulp-sourcemaps"); Source maps are an important piece of the debugging puzzle. Because our code will be transformed by the time it is running on our device, it makes debugging a little more difficult since line numbers and the like don't match. Sourcemaps provides the browser with a map between your ES2015 code and the final result so that debugging is a lot easier. Next, let's modify our projectTasks.copyCode method: var projectTasks = { …, copyCode: function copyCode() { var isRelease = (BUILD_MODE === "release"); gulp.src(CODE_FILES)             .pipe(cordovaTasks.performSubstitutions())             .pipe(isRelease ? gutil.noop() : sourcemaps.init())             .pipe(babel())             .pipe(concat("app.js"))             .pipe(isRelease ? gutil.noop() : sourcemaps.write())             .pipe(gulp.dest(CODE_DEST));     },... } Our task is now a little more complex, but that's only because we want to control when the source maps are generated. When babel() is called, it will convert ES2015 code to ES5 and also generate a sourcemap of those changes. This makes debugging easier, but it also increases the file size by quite a large amount. As such, when we're building in release mode, we don't want to include the sourcemaps, so we call gutil.noop instead, which will just do nothing. The sourcemap functionality requires us to call sourcemaps.init prior to any Gulp plugin that might generate sourcemaps. After the plugin that creates the sourcemaps executes, we also have to call sourcemaps.write to save the sourcemap back to the stream. We could also write the sourcemap to a separate .map file by calling sourcemaps.write("."), but you do need to be careful about cleaning that file up while creating a release build. babel is what is doing the actual hard work of converting ES2015 code to ES5. But it does need a little help in the form of a small support library. We'll add this library to src/www/js/lib/ by copying it from the gulp-babel module: $ cp node_modules/babel-core/browser-polyfill.js src/www/js/lib If you don't have the src/www/js/lib/directory yet, you'll need to create it before executing the previous command. Next, we need to edit src/www/index.html to include this script. While we're at it, let's make a few other changes: <!DOCTYPE html> <html> <head> <script src="cordova.js" type="text/javascript"></script> <script src="./js/lib/browser-polyfill.js" type="text/javascript"></script> <script src="./js/app/app.js" type="text/javascript"></script> </head> <body> <p>This is static content..., but below is dynamic content.</p> <div id="demo"></div> </body> </html> Finally, let's write some ES2015 code in src/www/js/app/index.js: function h ( elType, ...children ) { let el = document.createElement(elType); for (let child of children) { if (typeof child !== "object") {           el.textContent = child;       } else if (child instanceof Array) { child.forEach( el.appendChild.bind(el) );       } else { el.appendChild( child );       }   } return el; }   function startApp() { document.querySelector("#demo").appendChild( h("div", h("ul", h("li", "Some information about this app..."), h("li", "App name: {{{NAME}}}"), h("li", "App version: {{{VERSION}}}")       )     )   ); }   document.addEventListener("deviceready", startApp, false); This article isn't about how to write ES2015 code, so I won't bore you with all the details. Suffice it to say, the previous generates a few list items when the app is run using a very simple form of DOM templating. But it does so using the …​ (spread) syntax for variable parameters, the for … of loop and let instead of var. Although it looks a lot like JavaScript, it's definitely different enough that it will take some time to learn how best to use the new features. Linting your code You could execute a gulp emulate --platform ios (or android) right now, and the app should work. But how do we know our code will work when built? Better yet — how can we prevent a build if the code isn't valid? We do this by adding lint tasks to our Gulp configuration file. Linting is a lot like compiling — the linter checks your code for obvious errors and aborts if it finds any. There are various linters available (some better than others), but not all of them support ES2015 syntax yet. The best one that does is ESLint (http://www.eslint.org). Thankfully, there's a very simple Gulp plugin that uses it. We could stop at linting and be done, but code style is also important and can catch out potentially serious issues as well. As such, we're also going to be using the JavaScript Code Style checker or JSCS (https://github.com/jscs-dev/node-jscs). Let's create tasks to lint and check our coding style. First, add the following to the top of our Gulp configuration: var eslint = require("gulp-eslint"); var jscs = require("gulp-jscs"); var CONFIG_DIR = path.join(__dirname, "config"); var CODE_STYLE_FILES = [path.join(SOURCE_DIR, "www", "js", "app", "**", "*.js")]; var CODE_LINT_FILES = [path.join(SOURCE_DIR, "www", "js", "app", "**", "*.js")]; Now, let's create the tasks var projectTasks = { …, checkCodeStyle: function checkCodeStyle() { return gulp.src(CODE_STYLE_FILES) .pipe(jscs({ configPath: path.join(CONFIG_DIR, "jscs.json"), esnext: true })); }, lintCode: function lintCode() { return gulp.src(CODE_LINT_FILES) .pipe(eslint(path.join(CONFIG_DIR, "eslint.json"))) .pipe(eslint.format()) .pipe(eslint.failOnError()); } } … gulp.task("lint", projectTasks.lintCode); gulp.task("code-style", projectTasks.checkCodeStyle); Now, before you run this, you'll need two configuration files to tell each task what should be an error and what shouldn't be. If you want to change the settings, you can do so — the sites for ESLint and JSCS have information on how to modify the configuration files. config/eslint.json must contain "parser": "babel-eslint" in order to force it to use ES2015 syntax. This is set for JSCS in the Gulp configuration, however. config/jscs.json must exist and must not be empty. If you don't need to specify any rules, use an empty JSON object ({}). Now, if you were to execute gulp lint and our source code had a syntax error, you would receive an error message. The same goes for code styles — gulp code-style would generate an error if it didn't like the look of the code. Modify the build, emulate, and run tasks in the Gulp configuration as follows: gulp.task("build", ["lint", "code-style", "copy"], projectTasks.buildCordova); gulp.task("emulate", ["lint", "code-style", "copy"], projectTasks.emulateCordova); gulp.task("run", ["lint", "code-style", "copy"], projectTasks.runCordova); Now, if you execute gulp build and there is a linting or code style error, the build will fail with an error. This gives a little more assurance that our code is at least syntactically valid prior to distributing or running the code. Linting and style checks do not guarantee your code works logically. It just ensures that there are no syntax or style errors. If your program responds incorrectly to a gesture or processes some data incorrectly, a linter won't necessarily catch those issues. Uglifying your code Code uglification or minification sounds a bit painful, but it's a really simple step we can add to our workflow that will reduce the size of our applications when we build in the release mode. Uglification also tends to obfuscate our code a little bit, but don't rely on this for any security — obfuscation can be easily undone. To add the code uglification, add the following to the top of our Gulp configuration: var uglify = require("gulp-uglify"); We can then uglify our code by adding the following code immediately after .pipe(concat("app.js")) in our projectTasks.copyCode method: Next, modify our projectTasks.copyCode method to look like this: .pipe(isRelease ? uglify({preserveComments: "some"}) : gutil.noop()) Notice that we added the uglify method call, but only if the build mode is release. This means that we'll only trigger it if we execute gulp build --mode release. You can, of course, specify additional options. If you want to see all the documentation, visit https://github.com/mishoo/UglifyJS2/. Our options include certain comments (the ones most likely to be license-related) while stripping out all the other comments. Putting it all together You've accomplished quite a bit, but there's one last thing we want to mention: the default task. If gulp is run with no parameters, it looks for a default task to perform. This can be anything you want. To specify this, just add the following to your Gulp configuration: gulp.task("default", ["build"]); Now, if you execute gulp with no specific task, you'll actually start the build task instead. What you want to use for your default task is largely dependent upon your preferences. Your Gulp configuration is now quite large and complex. We've added a few additional features to it (mostly for config.xml). We've also added several other features to the configuration, which you might want to investigate further: BrowserSync for rapid iteration and testing The ability to control whether or not the errors prevent further tasks from being executed Help text Summary In this article, you've learned why a task runner is useful, how to install Gulp, and how to create several tasks of varying complexity to automate building your project and other useful tasks. Resources for Article: Further resources on this subject: Getting Ready to Launch Your PhoneGap App in the Real World [article] Geolocation – using PhoneGap features to improve an app's functionality, write once use everywhere [article] Using Location Data with PhoneGap [article]
Read more
  • 0
  • 0
  • 2737

article-image-working-xamarinandroid
Packt
03 Nov 2015
10 min read
Save for later

Working with Xamarin.Android

Packt
03 Nov 2015
10 min read
In this article written by Matthew Leibowitz, author of the book Xamarin Mobile Development for Android Cookbook, wants us to learn about the Android version that can be used as support for your project. (For more resources related to this topic, see here.) Supporting all Android versions As the Android operating system evolves, many new features are added and older devices are often left behind. How to do it... In order to add the new features of the later versions of Android to the older versions of Android, all we need to do is add a small package: An Android app has three platform versions to be set. The first is the API features that are available to code against. We set this to always be the latest in the Target Framework dropdown of the project options. The next version to set (via Minimum Android version) is the lowest version of the OS that the app can be installed on. When using the support libraries, we can usually target versions down to version 2.3. Lastly, the Target Android version dropdown specifies how the app should behave when installed on a later version of the OS. Typically, this should always be the latest so that the app will always function as the user expects. If we want to add support for the new UI paradigm that uses fragments and action bars, we need to install two of the Android support packages: Create or open a project in Xamarin Studio. Right-click on the project folder in the Solution Explorer list. Select Add and then Add Packages…. In the Add Packages dialog that is displayed, search for Xamarin.Android.Support. Select both Xamarin Support Library v4 and Xamarin Support Library v7 AppCompat. Click on Add Package. There are several support library packages, each adding other types of forward compatibility, but these two are the most commonly used. Once the packages are installed, our activities can inherit from the AppCompatActivity type instead of the usual Activity type: public class MyActivity : AppCompatActivity { } We specify that the activity theme be one of the AppCompat derivatives using the Theme property in the [Activity] attribute: [Activity(..., Theme = "@style/Theme.AppCompat", ...)] If we need to access the ActionBar instance, it is available via the SupportActionBar property on the activity: SupportActionBar.Title = "Xamarin Cookbook"; By simply using the action bar, all the options menu items are added as action items. However, all of them are added under the action bar overflow menu: The XML for action bar items is exactly the same as the options menu: <menu ... >   <item     android_id="@+id/action_refresh"     android_icon="@drawable/ic_action_refresh"     android_title="@string/action_refresh"/> </menu> To get the menu items out of the overflow and onto the actual action bar, we can customize the items to be displayed and how they are displayed: To add action items with images to the actual action bar as well as more complex items, all that is needed is an attribute in the XML, showAsAction: <menu ... >   <item ... app_showAsAction="ifRoom"/> </menu> Sometimes, we may wish to only display the icon initially and then, when the user taps the icon, expand the item to display the action view: <menu ... >   <item ... app_showAsAction="ifRoom|collapseActionView"/> </menu> If we wish to add custom views, such as a search box, to the action bar, we make use of the actionViewClass attribute: <menu ... >   <item ...   app_actionViewClass="android.support.v7.widget.SearchView"/> </menu> If the view is in a layout resource file, we use the actionLayout attribute: <menu ... >   <item ... app_actionLayout="@layout/action_rating"/> </menu> How it works... As Android is developed, new features are added and designs change. We want to always provide the latest features to our users, but some users either haven't upgraded or can't upgrade to the latest version of Android. Xamarin.Android provides three version numbers to specify which types can be used and how they can be used. The target framework version specifies what types are available for consumption as well as what toolset to use during compilation. This should be the latest as we always want to use the latest tools. However, this will make some types and members available to apps even if they aren't actually available on the Android version that the user is using. For example, it will make the ActionBar type available to apps running on Android version 2.3. If the user were to run the app, it would probably crash. In these instances, we can set the minimum Android version to be a version that supports these types and members. But, this will then reduce the number of devices that we can install our app on. This is why we use the support libraries; they allow the types to be used on most versions of Android. Setting the minimum Android version for an app will prevent the app from being installed on devices with earlier versions of the OS. The support libraries By including the Android Support Libraries in our app, we can make use of the new features but still support the old versions. Types from the Android Support Library are available to almost all versions of Android currently in use. The Android Support Libraries provide us with a type that we know we can use everywhere, and then that base type manages the features to ensure that they function as expected. For example, we can use the ActionBar type on most versions of Android because the support library made it available through the AppCompatActivity type. Because the AppCompatActivity type is an adaptive extension for the traditional Activity type, we have to use a different theme. This theme adjusts so that the new look and feel of the UI gets carried all the way back to the old Android versions. When using the AppCompatActivity type, the activity theme must be one of the AppCompat theme variations. There are a few differences in the use when using the support library. With native support for the action bar, the AppCompatActivity type has a property named ActionBar; however, in the support library, the property is named SupportActionBar. This is just a property name change but the functionality is the same. Sometimes ,features have to be added to the existing types that are not in the support libraries. In these cases, static methods are provided. The native support for custom views in menu items includes a method named SetActionView(): menuItem.SetActionView(someView); This method does not exist on the IMenuItem type for the older versions of Android, so we make use of the static method on the MenuItemCompat type: MenuItemCompat.SetActionView(menuItem, someView); The action bar While adding an action bar on older Android versions, it is important to inherit it from the AppCompatActivity type. This type includes all the logic required for including an action bar in the app. It also provides many different methods and properties for accessing and configuring the action bar. In newer versions of Android, all the features are included in the Activity type. Although the functionality is the same, we do have to access the various pieces using the support members when using the support libraries. An example would be to use the SupportActionBar property instead of the ActionBar property. If we use the ActionBar property, the app will crash on devices that don't natively support the ActionBar property. In order to render the action bar, the activity needs to use a theme that contains a style for the action bar or one that inherits from such a theme. For the older versions of Android, we can use the AppCompat themes, such as Theme.AppCompat. The toolbar With the release of Android version 5.0, Google introduced a new style of action bar. The new Toolbar type performs the same function as the action bar but can be placed anywhere on the screen. The action bar is always placed at the top of the screen, but a toolbar is not restricted to that location and can even be placed inside other layouts. To make use of the Toolbar type, we can either use the native type, or we can use the type found in the support libraries. Like any Android View, we can add the ToolBar type to the layout: <android.support.v7.widget.Toolbar   android_id="@+id/my_toolbar"   android_layout_width="match_parent"   android_layout_height="?attr/actionBarSize"   android_background="?attr/colorPrimary"   android_elevation="4dp"   android_theme="@style/ThemeOverlay.AppCompat.ActionBar"   app_popupTheme="@style/ThemeOverlay.AppCompat.Light"/> The difference is in how the activity is set up. First, as we are not going to use the default ActionBar property, we can use the Theme.AppCompat.NoActionBar theme. Then, we have to let the activity know which view is used as the Toolbar type: var toolbar = FindViewById<Toolbar>(Resource.Id.toolbar); SetSupportActionBar(toolbar); The action bar items Action item buttons are just traditional options menu items but are optionally always visible on the action bar. The underlying logic to handle item selections is the same as that for the traditional options menu. No change is required to be made to the existing code inside the OnOptionsItemSelected() method. The value of the showAsAction attribute can be ifRoom, never, or always. This value can optionally be combined, using a pipe, with withText and/or collapseActionView. There's more... Besides using the Android Support Libraries to handle different versions, there is another way to handle different versions at runtime. Android provides the version number of the current operating system through the Build.VERSION type. This type has a property, SdkInt, which we use to detect the current version. It represents the current API level of the version. Each version of Android has a series of updates and new features. For example, Android 4 has numerous updates since its initial release, new features being added each time. Sometimes, the support library cannot cover all the cases, and we have to write specific code for particular versions: int apiLevel = (int)Build.VERSION.SdkInt; if (Build.VERSION.SdkInt >= BuildVersionCodes.IceCreamSandwich) {   // Android version 4.0 and above } else {   // Android versions below version 4.0 } Although the preceding can be done, it introduces spaghetti code and should be avoided. In addition to different code, the app may behave differently on different versions, even if the support library could have handled it. We will now have to manage these differences ourselves each time a new version of Android is released. Summary In this article, we learned that as the technology grows, new features are added and released in Android and older devices are often left behind. Thus, using the given steps we can add the new features of the later versions of Android to the older versions of Android, all we need to do is add packages by following the simple steps given in here. Resources for Article: Further resources on this subject: Creating the POI ListView layout [article] Code Sharing Between iOS and Android [article] Heads up to MvvmCross [article]
Read more
  • 0
  • 0
  • 2465

article-image-working-local-and-remote-data-sources
Packt
02 Nov 2015
9 min read
Save for later

Working With Local and Remote Data Sources

Packt
02 Nov 2015
9 min read
In this article by Jason Kneen, the author of the book Appcelerator Titanium Smartphone Application Development Cookbook - Second Edition, we'll cover the following recipes: Reading data from remote XML via HTTPClient Displaying data using a TableView Enhancing your TableViews with custom rows Filtering your TableView with the SearchBar control Speeding up your remote data access with Yahoo! YQL and JSON Creating a SQLite database Saving data locally using a SQLite database Retrieving data from a SQLite database Creating a "pull to refresh" mechanism in iOS (For more resources related to this topic, see here.) As you are a Titanium developer, fully understanding the methods available for you to read, parse, and save data is fundamental to the success of the apps you'll build. Titanium provides you with all the tools you need to make everything from simple XML or JSON calls over HTTP, to the implementation of local relational SQL databases. In this article, we'll cover not only the fundamental methods of implementing remote data access over HTTP, but also how to store and present that data effectively using TableViews, TableRows, and other customized user interfaces. Prerequisites You should have a basic understanding of both the XML and JSON data formats, which are widely used and standardized methods of transporting data across the Web. Additionally, you should understand what Structured Query Language (SQL) is and how to create basic SQL statements such as Create, Select, Delete, and Insert. There is a great beginners' introduction to SQL at http://sqlzoo.net if you need to refer to tutorials on how to run common types of database queries. Reading data from remote XML via HTTPClient The ability to consume and display feed data from the Internet, via RSS feeds or alternate APIs, is the cornerstone of many mobile applications. More importantly, many services that you may wish to integrate into your app will probably require you to do this at some point or the other, so it is vital to understand and be able to implement remote data feeds and XML. Our first recipe in this article introduces some new functionality within Titanium to help facilitate this need. Getting ready To prepare for this recipe, open Titanium Studio, log in and create a new mobile project. Select Classic and Default Project. Then, enter MyRecipes as the name of the app, and fill in the rest of the details with your own information, as you've done previously. How to do it... Now that our project shell is set up, let's get down to business! First, open your app.js file and replace its contents with the following: // this sets the background color of the master View (when there are no windows/tab groups on it) Ti.UI.setBackgroundColor('#000'); // create tab group var tabGroup = Ti.UI.createTabGroup(); var tab1 = Ti.UI.createTab({ icon:'cake.png', title:'Recipes', window:win1 }); var tab2 = Ti.UI.createTab({ icon:'heart.png', title:'Favorites', window:win2 }); // // add tabs // tabGroup.addTab(tab1); tabGroup.addTab(tab2); // open tab group tabGroup.open(); This will get a basic TabGroup in place, but we need two windows, so we create two more JavaScript files called recipes.js and favorites.js. We'll be creating a Window instance in each file to do this we created the window2.js and chartwin.js files. In recipes.js, insert the following code. Do the same with favorites.js, ensuring that you change the title of the Window to Favorites: //create an instance of a window module.exports = (function() { var win = Ti.UI.createWindow({ title : 'Recipes', backgroundColor : '#fff' }); return win; })(); Next, go back to app.js, and just after the place where TabGroup is defined, add this code: var win1 = require("recipes"); var win2 = require("favorites"); Open the recipes.js file. This is the file that'll hold our code for retrieving and displaying recipes from an RSS feed. Type in the following code at the top of your recipes.js file; this code will create an HTTPClient and read in the feed XML from the recipe's website: //declare the http client object var xhr = Ti.Network.createHTTPClient(); function refresh() { //this method will process the remote data xhr.onload = function() { console.log(this.responseText); }; //this method will fire if there's an error in accessing the //remote data xhr.onerror = function() { //log the error to our Titanium Studio console console.log(this.status + ' - ' + this.statusText); }; //open up the recipes xml feed xhr.open('GET', 'http://rss.allrecipes.com/daily.aspx?hubID=79'); //finally, execute the call to the remote feed xhr.send(); } refresh(); Try running the emulator now for either Android or iPhone. You should see two tabs appear on the screen, as shown in the following screenshot. After a few seconds, there should be a stack of XML data printed to your Appcelerator Studio console log. How it works… If you are already familiar with JavaScript for the Web, this should make a lot of sense to you. Here, we created an HTTPClient using the Ti.Network namespace, and opened a GET connection to the URL of the feed from the recipe's website using an object called xhr. By implementing the onload event listener, we can capture the XML data that has been retrieved by the xhr object. In the source code, you'll notice that we have used console.log() to echo information to the Titanium Studio screen, which is a great way of debugging and following events in our app. If your connection and GET request were successful, you should see a large XML string output in the Titanium Studio console log. The final part of the recipe is small but very important—calling the xhr object's send() method. This kicks off the GET request; without it, your app would never load any data. It is important to note that you'll not receive any errors or warnings if you forget to implement xhr.send(), so if your app is not receiving any data, this is the first place to check. If you are having trouble parsing your XML, always check whether it is valid first! Opening the XML feed in your browser will normally provide you with enough information to determine whether your feed is valid or has broken elements. Displaying data using a TableView TableViews are one of the most commonly used components in Titanium. Almost all of the native apps on your device utilize tables in some shape or form. They are used to display large lists of data in an effective manner, allowing for scrolling lists that can be customized visually, searched through, or drilled down to expose child views. Titanium makes it easy to implement TableViews in your application, so in this recipe, we'll implement a TableView and use our XML data feed from the previous recipe to populate it with a list of recipes. How to do it... Once we have connected our app to a data feed and we're retrieving XML data via the XHR object, we need to be able to manipulate that data and display it in a TableView component. Firstly, we will need to create an array object called data at the top of our refresh function in the recipes.js file; this array will hold all of the information for our TableView in a global context. Then, we need to disseminate the XML, read in the required elements, and populate our data array object, before we finally create a TableView and set the data to be our data array. Replace the refresh function with the following code: function refresh() { var data = []; //empty data array //declare the http client object var xhr = Ti.Network.createHTTPClient(); //create the table view var tblRecipes = Ti.UI.createTableView(); win.add(tblRecipes); //this method will process the remote data xhr.onload = function() { var xml = this.responseXML; //get the item nodelist from our response xml object var items = xml.documentElement.getElementsByTagName("item"); //loop each item in the xml for (var i = 0; i < items.length; i++) { //create a table row var row = Ti.UI.createTableViewRow({ title: items.item(i).getElementsByTagName("title").item(0).text }); //add the table row to our data[] object data.push(row); } //end for loop //finally, set the data property of the tableView to our //data[] object tblRecipes.data = data; }; //open up the recipes xml feed xhr.open('GET', 'http://rss.allrecipes.com/daily.aspx?hubID=79'); //finally, execute the call to the remote feed xhr.send(); } The following screenshot shows the TableView with the titles of our recipes from the XML feed: How it works... The first thing you'll notice is that we are taking the response data, extracting all the elements that match the name item, and assigning it to items. This gives us an array that we can use to loop through and assign each individual item to the data array object that we created earlier. From there, we create our TableView by implementing the Ti.UI.createTableView() function. You should notice almost immediately that many of our regular properties are also used by tables, including width, height, and positioning. In this case, we did not specify these values, which means that by default, the TableView will occupy the screen. A TableView has an extra, and important, property—data. The data property accepts an array of data, the values of which can either be used dynamically (as we have done here with the title property) or be assigned to the subcomponent children of a TableRow. As you begin to build more complex applications, you'll be fully understanding just how flexible table-based layouts can be. Summary In this article, we covered fundamental methods of implementing remote data access over HTTP. As you are a Titanium developer, we had also understand the available methods to build a successful app. More importantly, many services that you may wish to integrate into your app will probably require you to do this at some point or the other, so it is vital to understand and be able to implement remote data feeds and XML Resources for Article: Further resources on this subject: Mobile First Bootstrap [article] Anatomy of a Sprite Kit project [article] Designing Objects for 3D Printing [article]
Read more
  • 0
  • 0
  • 2504
article-image-creating-instagram-clone-layout-using-ionic-framework
Packt
06 Oct 2015
7 min read
Save for later

Creating Instagram Clone Layout using Ionic framework

Packt
06 Oct 2015
7 min read
In this article by Zainul Setyo Pamungkas, author of the book PhoneGap 4 Mobile Application Development Cookbook, we will see how Ionic framework is one of the most popular HTML5 framework for hybrid application development. Ionic framework provides native such as the UI component that user can use and customize. (For more resources related to this topic, see here.) In this article, we will create a clone of Instagram mobile app layout: First, we need to create new Ionic tabs application project named ionSnap: ionic start ionSnap tabs Change directory to ionSnap: cd ionSnap Then add device platforms to the project: ionic platform add ios ionic platform add android Let's change the tab name. Open www/templates/tabs.html and edit each title attribute of ion-tab: <ion-tabs class="tabs-icon-top tabs-color-active-positive"> <ion-tab title="Timeline" icon-off="ion-ios-pulse" icon-on="ion-ios-pulse-strong" href="#/tab/dash"> <ion-nav-view name="tab-dash"></ion-nav-view> </ion-tab> <ion-tab title="Explore" icon-off="ion-ios-search" icon-on="ion-ios-search" href="#/tab/chats"> <ion-nav-view name="tab-chats"></ion-nav-view> </ion-tab> <ion-tab title="Profile" icon-off="ion-ios-person-outline" icon-on="ion-person" href="#/tab/account"> <ion-nav-view name="tab-account"></ion-nav-view> </ion-tab> </ion-tabs> We have to clean our application to start a new tab based application. Open www/templates/tab-dash.html and clean the content so we have following code: <ion-view view-title="Timeline"> <ion-content class="padding"> </ion-content> </ion-view> Open www/templates/tab-chats.html and clean it up: <ion-view view-title="Explore"> <ion-content> </ion-content> </ion-view> Open www/templates/tab-account.html and clean it up: <ion-view view-title="Profile"> <ion-content> </ion-content> </ion-view> Open www/js/controllers.js and delete methods inside controllers so we have following code: angular.module('starter.controllers', []) .controller('DashCtrl', function($scope) {}) .controller('ChatsCtrl', function($scope, Chats) { }) .controller('ChatDetailCtrl', function($scope, $stateParams, Chats) { }) .controller('AccountCtrl', function($scope) { }); We have clean up our tabs application. If we run our application, we will have view like this: The next steps, we will create layout for timeline view. Each post of timeline will be displaying username, image, Like button, and Comment button. Open www/template/tab-dash.html and add following div list: <ion-view view-title="Timelines"> <ion-content class="has-header"> <div class="list card"> <div class="item item-avatar"> <img src="http://placehold.it/50x50"> <h2>Some title</h2> <p>November 05, 1955</p> </div> <div class="item item-body"> <img class="full-image" src="http://placehold.it/500x500"> <p> <a href="#" class="subdued">1 Like</a> <a href="#" class="subdued">5 Comments</a> </p> </div> <div class="item tabs tabs-secondary tabs-icon-left"> <a class="tab-item" href="#"> <i class="icon ion-heart"></i> Like </a> <a class="tab-item" href="#"> <i class="icon ion-chatbox"></i> Comment </a> <a class="tab-item" href="#"> <i class="icon ion-share"></i> Share </a> </div> </div> </ion-content> </ion-view> Our timeline view will be like this: Then, we will create explore page to display photos in a grid view. First we need to add some styles on our www/css/styles.css: .profile ul { list-style-type: none; } .imageholder { width: 100%; height: auto; display: block; margin-left: auto; margin-right: auto; } .profile li img { float: left; border: 5px solid #fff; width: 30%; height: 10%; -webkit-transition: box-shadow 0.5s ease; -moz-transition: box-shadow 0.5s ease; -o-transition: box-shadow 0.5s ease; -ms-transition: box-shadow 0.5s ease; transition: box-shadow 0.5s ease; } .profile li img:hover { -webkit-box-shadow: 0px 0px 7px rgba(255, 255, 255, 0.9); box-shadow: 0px 0px 7px rgba(255, 255, 255, 0.9); } Then we just put list with image item like so: <ion-view view-title="Explore"> <ion-content> <ul class="profile" style="margin-left:5%;"> <li class="profile"> <a href="#"><img src="http://placehold.it/50x50"></a> </li> <li class="profile" style="list-style-type: none;"> <a href="#"><img src="http://placehold.it/50x50"></a> </li> <li class="profile" style="list-style-type: none;"> <a href="#"><img src="http://placehold.it/50x50"></a> </li> <li class="profile" style="list-style-type: none;"> <a href="#"><img src="http://placehold.it/50x50"></a> </li> <li class="profile" style="list-style-type: none;"> <a href="#"><img src="http://placehold.it/50x50"></a> </li> <li class="profile" style="list-style-type: none;"> <a href="#"><img src="http://placehold.it/50x50"></a> </li> </ul> </ion-content> </ion-view> Now, our explore page will look like this: For the last, we will create our profile page. The profile page consists of two parts. The first one is profile header, which shows user information such as username, profile picture, and number of post. The second part is a grid list of picture uploaded by user. It's similar to grid view on explore page. To add profile header, open www/css/style.css and add following styles bellow existing style: .text-white{ color:#fff; } .profile-pic { width: 30%; height: auto; display: block; margin-top: -50%; margin-left: auto; margin-right: auto; margin-bottom: 20%; border-radius: 4em 4em 4em / 4em 4em; } Open www/templates/tab-account.html and then add following code inside ion-content: <ion-content> <div class="user-profile" style="width:100%;heigh:auto;background-color:#fff;float:left;"> <img src="img/cover.jpg"> <div class="avatar"> <img src="img/ionic.png" class="profile-pic"> <ul> <li> <p class="text-white text-center" style="margin-top:-15%;margin-bottom:10%;display:block;">@ionsnap, 6 Pictures</p> </li> </ul> </div> </div> … The second part of profile page is the grid list of user images. Let's add some pictures under profile header and before the close of ion-content tag: <ul class="profile" style="margin-left:5%;"> <li class="profile"> <a href="#"><img src="http://placehold.it/100x100"></a> </li> <li class="profile" style="list-style-type: none;"> <a href="#"><img src="http://placehold.it/100x100"></a> </li> <li class="profile" style="list-style-type: none;"> <a href="#"><img src="http://placehold.it/100x100"></a> </li> <li class="profile" style="list-style-type: none;"> <a href="#"><img src="http://placehold.it/100x100"></a> </li> <li class="profile" style="list-style-type: none;"> <a href="#"><img src="http://placehold.it/100x100"></a> </li> <li class="profile" style="list-style-type: none;"> <a href="#"><img src="http://placehold.it/100x100"></a> </li> </ul> </ion-content> Our profile page will now look like this: Summary In this article we have seen steps to create Instagram clone with an Ionic framework with the help of an example. If you are a developer who wants to get started with mobile application development using PhoneGap, then this article is for you. Basic understanding of web technologies such as HTML, CSS and JavaScript is a must. Resources for Article: Further resources on this subject: The Camera API [article] Working with the sharing plugin [article] Building the Middle-Tier [article]
Read more
  • 0
  • 0
  • 7352

article-image-using-resources
Packt
06 Oct 2015
6 min read
Save for later

Using Resources

Packt
06 Oct 2015
6 min read
In this article written by Mathieu Nayrolles, author of the book Xamarin Studio for Android Programming: A C# Cookbook, wants us to learn of how to play sound and how to play a movie by using an user-interactive—press on button in a programmative way. (For more resources related to this topic, see here.) Playing sound There are an infinite number of occasions in which you want your applications to play sound. In this section, we will learn how to play a song from our application in a user-interactive—press on a button—or programmative way. Getting ready For using this you need to create a project on your own: How to do it… Add the following using parameter to your MainActivity.cs file. using Android.Media; Create a class variable named _myPlayer of the MediaPlayer class: MediaPlayer _myPlayer; Create a subfolder named raw under Resources. Place the sound you wish to play inside the newly created folder. We will use the Mario theme, free of rights for non-commercial use, downloaded from http://mp3skull.com. Add the following lines at the end of the OnCreate() method of your MainActivity. _myPlayer = MediaPlayer.Create (this, Resource.Raw.mario); Button button = FindViewById<Button> (Resource.Id.myButton); button.Click += delegate { _myPlayer.Start(); }; In the preceding code sample, the first line creates an instance of the MediaPlayer using the this statement as Context and Resource.Raw.mario as the file to play with this MediaPlayer. The rest of the code is trivial, we just acquired a reference to the button and create a behavior for the OnClick() event of the button. In this event, we call the Start() method of the _myPlayer() variable. Run your application and press on the button as shown on the following screenshot: You should hear the Mario theme playing right after you pressed the button, even if you are running the application on the emulator. How it works... Playing sound (and video) is an activity handled by the Media Player class of the Android platform. This class involves some serious implementations and a multitude of states in the same way as activities. However, as an Android Applications Developer and not as an Android Platform Developer we only require a little background on this. The Android multimedia framework includes—thought the Media Player class—a support for playing a very large variety of media such MP3 from the filesystem or from the Internet. Also, you can only play a song on the current sound device which could be the phone speakers, headset or even a Bluetooth enabled speaker. In other words, even if there are many sound outputs available on the phone, the current default settled by the user is the one where your sound will be played. Finally, you cannot play a sound during a call. There's more... Playing sound which is not stored locally Obviously, you may want to play sounds that are not stored locally—in the raw folder—but anywhere else on the phone, like SDcard or so. To do it, you have to use the following code sample: Uri myUri = new Uri ("uriString"); _myPlayer = new MediaPlayer(); _myPlayer.SetAudioStreamType (AudioManager.UseDefaultStreamType); _myPlayer.SetDataSource(myUri); _myPlayer.Prepare(); The first line defines a Uri for the targeting file to play. The three following lines set the StreamType, the Uri prepares the MediaPlayer. The Prepare() is a method which prepares the player for playback in a synchrone manner. Meaning that this instruction blocks the program until the player is ready to play, until the player has loaded the file. You could also call the PrepareAsync() which returns immediately and performs the loading in an asynchronous way. Playing online sound Using a code very similar to the one required to play sounds stored somewhere on the phone, we could play sound from the Internet. Basically, we just have to replace the Uri parameter by some HTTP address just like the following: String url = "http://myWebsite/mario.mp3"; _myPlayer = new MediaPlayer(); _myPlayer.SetAudioStreamType (AudioManager.UseDefaultStreamType); _myPlayer.SetDataSource(url); _myPlayer.Prepare(); Also, you must request the permission to access the Internet with your application. This is done in the manifest by adding an <uses-permission> tag for your application as shown by the following code sample: <application android_icon="@drawable/Icon" android_label="Splash"> <uses-permission android_name="android.permission.INTERNET" /> </application> See Also See also the next recipe for playing video. Playing a movie As a final recipe in this article, we will see how to play a movie with your Android application. Playing video, unlike playing audio, involves some special views for displaying it to the users. Getting ready For the last time, we will reuse the same project, and more specifically, we will play a Mario video under the button for playing the Mario theme seen in the previous recipe. How to do it... Add the following code to your Main.axml under Layout file: <VideoView android_id="@+id/myVideoView" android_layout_width="fill_parent" android_layout_height="fill_parent"> </VideoView> As a result, the content of your Main.axml file should look like the following screenshot: Add the following code to the MainActivity.cs file in the OnCreate() method: var videoView = FindViewById<VideoView> (Resource.Id.SampleVideoView); var uri = Android.Net.Uri.Parse ("url of your video"); videoView.SetVideoURI (uri); Finally, invoke the videoView.Start() method. Note that playing Internet based video, even short one, will take a very long time as the video need to be fully loaded while using this technique. How it works... Playing video should involve some special view to display it to users. This special view is named the VideoView tag and should be used as same as simple TextView tag. <VideoView android_id="@+id/myVideoView" android_layout_width="fill_parent" android_layout_height="fill_parent"> </VideoView> As you can see in the preceding code sample, you can apply the same parameters to VideoView tag as TextView tag such as layout based options. The VideoView tag, like the MediaPlayer for audio, have a method to set the video URI named SetVideoURI and another one to start the video named Start();. Summary In this article, we've learned how to play a sound clip as well as video on the Android application which we've developed. Resources for Article: Further resources on this subject: Heads up to MvvmCross [article] XamChat – a Cross-platform App [article] Gesture [article]
Read more
  • 0
  • 0
  • 5227

article-image-apps-different-platforms
Packt
01 Oct 2015
9 min read
Save for later

Apps for Different Platforms

Packt
01 Oct 2015
9 min read
In this article by Hoc Phan, the author of the book Ionic Cookbook, we will cover tasks related to building and publishing apps, such as: Building and publishing an app for iOS Building and publishing an app for Android Using PhoneGap Build for cross–platform (For more resources related to this topic, see here.) Introduction In the past, it used to be very cumbersome to build and successfully publish an app. However, there are many documentations and unofficial instructions on the Internet today that can pretty much address any problem you may run into. In addition, Ionic also comes with its own CLI to assist in this process. This article will guide you through the app building and publishing steps at a high level. You will learn how to: Build iOS and Android app via Ionic CLI Publish iOS app using Xcode via iTunes Connect Build Windows Phone app using PhoneGap Build The purpose of this article is to provide ideas on what to look for and some "gotchas". Apple, Google, and Microsoft are constantly updating their platforms and processes so the steps may not look exactly the same over time. Building and publishing an app for iOS Publishing on App Store could be a frustrating process if you are not well prepared upfront. In this section, you will walk through the steps to properly configure everything in Apple Developer Center, iTunes Connect and local Xcode Project. Getting ready You must register for Apple Developer Program in order to access https://developer.apple.com and https://itunesconnect.apple.com because those websites will require an approved account. In addition, the instructions given next use the latest version of these components: Mac OS X Yosemite 10.10.4 Xcode 6.4 Ionic CLI 1.6.4 Cordova 5.1.1 How to do it Here are the instructions: Make sure you are in the app folder and build for the iOS platform. $ ionic build ios Go to the ios folder under platforms/ to open the .xcodeproj file in Xcode. Go through the General tab to make sure you have correct information for everything, especially Bundle Identifier and Version. Change and save as needed. Visit Apple Developer website and click on Certificates, Identifiers & Profiles. For iOS apps, you just have to go through the steps in the website to fill out necessary information. The important part you need to do correctly here is to go to Identifiers | App IDs because it must match your Bundle Identifier in Xcode. Visit iTunes Connect and click on the My Apps button. Select the Plus (+) icon to click on New iOS App. Fill out the form and make sure to select the right Bundle Identifier of your app. There are several additional steps to provide information about the app such as screenshots, icons, addresses, and so on. If you just want to test the app, you could just provide some place holder information initially and come back to edit later. That's it for preparing your Developer and iTunes Connect account. Now open Xcode and select iOS Device as the archive target. Otherwise, the archive feature will not turn on. You will need to archive your app before you can submit it to the App Store. Navigate to Product | Archive in the top menu. After the archive process completed, click on Submit to App Store to finish the publishing process. At first, the app could take an hour to appear in iTunes Connect. However, subsequent submission will go faster. You should look for the app in the Prerelease tab in iTunes Connect. iTunes Connect has very nice integration with TestFlight to test your app. You can switch on and off this feature. Note that for each publish, you have to change the version number in Xcode so that it won't conflict with existing version in iTunes Connect. For publishing, select Submit for Beta App Review. You may want to go through other tabs such as Pricing and In-App Purchases to configure your own requirements. How it works Obviously this section does not cover every bit of details in the publishing process. In general, you just need to make sure your app is tested thoroughly, locally in a physical device (either via USB or TestFlight) before submitting to the App Store. If for some reason the Archive feature doesn't build, you could manually go to your local Xcode folder to delete that specific temporary archived app to clear cache: ~/Library/Developer/Xcode/Archives See also TestFlight is a separate subject by itself. The benefit of TestFlight is that you don't need your app to be approved by Apple in order to install the app on a physical device for testing and development. You can find out more information about TestFlight here: https://developer.apple.com/library/prerelease/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/BetaTestingTheApp.html Building and publishing an app for Android Building and publishing an Android app is a little more straightforward than iOS because you just interface with the command line to build the .apk file and upload to Google Play's Developer Console. Ionic Framework documentation also has a great instruction page for this: http://ionicframework.com/docs/guide/publishing.html. Getting ready The requirement is to have your Google Developer account ready and login to https://play.google.com/apps/publish. Your local environment should also have the right SDK as well as keytool, jarsigner, and zipalign command line for that specific version. How to do it Here are the instructions: Go to your app folder and build for Android using this command: $ ionic build --release android You will see the android-release-unsigned.apk file in the apk folder under /platforms/android/build/outputs. Go to that folder in the Terminal. If this is the first time you create this app, you must have a keystore file. This file is used to identify your app for publishing. If you lose it, you cannot update your app later on. To create a keystore, type the following command in the command line and make sure it's the same keytool version of the SDK: $ keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000 Once you fill out the information in the command line, make a copy of this file somewhere safe because you will need it later. The next step is to use that file to sign your app so it will create a new .apk file that Google Play allow users to install: $ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore HelloWorld-release-unsigned.apk alias_name To prepare for final .apk before upload, you must package it using zipalign: $ zipalign -v 4 HelloWorld-release-unsigned.apk HelloWorld.apk Log in to Google Developer Console and click on Add new application. Fill out as much information as possible for your app using the left menu. Now you are ready to upload your .apk file. First is to perform a beta testing. Once you are completed with beta testing, you can follow Developer Console instructions to push the app to Production. How it works This section does not cover other Android marketplaces such as Amazon Appstore because each of them has different processes. However, the common idea is that you need to completely build the unsigned version of the apk folder, sign it using existing or new keystore file, and finally zipalign to prepare for upload. Using PhoneGap Build for cross-platform Adobe PhoneGap Build is a very useful product that provides build-as-a-service in the cloud. If you have trouble building the app locally in your computer, you could upload the entire Ionic project to PhoneGap Build and it will build the app for Apple, Android, and Windows Phone automatically. Getting ready Go to https://build.phonegap.com and register for a free account. You will be able to build one private app for free. For additional private apps, there is monthly fee associated with the account. How to do it Here are the instructions: Zip your entire /www folder and replace cordova.js to phonegap.js in index.html as described in http://docs.build.phonegap.com/en_US/introduction_getting_started.md.html#Getting%20Started%20with%20Build. You may have to edit config.xml to ensure all plugins are included. Detailed changes are at PhoneGap documentation: http://docs.build.phonegap.com/en_US/configuring_plugins.md.html#Plugins. Select Upload a .zip file under private tab. Upload the .zip file of the www folder. Make sure to upload appropriate key for each platform. For Windows Phone, upload publisher ID file. After that, you just build the app and download the completed build file for each platform. How it works In a nutshell, PhoneGap Build is a convenience way when you are only familiar with one platform during development process but you want your app to be built quickly for other platforms. Under the hood, PhoneGap Build has its own environment to automate the process for each user. However, the user still has to own the responsibility of providing key file for signing the app. PhoneGap Build just helps attach the key to your app. See also The common issue people usually face with when using PhoneGap Build is failure to build. You may want to refer to their documentation for troubleshooting: http://docs.build.phonegap.com/en_US/support_failed-builds.md.html#Failed%20Builds Summary This article provided you with general information about tasks related to building and publishing apps for Android, for iOS and for cross-platform using PhoneGap, wherein you came to know how to publish an app in various places such as App Store and Google Play. Resources for Article: Further resources on this subject: Our App and Tool Stack[article] Directives and Services of Ionic[article] AngularJS Project [article]
Read more
  • 0
  • 0
  • 5248
article-image-learning-nodejs-mobile-application-development
Packt
23 Sep 2015
5 min read
Save for later

Learning Node.js for Mobile Application Development

Packt
23 Sep 2015
5 min read
  In Learning Node.js for Mobile Application Development by Christopher Svanefalk and Stefan Buttigieg, the overarching goal of this article is to give you the tools and know-how to install Node.js on multiple OS platforms and how to verify the installation. After reading this article you will know how to install, configure and use the fundamental software components. You will also have a good understanding of why these tools are appropriate for developing modern applications. (For more resources related to this topic, see here.) Why Node.js? Modern apps have several requirements which cannot be provided by the app itself, such as central data storage, communication routing, and user management. In order to provide such services, apps rely on an external software component known as a backend. The backend we will use for this is Node.js, a powerful but strange beast in its category. Node.js is known for being both reliable and highly performing. Node.js comes with its own package management system, NPM (Node Package Manager), through which you can easily install, remove and manage packages for your project. What this article covers? This article covers the installation of Node.js on multiple OS platforms and how to verify the installation. The installation Node.js is delivered as a set of JavaScript libraries, executing on a C/C++ runtime that is built around the Google V8 JavaScript Engine. The two come bundled together for most major operating systems, and we will look at the specifics of installing it. Google V8 JavaScript Engine is the same JavaScript engine that is used in the Chrome browser, built for speed and efficiency. Windows For Windows, there is a dedicated MSI wizard that can be used to install Node.js, which can be downloaded from the project's official website. To do so, go to the main page, navigate to Downloads, and then select Windows Installer. After it is downloaded, run MSI, follow the steps given to select the installation options, and conclude the install. Keep in mind that you will need to restart your system in order to make the changes effective. Linux Most major Linux distributions provide convenient installs of Node.js through their own package management systems. However, it is important to keep in mind that for many of them, NPM will not come bundled with the main Node.js package. Rather, it will be provided as a separate package. We will show how to install both in the following section. Ubuntu/Debian Open a terminal and issue sudo apt-get update to make sure that you have the latest package listings. After this, issue apt-get install nodejsnpm in order to install both Node.js and NPM in one swoop. Fedora/RHEL/CentOS On Fedora 18 or later, open a terminal and issue sudo yum install nodejsnpm. The system will perform the full setup for you. If you are running RHEL or CentOS, you will need to enable the optional EPEL repository. This can be done in conjunction with the install process, so that you do not need to do it again while upgrading, by issuing the sudo yum install nodejsnpm --enablerepo=epel command. Verifying your installation Now that we have finished the install, let's do a sanity check and make sure that everything works as expected. To do so, we can use the Node.js shell, which is an interactive runtime environment that is used to execute JavaScript code. To open it, first open a terminal, and then issue the following on it: node This will start the interpreter, which will appear as a shell, with the input line starting with the > sign. Once you are in it, type the following: console.log(“Hello world!); Then, press Enter. The Hello world! phrase will appear on the next line. Congratulations, your system is now set up to run Node.js! Mac OS X For Mac OS X, you can find a ready-to-install PKG file by going to www.nodejs.org, navigating to Downloads, and selecting the Mac OS X Installer option. Otherwise, you can click on Install, and your package file will automatically be downloaded as shown in the followin screenshot: Once you have downloaded the file, run it and follow the instructions on the screen. It is recommended that you keep all the offered default settings, unless there are compelling reasons for you to change something with regard to your specific machine. Verifying your installation for Mac OS X After the install finishes, open a terminal and start the Node.js shell by issuing the following command: node This will start the interactive node shell where you can execute JavaScript code. To make sure that everything works, try issuing the following command to the interpreter: console.log(“hello world!”); After pressing Enter, the Hello world! phrase will appear on your screen. Congratulations, Node.js is all set up and good to go! Who this article is written for Intended for web developers of all levels of expertise who want to deep dive into cross-platform mobile application development without going through the pains of understanding the languages and native frameworks which form an integral part of developing for different mobile platforms. This article will provide the readers with the necessary basic idea to develop mobile applications with near-native functionality and help them understand the process to develop a successful cross-platform mobile application. Summary In this article, we learned the different techniques that can be used to install Node.js across different platforms. Read Learning Node.js for Mobile Application Development to dive into cross-platform mobile application development. The following are some other related titles: Node.js Design Patterns Web Development with MongoDB and Node.js Deploying Node.js Node Security Resources for Article: Further resources on this subject: Welcome to JavaScript in the full stack[article] Introduction and Composition[article] Deployment and Maintenance [article]
Read more
  • 0
  • 0
  • 5564

article-image-application-development-workflow
Packt
08 Sep 2015
15 min read
Save for later

Application Development Workflow

Packt
08 Sep 2015
15 min read
 In this article by Ivan Turkovic, author of the book PhoneGap Essentials, you will learn some of the basics on how to work with the PhoneGap application development and how to start building the application. We will go over some useful steps and tips to get the most out of your PhoneGap application. In this article, you will learn the following topics: An introduction to a development workflow Best practices Testing (For more resources related to this topic, see here.) An introduction to a development workflow PhoneGap solves a great problem of developing mobile applications for multiple platforms at the same time, but still it is pretty much open about how you want to approach the creation of an application. You do not have any predefined frameworks that come out of-the-box by default. It just allows you to use the standard web technologies such as the HTML5, CSS3, and JavaScript languages for hybrid mobile application development. The applications are executed in wrappers that are custom-built to work on every platform and the underlying web view behaves in the same way on all the platforms. For accessing device APIs, it relies on the standard API bindings to access every device's sensors or the other features. The developers who start using PhoneGap usually come from different backgrounds, as shown in the following list: Mobile developers who want to expand the functionality of their application on other platforms but do not want to learn a new language for each platform Web developers who want to port their existing desktop web application to a mobile application; if they are using a responsive design, it is quite simple to do this Experienced mobile developers who want to use both the native and web components in their application, so that the web components can communicate with the internal native application code as well The PhoneGap project itself is pretty simple. By default, it can open an index.html page and load the initial CSS file, JavaScript, and other resources needed to run it. Besides the user's resources, it needs to refer the cordova.js file, which provides the API bindings for all the plugins. From here onwards, you can take different steps but usually the process falls in two main workflows: web development workflow and native platform development. Web project development A web project development workflow can be used when you want to create a PhoneGap application that runs on many mobile operating systems with as little as possible changes to a specific one. So there is a single codebase that is working along with all the different devices. It has become possible with the latest versions since the introduction of the command-line interface (CLI). This automates the tedious work involved in a lot of the functionalities while taking care of each platform, such as building the app, copying the web assets in the correct location for every supported platform, adding platform-specific changes, and finally running build scripts to generate binaries. This process can be automated even more with build system automating tasks such as Gulp or Grunt. You can run these tasks before running PhoneGap commands. This way you can optimize the assets before they are used. Also you can run JSLint automatically for any change or doing automatic builds for every platform that is available. Native platform development A native platform development workflow can be imagined as a focus on building an application for a single platform and the need to change the lower-level platform details. The benefit of using this approach is that it gives you more flexibility and you can mix the native code with a WebView code and impose communication between them. This is appropriate for those functionalities that contain a section of the features that are not hard to reproduce with web views only; for example, a video app where you can do the video editing in the native code and all the social features and interaction can be done with web views. Even if you want to start with this approach, it is better to start the new project as a web project development workflow and then continue to separate the code for your specific needs. One thing to keep in mind is that, to develop with this approach, it is better to develop the application in more advanced IDE environments, which you would usually use for building native applications. Best practices                            The running of hybrid mobile applications requires some sacrifices in terms of performance and functionality; so it is good to go over some useful tips for new PhoneGap developers. Use local assets for the UI As mobile devices are limited by the connection speeds and mobile data plans are not generous with the bandwidth, you need to prepare all the UI components in the application before deploying to the app store. Nobody will want to use an application that takes a few seconds to load the server-rendered UI when the same thing could be done on the client. For example, the Google Fonts or other non-UI assets that are usually loaded from the server for the web applications are good enough as for the development process, but for the production; you need to store all the assets in the application's container and not download them during its run process. You do not want the application to wait while an important part is being loaded. The best advice on the UI that I can give you is to adopt the Single Page Application (SPA) design; it is a client-side application that is run from one request from a web page. Initial loading means taking care of loading all the assets that are required for the application in order to function, and any further updates are done via AJAX (such as loading data). When you use SPA, not only do you minimize the amount of interaction with the server, you also organize your application in a more efficient manner. One of the benefits is that the application doesn't need to wait for every deviceready event for each additional page that it loads from the start. Network access for data As you have seen in the previous section, there are many limitations that mobile applications face with the network connection—from mobile data plans to the network latency. So you do not want it to rely on the crucial elements, unless real-time communication is required for the application. Try to keep the network access only to access crucial data and everything else that is used frequently can be packed into assets. If the received data does not change often, it is advisable to cache it for offline use. There are many ways to achieve this, such as localStorage, sessionStorage, WebSQL, or a file. When loading data, try to load only the data you need at that moment. If you have a comment section, it will make sense if you load all thousand comments; the first twenty comments should be enough to start with. Non-blocking UI When you are loading additional data to show in the application, don't try to pause the application until you receive all the data that you need. You can add some animation or a spinner to show the progress. Do not let the user stare at the same screen when he presses the button. Try to disable the actions once they are in motion in order to prevent sending the same action multiple times. CSS animations As most of the modern mobile platforms now support CSS3 with a more or less consistent feature set, it is better to make the animations and transitions with CSS rather than with the plain JavaScript DOM manipulation, which was done before CSS3. CSS3 is much faster as the browser engine supports the hardware acceleration of CSS animations and is more fluid than the JavaScript animations. CSS3 supports translations and full keyframe animations as well, so you can be really creative in making your application more interactive. Click events You should avoid click events at any cost and use only touch events. They work in the same way as they do in the desktop browser. They take a longer time to process as the mobile browser engine needs to process the touch or touchhold events before firing a click event. This usually takes 300 ms, which is more than enough to give an additional impression of slow responses. So try to start using touchstart or touchend events. There is a solution for this called FastClick.js. It is a simple, easy-to-use library for eliminating the 300 ms delay between a physical tap and the firing of a click event on mobile browsers. Performance The performance that we get on the desktops isn't reflected in mobile devices. Most of the developers assume that the performance doesn't change a lot, especially as most of them test the applications on the latest mobile devices and a vast majority of the users use mobile devices that are 2-3 years old. You have to keep in mind that even the latest mobile devices have a slower CPU, less RAM, and a weaker GPU. Recently, mobile devices are catching up in the sheer numbers of these components but, in reality, they are slower and the maximum performance is limited due to the battery life that prevents it from using the maximum performance for a prolonged time. Optimize the image assets We are not limited any more by the app size that we need to deploy. However, you need to optimize the assets, especially images, as they take a large part of the assets, and make them appropriate for the device. You should prepare images in the right size; do not add the biggest size of the image that you have and force the mobile device to scale the image in HTML. Choosing the right image size is not an easy task if you are developing an application that should support a wide array of screens, especially for Android that has a very fragmented market with different screen sizes. The scaled images might have additional artifacts on the screen and they might not look so crisp. You will be hogging additional memory just for an image that could leave a smaller memory footprint. You should remember that mobile devices still have limited resources and the battery doesn't last forever. If you are going to use PhoneGap Build, you will need to make sure you do not exceed the limit as the service still has a limited size. Offline status As we all know, the network access is slow and limited, but the network coverage is not perfect so it is quite possible that your application will be working in the offline mode even in the usual locations. Bad reception can be caused by being inside a building with thick walls or in the basement. Some weather conditions can affect the reception too. The application should be able to handle this situation and respond to it properly, such as by limiting the parts of the application that require a network connection or caching data and syncing it when you are online once again. This is one of the aspects that developers usually forget to test in the offline mode to see how the app behaves under certain conditions. You should have a plugin available in order to detect the current state and the events when it passes between these two modes. Load only what you need There are a lot of developers that do this, including myself. We need some part of the library or a widget from a framework, which we don't need for anything other than this, and yet we are a bit lazy about loading a specific element and the full framework. This can load an immense amount of resources that we will never need but they will still run in the background. It might also be the root cause of some of the problems as some libraries do not mix well and we can spend hours trying to solve this problem. Transparency You should try to use as little as possible of the elements that have transparent parts as they are quite processor-intensive because you need to update screen on every change behind them. The same things apply to the other visual elements that are processor-intensive such as shadows or gradients. The great thing is that all the major platforms have moved away from flashy graphical elements and started using the flat UI design. JSHint If you use JSHint throughout the development, it will save you a lot of time when developing things in JavaScript. It is a static code analysis tool for checking whether the JavaScript source code complies with the coding rules. It will detect all the common mistakes done with JavaScript, as JavaScript is not a compiled language and you can't see the error until you run the code. At the same time, JSHint can be a very restrictive and demanding tool. Many beginners in JavaScript, PhoneGap, or mobile programming could be overwhelmed with the number of errors or bad practices that JSHint will point out. Testing The testing of applications is an important aspect of build applications, and mobile applications are no exception. With a slight difference for most of the development that doesn't require native device APIs, you can use the platform simulators and see the results. However, if you are using the native device APIs that are not supported through simulators, then you need to have a real device in order to run a test on it. It is not unusual to use desktop browsers resized to mobile device screen resolution to emulate their screen while you are developing the application just to test the UI screens, since it is much faster and easier than building and running the application on a simulator or real device for every small change. There is a great plugin for the Google Chrome browser called Apache Ripple. It can be run without any additional tools. The Apache Ripple simulator runs as a web app in the Google Chrome browser. In Cordova, it can be used to simulate your app on a number of iOS and Android devices and it provides basic support for the core Cordova plugins such as Geolocation and Device Orientation. You can run the application in a real device browser or use the PhoneGap developer app. This simplifies the workflow as you can test the application on your mobile device without the need to re-sign, recompile, or reinstall your application to test the code. The only disadvantage is that with simulators, you cannot access the device APIs that aren't available in the regular web browsers. The PhoneGap developer app allows you to access device APIs as long as you are using one of the supplied APIs. It is good if you remember to always test the application on real devices at least before deploying to the app store. Computers have almost unlimited resources as compared to mobile devices, so the application that runs flawlessly on the computer might fail on mobile devices due to low memory. As simulators are faster than the real device, you might get the impression that it will work on every device equally fast, but it won't—especially with older devices. So, if you have an older device, it is better to test the response on it. Another reason to use the mobile device instead of the simulator is that it is hard to get a good usability experience from clicking on the interface on the computer screen without your fingers interfering and blocking the view on the device. Even though it is rare that you would get some bugs with the plain PhoneGap that was introduced with the new version, it might still happen. If you use the UI framework, it is good if you try it on the different versions of the operating systems as they might not work flawlessly on each of them. Even though hybrid mobile application development has been available for some time, it is still evolving, and as yet there are no default UI frameworks to use. Even the PhoneGap itself is still evolving. As with the UI, the same thing applies to the different plugins. Some of the features might get deprecated or might not be supported, so it is good if you implement alternatives or give feedback to the users about why this will not work. From experience, the average PhoneGap application will use at least ten plugins or different libraries for the final deployment. Every additional plugin or library installed can cause conflicts with another one. Summary In this article, we learned more advanced topics that any PhoneGap developer should get into more detail once he/she has mastered the essential topics. Resources for Article: Further resources on this subject: Building the Middle-Tier[article] Working with the sharing plugin[article] Getting Ready to Launch Your PhoneGap App in the Real World [article]
Read more
  • 0
  • 0
  • 4209