Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect if WhatsApp is installed

Using Android or a desktop browser, please visit this WhatsApp test page and click the Send button. If WhatsApp is not installed it will show you a message.

How does the code detection on that page work? I tried the following but nothing happens.

try {
  location.href = 'whatsapp://send/?phone=62812345678&text=test';
} catch (e) {
  console.log(e);
}
like image 637
uingtea Avatar asked Jan 06 '20 23:01

uingtea


1 Answers

Looking at the page, it appears that at least on Chrome, they programmatically open an iframe with the src of whatsapp://send/?phone=<number>&text=test. They then start a 1000ms timeout after which the "Looks like you don't have WhatsApp installed!" text is shown. This timeout is cancelled by an blur event handler, meaning that their check is based on your device opening WhatsApp when that URL is loaded, which blurs the window.

The function which triggers after the timeout also seems to check if the timeout took longer than 1250ms. I suspect that this handles the case where your phone's browser stops executing JS timers when it changes apps.

On IE, they use window.navigator.msLaunchUri, which accepts a noHandlerCallback.

See for yourself by opening your browser's devtools and searching for WhatsAppApiOpenUrl. On Chrome, the Search can be found from the devtools' menu:

screenshot of where the Search tool can be found in Chrome's devtools

Here's some example code.

const detectWhatsapp = (phone, text) => {
  const uri = `whatsapp://send/?phone=${encodeURIComponent(
    phone
  )}&text=${encodeURIComponent(text)}`;

  const onIE = () => {
    return new Promise((resolve) => {
      window.navigator.msLaunchUri(
        uri,
        () => resolve(true),
        () => resolve(false)
      );
    });
  };

  const notOnIE = () => {
    return new Promise((resolve) => {
      const a =
        document.getElementById("wapp-launcher") || document.createElement("a");
      a.id = "wapp-launcher";
      a.href = uri;
      a.style.display = "none";
      document.body.appendChild(a);

      const start = Date.now();
      const timeoutToken = setTimeout(() => {
        if (Date.now() - start > 1250) {
          resolve(true);
        } else {
          resolve(false);
        }
      }, 1000);

      const handleBlur = () => {
        clearTimeout(timeoutToken);
        resolve(true);
      };
      window.addEventListener("blur", handleBlur);

      a.click();
    });
  };

  return window.navigator.msLaunchUri ? onIE() : notOnIE();
};

Please note that it adds an event listener each time it's called. If you're rolling this out into production, please use window.removeEventListener to remove handleBlur after the promise resolves. It also appends a DOM node into the body, if that matters to you.

Usage example:

detectWhatsapp('<your number here>', 'test').then(hasWhatsapp =>
  alert(
     'You ' + 
        (hasWhatsapp ? 'have WhatsApp' : "don't have WhatsApp")
  )
)
like image 98
cbr Avatar answered Sep 28 '22 00:09

cbr