On Android, platform.resume and .pause events fire as soon as a call is made, not only after returning to the app

My Ionic 2 app uses the callnumber cordova plugin (https://github.com/Rohfosho/CordovaCallNumberPlugin) to open the phone dialer. I want my users to leave a comment after the call is completed. So I use the platform.resume event to find out when the user hangs up the phone and returns to my own app.

The problem is that as soon as I open the dialer, the resume and pause events are fired. They’re fired again when the user returns to my app.
I was only expecting the resume event to fire after returning to the app, not before leaving it. And I was expecting the pause event to be fired as soon as the user leaves my app, but not as soon as he returns.
I hope someone can help me with this?

To reproduce:

  1. Start a new Ionic 2 app:
    $ ionic start test tabs -v2

  2. Add the android platform:
    $ ionic cordova platform add android

  3. Add the Call Number plugin
    $ ionic cordova plugin add call-number
    $ npm install --save @ionic-native/call-number

  4. Add the plugin to the module definitions in app.module.ts

    import { CallNumber } from ‘@ionic-native/call-number’;

    providers: [

    CallNumber,

    ]

  5. constructor in app.components.ts:

  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
    platform.ready().then(() =>
    {
      statusBar.styleDefault();
      splashScreen.hide();

      platform.resume.subscribe ( (e) => {
        console.trace("resume called"); });
      
      platform.pause.subscribe ( (e) => {
        console.trace("pause called"); });
    });
  }
  1. In home.html:

<button ion-button (click)="doCall()">Make the call</button>

  1. in home.ts:

import { CallNumber } from '@ionic-native/call-number';

constructor:

constructor(public navCtrl: NavController, private callnumber: CallNumber)

And the “doCall” method:

  doCall()
  {
    console.trace("starting doCall");
    this.callnumber.callNumber("+3109876543", false)
      .then( (data) =>
      {
        console.trace("called callnumber");
      })
      .catch( (error) =>
      {
        console.log('Error launching dialer',error);
      });
  }

Note: the number to call (first param in callNumber) is fictional

The logs will show that right after “doCall” is called the resume and pause events are triggered, at the moment the phone dialer is opened. After hanging up the phone and returning to the app both these events are triggered again.

It seems it has something to do with the ‘ask for permissions’ dialog. With no permissions (1st time), it goes something like this:

start call => log “pause” => app asks for permissions => give permission => resume, pause, resume, pause => hang up => resume

A second go:

start call => log pause, resume, pause => hang up => resume

It seems that when showing the permissions screen on the first time, and subsequent times behind the scenes(?), focus is removed from the app, after which it resumes and pauses again.

The part “After hanging up the phone and returning to the app both these events are triggered again.” works different for me - this just logs ‘resume’ like you would expect.

Tested on Android emulator (API level 23 / 6.0).

Something of a workaround, assuming you want to do something if the call lasted at least 15 seconds:

import { Component } from '@angular/core';
import { Platform, NavController } from 'ionic-angular';
import { CallNumber } from '@ionic-native/call-number';

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

  startCall: any; // null or date object

  constructor(public navCtrl: NavController, private callnumber: CallNumber, private platform: Platform) {
  }

  ionViewDidLoad() {
    this.platform.ready().then(() => {
      this.platform.resume.subscribe((e) => {
        // if we come back to the app and startCall was set by callNumber, we can assume we came back from a call
        if (this.startCall) {
          // get time diff (in seconds) from start of call
          let secondsPassed = ((new Date).getTime() - this.startCall.getTime()) / 1000;

          // if more than 15 secs ago, we want to ask for details
          if (secondsPassed >= 15) {
            // ... do your thing ...
            alert('more than 15 secs have passed');

            // reset the startCall variable so we're not responding to resume events without calling first
            this.startCall = null;
          }
        }
      });
    });
  }

  doCall() {
    this.callnumber
        .callNumber("+3109876543", false)
        .then((data) => {
          this.startCall = new Date;
        })
        .catch((error) => {
          console.error('Error launching dialer',error);
        });
  }
}
2 Likes

Brilliant, thanks a lot!

Hello,

i’m new on angular2 and I can’t resolve a problem. Sorry for my english i’m a french man. I’m using calendar native to save a event using google calendar. When I’m coming out of my application and when I’m trying to come back to my application, my application crashes I don’t know why, It’s because I did not use the resume and pause in my component?

Did you try this on iOS? I am not receiving resume events on iOS.

  public call(phoneNumber: string): void {
    const startDate: Date = new Date();
    if (this.platform.is("cordova")) {
      this.callNumber
        .callNumber(phoneNumber, true)
        .then(() => {
          console.log("create subscription");
          this.onResumeSubscription$ = this.platform.resume.subscribe(() => {
            console.log("subscriprion executed");
            this.openCallNoteAndSaveCall(startDate);
            this.onResumeSubscription$.unsubscribe();
          });
        })
        .catch(() => null);
    } else {
      location.href = `tel:${phoneNumber}`;
      this.openCallNoteAndSaveCall(startDate);
    }
  }

I like this idea of having a flag to trigger the subscription function or not. But I see this creates a new subscription every time the page is loaded. So for example this pattern of execution will create 2 subscriptions:

  • go to home page
  • a resume subscription is created
  • navigate to settings page (settings used as example another page is what I mean)
  • return to home page
    here another subscription is created
  • put the app in the background
  • resume the app
    The code within the resume subscription is then executed twice.

Do you see a way around this? Maybe a different nav event?

I added an event for ionViewWillLeave and unsubscribed the resume. It then re-subscribes when returning to the page. Still think it is strange that the class is created anew each time a new page is pushed to the nav controller I would imagine it to just re-load it from the cache not create a new one. Makes me worry about memory usage is there a stack repeating pages each time they are pushed?

This just happens on Android. On iOS seems to work fine.
It still happens with any Ionic native plugin which pops a screen upon the app. It will always execute the resume callback as if our app was in background mode.
In my case, firing the event just after X seconds will not solve my problem, I don’t need it to fire at all (also, I think that if you change your phone date you could “hack” that logic, so it’s not a good solution in security matters like showing a pin-pad)