2

I would like to ask how to turn on a phone flashlight on a web app using JavaScript and HTML. I had created a web app that is able to scan QR codes using a phone camera. I had managed to turn on a phone camera, but I faced some difficulties regarding to turning on the phone flashlight on a different platform which is Android and iOS.

For Android OS, I had managed to turn on the phone camera on my web app, and was also able to turn on the phone flashlight right after the camera has been turned on. However, after I turned on the flashlight, closed the web app, and reopened it again, the flashlight wasn't working anymore.

For iOS, the camera seems to be turned on, but I can't view anything through the web app. Besides, the phone flashlight doesn't work as well.

My code:

HTML part

<!DOCTYPE html>
<html>
 <head>
  <title>HOME PAGE</title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1" >
  <script src="https://rawgit.com/sitepoint-editors/jsqrcode/master/src/qr_packed.js"></script>
 </head>

 <body>
     <button class="switch"><i class="fa fa-lightbulb-o" id="flashlight-icon"></i>Flashlight</button>
        
     <canvas id="qr-canvas"></canvas>
      <div id="qr-result" hidden="">
        <b>Data:</b> <span id="outputData"></span>
      </div>

    <script src="src/torch.js"></script>  
    <script src="src/qrCodeScanner.js"></script>
  
 </body>
</html>

torch.js:

//Test browser support
const SUPPORTS_MEDIA_DEVICES = 'mediaDevices' in navigator;

if (SUPPORTS_MEDIA_DEVICES) {
  //Get the environment camera (usually the second one)
  navigator.mediaDevices.enumerateDevices().then(devices => {
  
    const cameras = devices.filter((device) => device.kind === 'videoinput');

    if (cameras.length === 0) {
      throw 'No camera found on this device.';
    }
    const camera = cameras[cameras.length - 1];

    // Create stream and get video track
    navigator.mediaDevices.getUserMedia({
      video: {
        deviceId: camera.deviceId,
        facingMode: ['user', 'environment'],
        height: {ideal: 1080},
        width: {ideal: 1920}
      }
    }).then(stream => {
      const track = stream.getVideoTracks()[0];

      //Create image capture object and get camera capabilities
      const imageCapture = new ImageCapture(track)
      const photoCapabilities = imageCapture.getPhotoCapabilities().then(() => {

        //todo: check if camera has a torch

        //let there be light!
        const btn = document.querySelector('.switch');
        btn.addEventListener('click', function(){
          track.applyConstraints({
            advanced: [{torch: true}]
          });
        });
      });
    });
  });
  
  //The light will be on as long the track exists
  
  
}

qrCodeScanner.js:

const qrcode1 = window.qrcode;
const video = document.createElement("video");
const canvasElement = document.getElementById("qr-canvas");
const canvas = canvasElement.getContext("2d");
const qrResult = document.getElementById("qr-result");
const outputData = document.getElementById("outputData");
const btnScanQR = document.getElementById("btn-scan-qr");

let scanning = false;

qrcode1.callback = res => {
  if (res) {
    outputData.innerText = res;
        if (res.match("^[0-9A-Z]{4}$")) {
                window.location="worker.php?ennm="+res;
                scanning = false;
                video.srcObject.getTracks().forEach(track => {
                  track.stop();
                });

                qrResult.hidden = false;
                canvasElement.hidden = true;
                btnScanQR.hidden = false;

          } else {
            alert("Wrong scanning!");
            window.location="qr.php";
          }

  }
};

  navigator.mediaDevices
    .getUserMedia({ video: { facingMode: "environment" } })
    .then(function(stream) {
      scanning = true;
      canvasElement.hidden = false;
      video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
      video.srcObject = stream;
      video.play();
      tick();
      scan();
    });

function tick() {
  canvasElement.height = video.videoHeight;
  canvasElement.width = video.videoWidth;
  canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);

  scanning && requestAnimationFrame(tick);
}

function scan() {
  try {
    qrcode1.decode();
  } catch (e) {
    setTimeout(scan, 300);
  }
}

Is there anything wrong with my code? Please, can anyone point out which part is wrong? I am a newbie to JavaScript and I would really appreciate it if someone can help me, thanks in advance!

MeDead
  • 197
  • 2
  • 9
