I am working on Cordova app. I wannt to implement a qrcode reader. I tried plugins available in Cordova but they all are buggy and some doesnot provide preview of scanner/video on same screen.
So I decided to use instascan which is a js based library to be used with webcams. I used it and implemented it in a simple cordova app and its working.
Now I see preview of my scan (camera video which is currently being scanned) and it scans perfectly.
But later I merged that code with my actual Cordova app which uses Vue cli. Now I am getting:
Error: Cannot access video stream (NotReadableError)
This error is probably (as I read) due to Chrome's https policy. But the problem is, Cordova uses webview and another cordova app which is basic cordova instance with this plugin only is working perfectly.
My implementation:
mounted: function () {
var _this = this;
this.$ons.ready(function () { // this is ready event fired by Onsen UI when cordova's native APIs are loaded
var scanner = new Instascan.Scanner({
continuous: true,
mirror: false,
video: document.getElementById('scannerPreview'),
});
scanner.addListener('scan', function (content) {
alert('scan' + content);
});
Instascan.Camera.getCameras().then(function (cameras) {
if (cameras.length > 0) {
if (cameras.length === 1) {
scanner.start(cameras[0]);
} else {
scanner.start(cameras[1]);
}
scanner.start(cameras[0]);
} else {
alert('No cameras found.');
}
}).catch(function (e) {
alert(e);
});
});
},
first add the permissions plugin:
cordova plugin add cordova-plugin-permission
And then you must request the permits in the camera:
permissions.requestPermission(permissions.CAMERA, success, error);
function error() {
return false;
}
function success( status ) {
if( !status.hasPermission ) error();
return true;
}
Related
I am currently working on a project on Android using the Expo client for react native. I am trying to open a webpage using WebBrowser, passing my app development URI to the website. The website basically just redirects to the given URI after 5 seconds. However it never seems to open anything. I've used almost the exact same code as here: https://github.com/expo/examples/tree/master/with-webbrowser-redirect. When I load this project into expo and run, it redirects fine. However, when I copy the code for the website and app into my own project, website opens and displays, but redirect does nothing. It just stays there. Here is the code for opening the browser.
_openBrowserAsync = async () => {
try {
this._addLinkingListener();
let result = await WebBrowser.openBrowserAsync(
'https://wexley-auth.firebaseapp.com/?linkingUri=exp://192.168.1.2:19000'
);
this._removeLinkingListener();
this.setState({ result });
} catch (error) {
alert(error);
console.log(error);
}
};
The linking listener never fires the callback which should dismiss the browser. My app URI should be exp://192.168.1.2:19000 as expo developer tools shows me this when I connect over LAN. Ive also tried using Linking.makeUrl() instead of sending URI in string manually. Neither method works for me. Relevant website code:
document.addEventListener("DOMContentLoaded", function(event) {
var links = document.querySelectorAll('a');
var baseUri='';
// Take the uri from the params
var qs = decodeURIComponent(document.location.search);
if (qs) {
baseUri = qs.split('?linkingUri=')[1];
}
var redirectInterval = setInterval(function() {
var countdown = document.querySelector('.countdown');
var t = parseInt(countdown.innerText, 10);
t -= 1;
if (t === 0) {
clearInterval(redirectInterval);
window.location.href = baseUri;
}
}, 1000);
});
Am I missing a step? Do I need to setup a scheme for opening my app or should this work out of the box? Am I using the wrong URI? I noticed that in the example code, the app.json has the following fields that my app.json doesn't have:
"scheme": "expo.examples.with-webbrowser-redirect",
"platforms": [
"android",
"ios"
]
I use navigator.mediaDevices.enumerateDevices to retrieve list of all video devices (element.kind === 'videoinput') and then call navigator.mediaDevices.getUserMedia(constraints) call to rotate video devices (using deviceId as constraint). Everything works fine on Windows Chrome / Firefox, but on android phone (tried Samsung, Asus, Huawei with Android 8/9) this call fails for back camera with NotReadableError / Could not start video source (for Chrome) or AbortError / Starting video failed (for Firefox).
Strangely same code works ok in iOS / Safari.
Also this only happens when WebRTC call is present in browser. If there is no call I can select any video device.
Also if I select back camera first and try to establish the call, it does not work, I get similar error.
I know it's far-fetched but maybe someone had same/similar issue?
All browser versions are up-to-date.
[UPDATE - code snippet and log]
switchCamera() {
try {
if (this.localStream) {
const tracks = this.localStream.getTracks();
console.log('switchCamera stopping this.localStream tracks', tracks);
tracks.forEach((track: MediaStreamTrack) => {
console.log('switchCamera stopping track', track);
track.stop();
});
console.log('switchCamera stop stream');
}
const constraints = {
audio: true,
video: { facingMode: this.faceCamera ? 'environment' : 'face' }
};
this.faceCamera = !this.faceCamera;
console.log('switchCamera constraints: ', constraints);
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
console.log('getUserMedia:', stream);
this.logText('got stream');
this.localVideo.srcObject = stream;
const videoTracks = stream.getVideoTracks();
const audioTracks = stream.getAudioTracks();
console.log('videoTracks', videoTracks);
if (videoTracks.length > 0) {
console.log(`Using video device: ${videoTracks[0].label}`);
}
const videoTrack = videoTracks[0];
const audioTrack = audioTracks[0];
console.log('Replacing track for pc', videoTrack, audioTrack);
const pc = this.session.sessionDescriptionHandler.peerConnection;
const videoSender = pc.getSenders().find(s => {
return s.track && s.track.kind === videoTrack.kind;
});
const audioSender = pc.getSenders().find(s => {
return s.track && s.track.kind === audioTrack.kind;
});
if (videoSender) {
console.log('videoSender.replaceTrack', videoTrack);
videoSender.replaceTrack(videoTrack);
}
if (audioSender) {
console.log('audioSender.replaceTrack', audioTrack);
audioSender.replaceTrack(audioTrack);
}
})
.catch(e => {
console.log('getUserMedia error:', e.name, e.code, e.message);
});
} catch (e) {
window.alert(e);
}
}
this is the log from chrome remote device debug:
The error is "NotReadableError", "Could not start video source" which means that the underlying device handle could not be obtained by chrome.
Again, safari/ios works ok.
For mobile devices, there is a dedicated way of how to select between front & back camera.
VideoFacingMode - https://www.w3.org/TR/mediacapture-streams/#dom-videofacingmodeenum
TL;DR
window.navigator.mediaDevices.enumerateDevices().then(devices => {
if (devices.filter(device => device.kind === 'videoinput').length > 1) {
navigator.mediaDevices.getUserMedia({video: {facingMode: 'user' /*'environment'*/}}).then(console.log.bind(this))
}
})
It works for mobile Safari, Chrome and FF.
NOTE
Remember, to stop the previous video track before calling the
getUserMedia with video again, otherwise, you will get an
exception.
Ok, so I narrowed it down to calling navigator.mediaDevices.getUserMedia() in ngInit() (this is Angular app).
Even if I remove all code in .then() handler function, the effect is the same.
Only removing this call solves the issue.
Not sure at this time why such behavior, will investigate it more thoroughly and update.
To switch between front and back cameras on mobile, you need to stop the previous stream before opening a new stream.
if (videoIn.srcObject) {
videoIn.srcObject.getTracks().forEach((track) => {
track.stop();
});
I have this ionic app where I put code to open the browser and go to app store or google play, depending on OS.
The call to App Store works. The string for app store is:
market = 'https://itunes.apple.com/us/app/my_app_name/id12345';
while for android is:
market = 'market://details?id=<package_name>';
The code to open the browser is:
cordova.InAppBrowser.open(market, '_blank', 'location=yes');
When I try to open in android, some kind of browser opens and displays the message:
"Web page not available. The webpage at market://details?id=my_app_id might be temporarly down ..."
Before this, the string for google play was the one you normally use in a browser, which is:
http://play.google.com/store/apps/details?id=<package_name>
In that case the message was about the broser not supporting the call to google play. It asked if I wanted to download google play app.
I guess the right way is to use the "market"-prefix? But still dont understand why its not showing the app.
Ok, so I looked into 'cordova-plugin-market' source code for ios. This is how the url to appstore is built:
NSString *url = [NSString stringWithFormat:#"itms-apps://itunes.apple.com/app/%#", appId];
And this is how url looks if you want to navigate from PC browser:
https://itunes.apple.com/app/id333903271 (twitter for example)
So if you use this plugin for Ios, consider adding 'id' prefix, and instead of using bundle Id (package name) use Apple ID of your app. For android of course com.example.package will be enough
let appId;
if (this.platform.is("android")) {
appId = "com.example.package"
} else {
appId = "id1234567"
}
this.market.open(appId).then(response => {
console.debug(response);
}).catch(error => {
console.warn(error);
});
}
I added
cordova plugin add https://github.com/xmartlabs/cordova-plugin-market
And then it worked.
import { InAppBrowserOptions, InAppBrowser } from '#ionic-native/in-app-browser';
import { Market } from "#ionic-native/market";
import { Platform } from "ionic-angular";
constructor(private market:Market, private inappBrowswer:InAppBrowser,
private platform:Platform)
{
this.onUpdateNow();//Call the update function
}
onUpdateNow() {
this.platform.ready().then(() => {
if (this.platform.is("ios")) {
//replace '310633997' with your iOS App ID
this.openInAppStore('itms-apps://itunes.apple.com/app/310633997'); //call the openInAppStore
} else if (this.platform.is("android")) {
//replace 'com.whatsapp.saint' with your Android App ID
this.market.open("com.whatsapp.saint").then(response => {
console.log(response);
}).catch(error => {
console.log(error);
});
}
});
}
openInAppStore(link) {
let options: InAppBrowserOptions = {
location: 'yes',//Or 'no'
};
let target = "_blank";
this.inappBrowswer.create(link, target, options);
}
I tried the plugin with capacitor and its working like a charm using the package name and the app id (for android and ios accordingly):
public goToStore() {
const appId = 1234512345;
const packageName = 'com.test.app';
Capacitor.getPlatform() === 'android' ? this.market.open(packageName) : this.market.open(appId);
}
The plugins:
"#ionic-native/market": "5.36.0",
"cordova-plugin-market": "1.2.0",
I've been trying to run the following code but the callbacks [ok() and ko()] are not called.
Using Worklight 6.2 (Cordova 3.4).
function wlCommonInit() {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
success, fail);
window.resolveLocalFileSystemURL(cordova.file.applicationDirectory
+ "www/index.html", ok, ko);
}
function ko(e) {
alert("NO");
}
function ok(fileEntry) {
alert("OK");
}
On the other hand requestFileSystem callbacks are called regularly.
The code snippet in the question will not work in Android due to a Cordova defect: https://issues.apache.org/jira/browse/CB-7273.
To progress further, it would help to understand what are your plans for the file itself.
Do you simply want the path to the file
Or do you want to alter the contents of the file?
Or?
You can read more about file system operations in Cordova in this question/answer: Where does LocalFileSystem.PERSISTENT point to?
I managed to access local fiiles in Worklight running an Android environment using a XMLHttpRequest:
//Works only on Android
function prendiCaricaRisorsaWorklight(percorsoPiuNomeFile) {
var xmlhttp = new XMLHttpRequest();
var risposta = "";
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4
&& (xmlhttp.status == 200 || xmlhttp.status == 0)) {
risposta = xmlhttp.responseText;
alert(risposta);
}
}
xmlhttp.open("GET", "file:///android_asset/www/default/"
+ percorsoPiuNomeFile, true);
xmlhttp.send(null);
}
Usage example:
prendiCaricaRisorsaWorklight("css/cssInterno.css");
prendiCaricaRisorsaWorklight("js/jsInterno.js");
This shows on Android an alert with the file content.
I'm currently trying to get the SoundCloud API working under PhoneGap/Cordova and Android. Here's the code that is working fine within the browser:
var track_url = 'http://soundcloud.com/mymusic/mymusic'
;
SC.get('/resolve', {
url : track_url
}, function(track) {
$("#stream").live("click", function() {
SC.stream("/tracks/" + track.id, function(sound) {
sound.play();
$("#stop").live("click", function() {
sound.stop();
});
});
});
});
On my android debug device however there is no sound. LogCat doesn't give any standard or cordova errors. Has anyone encountered this before?
Many Thanks in advance
Your code looks okay and I was able to test it successfully on Android. The only thing I can think of is that the resolve call is returning a 404 or similar. Have you tried checking for an error?
var track_url = 'http://soundcloud.com/your/track';
SC.initialize({
client_id: 'YOUR_CLIENT_ID'
});
SC.get('/resolve', { url: track_url }, function(track, error) {
if (error) {
alert("Error: " + error.message);
return;
}
// ... rest of your code
});
Try that and see if you get anything, if not, leave a comment.