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:
You then click the button, it presents a 'working' message, and when done, shows you a result:
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.
And here is Alex, who did not have a picture, but who now does, and is much improved:
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?
Archived Comments
I would probably pay if I could customize what the actual image is, and you used @ionic/storage to remember which contacts it changed so it is reversible.
Well part of the idea is to not make you select an image for each, because then that's probably not much better than you doing it yourself, know what I mean? I could see offering options from the various different image placement services.
maybe get it from gravatar if the contact has email account, or from a social network (if possible).
BTW, I want 10% if you use my idea
Oh - Gravatar is a damn good idea, but do folks still use that? It seems like it was big 5+ years ago or so, but I never hear people talking about, nor see sites really using it.
I don't know, I probably configured mine 5 years ago, but some sites are still picking my image from there. I don't think people have deleted their account, so if they registered long time ago, you can still pick their old picture even if they don't use it anymore.
Hello
I'm trying run this example and this error show in console:
EXCEPTION: Uncaught (in promise): TypeError: navigator.contacts is undefined
Did you install the plugin?
Yes, the plugin is intalled.
ionic plugin add cordova-plugin-contacts
And you are running it via the emulator?
Runing in Firefox Navigator with ionic serve command.
Don't. :) Run in the iOS/Android emulator or a device.
Ok. Thaks. I'll try it.
Dont work on emulator.
Do you get the exact same error? Did you modify my code?
I don't modify yuor code :-(.
Alert dialog says:
Unfortunately, fixcontacts has stopped.
What do you see when you remote debug?
Yes.
Genymotion.
I'm trying to run in device Android later.
Thanks.