jason
  • 21
  • 1
  • 4
  • Are you using HTTP or HTTPS protocol? – Aldin Aug 14 '21 at 20:52
  • Hi, there, I am using HTTPS protocol. – jason Aug 14 '21 at 21:20
  • Can you try using HTTP protocol OR uploading this file to another server which uses HTTPS? – Aldin Aug 14 '21 at 21:24
  • Ok, thanks for your suggestion, I will try it out, may I know would it be any difference if I switched to HTTP protocol or to another server? or perhaps just because I got wrong code so I couldn't to solve the issue? – jason Aug 14 '21 at 21:46
  • My suspicions are that SSL is creating problem here, so that's why I am suggesting to try HTTP or changing server... Btw. HTTPS, which uses SSL, provides identity verification and security, so you know you’re connected to the correct website and no one can eavesdrop on you. That’s the theory, anyway. In practice, SSL on the web is kind of a mess. This doesn’t mean that HTTPS and SSL encryption are worthless, as they’re definitely much better than using unencrypted HTTP connections. Even in a worst case scenario, a compromised HTTPS connection will only be as insecure as an HTTP connection. – Aldin Aug 14 '21 at 21:59
  • Therefore, I think code is okay. – Aldin Aug 14 '21 at 21:59
  • 1
    Oh I see, ok, I got it, thanks for your suggestion and answer, I will give it a try. I will post my output regardless it is satisfy or not later. – jason Aug 14 '21 at 22:12
  • Any update on this one? – Aldin Aug 15 '21 at 08:46
  • I had uploaded to different server with HTTPS, still no luck for iOS, but I haven't test on Android OS yet. Do you know any web hosting page that able to run with HTTP protocol? Because the web hosting page that I used is 000webhost.com which has HTTPS protocol, i don't know how to change from HTTPS to HTTP on 000webhost.com – jason Aug 15 '21 at 10:57
  • Is 000webhost using nginx or apache, you can use rule rewriting in order to force HTTP or HTTPS connection to be established. – Aldin Aug 15 '21 at 17:20
  • I am not sure if it is using nginx or apache, I think most probably is using apache. I found rewriting rule ```RewriteCond %{HTTPS} =on RewriteRule ^(.*)$ http://%{HTTP_HOST}/$1 [L,R=301]```, but I am not sure what should I filled in for HTTP_HOST, do you have any idea? – jason Aug 16 '21 at 07:40
  • `RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ http://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]` You don't have to fill out HTTP_HOST, leave it as it is – Aldin Aug 16 '21 at 10:47
  • Oh I see, ok thanks for your sharing, I will it try it out. – jason Aug 19 '21 at 09:25
  • 1
    @Aldin I can't understand how you can try to help someone with answers that has nothing to do with the problem. The camera access doesn't work without https! This is one point. The second point is that Apple was still not able to implement the torch in the safari browser. So, you can play around with different SSL certificate or with .htaccess, but nothing will help until Apple will integrate this functionality in iOS (and we know, they are never fast). – Adriano Nov 19 '21 at 15:37

1 Answers1

0

I already figured out the solution by myself, I just combined torch.js with qrCodeScanner.js into the following js:

const qrcode1 = window.qrcode;
const qrResult = document.getElementById("qr-result");
const outputData = document.getElementById("outputData");
const btnScanQR = document.getElementById("btn-scan-qr");
const video = document.createElement("video");
const canvasElement = document.getElementById("qr-canvas");
const canvas = canvasElement.getContext("2d");

let scanning = false;

// scanning qr code
qrcode1.callback = res => {
  if (res) {
    outputData.innerText = res;
        if (res.match("^[0-9A-Z]{4}$")) {
                window.location="worker.php?ennm="+res;
                scanning = false;

                video.srcObject.getTracks().forEach(track => {
                  track.stop();
                });

                qrResult.hidden = false;
                canvasElement.hidden = true;
                btnScanQR.hidden = false;


          } else {
            alert("WARNING! You are scanning the wrong QR code, please try again. Please be aware that this is not MySejahtera app, thanks.");
            window.location="qr.php";
          }
  }
};

//Test browser support
const SUPPORTS_MEDIA_DEVICES = 'mediaDevices' in navigator;

if (SUPPORTS_MEDIA_DEVICES) {
  //Get the environment camera (usually the second one)
  navigator.mediaDevices.enumerateDevices().then(devices => {
  
    const cameras = devices.filter((device) => device.kind === 'videoinput');

    if (cameras.length === 0) {
      throw 'No camera found on this device.';
    }
    const camera = cameras[cameras.length - 1];

    // Create stream and get video track
    navigator.mediaDevices.getUserMedia({
      video: {
        facingMode: ['environment']
      }

    })

    .then(function(stream) {
      scanning = true;
      canvasElement.hidden = false;
      video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
      video.srcObject = stream;
      video.play();
      tick();
      scan();
      
      const track = stream.getVideoTracks()[0];

      //Create image capture object and get camera capabilities
      const imageCapture = new ImageCapture(track)
      const photoCapabilities = imageCapture.getPhotoCapabilities().then(() => {

        //let there be light!
        const btn = document.querySelector('.switch');
        btn.addEventListener('click', function(){
          track.applyConstraints({
            advanced: [{torch: true}]
          });
        });
      });
    });
  });
  
  //The light will be on as long the track exists
  
}


function tick() {
  canvasElement.height = video.videoHeight;
  canvasElement.width = video.videoWidth;
  canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
  scanning && requestAnimationFrame(tick);
}

function scan() {
  try {
    qrcode1.decode();
  } catch (e) {
    setTimeout(scan, 300);
  }
}

Then, I hid the qrCodeScanner.js path in HTML, it works like a charm! However, this is only works in Android phone, js function of web app that triggered flashlight in iOS is inapplicable.

jason
  • 21
  • 1
  • 4