Monday, August 15, 2016

The Definitive Ionic Starter Guide_part1


Ionic is a powerful tool for building mobile apps using HTML, CSS, and JavaScript. This starter guide focuses on getting up and running with a realistic mobile app and learning about the primary features of Ionic. You will see many features of Ionic with a complete app example. You will end this guide with a strong command of Ionic and how it enables you to build beautiful, functional mobile apps.

Ionic is build on Angular and I will assume you are at least moderately familiar with Angular and the basics of web applications. If you need some help with Angular, I recommend reading up from Todd Motto's post AngularJS Tutorial: A Comprehensive 10,000 Word Guide. It is also assumed you have NodeJS installed.

During this guide you will learn how to build a stock tracking app. You can preview the completed app here and see the whole project on GitHub at gnomeontherun/ionic-definitive-guide. You can resize the browser to see how it would appear on mobile, or if you are using Chrome you can use the device emulator feature.

Ionic, the missing hybrid app SDK

Ionic is built for hybrid apps, which are mobile apps that are written with HTML, CSS, and JavaScript instead of the native platform languages (Java for Android, Swift for iOS). When building a native app, you have access to the SDK which includes interface components such as tabs and complex lists. These are the interface controls that you are familiar with from using mobile apps, and Ionic provides a comprehensive set of components for building hybrid apps.

However, Ionic is really more than just interface components. It also:
  • provides a fantastic CLI utility for managing projects.
  • leverages SASS to easily customize components.
  • is built and maintained by a professional team, with a strong community behind it.
  • incorporates a pluggable architecture for including 'ions' (additional components and features).
  • has a large set of icons.
In addition, Ionic has a whole platform of services to support your apps, such as Creator for a visual drag/drop design experience, View for sharing a preview/beta version of your app with anyone, and Push for easily setting up push notifications. Recently added were Deploy and Analytics which are currently in alpha. You can expect Ionic to expand into a complete platform to serve the needs of app developers.

Setup for Ionic

First things first, we need to get Ionic setup. For this tutorial we will be previewing in a browser, not on a mobile device (though you could if you follow the Ionic guide for details on emulating and loading to a device). You need to have Node installed on your system before you can install Ionic (Note: io.js may not work properly). Run this command in the terminal/command line prompt:
  1. $ npm install -g ionic
Here the Node Package Manager (NPM) will download and install the Ionic CLI. This is essential for building Ionic apps, and we'll use it to setup, preview, and build our app. Let's cover the primary types of features in Ionic.

Ionic components & services

Before we jump into building our app, I wanted to give you a quick overview of Ionic's primary features. Ionic provides two primary features: components and services.

Ionic Components

Components are the user interface elements you declare using markup and CSS classes such as tabs, headers, slideshows, side menus, and more. These components either just CSS classes (like CSS frameworks like Bootstrap) or Angular directives. Some of the CSS components (inputcardbutton) don't provide additional features, but Ionic provides nicer styling that works well on mobile. The directive components (sidemenulistslidebox) are available as HTML tags.
  1. <!-- Card: CSS Component Example -->
  2. <div class="list card">
  3.   <div class="item">Basic card!</div>
  4. </div>
  5. <!-- SlideBox: Directive Component Example -->
  6. <ion-slide-box slide-interval="10000" does-continue="true">
  7.   <ion-slide>Slide 1</ion-slide>
  8.   <ion-slide>Slide 2</ion-slide>
  9. </ion-slide-box>
In the two examples above, the first is a visual card like you might see in many apps like Google Now. It is created simply by using the CSS classes. The second is a slide box, which is a directive and declared using HTML tags. The slide box example could also include attributes, like slide-interval which provide configuration to the slide box (in this case sets the length of time for each slide to display).

Ionic Services

Services are programmatic user interface elements that are declared in JavaScript, and are provided using Angular's services architecture. These are typically used in your controllers, and provide interface elements that have a limited display time (such as modals, popups, loaders). Just like Angular services, Ionic services all start with a $ and are very clearly named like you see in this example.
  1. function Controller($scope, $ionicSlideBoxDelegate) {
  2.   $scope.next = function() {
  3.     $ionicSlideBoxDelegate.next();
  4.   }
  5.   $scope.previous = function() {
  6.     $ionicSlideBoxDelegate.previous();
  7.   }
  8. }
In this controller, there are two scope methods that control the slide box using the service (any services that manage a component are delegate services). This would allow any custom button to change the slide, such as these two buttons.
  1. <button ng-click="next()">Next</button>
  2. <button ng-click="previous()">Previous</button>
Other services create a new visual experience, such as the loader. These will inject content into the current view as needed to create the desired effect, in this case a loading screen will overlay with a message, and after 2 seconds it will automatically hide.
  1. function Controller($timeout, $ionicLoading) {
  2.   $ionicLoading.show({
  3.     template: 'Loading'
  4.   });
  5.   $timeout(function() {
  6.     $ionicLoading.hide();
  7.   }, 2000);
  8. }
Now let's get a new project started and see these things in action. If at any point you want to see the entire codebase, check out the gnomeontherun/ionic-definitive-guide GitHub project.

Starting the Ionic project

Let the fun begin! The first thing is to generate a new project. The Ionic CLI we installed before will help us do this.
  1. $ ionic start stocks https://github.com/ionic-in-action/starter
  2. $ cd stocks
  3. $ ionic serve
This will create a new blank app called stocks based on the starter app I created for my book Ionic in Action. This starter app is blank, and is ideal for new projects. The ionic serve command should have opened up the new app in your browser, and it will be just a blank page. Have no fear, we shall fix that now.

Setting up Sass for styling

