THE BLOG

23
Dec

Ionic – In-app browser with ngCordova’s $cordovaInAppBrowser.

In the apps that we’ve built, there’s quite often been the need to open links to elsewhere on the internet. A simple href will open your phone’s web browser, and load the link in there. However, though this serves a purpose, it’s not an ideal user experience because you are taken totally out of the app.

To remedy this, ngCordova has a nice plugin that provides you with the ability to open a browser in-app. It’s not even remotely difficult to use, but hopefully people will find this useful, nevertheless.

We’re just going to cover the basics of opening a browser and navigating to a URL here, but the documentation does include some other usable functions, should you be that way inclined.

Let’s Start…

As with other posts, to make things simple we’ll start from the beginning and create a fresh Ionic Framework project through terminal/Git Bash (Linux/Windows)

ionic start IonicProject blank
cd IonicProject
ionic platform add android

Here you’ve created a fresh Ionic project, gone into the newly created Ionic app directory, and added the ability to build for the Android platform.

Next, follow the steps to install ngCordova.

Add the plugin…

In the same terminal/git bash add the ngCordova $cordovaInAppBrowser plugin:

cordova plugin add org.apache.cordova.inappbrowser

Onto the development. First we’ll create a button with an ng-click function, and pass through the URL we’d like to navigate to.

Let’s take a look at the HTML…

<button ng-click="watchYoutube("http://www.youtube.com")">Watch on Youtube!</p>

Next, our controller code…

.controller('InAppBrowserCtrl', function($scope, $cordovaInAppBrowser) {
  $scope.watchYoutube = function(url) {
    $cordovaInAppBrowser
     .open(url, '_blank')
     .then(function(event) {
       // success
     }, function(event) {
       // error
    });
  };
)};

All nice and simple stuff. Clicking the button passes the URL through to the $scope.watchYoutube() function, and the $cordovaInAppBrowser.open(url...) then opens the browser and navigates to that link. Obviously you can enter any success/error code if you see fit, but it’s not necessary.

Simple!

18
Dec

Ionic – Download progress bar for ngCordova’s $cordovaFile.

Part 1:

In an earlier post we described how we downloaded a file to our app, using $cordovaFile.

Users like to see how much of something has downloaded, so we decided that whilst the file was downloading, we’d display a download progress bar in the app.

Here’s how we did it…

First the HTML:

<!--Using the HTML5 progress element--> 
<progress ng-hide="download.progress==1;" max="1" value="{{download.progress}}"></progress>

As you can see, the progress bar has a ‘max’ value attribute of 1. The amount of progress displayed on the bar is calculated using the ‘value’ attribute. E.g. A ‘value’ of 0.3 is 30% of the ‘max’ value of 1, and so the progress bar is displayed at 30%.

Our value attribute is {{download.progress}}. Calculating the download progress of a downloading file isn’t difficult, as this is conveniently provided by the $cordovaFile.download() function.
The third argument of the .then() function provides the download’s progress information.

The controller’s download code…

exampleApp.controller('ExampleController', function($scope, $ionicPlatform, $cordovaFile) {
  var directory = 'downloads';
  var filename = 'download.mp3';

  $ionicPlatform.ready(function() {})
  .then(function() {
    return $cordovaFile.createDir(directory, false);
  })
  .then(function() {
    return $cordovaFile.createFile(directory + '/' + filename, false);
  })
  .then(function(newFile) {
    return $cordovaFile.downloadFile(url, newFile.nativeURL);
  })
  .then(function(result) {
    // Success!
  }, function(err) {
    // Error
  }, function (progress) {
    // constant progress updates
     download.progress = progress.loaded/progress.total;      
  });
});

In the progress callback (at the bottom of the code, above) we assign download.progress it’s value.

While this works great, there’s a problem…

