quagga2 icon indicating copy to clipboard operation
quagga2 copied to clipboard

Demos not working in Chrome for Android or other browsers in android

Open npsantini opened this issue 4 years ago • 11 comments

I've been using quaggajs for a while now and all of the sudden it doesn't work on mobile versions of chrome. I believe this could be a relatively new issue, but it could be a few months old.

I'm using the latest version of Chrome for Android and I'm on Android version 10.

Is there a known fix for this or has anyone else run into this problem?

npsantini avatar Oct 09 '20 15:10 npsantini

Thank you for filing an issue! Please be patient. :-)

github-actions[bot] avatar Oct 09 '20 15:10 github-actions[bot]

Hi @npsantini ! My phone just updated Chrome yesterday, 86.0.4240.75, and my application seems to be working as normal.

Are you able to provide any more specific details, as to what "doesn't work" means, or get to the console log or anything?

ericblade avatar Oct 09 '20 17:10 ericblade

Facing this issue too when using live stream on Android -- image appears for a second then just turns black. Downgrading back to Chrome v80 or use Microsoft Edge works though. Devices are Redmi S2 and Vivo 1820

My code:

let quaggaStarted = false
let enteringQty = false
var App = {
    init : function() {
        Quagga.init(this.state, function(err) {
            if (err) {
                console.log(err);
                return;
            }
            App.attachListeners();
            App.checkCapabilities();
            Quagga.start();
            quaggaStarted = true
        });
    },
    checkCapabilities: function() {
        var track = Quagga.CameraAccess.getActiveTrack();
        var capabilities = {};
        if (typeof track.getCapabilities === 'function') {
            capabilities = track.getCapabilities();
        }
        this.applySettingsVisibility('zoom', capabilities.zoom);
        this.applySettingsVisibility('torch', capabilities.torch);
    },
    updateOptionsForMediaRange: function(node, range) {
        console.log('updateOptionsForMediaRange', node, range);
        var NUM_STEPS = 6;
        var stepSize = (range.max - range.min) / NUM_STEPS;
        var option;
        var value;
        while (node.firstChild) {
            node.removeChild(node.firstChild);
        }
        for (var i = 0; i <= NUM_STEPS; i++) {
            value = range.min + (stepSize * i);
            option = document.createElement('option');
            option.value = value;
            option.innerHTML = value;
            node.appendChild(option);
        }
    },
    applySettingsVisibility: function(setting, capability) {
        // depending on type of capability
        if (typeof capability === 'boolean') {
            var node = document.querySelector('input[name="settings_' + setting + '"]');
            if (node) {
                node.parentNode.style.display = capability ? 'block' : 'none';
            }
            return;
        }
        if (window.MediaSettingsRange && capability instanceof window.MediaSettingsRange) {
            var node = document.querySelector('select[name="settings_' + setting + '"]');
            if (node) {
                this.updateOptionsForMediaRange(node, capability);
                node.parentNode.style.display = 'block';
            }
            return;
        }
    },
    initCameraSelection: function(){
        var streamLabel = Quagga.CameraAccess.getActiveStreamLabel();

        return Quagga.CameraAccess.enumerateVideoDevices()
        .then(function(devices) {
            function pruneText(text) {
                return text.length > 30 ? text.substr(0, 30) : text;
            }
            var $deviceSelection = document.getElementById("deviceSelection");
            while ($deviceSelection.firstChild) {
                $deviceSelection.removeChild($deviceSelection.firstChild);
            }
            devices.forEach(function(device) {
                var $option = document.createElement("option");
                $option.value = device.deviceId || device.id;
                $option.appendChild(document.createTextNode(pruneText(device.label || device.deviceId || device.id)));
                $option.selected = streamLabel === device.label;
                $deviceSelection.appendChild($option);
            });
        });
    },
    attachListeners: function() {
        var self = this;

        self.initCameraSelection();
        $(".controls").on("click", "button.stop", function(e) {
            e.preventDefault();
            Quagga.stop();
            quaggaStarted = false;
        });

        $(".controls .reader-config-group").on("change", "input, select", function(e) {
            e.preventDefault();
            var $target = $(e.target),
                value = $target.attr("type") === "checkbox" ? $target.prop("checked") : $target.val(),
                name = $target.attr("name"),
                state = self._convertNameToState(name);

            console.log("Value of "+ state + " changed to " + value);
            self.setState(state, value);
        });
    },
    _accessByPath: function(obj, path, val) {
        var parts = path.split('.'),
            depth = parts.length,
            setter = (typeof val !== "undefined") ? true : false;

        return parts.reduce(function(o, key, i) {
            if (setter && (i + 1) === depth) {
                if (typeof o[key] === "object" && typeof val === "object") {
                    Object.assign(o[key], val);
                } else {
                    o[key] = val;
                }
            }
            return key in o ? o[key] : {};
        }, obj);
    },
    _convertNameToState: function(name) {
        return name.replace("_", ".").split("-").reduce(function(result, value) {
            return result + value.charAt(0).toUpperCase() + value.substring(1);
        });
    },
    detachListeners: function() {
        $(".controls").off("click", "button.stop");
        $(".controls .reader-config-group").off("change", "input, select");
    },
    applySetting: function(setting, value) {
        var track = Quagga.CameraAccess.getActiveTrack();
        if (track && typeof track.getCapabilities === 'function') {
            switch (setting) {
            case 'zoom':
                return track.applyConstraints({advanced: [{zoom: parseFloat(value)}]});
            case 'torch':
                return track.applyConstraints({advanced: [{torch: !!value}]});
            }
        }
    },
    setState: function(path, value) {
        var self = this;

        if (typeof self._accessByPath(self.inputMapper, path) === "function") {
            value = self._accessByPath(self.inputMapper, path)(value);
        }

        if (path.startsWith('settings.')) {
            var setting = path.substring(9);
            return self.applySetting(setting, value);
        }
        self._accessByPath(self.state, path, value);

        console.log(JSON.stringify(self.state));
        App.detachListeners();
        Quagga.stop();
        App.init();
    },
    inputMapper: {
        inputStream: {
            constraints: function(value){
                if (/^(\d+)x(\d+)$/.test(value)) {
                    var values = value.split('x');
                    return {
                        width: {min: parseInt(values[0])},
                        height: {min: parseInt(values[1])}
                    };
                }
                return {
                    deviceId: value
                };
            }
        },
        numOfWorkers: function(value) {
            return parseInt(value);
        },
        decoder: {
            readers: function(value) {
                if (value === 'ean_extended') {
                    return [{
                        format: "ean_reader",
                        config: {
                            supplements: [
                                'ean_5_reader', 'ean_2_reader'
                            ]
                        }
                    }];
                }
                return [{
                    format: value + "_reader",
                    config: {}
                }];
            }
        }
    },
    state: {
        inputStream: {
            type : "LiveStream",
            constraints: {
                width: {min: 640},
                height: {min: 480},
                aspectRatio: {min: 1, max: 2}, //https://github.com/serratus/quaggaJS/issues/444
                facingMode: "environment" // or user
            }
        },
        locator: {
            patchSize: "medium",
            halfSample: true
        },
        numOfWorkers: 2,
        frequency: 5,
        decoder: {
            readers : [{
                format: "ean_reader",
                config: {}
            }]
        },
        locate: true
    },
    lastResult : null
};