Ionic comes with a very helpful feature for customizing the default components and color presets using Sass. It is also recommended that you write any custom styles in the same way to take advantage of the variables and auto-generation provided by Ionic.

Sass support is not enabled by default in a new project. The following command will setup your project with Sass support.
  1. $ ionic setup sass
This command will generate a new CSS file based on the the scss/ionic.app.scss file, and output it to css/ionic.app.css. Then it will update the index.html file to load the new CSS file instead. Lastly, it enables Ionic commands to automatically regenerate the styles when you build the app, so you can't forget.

This app needs some styling, so you'll need to replace the contents of scss/ionic.app.scss file with the following
  1. // Override variables
  2. $light: #eee;
  3. $lighter: #fff;
  4. $darker: #363636;
  5. $assertive: #B33F33;
  6. $balanced: #70AB23;
  7. $positive: #366091;

  8. // The path for our ionicons font files, relative to the built CSS in www/css
  9. $ionicons-font-path: "../lib/ionic/fonts" !default;

  10. // Include all of Ionic
  11. @import "www/lib/ionic/scss/ionic";

  12. // Dark theme for backgrounds.
  13. .view, .pane, .modal {
  14.   background: $darker;
  15. }

  16. // Style the form inside of the footer bar
  17. .bar-search {
  18.   padding: 0;

  19.   form {
  20.     display: block;
  21.     width: 100%;
  22.   }

  23.   .item {
  24.     padding: 3.75px 10px;
  25.   }
  26. }

  27. // Make the item-dark style default by extending it back on top of item
  28. .item {
  29.   @extend .item-dark;
  30. }

  31. //
  32. .item-reorder .button.icon {
  33.   color: $light;
  34. }

  35. // Make inputs nicer on dark
  36. .item-input {

  37.   input {
  38.     margin-right: 30px;
  39.     padding-left: 5px;
  40.     background: $darker;
  41.     color: $light;
  42.   }
  43.   .input-label {
  44.     color: $light;
  45.   }
  46. }
  47. .item-input-wrapper {
  48.   background: $darker;

  49.   input {
  50.     color: $lighter;
  51.   }
  52. }

  53. // Make toggler nicer on dark
  54. .toggle .track {
  55.   background-color: $dark;
  56.   border-color: $darker;
  57. }
  58. .toggle input:checked + .track {
  59.   background-color: $positive;
  60.   border-color: $positive;
  61. }

  62. // Remove a bottom border
  63. .tabs-striped .tabs {
  64.   border-bottom: 0;
  65. }

  66. // Allow the tabs to be as wide as needed
  67. .tab-item {
  68.   max-width: none;
  69. }

  70. // Styles for the quote component
  71. .quote {
  72.   background: $darker;
  73.   padding: 5px;
  74.   border-radius: 4px;
  75.   display: block;
  76.   position: absolute;
  77.   top: 1px;
  78.   right: 16px;
  79.   text-align: center;
  80.   width: 90px;
  81.   height: 50px;
  82.   color: $lighter;

  83.   .spinner svg {
  84.     margin-top: 6px;
  85.   }

  86.   &.positive {
  87.     background: $balanced;
  88.     color: $light;
  89.   }

  90.   &.negative {
  91.     background: $assertive;
  92.     color: $light;
  93.   }

  94.   .quote-change {
  95.     font-size: 0.8em;
  96.     color: $light;
  97.   }
  98. }
