Tag: $cordovaFile

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.

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..