Because the progress is calculated in the controller, if you navigate away from that page, and then return, the previous state is forgotten, and therefore so is the progress. To solve this, we need to move our download code into a Service, and $broadcast the download ‘event’ so that the download information is available throughout the whole app. When done properly, this will mean that the download (and it’s progress) will be accessible from any screen, and it won’t be forgotten when we change views.

Part 2:

Firstly we move the Controller’s download code into a Service, so that it can be accessed anywhere in the app.

Service Code…

exampleApp.factory('ExampleService', function($scope, $ionicPlatform, $cordovaFile, $interval, $rootScope) {
  var directory = 'downloads';
  var filename = 'download.mp3';
  var downloading = [];

  $ionicPlatform.ready(function() {})
  .then(function() {
    return $cordovaFile.createDir(directory, false);
  })
  .then(function() {
    return $cordovaFile.createFile(directory + '/' + filename, false);
  })
  .then(function(newFile) {
    downloading.push(podcast);
    return $cordovaFile.downloadFile(url, newFile.nativeURL);
  })
  .then(function(result) {
    // Success!
  }, function(err) {
    // Error
  }, function (progress) {
    // constant progress updates
     downloading.forEach(function(download){
       if(download.num == podcast.num){
          download.progress = progress.loaded/progress.total;
       }
     });           
  });
});

In addition to moving the download code into a Service (yes, a factory is a type of Service), you’ll notice that in the code we define an array called downloading, and when we download a podcast, we push it to that array. Then, in the progress callback we check forEach download in the downloading array (you may be downloading multiple podcasts), and determine whether the download is the same as the podcast you are currently downloading. We do this by comparing the .num. If they are the same, we assign the download progress to download.progress.

The next (and most important) thing we add to the Service, is a $broadcast event for the download, inside an $interval. The $broadcast will broadcast every download that currently belongs to the downloading[] array, to the $rootScope. Each ‘download’ is assigned to a ‘podcast’ variable (podcast:download).
We use $interval to define how often the download is broadcast. In our case, we set the interval to tick every 1 second.

The entire Service code…

exampleApp.factory('ExampleService', function($scope, $ionicPlatform, $cordovaFile, $interval, $rootScope) {
  var directory = 'downloads';
  var filename = 'download.mp3';
  var downloading = [];

  $interval(function(){
     downloading.forEach(download){
       $rootScope.$broadcast('download-progress', function(){
         podcast:download
       })
     }
  }, 1000);

  $ionicPlatform.ready(function() {})
  .then(function() {
    return $cordovaFile.createDir(directory, false);
  })
  .then(function() {
    return $cordovaFile.createFile(directory + '/' + filename, false);
  })
  .then(function(newFile) {
    downloading.push(podcast);
    return $cordovaFile.downloadFile(url, newFile.nativeURL);
  })
  .then(function(result) {
    // Success!
  }, function(err) {
    // Error
  }, function (progress) {
    // constant progress updates
     downloading.forEach(function(download){
       if(download.num == podcast.num){
          download.progress = progress.loaded/progress.total;
       }
     });           
  });
});

So now, if there is a podcast downloading, the information about that downloading podcast will be broadcast to the whole app, every 1 second. The next thing we need to do, is access this information in the Controller for whatever view we happen to be displaying the progress bar.

To do this, we listen for the $broadcast event in the Controller, by using $scope.$on('download-progress'). Within $on(), we loop through the array ($scope.downloads) of all the downloads we have on the app, and find the download that matches the currently downloading podcast that is being broadcast. We get an array of all the downloads on the app by calling ExampleService.allDownloads() – we then assign this to $scope.downloads. This array includes both partially and fully downloaded podcasts. When we find a match, we copy the information of the downloading podcast that is being broadcasted, to the download in the $scope.downloads array. This means that every second the download will have updated information about the broadcasted download, including the progress!