Quagga.onProcessed(function(result) {
    var drawingCtx = Quagga.canvas.ctx.overlay,
        drawingCanvas = Quagga.canvas.dom.overlay;

    if (result) {
        if (result.boxes) {
            drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
            result.boxes.filter(function (box) {
                return box !== result.box;
            }).forEach(function (box) {
                Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "green", lineWidth: 2});
            });
        }

        if (result.box) {
            Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: "#00F", lineWidth: 2});
        }

        if (result.codeResult && result.codeResult.code) {
            Quagga.ImageDebug.drawPath(result.line, {x: 'x', y: 'y'}, drawingCtx, {color: 'red', lineWidth: 3});
        }
    }
});

Quagga.onDetected(function(result) {
    if(!enteringQty){
        enteringQty = true
        var code = result.codeResult.code;
        findBarSku(code, () => {
            enteringQty = false
        })
    }


    // if (App.lastResult !== code) {
    //     App.lastResult = code;
    //     var $node = null, canvas = Quagga.canvas.dom.image;

    //     $node = $('<li><div class="thumbnail"><div class="imgWrapper"><img /></div><div class="caption"><h4 class="code"></h4></div></div></li>');
    //     $node.find("img").attr("src", canvas.toDataURL());
    //     $node.find("h4.code").html(code);
    //     $("#result_strip ul.thumbnails").prepend($node);
    // }
});

suparpat avatar Oct 21 '20 09:10 suparpat

Any chance this is related to Android 11 ? https://github.com/ericblade/quagga2/issues/266

I am not able to repro using the most recent Chrome update on my Android 9 phone, and that's the best I've got access to.

If you have a public hosting of your code, I'd be happy to check it from my phone as well.

ericblade avatar Oct 21 '20 17:10 ericblade

The devices I'm using are on Android 10. I would share the link, but we have the project limited to specific IP addresses.

npsantini avatar Oct 29 '20 21:10 npsantini

@suparpat that's a bit of a long example for me to digest all at once, any chance you can simplify further?

@npsantini hmm. android 9 doesn't seem to be having any issues here, with latest chromium available there. At least not with any of my app code. I'm.. not sure how to proceed.

ericblade avatar Oct 30 '20 06:10 ericblade

... I did not mean to close this.

ericblade avatar Oct 30 '20 06:10 ericblade

@ericblade I have done some testing on a few other devices and it does appear to be an Android version problem. It works on Android 9, but not Android 10. All other devices I've tested seem to work fine included chrome for desktop on windows, iphone 14.2 in safari, and ipad os 14.1.

npsantini avatar Dec 16 '20 16:12 npsantini

@ericblade The beta demos do seem to work in Chrome on Android 10. The live stream pulls up the front facing camera.

What would be the difference that causes the beta examples to work, but not the other non-beta demos?

npsantini avatar Dec 16 '20 16:12 npsantini

@npsantini good question. i didn't even realize there was a "beta" branch in the old repo, until loooooooooooong after i forked. Might be worth doing a diff on the beta branch.

I.. don't know when i might have time to do that, things are pretty wild right now.

ericblade avatar Dec 17 '20 06:12 ericblade

Has anyone been able to resolve this issue? I'm having problems on Android Version 9... which seems to be working for you guys 🤔

brada1703 avatar Aug 04 '21 21:08 brada1703

having not received any reports of obvious Android issues in a year now, I'm thinking this problem was resolved in a Chrome update. If anyone has an issue, lmk, open a new ticket. thanks!

ericblade avatar Sep 06 '22 08:09 ericblade