I've been trying to access the rear camera on an LG G4 Android phone running Chrome. I'm able to filter out the video sources from MediaStreamTrack.getSources(), but when I try to set a constraint to prefer the rear camera, I get the error TypeError: Failed to execute 'webkitGetUserMedia' on 'Navigator': Malformed constraints object. Below I have the code I'm using to filter the video sources:
if (navigator.getUserMedia) {
if (MediaStreamTrack.getSources) {
MediaStreamTrack.getSources(function(sourceInfos) {
var sources = [];
_.forEach(sourceInfos, function(info) {
if (info.kind === 'video') {
sources.push(info);
}
})
handleSources(sources);
})
}
}
Then I'm trying to select a source in the handleSources function mentioned above:
function handleSources(sources) {
var constraints = {
video: {
facingMode: 'environment' // Yeah, this definitely doesn't work.
}
}
getMedia(constraints); // This calls getUserMedia with the selected contraints
}
I've tried tons of different formats for the constraints object, but none of them seem to work. I know I'd be able to loop through all the sources and select the environmental camera from there, but I'd love to know how the actual syntax for this works. Googling for the answer only brings up https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#Parameters, the syntax of which doesn't work.
It would appear as though earlier/different versions of the Android browser implement a different API for camera discovery. I have found that each phone I have access to (emulator and physical) seems to respect a different set of options. To make matters worse this seems to be an area where various documentation repositories insist on ignoring or removing the previously implemented APIs (even though we still need to know how to use them if we are going to be able to implement on anything but the newest phones).
The two major flavors of the API that I have found are the one that's currently documented (you refer to that API above) and one that was documented in a version of the WebRTC specification from October 2013. That flavor has a significantly different constraints specification that includes mandatory and optional properties. Your call above to getMedia would look like this under the older specification:
var constraints = {
video: {
mandatory: {
facingMode: 'environment'
}
}
}
getMedia(constraints);
Alternately, you can use optional settings, which are provided as an array instead so you can have multiple choices (these are evaluated in order):
var constraints = {
video: {
optional: [{
facingMode: 'environment'
}]
}
}
getMedia(constraints);
That having been said, your mileage may vary when it comes to finding filters that work. For example, the facingMode filter above does not function in my Android 5.0 emulator (it doesn't throw an error but it also doesn't present the environment-facing camera); however, using a device ID does work (which looks like this when mapped to your example):
var constraints = {
video: {
mandatory: {
sourceId: '<your source ID here>'
}
}
}
getMedia(constraints);
In the case of the Android 5.0 emulated device that I have done some testing with I am able to use MediaStreamTrack.getSources() to find the device I want (it returns the facing property with each camera). Note that the "recommended" replacement method navigator.mediaDevices.enumerateDevices() method is not present in this emulated device.
There are numerous other issues that you will see when using different emulated and physical devices, each of which has been quite problematic for me when implementing these APIs in the real world. I highly recommend using a combination of multiple physical devices (if you are in a work environment where you can get access to them), BrowserStack (to give you lots of real and emulated devices to test on), console.log(), and Vorlon.js (to view the console.log() output in real-time from all those emulated devices so you can see what's really going on).
I am currently working on this exact problem right now - if I find anything additional with respect to different API flavors that need supporting I will post an update here.
If you look at the Browser Compatibility section of the MDN page you linked to you'll see:
Chrome uses an outdated constraint syntax, but the syntax described here is available through the adapter.js polyfill.
You'll be happy to know that adapter.js now supports the facingMode constraint on Chrome for Android (use https fiddle for Chrome):
var gum = mode =>
navigator.mediaDevices.getUserMedia({video: {facingMode: {exact: mode}}})
.then(stream => (video.srcObject = stream))
.catch(e => log(e));
var stop = () => video.srcObject && video.srcObject.getTracks().map(t => t.stop());
var log = msg => div.innerHTML += msg + "<br>";
<button onclick="stop();gum('user')">Front</button>
<button onclick="stop();gum('environment')">Back</button>
<div id="div"></div><br>
<video id="video" height="320" autoplay></video>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
The { exact: } syntax means the constraint is required, and things fail if the user doesn't have the right camera. If you leave it out then the constraint is optional (though Firefox for Android will let users override the choice in the camera chooser in the permission prompt in that case).
adapter.js also supports navigator.mediaDevices.enumerateDevices(), which has replaced MediaStreamTrack.getSources.
Related
I have a problem using an actual Ionic Angular in combination with NFC reader.
I can successfully read the NFC Tag with the example provided in:
https://ionicframework.com/docs/native/nfc
The Problem that i have is, that after the reading of the NFC somehow the Angular ChangeDetection is broken and the changes are not displayed. When i click on a button that does nothing, the changes are displayed.
I know i can trigger the ChangeDetection manually, which would work, but then i would have to trigger the ChangeDetection everywhere in the component, which i think should not be the case.
Does anyone have an idea what i am doing wrong?
First i thought maybe it has something to do with the observable, but i tried adding a interval observable, which works just like expected.
Template:
<p><ion-button (click)="startNFC()">start NFC readermode</ion-button></p>
<p><ion-button (click)="startNFCListener()">start NFC listener</ion-button></p>
<p><ion-button (click)="startInterval()">start Interval</ion-button></p>
<p><ion-button (click)="doNothing()">do nothing</ion-button></p>
<p>
{{nfcReading}}
{{nfcTag}}
<ion-spinner *ngIf="nfcReading"> </ion-spinner>
</p>
Code:
startNFC() {
console.log("startNFC");
this.nfcReading = true;
let flags = this.nfc.FLAG_READER_NFC_A | this.nfc.FLAG_READER_NFC_V;
this.readerModeSub = this.nfc.readerMode(flags).subscribe(
tag => {
console.log(JSON.stringify(tag));
this.nfcTag = this.nfc.bytesToHexString(tag.id);
this.nfcReading = false;
this.readerModeSub.unsubscribe();
},
err => {
console.log("Error reading tag", err);
this.nfcReading = false;
}
);
}
doNothing() {
// this really does nothing... it is just to demonstrate that this triggers the changedetection
}
i get can see that the subsciption event is triggered in console, but it is not shown in the HTML.
When clicking the do nothing Button. The tag is shown.
I created a fresh type=angular blank project and test on a Google Pixel 3a hardware.
i have uploaded a sample project to demonstrate my problem.
https://github.com/autlunatic/Ionic-NFC-Android-Test/
I'm creating an omni-channel application using Kony and though it's all a single Javascript codebase, I'd like to conditionally execute some logic depending on whether the app is running on iOS, Android or a web browser. Something like:
if(isAndroid()) {
//Do some stuff specific to Android.
}
else if(isIos()) {
//Do some stuff specific to iOS.
}
else if(isWeb()) {
//Do some stuff specific to Web.
}
Kony supports Preprocessor Directives such as #ifdef much like the C compiler's preprocessors. Since Kony projects are written in Javascript, these statements must be added in the form of special comments in order not to break the Javascript syntax. So for example #ifdef becomes //#ifdef.
These directives can be used to write code which gets built into the application or not depending on the host OS. So I've solved this by writing this:
var channel;
//#ifdef PLATFORM_NATIVE_IOS
channel = "ios"
//#endif
//#ifdef PLATFORM_NATIVE_ANDROID
channel = "android"
//#endif
And then writing the rest of my logic based on the value of my channel variable.
For a full list of the macros defined which you can use in these //#ifdef statements you can look at the first few lines in the kony_sdk.js module created by default in every Kony Visualizer project.
Another solution is to rely on the kony.os.deviceInfo function from the kony.os namespace.
var deviceInfo = kony.os.deviceInfo();
var os = deviceInfo.name /*android and web*/ || deviceInfo.osname /*iOS*/;
if(os === "i-phone" || os === "i-pad"){
//Do some stuff specific to iOS
}
else if(os === "android"){
//Do some stuff specific to Android
}
else if(os === "thinclient"){
//Do some stuff specific to web.
}
This is perhaps cleaner, but the result is that all the application logic gets bundled into every build regardless of which platform it's for. So this is only better if the amount of logic you want to run conditionally is small — Arguably because you don't want to pollute your Android codebase with a bunch of logic that will only execute on iOS or vice versa.
I am trying to read from a Bloodsugar Meter using NFC, right now on an Android, haven't tried iOS yet (don't have a phone with NFC).
I am using react-native-nfc-manager as library and the example that comes with it:
https://github.com/whitedogg13/react-native-nfc-manager
I am receiving this tag:
{ "techTypes":["android.nfc.tech.NfcV","android.nfc.tech.NdefFormatable"], "id":"87C5280D002602E0"}
I can see that NfcV is covered in this library, but how do I read it as that type?
I am following the example so I haven't set anything in my manifest or my build.gradle. I have linked it and it is working, but im missing the last part it seems.
By following the example it looks like I am supposed to use a method like this:
_parseText = (tag) => {
try {
if (Ndef.isType(tag.ndefMessage[0], Ndef.TNF_WELL_KNOWN, Ndef.RTD_TEXT)) {
return Ndef.text.decodePayload(tag.ndefMessage[0].payload);
}
} catch (e) {
console.log(e);
}
return null;
}
But my tag doesn't have a ndefMessage[0].
Since your tag does not contain Ndef in its techTypes list, it does not contain an NDEF message. Consequently, you won't be able to read any such message. As your "tag" is a blood sugar meter, I assume that it's not even expected to contain an NDEF message.
Instead, you will have to find out what commands the blood sugar meter actually supports (probably it will support the ISO/IEC 15693 READ SINGLE BLOCK command (see here). In order to send such low-level commands, you will need to use the Generic NfcTech API by requesting the tag technology:
NfcManager.requestTechnology(NfcTech.NfcV)
You can then use the transceive method to exchange arbitrary commands:
NfcManager.transceive(...)
I try to open a website in chrome and native android browser (emulated Android 5.0 and 6) to check several items.
every time i run the test, the choosen browser get started and in the url field stays 'data:,' btw the actual tab is also loaded two times.
I know there is a way to do this with selenium webdriver, but this is not the right way for me because i used the followed code for several emulated devices to test this website and on iOS/Mac osx its working.
So why not on android. My way to test this website is with capybara, appium and ruby.
on iOS there exists a
:safariInitialUrl =>'http://www.mypage.com'
cap.
but not for android.
So, my question is: how is it possible to start my website on an emulated android devices without using selenium webdriver.
code:
Capybara.register_driver :androidphone do |app|
capabilities = {
:deviceName => 'nex5_5',
:avd => 'nex5_5',
:browserName => 'Chrome',
:platformVersion => '5.0',
:platformName => 'Android',
:automationName => 'Appium',
}
url = "http://localhost:4723/wd/hub"
appium_lib_options = {
server_url: url
}
all_options = {
appium_lib: appium_lib_options,
caps: capabilities,
}
Appium::Capybara::Driver.new app, all_options
You have to add this line in your config file :
exports.config = {
directConnect:false,
}
I think you have also to add the Timeout in your configuration file, if you are using jasmine framework for example add this code :
jasmineNodeOpts: {
defaultTimeoutInterval: 30000
}
I am trying to check disk space available in mobile using below code:
cordova.exec(function(result) {
var diskSizeInMB = result/1024;
alert(diskSizeInMB)
}, function(error) {
alert("Error: " + error);
}, "File", "getFreeDiskSpace", []);
In Android device it gives me correct result, whereas if I use iPhone/iPad it always returns me 0 as a output. Can anyone guide me how to resolve this issue? Or is there a way to check the disk size in iOS using cordova without writing custom plugin? To make this happen I haven't changed any configuration changes
It was a bug, but was fixed long time ago.
Old answer:
I confirm there is a bug on the code, the problem is getFreeDiskSpace is undocumented and cordova team want to remove the native code as getFreeDiskSpace isn't part of the w3c file API.
If you want to fix the bug for you app, go to CDVFile.m and change the line
NSNumber* pNumAvail = [self checkFreeDiskSpace:self.appDocsPath];
to
NSNumber* pNumAvail = [self checkFreeDiskSpace:self.rootDocsPath];
on the getFreeDiskSpace method