By Gary simon - Jan 24, 2017
This is the final lesson that's a part of our Free Ionic Course, and we will bring everything together that we learned, by creating an Ionic app from scratch.
The name of the app is "WifeAlert", ha! We'll use several Ionic Native plugins (Geofence, Geolocation, and SMS) to define a parameter from which the app will send a text message if the phone travels outside of said parameter.
While it's a silly concept of an app, it will help us learn how to work with Ionic components, native plugins, and custom themes. Watch the video below if you would like to see the app in action.
So, let's get started!
Be sure to subscribe to our youtube channel.
First, make sure you have both ionic and cordova installed. If you don't, refer to this earlier clip: Installing Ionic
Hop into your console of choice and run the following command:
$ ionic start wifealert blank --v2
$ cd wifealert
So, we're starting a new Ionic 2 project called wifealert, with the blank project template and --v2 designating that this is an Ionic 2 app.
Simple enough!
As mentioned above, we will use 3 Ionic Native plugins, so they must be installed. In the project folder, type:
$ ionic plugin add cordova-plugin-geofence cordova-plugin-geolocation cordova-sms-plugin
This will add the our plugins all in one command.
Apart from the home page, we will use a second page called active that's displayed when a Geofence is active.
So, while we're here in the console, we might as well add it now:
$ ionic g page active
g is short-hand for generate. It will place this in a folder called active in the /src/pages/ folder.
I will build out my project for an Android phone, since that is what I own. In order to do that, you have to add android as a platform with cordova. You will need to add either android or ios, as this project will not work on a desktop environment.
For Android, you will need Android Studio with the Android-SDK, along with Java.
There is a platform guide though that gives you all of the information you need for both iOS and Android:
Once you have what you need installed, run: (or with ios)
$ cordova platform add android
First, we need a few imports at the top of our /src/pages/home/home.ts file:
import { Component } from '@angular/core';
import { NavController, Platform } from 'ionic-angular';
import { Geofence, Geolocation, SMS } from 'ionic-native';
import { ActivePage } from '../active/active';
We're adding Platform from ionic-angular which allows us to get information about the current device.
Then we're adding our 3 Ionic Native plugins, and also importing our ActivePage we generated with the Ionic-CLI.
Next, inside export class HomePage we need to define 3 properties:
export class HomePage {
radius: number = 100;
error: any;
success:any;
constructor(public navCtrl: NavController) {
}
}
Radius will be set to 100 meters by default. You probably shouldn't set the radius less than 100, otherwise it can be inaccurate and misfire.
Then we have 2 properties for error and success.
In the next code snippet, we'll focus just on the constructor. Change it to:
constructor(public navCtrl: NavController, private platform: Platform) {
this.platform.ready().then(() => {
Geofence.initialize().then(
() => console.log('Geofence Plugin Ready'),
(err) => console.log(err)
);
});
}
So through dependency injection, we're adding the Platform member that we imported at the top.
And then we're calling this.platform.read().then to run Geofence.initialize(). All this means is that once the device is ready, or already ready, it will start the Geofence plugin. When this happens for the first time, the user will be prompted to allow the app access to location and notifications.
Underneath the constructor, paste the following method:
setGeofence(value: number) {
Geolocation.getCurrentPosition({
enableHighAccuracy: true
}).then((resp) => {
var longitude = resp.coords.longitude;
var latitude = resp.coords.latitude;
var radius = value;
let fence = {
id: "myGeofenceID1",
latitude: latitude,
longitude: longitude,
radius: radius,
transitionType: 2
}
Geofence.addOrUpdate(fence).then(
() => this.success = true,
(err) => this.error = "Failed to add or update the fence."
);
Geofence.onTransitionReceived().subscribe(resp => {
SMS.send('5555555555', 'OMG She lied, leave her now!');
});
this.navCtrl.push(ActivePage);
}).catch((error) => {
this.error = error;
});
}
setGeofence is a method we will call when we press a button. It passes in a radius value that we'll define with a range slider.
Then, we use Geolocation.getCurrentPosition() to get the phone's current position. We're passing in enableHighAccuracy to ensure we get a high quality position.
If it's successful, we define a few variables for longitude, latitude and radius, which are then defined in fence. fence contains properties that are needed to create or update a Geofence.
Then we use Geofence.addOrUpdate() to create or update the fence.
And Geofence.onTransitionReceived() to send our text message through SMS.send. This is called when the TransitionType of 2 (Leaving) occurs. You will want to enter the number of your device, not the current device. Oh and hey, by the way, if she visits her text messages, she will see this text in her history!
At the end, we navigate to the ActivePage through this.navCtrl.push.
In home.html let's add:
<ion-content class="page">
<h1>WifeAlert</h1>
<div class="loader"></div>
<div class="bottom-container">
<ion-item>
<ion-range [(ngModel)]="radius" #radiusVal min="40" max="300" color="primary" pin="true" snaps="true" step="20">
</ion-range>
</ion-item>
<ion-label color="secondary" id="lbl">Fence Parameter (Meters)</ion-label>
<button ion-button large outline block (click)="setGeofence(radiusVal.value)" id="cta" color="primary">Set Geofence</button>
<p *ngIf="error">{{ error }}</p>
</div>
</ion-content>
At the top, we'll have the name "WifeAlert" along with an animated loader graphic.
Then, we use ion-range which will allow us to set the radius of the Geofence.
We use an ion-button to call our custom setGeofence method, where we pass the value of the range.
ion-range, ion-item {
background-color: transparent !important;
color:#fff;
}
.item-inner {
border-bottom:0 !important;
}
h1 {
padding: 5em 0;
text-align:center;
font-weight: 100;
}
.bottom-container {
display:block;
width:100%;
padding: 2em 2em 5em 2em;
}
.item-md, .item-inner {
padding-left:0;
padding-right:0 !important;
}
#cta {
margin: 2em auto 0 auto;
display:block;
}
#lbl {
text-transform:uppercase;
letter-spacing: 2px;
font-size:.85em;
margin-top:0;
text-align:center;
}
.range-md .range-pin {
-webkit-transform: translate3d(0, 0, 0) scale(1);
transform : translate3d(0, 0, 0) scale(1);
}
html,
body {
height: 100%;
width: 100%;
overflow: hidden;
}
.loader {
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
border-style: solid;
border-radius: 50%;
border-color: #2BFCBA;
animation: 1s pulse infinite;
}
@keyframes pulse {
0% {
height: 0px;
width: 0px;
border-width: 32px;
opacity: 0;
}
40% {
opacity: 1;
}
80% {
height: 64px;
width: 64px;
border-width: 0px;
opacity: 0;
}
100%{
opacity: 0;
}
}
In here, we're just defining a few CSS rulesets to style the page and also the loader.
Also, head on over to /src/app/app.scss to define a single ruleset that will be used by both of our pages:
.page {
background-color: #263E44 !important;
color:#fff;
}
And then finally, we'll define a new primary and secondary color in /src/theme/variables.scss:
$colors: (
primary: #0AFDA4,
secondary: #578F7A,
.. others..
);
Now, inside of the /src/pages/active/active.ts component, I will simply paste the entire contents, because there is not much:
import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { Geofence } from 'ionic-native';
import { HomePage } from '../home/home';
@Component({
selector: 'page-active',
templateUrl: 'active.html'
})
export class ActivePage {
constructor(public navCtrl: NavController, public navParams: NavParams) {}
ionViewDidLoad() {
console.log('ionViewDidLoad ActivePage');
}
removeFence() {
Geofence.removeAll();
this.navCtrl.push(HomePage);
}
}
All we have here is removeFence() which is a method that's called when a user clicks on a button to remove all fences.
The HTML for this page is very similar to the Home page HTML:
<ion-content class="page">
<h1>WifeAlert</h1>
<div class="loader"></div>
<div class="bottom-container">
<ion-label color="secondary" id="lbl">MONITORING CURRENTLY ACTIVATED</ion-label>
<button ion-button large outline block (click)="removeFence()" id="cta" color="primary">Remove Fence</button>
</div>
</ion-content>
Just a simple button that callst he removeFence() method. Great, we're almost there!
The last thing we need to do is import our ActivePage and add it to the declarations and entryComponents.
In /src/app/app.module.ts add:
.. other imports ..
import { ActivePage } from '../pages/active/active';
@NgModule({
declarations: [
.. other declarations ..
ActivePage
],
.. removed bootstrap and imports just for brevity ..
entryComponents: [
.. other entryComponents ..
ActivePage
]
})
And that's it!
Let's try running our app now.
Connect your phone via USB cable (ensure debugging, the device's GPS, and all that jazz is on) and in the console, type:
$ ionic run android --device
It will take a good minute on the first run, and then the app will load up in the phone and if all goes according to plan, you will see a screen similar to this:
Give it access to the GPS. Then you can specify a distance in meters, and click the SET GEOFENCE button.
Now, remember, if you exceed the distance specified for the first time it will prompt you to grant the app permission to send a text. So, if you're really thinking of being a stalking weirdo, you might want to do that first.
Give it a test with 2 phones. If you exceed the Geofence, it should send a text to the number you specified in the home.ts code.
Hopefully you learned a lot about how to make an Ionic app! We learned how to create a very simple app, that used multiple Ionic native plugins, as well as a few different components.
As you can see, it's a very powerful framework for building hybrid apps. I'll see you next time!