At the top of these styles, several of the Ionic variables are overridden with some custom colors (so it doesn't appear with the same colors as default Ionic). The rest of the styles are commented, and it will be easier to review all of the styles when the app is completed.

This will setup all of the styles for the app. It is best to start a project setting up Sass in your project at the start, though you can do it anytime. Next you will start getting the base navigation setup.

Add Ionic's navigation components

Navigation is core to all apps, and Ionic provides several components that can be useful for navigation. In this example, you will use the ionTabs and ionNavView components to have tabs at the bottom to navigate between views. The ionSideMenus component is often used as well to expose a list of links for navigation.

Ionic is built using the popular ui-router project, which is an enhanced replacement for Angular's core ngRoute component. Ionic adds another layer of enhancements on top of ui-router, which is baked into the Ionic components and services. It leverages the idea of declaring states, which is a place in the app that describes the associated controller, view, template, and possibly other details. It will become more clear as we see some examples.

The ionNavView component is typically the center of an Ionic app's navigation, and works with several other components to allow you to craft intuitive navigation. Open up the www/index.html file, and update the body of the HTML file with the following.
  1. <body ng-app="App">
  2.   <!-- The ionNavBar updates the title and buttons as the application state changes -->
  3.   <ion-nav-bar class="bar-positive">
  4.     <!-- The ionNavBackButton knows when to show or hide based on current state -->
  5.     <ion-nav-back-button></ion-nav-back-button>
  6.   </ion-nav-bar>
  7.   <!-- Primary ionNavView which will load the views -->
  8.   <ion-nav-view></ion-nav-view>
  9. </body>
Here you see three components, ionNavViewionNavBar, and ionNavBackButton. The ionNavBar will contain our header content, such as the ionNavBackButton and a title of the state. Navbars are very common in apps, and it will automatically update the title as the user navigates between views, and conditionally show the ionNavBackButton when a user is allowed to go back in the history.

Now if you save these changes and look at the browser, you'll now see a blue navbar along the top. The ionNavBar is gray by default, but with the class bar-positive added it adopts a new color. There is a set of color presets that many components can adopt, and you'll see them sprinkled in throughout the example.

This app isn't very impressive yet, because we haven't defined any states to actually load (hence the blank screen). Let's get the first state setup.

Add the tabs state

It is time to add the tabs state's template. You will declare the template which will contain the tabs component with two tabs for the tabs.quotes and tabs.portfolio states. You'll need to create a new file at www/views/tabs/tabs.html and add the following markup.
  1. <!-- ionTabs wraps the ionTab directives -->
  2. <ion-tabs class="tabs-icon-top tabs-dark tabs-striped">
  3.   <!-- Quotes tab -->
  4.   <ion-tab title="Quotes" icon-on="ion-ios-pulse-strong" icon-off="ion-ios-pulse" ui-sref="tabs.quotes">
  5.     <!-- ionNavView for the quotes tab -->
  6.     <ion-nav-view name="quotes"></ion-nav-view>
  7.   </ion-tab>
  8.   <!-- Portfolio tab -->
  9.   <ion-tab title="Portfolio" icon-on="ion-ios-paper" icon-off="ion-ios-paper-outline" ui-sref="tabs.portfolio">
  10.     <!-- ionNavView for the portfolio tab -->
  11.     <ion-nav-view name="portfolio"></ion-nav-view>
  12.   </ion-tab>
  13. </ion-tabs>
Here the ionTabs component contains two ionTab components, which will display two tabs. The attributes declare icons for when the tab is active and inactive, as well as a link to a particular state using the ui-sref attribute. Inside of each ionTab are ionNavView components, which must have a name. You can only have one ionNavView that is unnamed, and that is in the index.html (which acts as the default). Later, when you create the tab states you will declare the states to match these specific ionNavViews. This is done so that each tab can have its own, independent navigation history, which will be demonstrated in depth later.

Now create www/views/tabs/tabs.js and add the following JavaScript that declares the 'tabs' state. Be sure to also add a script tag to the index.html file after the app.js file to load this file.
  1. angular.module('App')

  2. .config(function($stateProvider) {
  3.   $stateProvider
  4.     .state('tabs', {
  5.       abstract: true,
  6.       url: '/tabs',
  7.       templateUrl: 'views/tabs/tabs.html'
  8.     });
  9. });
The tabs view is abstract, which is unique to properly support nested ionNavView components like you see inside of the tabs. This simply means you will never go directly to just the tabs state, but in fact you will always go to a child state. Or in other words, it means you always go directly to one of the two tabs because it doesn't make sense to navigate to the tabs without one of them being selected.
Sadly, yet again, the screen is blank. Remember, the tabs view is abstract so you can only see it when you navigate to a child tab state. That means the next step is to create the first child tab state, so the tabs will appear and some very interesting things will start to happen.

Add the services

But wait! You will need some Angular services to help manage data for this app, and I don't want to spend a lot of time on them. You can look through the comments to see how they work, and they should be familiar to Angular developers. The first is a simple service to help manage data in localStorage, and the second is a service to load stock quotes from Yahoo! Finance. If you are not familiar with Angular and creating services, it is best to take a moment and review the Angular services documentation.

First create the file www/js/localstorage.js with the content in the code below. Be sure to add a script tag to index.html.
  1. angular.module('App')

  2. .factory('LocalStorageService', function() {

  3.   // Helper methods to manage an array of data through localstorage
  4.   return {
  5.     // This pulls out an item from localstorage and tries to parse it as JSON strings
  6.     get: function LocalStorageServiceGet(key, defaultValue) {
  7.       var stored = localStorage.getItem(key);
  8.       try {
  9.         stored = angular.fromJson(stored);
  10.       } catch(error) {
  11.         stored = null;
  12.       }
  13.       if (defaultValue && stored === null) {
  14.         stored = defaultValue;
  15.       }
  16.       return stored;
  17.     },
  18.     // This stores data into localstorage, but converts values to a JSON string first
  19.     update: function LocalStorageServiceUpdate(key, value) {
  20.       if (value) {
  21.         localStorage.setItem(key, angular.toJson(value));
  22.       }
  23.     },
  24.     // This will remove a key from localstorage
  25.     clear: function LocalStorageServiceClear(key) {
  26.       localStorage.removeItem(key);
  27.     }
  28.   };

  29. });
Now create another file at www/js/quotes.js with the following code. Again, make sure to add a script tag to the index.html.
  1. angular.module('App')

  2. .factory('QuotesService', function($q, $http) {

  3.   // Create a quotes service to simplify how to load data from Yahoo Finance
  4.   var QuotesService = {};

  5.   QuotesService.get = function(symbols) {
  6.     // Convert the symbols array into the format required for YQL
  7.     symbols = symbols.map(function(symbol) {
  8.       return "'" + symbol.toUpperCase() + "'";
  9.     });
  10.     // Create a new deferred object
  11.     var defer = $q.defer();
  12.     // Make the http request
  13.     $http.get('https://query.yahooapis.com/v1/public/yql?q=select * from yahoo.finance.quotes where symbol in (' + symbols.join(',') + ')&format=json&env=http://datatables.org/alltables.env').success(function(quotes) {
  14.       // The API is funny, if only one result is returned it is an object, multiple results are an array. This forces it to be an array for consistency
  15.       if (quotes.query.count === 1) {
  16.         quotes.query.results.quote = [quotes.query.results.quote];
  17.       }
  18.       // Resolve the promise with the data
  19.       defer.resolve(quotes.query.results.quote);
  20.     }).error(function(error) {
  21.       // If an error occurs, reject the promise with the error
  22.       defer.reject(error);
  23.     });
  24.     // Return the promise
  25.     return defer.promise;
  26.   };

  27.   return QuotesService;
  28. });
                                                              Ok, now it is time to create the quotes state, which leverages these two services.
                                                              (continue)

                                                              If you found this post interesting, follow and support us.
                                                              Suggest for you:

                                                              Android Application Programming - Build 20+ Android Apps

                                                              The Complete Android Developer Course: Beginner To Advanced!

                                                              Android: From Beginner to Paid Professional

                                                              The Complete Android Developer Course - Build 14 Apps

                                                              Python For Android Hacking Crash Course: Trojan Perspective

                                                              Sunday, August 14, 2016

                                                              Google Play Services: Awareness AP_part 2 (end)

                                                              3. Using the Snapshot API

                                                              When you want to gather information about a user's current context, you can use the snapshot functionality of the Awareness API. This API will gather information depending on the type of API call made, and will cache this information for quick access across various apps.

                                                              Headphones
                                                              One of the new additions to Play Services through the Awareness API is the ability to detect a device's headphone state (plugged in or unplugged). This can be done by calling getHeadphoneState() on the Awareness API and reading the HeadphoneState from the HeadphoneStateResult.
                                                              1. private void detectHeadphones() {
                                                              2.     Awareness.SnapshotApi.getHeadphoneState(mGoogleApiClient)
                                                              3.             .setResultCallback(new ResultCallback<HeadphoneStateResult>() {
                                                              4.                 @Override
                                                              5.                 public void onResult(@NonNull HeadphoneStateResult headphoneStateResult) {
                                                              6.                     HeadphoneState headphoneState = headphoneStateResult.getHeadphoneState();
                                                              7.                     if (headphoneState.getState() == HeadphoneState.PLUGGED_IN) {
                                                              8.                         Log.e("Tuts+", "Headphones are plugged in.");
                                                              9.                     } else {
                                                              10.                         Log.e("Tuts+", "Headphones are NOT plugged in.");
                                                              11.                     }
                                                              12.                 }
                                                              13.             });
                                                              14. }
                                                              Once you know the state of the headphones, your app can perform whatever actions are needed based on that information.

                                                              Location

                                                              Although previously available as a component in Google Play Services, the location feature of the Awareness API has been optimized for efficiency and battery usage. Rather than using the traditional Location API and receiving a location at specified intervals, you can request a one-time location snapshot like so.
                                                              1. private void detectLocation() {
                                                              2.     if( !checkLocationPermission() ) {
                                                              3.         return;
                                                              4.     }
                                                              5.  
                                                              6.     Awareness.SnapshotApi.getLocation(mGoogleApiClient)
                                                              7.             .setResultCallback(new ResultCallback<LocationResult>() {
                                                              8.                 @Override
                                                              9.                 public void onResult(@NonNull LocationResult locationResult) {
                                                              10.                     Location location = locationResult.getLocation();
                                                              11.  
                                                              12.                     Log.e("Tuts+", "Latitude: " + location.getLatitude() + ", Longitude: " + location.getLongitude());
                                                              13.  
                                                              14.                     Log.e("Tuts+", "Provider: " + location.getProvider() + " time: " + location.getTime());
                                                              15.  
                                                              16.                     if( location.hasAccuracy() ) {
                                                              17.                         Log.e("Tuts+", "Accuracy: " + location.getAccuracy());
                                                              18.                     }
                                                              19.                     if( location.hasAltitude() ) {
                                                              20.                         Log.e("Tuts+", "Altitude: " + location.getAltitude());
                                                              21.                     }
                                                              22.                     if( location.hasBearing() ) {
                                                              23.                         Log.e("Tuts+", "Bearing: " + location.getBearing());
                                                              24.                     }
                                                              25.                     if( location.hasSpeed() ) {
                                                              26.                         Log.e("Tuts+", "Speed: " + location.getSpeed());
                                                              27.                     }
                                                              28.                 }
                                                              29.             });
                                                              30. }
                                                              As you can see, you will first need to verify that the user has granted the location permission. If they have, you can retrieve a standard Location object with a large amount of data about the user's location and speed, as well as information on the accuracy of this data.

                                                              You will want to verify that a specific piece of information exists before using it, as some data may not be available. Running this code should output all available data to the Android system log.
                                                              1. E/Tuts+: Latitude: 39.9255456, Longitude: -105.02939579999999
                                                              2. E/Tuts+: Provider: Snapshot time: 1468696704662
                                                              3. E/Tuts+: Accuracy: 20.0
                                                              4. E/Tuts+: Altitude: 0.0
                                                              5. E/Tuts+: Speed: 0.0
                                                              Places 

                                                              While not as robust as the standard Places API, the Awareness API does provide a quick and easy to use way to gather information about places near the user. This API call will return a List of PlaceLikelihood objects that contains a Place and a float representing how likely it is that a user is at that place (hence the object's name).

                                                              Each Place object may contain a name, address, phone number, place type, user rating, and other useful information. You can request the nearby places for the user after verifying that the user has the location permission granted.
                                                              1. private void detectNearbyPlaces() {
                                                              2.     if( !checkLocationPermission() ) {
                                                              3.         return;
                                                              4.     }
                                                              5.  
                                                              6.     Awareness.SnapshotApi.getPlaces(mGoogleApiClient)
                                                              7.             .setResultCallback(new ResultCallback<PlacesResult>() {
                                                              8.                 @Override
                                                              9.                 public void onResult(@NonNull PlacesResult placesResult) {
                                                              10.                     Place place;
                                                              11.                     for( PlaceLikelihood placeLikelihood : placesResult.getPlaceLikelihoods() ) {
                                                              12.                         place = placeLikelihood.getPlace();
                                                              13.                         Log.e("Tuts+", place.getName().toString() + "\n" + place.getAddress().toString() );
                                                              14.                         Log.e("Tuts+", "Rating: " + place.getRating() );
                                                              15.                         Log.e("Tuts+", "Likelihood that the user is here: " + placeLikelihood.getLikelihood() * 100 + "%");
                                                              16.                     }
                                                              17.                 }
                                                              18.             });
                                                              19. }
                                                              When running the above method, you should see output similar to the following in the Android console. If a value is not available for a number, -1 will be returned.
                                                              1. E/Tuts+: North Side Tavern
                                                              2.          12708 Lowell Blvd, Broomfield, CO 80020, USA
                                                              3. E/Tuts+: Rating: 4.7
                                                              4. E/Tuts+: Likelihood that the user is here: 10.0%
                                                              5. E/Tuts+: Quilt Store
                                                              6.          12710 Lowell Blvd, Broomfield, CO 80020, USA
                                                              7. E/Tuts+: Rating: 4.3
                                                              8. E/Tuts+: Likelihood that the user is here: 10.0%
                                                              9. E/Tuts+: Absolute Floor Care
                                                              10.          3508 W 126th Pl, Broomfield, CO 80020, USA
                                                              11. E/Tuts+: Rating: -1.0
                                                              Weather
                                                              Another of the new additions to Google Play Services through the Awareness API is the ability to get the weather conditions for a user. This feature also requires the location permission for users on Marshmallow and later.

                                                              Using this request, you will be able to get the temperature in the user's area in either Fahrenheit or Celsius. You can also find out what the temperature feels like, the dew point (the temperature where water in the air can begin to condense into dew), the humidity percentage, and the weather conditions.
                                                              1. private void detectWeather() {
                                                              2.     if( !checkLocationPermission() ) {
                                                              3.         return;
                                                              4.     }
                                                              5.  
                                                              6.     Awareness.SnapshotApi.getWeather(mGoogleApiClient)
                                                              7.             .setResultCallback(new ResultCallback<WeatherResult>() {
                                                              8.                 @Override
                                                              9.                 public void onResult(@NonNull WeatherResult weatherResult) {
                                                              10.                     Weather weather = weatherResult.getWeather();
                                                              11.                     Log.e("Tuts+", "Temp: " + weather.getTemperature(Weather.FAHRENHEIT));
                                                              12.                     Log.e("Tuts+", "Feels like: " + weather.getFeelsLikeTemperature(Weather.FAHRENHEIT));
                                                              13.                     Log.e("Tuts+", "Dew point: " + weather.getDewPoint(Weather.FAHRENHEIT));
                                                              14.                     Log.e("Tuts+", "Humidity: " + weather.getHumidity() );
                                                              15.  
                                                              16.                     if( weather.getConditions()[0] == Weather.CONDITION_CLOUDY ) {
                                                              17.                         Log.e("Tuts+", "Looks like there's some clouds out there");
                                                              18.                     }
                                                              19.                 }
                                                              20.             });
                                                              21. }
                                                              The above code should output something similar to this.
                                                              1. E/Tuts+: Temp: 88.0
                                                              2. E/Tuts+: Feels like: 88.0
                                                              3. E/Tuts+: Dew point: 50.0
                                                              4. E/Tuts+: Humidity: 28
                                                              5. E/Tuts+: Looks like there's some clouds out there
                                                              One important thing to note here is that the weather condition value is stored as an int. The entire list of condition values can be found in the Weather object.
                                                              1. int CONDITION_UNKNOWN = 0;
                                                              2. int CONDITION_CLEAR = 1;
                                                              3. int CONDITION_CLOUDY = 2;
                                                              4. int CONDITION_FOGGY = 3;
                                                              5. int CONDITION_HAZY = 4;
                                                              6. int CONDITION_ICY = 5;
                                                              7. int CONDITION_RAINY = 6;
                                                              8. int CONDITION_SNOWY = 7;
                                                              9. int CONDITION_STORMY = 8;
                                                              10. int CONDITION_WINDY = 9;
                                                              Activity
                                                              Your user's activity will play a large part in how they interact with their device, and detecting that activity will allow you to provide a more fluid user experience.

                                                              For example, if you have a fitness app, you may want to detect when a user is running so you can start recording a Google Fit session, or you may want to send a notification to your user if you detect that they have been still for too many hours during the day.

                                                              Using the getDetectedActivity() call in the Awareness API, you can get a list of probable activities and how long the user has been doing each one.
                                                              1. private void detectActivity() {
                                                              2.     Awareness.SnapshotApi.getDetectedActivity(mGoogleApiClient)
                                                              3.             .setResultCallback(new ResultCallback<DetectedActivityResult>() {
                                                              4.                 @Override
                                                              5.                 public void onResult(@NonNull DetectedActivityResult detectedActivityResult) {
                                                              6.                     ActivityRecognitionResult result = detectedActivityResult.getActivityRecognitionResult();
                                                              7.                     Log.e("Tuts+", "time: " + result.getTime());
                                                              8.                     Log.e("Tuts+", "elapsed time: " + result.getElapsedRealtimeMillis());
                                                              9.                     Log.e("Tuts+", "Most likely activity: " + result.getMostProbableActivity().toString());
                                                              10.  
                                                              11.                     for( DetectedActivity activity : result.getProbableActivities() ) {
                                                              12.                         Log.e("Tuts+", "Activity: " + activity.getType() + " Likelihood: " + activity.getConfidence() );
                                                              13.                     }
                                                              14.                 }
                                                              15.             });
                                                              16. }
                                                              The above method will display the most likely activity for the user, how long they've been in that state, and the list of all possible activities.
                                                              1. E/Tuts+: time: 1468701845962
                                                              2. E/Tuts+: elapsed time: 15693985
                                                              3. E/Tuts+: Most likely activity: DetectedActivity [type=STILL, confidence=100]
                                                              4. E/Tuts+: Activity: 3 Likelihood: 100
                                                              The DetectedActivity type values can be mapped to the following values:
                                                              1. int IN_VEHICLE = 0;
                                                              2. int ON_BICYCLE = 1;
                                                              3. int ON_FOOT = 2;
                                                              4. int STILL = 3;
                                                              5. int UNKNOWN = 4;
                                                              6. int TILTING = 5;
                                                              7. int WALKING = 7;
                                                              8. int RUNNING = 8;
                                                              Beacons

                                                              The final type of snapshot—and most difficult to set up because it requires a real-world component—involves BLE (Bluetooth Low Energy) beacons. While the Nearby API is beyond the scope of this tutorial, you can initialize beacons for your own Google Services project using Google's Beacon Tools app.

                                                              An important thing to note is that once you have registered a beacon to a Google API project, you cannot unregister it without resetting the beacon id. This means if you delete that project, the beacon will need to be reconfigured using your manufacturer's app.  For the Awareness API, the namespace must match the Google project that you are using for your Awareness API calls. The above beacon was already registered to a personal test Google project, hence the different namespace (reflected-disk-355) from that of the sample project associated with this tutorial.

                                                              In the above screenshot, you can see one item under Attachments. The namespace for this attachment is reflected-disk-355 (this tutorial's example project's namespace is tutsplusawareness) and the type is nearby. You will need this information for your own beacons in order to detect attachments with the Awareness API.

                                                              When you have beacons configured, you can return to your code. You will need to create a List of BeaconState.TypeFilter objects so that your app can filter out beacons and attachments that do not relate to your application.
                                                              1. private static final List BEACON_TYPE_FILTERS = Arrays.asList(
                                                              2.         BeaconState.TypeFilter.with(
                                                              3.              //replace these with your beacon's values
                                                              4.                 "namespace",
                                                              5.                 "type") );
                                                              If you have reason to believe that your user is near a beacon, you can request attachments from beacons that fit the filter requirements above. This will require the location permission for users on Marshmallow and later.
                                                              1. private void detectBeacons() {
                                                              2.     if( !checkLocationPermission() ) {
                                                              3.         return;
                                                              4.     }
                                                              5.  
                                                              6.     Awareness.SnapshotApi.getBeaconState(mGoogleApiClient, BEACON_TYPE_FILTERS)
                                                              7.             .setResultCallback(new ResultCallback<BeaconStateResult>() {
                                                              8.                 @Override
                                                              9.                 public void onResult(@NonNull BeaconStateResult beaconStateResult) {
                                                              10.                     if (!beaconStateResult.getStatus().isSuccess()) {
                                                              11.                         Log.e("Test", "Could not get beacon state.");
                                                              12.                         return;
                                                              13.                     }
                                                              14.                     BeaconState beaconState = beaconStateResult.getBeaconState();
                                                              15.                     if( beaconState == null ) {
                                                              16.                         Log.e("Tuts+", "beacon state is null");
                                                              17.                     } else {
                                                              18.                         for(BeaconState.BeaconInfo info : beaconState.getBeaconInfo()) {
                                                              19.                             Log.e("Tuts+", new String(info.getContent()));
                                                              20.                         }
                                                              21.                     }
                                                              22.                 }
                                                              23.             });
                                                              24. }
                                                              This code will log out information for the attachment associated with the example beacon above. For this example, I have configured two beacons with the same namespace and type to demonstrate that multiple beacons can be detected at once.
                                                              1. E/Tuts+: Oh hi tuts+
                                                              2. E/Tuts+: Portable Beacon info
                                                              4. Using the Fences API

                                                              While the Snapshot API can grab information about the user's context at a particular time, the Fences API listens for specific conditions to be met before allowing an action to occur. The Fences API is optimized for efficient battery and data usage, so as to be courteous to your users.

                                                              There are five types of conditions that you can check for when creating fences:
                                                              • device conditions, such as user having headphones unplugged or plugged in
                                                              • location, similar to a standard geofence
                                                              • the presence of specific BLE beacons
                                                              • user activity, such as running or driving
                                                              • time
                                                              At this time, weather conditions and places do not have support for fences. You can make a fence that uses any of the supported features; however, a really handy feature of this API is that logical operations can be applied to conditions. You can take multiple fences and use and, or, and not operations to combine the conditions to fit your app's needs.

                                                              Create a BroadcastReceiver

                                                              Before you create your fence, you will need to have a key value representing each fence that your app will listen for. To finish off this tutorial, you will build a fence that detects when a user is sitting at a set location, such as their home.
                                                              1. private final static String KEY_SITTING_AT_HOME = "sitting_at_home";
                                                              Once you have a key defined, you can listen for a broadcast Intent that contains that key.
                                                              1. public class FenceBroadcastReceiver extends BroadcastReceiver {
                                                              2.  
                                                              3.     @Override
                                                              4.     public void onReceive(Context context, Intent intent) {
                                                              5.         if(TextUtils.equals(ACTION_FENCE, intent.getAction())) {
                                                              6.             FenceState fenceState = FenceState.extract(intent);
                                                              7.  
                                                              8.             if( TextUtils.equals(KEY_SITTING_AT_HOME, fenceState.getFenceKey() ) ) {
                                                              9.                 if( fenceState.getCurrentState() == FenceState.TRUE ) {
                                                              10.                     Log.e("Tuts+", "You've been sitting at home for too long");
                                                              11.                 }
                                                              12.             }
                                                              13.         }
                                                              14.     }
                                                              15. }
                                                              Create Fences

                                                              Now that you have a receiver created to listen for user events, it's time to create your fences. The first AwarenessFence you will create will listen for when the user is in a STILL state.
                                                              1. AwarenessFence activityFence = DetectedActivityFence.during(DetectedActivityFence.STILL);
                                                              The second fence you will create will wait for the user to be in range of a specific location. While this sample has values for latitude and longitude already set, you will want to change them to match whichever coordinates match your location for testing.
                                                              1. AwarenessFence homeFence = LocationFence.in(39.92, -105.7, 100000, 1000 );
                                                              Now that you have two fences, you can combine them to create a third AwarenessFence by using the AwarenessFence.and operation.
                                                              1. AwarenessFence sittingAtHomeFence = AwarenessFence.and(homeFence, activityFence);
                                                              Finally, you can create a PendingIntent that will be broadcast to your BroadcastReceiver and add it to the Awareness API using the updateFences method.
                                                              1. Intent intent = new Intent(ACTION_FENCE);
                                                              2. PendingIntent fencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
                                                              3.  
                                                              4. mFenceBroadcastReceiver = new FenceBroadcastReceiver();
                                                              5. registerReceiver(mFenceBroadcastReceiver, new IntentFilter(ACTION_FENCE));
                                                              6.  
                                                              7. FenceUpdateRequest.Builder builder = new FenceUpdateRequest.Builder();
                                                              8. builder.addFence(KEY_SITTING_AT_HOME, sittingAtHomeFence, fencePendingIntent);
                                                              9.  
                                                              10. Awareness.FenceApi.updateFences( mGoogleApiClient, builder.build() );
                                                              Now, the app will log a message when the user is sitting down within range of the specified location.

                                                              Conclusion

                                                              In this tutorial, you have learned about the Awareness API and how to gather current information about the user's environment. You have also learned how to register a listener for changes in the user's context and act when specific conditions have been met.

                                                              With this information, you should be able to expand your own apps and provide users with more amazing experiences based on their current location, activity, and other useful values.
                                                              Written by Paul Trebilcox-Ruiz

                                                              If you found this post interesting, follow and support us.
                                                              Suggest for you:

                                                              Android Application Programming - Build 20+ Android Apps

                                                              The Complete Android Developer Course: Beginner To Advanced!

                                                              Android: From Beginner to Paid Professional

                                                              The Complete Android Developer Course - Build 14 Apps

                                                              Python For Android Hacking Crash Course: Trojan Perspective

                                                              Friday, August 12, 2016

                                                              Google Play Services: Awareness AP_part 1

                                                              Making an application context-aware is one of the best ways to offer useful services to your users. While there are still multiple ways to do this—including geofences, activity recognition, and other location services—Google has recently released the Awareness API, which allows developers to create apps that intelligently react to the user's real world situation. The Awareness API combines the Places API, Locations API, Activity Recognition, and Nearby API, as well as adding support for headphone state and weather detection.

                                                              In this tutorial you will learn about the Awareness API and how to access snapshots of data, as well as how to create listeners (known as fences, taking their name from geofences) for combinations of user conditions that match the goals of your applications. This can be useful for a wide variety of apps, such as location-based games, offering coupons to users in stores, and starting a music app when you detect a user exercising. All code for this sample application can be found on GitHub.

                                                              1. Setting Up the Developer Console

                                                              Before diving into your Android application, you will need to set up Google Play Services through the Google API Console. If you already have a project created, you can skip the first step of this section. If not, you can click the above link and follow along to create a new project for your application.

                                                              Step 1: Creating a Project
                                                              To create a new project, click on the blue Create Project button in the top center of the screen.


                                                              This presents you with a dialog that asks for a project name. For this tutorial, I have created a project called TutsPlusAwareness. There are some restrictions on what you can name your project as only letters, numbers, quotes, hyphens, spaces, and exclamation points are allowed characters.


                                                              Once you hit Create, a dialog appears in the lower right corner of the page indicating that the project is being created. Once it has disappeared, your project will be available for setting up. You should see a screen similar to the following. If not, click on the Google APIs logo in the top left corner to be taken to the API manager screen.


                                                              Step 2: Enabling the Necessary API
                                                              From the Google APIs Overview screen, select the search box and search for the Awareness API.


                                                              Once you have selected Awareness API from the returned results, click on the blue Enable button to allow your app to use the Awareness API. If this is the first API you have enabled, you will be prompted to create a set of credentials. Continue to the Credentials page for step 3 of this tutorial.


                                                              In addition to the Awareness API, there are additional APIs that you may need to enable. If your app uses the Places functionality of the Awareness API, you will need to enable Google Places API for Android.

                                                              If your app uses beacons, you will also need to enable the Nearby Messages API.

                                                              Step 3: Creating An Android API Key
                                                              In order to use the enabled APIs, you will need to generate an API key for your Android app. On the credentials page for your Google project, select Awareness API from the top dropdown menu and Android from the second.

                                                              Next you will be taken to a screen where you can enter a package name for your app and the SHA1 certificate for the app's signing key. In order to get the signing key for your debug key on Linux or OS X, enter the following command in a terminal window.
                                                              1. keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
                                                              On Windows, you can run the same command with the path set to the location of your debug.keystore file.

                                                              Once you click the Create API key button, you will be given the API key that you will need to use in your Android application.


                                                              2. Setting Up the Android Project

                                                              Once you have an API key created and the proper APIs enabled, it's time to start setting up your Android project. For this tutorial, we'll create a test application in order to learn the API.

                                                              In order to demonstrate all of the features of the Awareness API, this tutorial will focus on using a simple list representing each feature used. Although the details of creating this list will not be discussed, you can find a complete implementation in the GitHub source for this tutorial.

                                                              Step 1: Set Up Play Services
                                                              First you will need to include the Play Services library in your build.gradle file. While you can include all of Google Play Services, it's best to include only the packages you need for your app.

                                                              In this case, the Awareness API is available in the ContextManager package, and it can be included in your project by adding the following line within your dependencies node. You will also want to make sure the AppCompat library is included, as this will be used for checking permissions on Marshmallow and above devices.
                                                              1. compile 'com.google.android.gms:play-services-contextmanager:9.2.0'
                                                              2. compile 'com.android.support:appcompat-v7:23.4.0'
                                                              Once you have added the above line, sync your project and open the strings.xml file for your project. You will want to place your API key from the Google API Console into a string value.
                                                              1. <string name="google_play_services_key">YOUR API KEY HERE</string>
                                                              After you have added your API key, you will need to open your project's AndroidManifest.xml file. Depending on what features of the Awareness API you use, you will need to include permissions for your app. If your app uses the beacon, location, places or weather functionality of the Awareness API, then you will need to include the ACCESS_FINE_LOCATION permission. If you need to use the activity recognition functionality, then you will require the ACTIVITY_RECOGNITION permission.
                                                              1. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
                                                              2. <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
                                                              Next you will need to declare meta-data within the application node that ties your app to the Google API services. Depending on what your app uses, you will need to also include the com.google.android.geo and com.google.android.nearby metadata for using location, beacons and places features.
                                                              1. <meta-data
                                                              2.     android:name="com.google.android.awareness.API_KEY"
                                                              3.     android:value="@string/google_play_services_key" />
                                                              4.  
                                                              5. <!-- places/location declaration -->
                                                              6. <meta-data
                                                              7.     android:name="com.google.android.geo.API_KEY"
                                                              8.     android:value="@string/google_play_services_key" />
                                                              9.  
                                                              10. <!-- Beacon snapshots/fences declaration -->
                                                              11. <meta-data
                                                              12.     android:name="com.google.android.nearby.messages.API_KEY"
                                                              13.     android:value="@string/google_play_services_key" />
                                                              Next you will need to open your MainActivity.java file. Add the GoogleApiClient.OnConnectionFailedListener interface to your class and connect to Google Play Services and the Awareness API in your onCreate(Bundle) method.
                                                              1. public class MainActivity extends AppCompatActivity implements
                                                              2.         GoogleApiClient.OnConnectionFailedListener {
                                                              3.              
                                                              4.     @Override
                                                              5.     protected void onCreate(Bundle savedInstanceState) {
                                                              6.         super.onCreate(savedInstanceState);
                                                              7.         setContentView(R.layout.activity_main);
                                                              8.  
                                                              9.         mGoogleApiClient = new GoogleApiClient.Builder(this)
                                                              10.                 .addApi(Awareness.API)
                                                              11.                 .enableAutoManage(this, this)
                                                              12.                 .build();
                                                              13.         mGoogleApiClient.connect();
                                                              14.     }
                                                              15.      
                                                              16.     @Override
                                                              17.     public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {}
                                                              18. }
                                                              Step 2: Permissions
                                                              Now that Play Services are configured within your Android app, you will need to ensure that your users on Android Marshmallow or higher have granted permission for your application to use their location. You can check for this permission in onCreate(Bundle) and before you access any features that require the location permission in order to avoid crashes within your app.
                                                              1. private boolean checkLocationPermission() {
                                                              2.     if( !hasLocationPermission() ) {
                                                              3.         Log.e("Tuts+", "Does not have location permission granted");
                                                              4.         requestLocationPermission();
                                                              5.         return false;
                                                              6.     }
                                                              7.  
                                                              8.     return true;
                                                              9. }
                                                              10.  
                                                              11. private boolean hasLocationPermission() {
                                                              12.     return ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION )
                                                              13.             == PackageManager.PERMISSION_GRANTED;
                                                              14. }
                                                              If the location permission has not already been granted, you can then request that the user grant it.
                                                              1. private final static int REQUEST_PERMISSION_RESULT_CODE = 42;
                                                              2.  
                                                              3. private void requestLocationPermission() {
                                                              4.     ActivityCompat.requestPermissions(
                                                              5.             MainActivity.this,
                                                              6.             new String[]{ Manifest.permission.ACCESS_FINE_LOCATION },
                                                              7.             REQUEST_PERMISSION_RESULT_CODE );
                                                              8. }
                                                              This will cause a system dialog to appear asking the user if they would like to grant permission to your app to know their location.

                                                              When the user has responded, the onRequestPermissionsResult() callback will receive the results, and your app can respond accordingly.
                                                              1. @Override
                                                              2. public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
                                                              3.                                        @NonNull int[] grantResults) {
                                                              4.     switch (requestCode) {
                                                              5.         case REQUEST_PERMISSION_RESULT_CODE: {
                                                              6.             // If request is cancelled, the result arrays are empty.
                                                              7.             if (grantResults.length > 0
                                                              8.                     && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                                                              9.                 //granted
                                                              10.             } else {
                                                              11.                 Log.e("Tuts+", "Location permission denied.");
                                                              12.             }
                                                              13.         }
                                                              14.     }
                                                              15. }
                                                              At this point you should be finished setting up your application for use of the Awareness API.
                                                              Written  by Paul Trebilcox-Ruiz

                                                              If you found this post interesting, follow and support us.
                                                              Suggest for you:

                                                              Android Application Programming - Build 20+ Android Apps

                                                              The Complete Android Developer Course: Beginner To Advanced!

                                                              Android: From Beginner to Paid Professional

                                                              The Complete Android Developer Course - Build 14 Apps

                                                              Python For Android Hacking Crash Course: Trojan Perspective