Controller code…

 .controller('DownloadsCtrl', function($scope, $ionicPlatform, ExampleService) {

   $scope.downloads = ExampleService.allDownloads();
 
   $scope.$on('download-progress', function(event, args) {
     $scope.downloads.forEach(function(d) {
       if (d.num == args.podcast.num) {
         angular.copy(args.podcast, d);
       }
     });
   });

As you can see above, we loop through every (partial or full) download in $scope.downloads, and compare each download with the downloading podcast that is being broadcast. args contains all information about the downloading podcast. If the two match, then we copy the ALL of information of the broadcasted download to the download in the $scope.downloads array. Since the progress is part of that information, this gets updated every second as well, giving us a fully functional, and reliable, progress bar.

16
Dec

Ionic – Adding an App Icon and Splash Screen

Adding an app icon and a splash screen are generally quite essential to the finishing of an app. Up until recently, because of the various different device screen sizes and resolutions, this caused a not insubstantial amount of trauma to us developers.

With Ionic’s CLI release 1.2.14, this process has been made as simple and seamless as most other things contained in the Ionic framework.

Here’s the blog post from Ionic detailing the changes they’ve made to this process: http://ionicframework.com/blog/automating-icons-and-splash-screens/

Unfortunately, despite the process now being simple, the Ionic blog post doesn’t actually take you through the whole process, and it took us a little while to figure out exactly how to do it. Hopefully the following should be instructive enough for any devs who’ve run into the same problems that we did with this…

Ok, firstly:

1. For the app icon, you need an icon with size: 192px*192px.
2. For the splash screen, you need an overall size of at least: 2208px*2208px, and any centre graphic you may have should fit within a 1200px*1200px centre square.

Next:

1. Create a folder named ‘resources’ at the top level of your project, e.g: ‘projectName/resources’.
2. Place both the app icon and the splash screen in the ‘resources’ folder.

If necessary, update the Ionic CLI by running:

npm install -g ionic

Then run:

ionic resources

Running ‘ionic resources’ sends your splash screen and app icon to Ionic’s image resizing and cropping server, which creates the various different image sizes necessary for compatibility with all devices.

If you now build your app, you’ll notice that the app icon is present. The splash screen, however, is not – though we only tested this on an Android device. To get the splash screen to display (on Android, at least), you need add the following preferences to your project’s config.xml file:

 <preference name="SplashScreen" value="screen"/>
 <preference name="SplashScreenDelay" value="10000"/>

The ‘SplashScreenDelay’ preference is optional.

If you now build the app both the app icon and the splash screen should be present.

14
Dec

Ionic – Opening a downloaded file with ngCordova’s $cordovaFile.

After downloading our files to our directory, logically, the next step was to display those files in the app.

The documentation on the ngCordova site for listing the contents of a directory, is beyond scant, as there is no mention of any function that would allow you to do it.

After going into the source code, we found a function that would nicely do the trick…Continue Reading..

12
Dec

Ionic – Downloading a file with ngCordova’s $cordovaFile.

Recently we decided to build an Android podcast app using the Ionic framework and ngCordova. ngCordova is an angularJs wrapper for Apache Cordova plugins, which in theory, should make them nice and easy to use within your angular/cordova apps. Here’s the link to ngCordova: http://ngcordova.com/docs/

Unfortunately the documentation is rather scant, and solutions to most of the issues we were encountering were absent from Google, so we thought we’d document the process here. We found that many people were having similar problems to us (on the Ionic forums/StackOverflow), so hopefully this should help you guys out.Continue Reading..

10
Dec

Ionic – Black screen on iOS and Android device when using Live Reload

As well as running ‘ionic serve’ to test your app in a browser, it’s possible to test out your app on a device by using ‘ionic run’. This works fine, but it’d be nice to have a live reload on your device for whenever you make a change to your code. This also works fine until you run into an error, at which point you’ll be hankering for some console or server errors to help you out. Ordinarily see these in your browser, when using ‘ionic serve’.

Fortunately, in order to experience live reload, and to see these console/server errors in your terminal/Git Bash, Ionic have kindly provided us with these features. Continue Reading..