Working with Ionic Native - Contact Fixer

I've blogged a few times now about Ionic Native, but if you're new to it, you can think of "Angular2/Ionic2 Friendly Wrappers" for many different Cordova plugins. Today I'm sharing what may be my coolest demo yet. No, wait, seriously, it is, honest! This demo does something I think every phone should have built in, and if I can get off my lazy butt, I'll be submitting this to the App Store this week. So what did I build?

I noticed recently that both both iOS and Android will provide a contact picture even when you haven't selected a unique one for them. I believe, in both cases, it won't use the default picture when you get a call from them, but in the contacts app it will display it. So that's cool. I know it isn't new, but I like the fact that I can see someone's face when I get a phone call or text message from them.

However - I don't always have time to snap pictures of people when I'm adding them to my contacts. This made me curious. Given that we have a Contacts plugin and a Contacts Ionic Native wrapper, could I actually set a picture for contacts that didn't have them?

Turns out that yes, you can! And of course, that means only one thing. I could build an app to "fix" those contacts and give them better pictures. Here's how 3 of the default iOS simulator contacts are displayed:


And here are the fixed versions:


In case it is a bit too small, here is a closeup on the awesomeness:

I'm going to be so rich when I put this in the App Store. Ok, let's take a look at the app itself. First, the UI, which is incredibly simple since the app does one thing and one thing only.

On startup, we display some text explaining what we're about to do:

App start

You then click the button, it presents a 'working' message, and when done, shows you a result:

App done

And that's it. I could maybe actually show all the contacts and their new pictures, but honestly, this felt like it was enough. Let's look at the code. First, the view.


<ion-header>
  <ion-navbar>
    <ion-title>
      Contact Fixer
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  
  <p>
  This application will scan your contacts and find ones without a contact picture. For each one it finds, it will fix that problem by selecting a random cat picture. This is a <strong>one way</strong> operation
  that cannot be undone - use with caution!
  </p>

  <button ion-button color="danger" (click)="fixContacts()" full round>Make It So!</button>

</ion-content>

And now the real meat of the app, the code behind this view.


import { Component } from '@angular/core';
import { NavController, AlertController, LoadingController } from 'ionic-angular';

import { Contact, Contacts, ContactField } from 'ionic-native';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(public navCtrl: NavController, public alertCtrl:AlertController, public loadingCtrl:LoadingController) {
    
  }

  getRandomInt (min, max) {
   return Math.floor(Math.random() * (max - min + 1)) + min;
  }
  
  randomCat() {
    let w = this.getRandomInt(200,500);
    let h = this.getRandomInt(200,500);
    return `https://placekitten.com/${w}/${h}`;
  }

  //Credit: http://stackoverflow.com/a/20285053/52160
  toDataUrl(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.responseType = 'blob';
    xhr.onload = function() {
      var reader = new FileReader();
      reader.onloadend = function() {
      callback(reader.result);
    }
      reader.readAsDataURL(xhr.response);
    };
    xhr.open('GET', url);
    xhr.send();
  }

  fixContacts() {

    let loader = this.loadingCtrl.create({
      content: "Doing important work...",
    });
    loader.present();


    let fixed = 0;
    let proms = [];

    Contacts.find(["name"]).then((res) => {

      res.forEach( (contact:Contact) => {

        if(!contact.photos) {
          console.log('FIXING '+contact.name.formatted);
          //console.log(contact);

          proms.push(new Promise( (resolve, reject) => {

            
            this.toDataUrl(this.randomCat(), function(s) {

              var f = new ContactField('base64',s,true);

              contact.photos = [];
              contact.photos.push(f);
              console.log('FIXED '+contact.name.formatted);
              contact.save();
              fixed++;
              resolve();
              
            });
            
          }));
        }

      });

      Promise.all(proms).then( (res) => {
        
        loader.dismissAll();

        console.log('all done, fixed is  '+fixed);
        let subTitle, button;

        if(fixed === 0) {
          subTitle = "Sorry, but every single one of your contacts had a picture. I did nothing.";
          button = "Sad Face";
        } else {
          subTitle = `I've updated ${fixed} contact(s). Enjoy!`;
          button = "Awesome";      
        }

        this.alertCtrl.create({
            title:'Contacts Updated',
            subTitle:subTitle,
            buttons:[button]
        }).present();

      });

    });
  
  }

}

Things get kicked off when the button is clicked on the view and fixContacts() is fired. I turn on a loading component to present something to the user to let them know the app is doing something.

Next, I ask the Contacts API to return every contact. I have to pass a 'search field', even though I'm not actually passing a search value. That's a bit wonky, but that's how the plugin works, it isn't a bug in Ionic Native's implementation.

I then iterate over every contact. The photos property is empty when no pictures exist for the contact. When I find that, I kick off a process to make a new picture using the Placekitten service. I simply generate a random size and that will give me a random cat. I conver that to a base64 string and then store it in the contact.

Because this process is asynchronous, I use an array of promises I can then call then() on to know when they are all done.

Finally, I report what I did to the user using the Alert component. And that's that. Here's output from my real device. First, Max, who already had a picture.

Max

And here is Alex, who did not have a picture, but who now does, and is much improved:

Alex

You can find the complete source code here: https://github.com/cfjedimaster/Cordova-Examples/tree/master/fixcontacts

So - who would pay 99 cents for this?

Like This?

If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can also subscribe to the email feed to get notified of new posts.

See Also