I came across several questions on this subject. I'm trying to select the rear camera on an Android device running Chrome.
So, after some reading :
var selector = document.getElementById('video-source-selector');
navigator.mediaDevices.enumerateDevices()
.then(function(devices) {
var videoDevices = devices.map(function (item) {
if(item.kind === 'videoinput'){
return item;
}
}).filter(function( element ) {
return element !== undefined;
});
var max = videoDevices.length;
videoDevices.forEach(function(device, i) {
var html = '';
var div = document.createElement('div');
if(i === max-1){ // last element reached
html += '<option value="'+device.deviceId+'" selected>'+ device.label +'</option>';
}
else {
html += '<option value="'+device.deviceId+'">'+ device.label +'</option>';
}
div.innerHTML = html;
selector.appendChild(div.childNodes[0]);
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
});
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
});
selector.addEventListener("change", function(){
console.log(selector.value); // Works as supposed : returns the ID of the selected device
});
Then, as I'm using Three.js in this app, I'm binding this ID to Jerome Etienne three extension WebcamGrabbing (https://github.com/jeromeetienne/threex.webar):
var videoGrabbing = new THREEx.WebcamGrabbing(selector.value);
Then I had to modify THREEx.WebcamGrabbing class this way (I removed the irrelevant parts):
THREEx.WebcamGrabbing = function(sourceDeviceId){
...
console.log('webcamgrabbing : ', sourceDeviceId); // returns the expected ID
var constraints = {
video: {
optional: [{
sourceId: sourceDeviceId
}]
}
}
// try to get user media
navigator.getUserMedia( constraints, function(stream){
domElement.src = URL.createObjectURL(stream);
}, function(error) {
console.error("Cant getUserMedia()! due to ", error);
});
...
}
But still, Chrome on Android is still giving me the stream of the face camera, whatever device I select...
What do I miss?
EDIT : Based on this topic (GetUserMedia - facingmode), I came up with some logs to see what's happening here :
var constraints = {
audio: false,
video: { facingMode: { exact: "environment" } }
}
console.log('Try to get stream with constraints:', constraints);
navigator.getUserMedia( constraints, function(stream){
var videoTracks = stream.getVideoTracks();
console.log('Got stream with constraints:', constraints); // Ok
console.log('Using video device: ' + videoTracks[0].label); // > Using video device: camera 0, facing back
for(var i = 0; i < videoTracks.length; i++){
console.log('Found video device with contraints : ', videoTracks[i].label); // Found video device with contraints : camera 0, facing back
}
domElement.src = URL.createObjectURL(stream);
}, function(error) {
console.error("Cant getUserMedia()! due to ", error);
});
An alternative way to select the back camera on chrome is to use the enumerateDevices method.
First get all the video input id's:
navigator.mediaDevices.enumerateDevices().then(function(devices) {
devices.forEach(function(device) {
if(device.kind=="videoinput"){
//If device is a video input add to array.
}
});
Then the first element of the array will contain the id of the front camera, the second element will contain the id of the back camera.
Finally put the id of the camera that you want to use
navigator.getUserMedia({audio: false, video: { sourceId: VideoId } }, successCallback, errorCallback);
Related
I have 3 tabs, that I need to test if they are clicked or not, but I can't seem to find a good expected condition to use. Right now I am using a browser. sleep with higher times, since the tab takes a couple of seconds to load.
The tabs:
Code:
let locator = {
upcomingTabButton : element(by.css('div.toolbar-content.toolbar-content-md ion-segment ion-segment-button:nth-child(1)')),
pendingTabButton : element(by.css('div.toolbar-content.toolbar-content-md ion-segment ion-segment-button:nth-child(2)')),
completedTabButton : element(by.css('div.toolbar-content.toolbar-content-md ion-segment ion-segment-button:nth-child(3)')),
activeTab : element(by.css('ion-segment ion-segment-button.segment-button.segment-activated')),
upcomingActiveTab: element(by.cssContainingText('ion-segment ion-segment-button.segment-button.segment-activated', 'UPCOMING')),
};
describe('When x tab button is pressed, ', function () {
it('if x = PENDING tab, then it should be activated', () => {
browser.sleep(1000);
browser.wait(EC.elementToBeClickable(locator.pendingTabButton),5000);
locator.pendingTabButton.click();
browser.sleep(10000);
expect(element(by.css('ion-segment ion-segment-button.segment-button.segment-activated')).getText()).toEqual('PENDING');
});
it('if x = UPCOMING tab, then it should be activated', () => {
browser.wait(EC.elementToBeClickable(locator.upcomingTabButton),5000);
locator.upcomingTabButton.click();
//browser.wait(EC.presenceOf(locator.upcomingActiveTab),5000);
browser.sleep(10000);
expect(element(by.css('ion-segment ion-segment-button.segment-button.segment-activated')).getText()).toEqual('UPCOMING');
});
it('if x = COMPLETED tab, then it should be activated', () => {
browser.wait(EC.elementToBeClickable(locator.completedTabButton),5000);
locator.completedTabButton.click();
//browser.wait(EC.presenceOf(locator.activeTab),5000);
browser.sleep(10000);
expect(element(by.css('ion-segment ion-segment-button.segment-button.segment-activated')).getText()).toEqual('COMPLETED');
});
});
I tried some expected conditions but nothing worked since I need to see when the tab is selected and I didn't found any exp condition like that.
I am using the browser.sleep(10000) 10 seconds, but that's, not a good way to use.
Can anyone help me here?
The tabs content takes like 5/6 seconds to load
/**
* wait until some element is present
*
*/
this.waitElement = function(ElmFinder){
var i = 0;
var _retryOnErr = function(err) {
console.log(' <<< warning: wait retrying iteration: ' + i + ' >>> ');
browser.sleep(500);
return false;
};
browser.wait(function() {
i++;
return ElmFinder.isDisplayed().then(function(present) {
if (present) {
return true;
} else {
console.log("element is not present");
return _retryOnErr();
}
}, _retryOnErr);
}, 120*1000, 'Error waiting for element present: ').
then(function(waitRetValue) {
return waitRetValue; // usually just `true`
}, function(err) {
throw err + ' after ' + i + ' iterations.';
});
};
I'm trying to get a thumbnail image from a video player that I set the Url when I choose a video from gallery or capture a video, so this my code in controller:-
var intent = Titanium.Android.createIntent({
action: Ti.Android.ACTION_PICK,
type : "video/*"
});
intent.addCategory(Ti.Android.CATEGORY_DEFAULT);
var curActivity = $.createPost.getActivity();
curActivity.startActivityForResult(intent, function(event) {
if (event.resultCode == Ti.Android.RESULT_OK) {
if (event.intent.data != null) {
// If everything went OK, save a reference to the video URI
Ti.API.info('here '+ event.intent.data);
Ti.API.info('video: '+ event.intent);
var videoPath = event.intent.data;
video = Ti.Filesystem.getFile(videoPath);
$[e.source.fileView].setUrl(event.intent.data);
// $[e.source.fileView].setMedia(file);
$[e.source.fileView].setVisible(true);
$[e.source.removeBtn].setVisible(true);
$.video.requestThumbnailImagesAtTimes([0,1,2,6,12], Titanium.Media.VIDEO_TIME_OPTION_NEAREST_KEYFRAME, function (response) {
alert('text');
Ti.API.info("Thumbnail callback called, success = " + response.success);
Ti.API.info("Thumbnail callback called, time = " + response.time);
Ti.API.info("Thumbnail callback called, code = " + response.code);
if(response.success) {
videoThumb = response.image;
}
});
}
else {
Ti.API.error('Could not retrieve media URL!');
}
}
else if (event.resultCode == Ti.Android.RESULT_CANCELED) {
Ti.API.trace('User cancelled video capture session.');
}
else {
Ti.API.error('Could not record video!');
}
});
I get the video and it appears in the video player ($.video) but I can't get the thumbnail at all.
this code works just fine in IOS
$.video.requestThumbnailImagesAtTimes([0,1,2,6,12], Titanium.Media.VIDEO_TIME_OPTION_NEAREST_KEYFRAME, function (response) {
alert('text');
Ti.API.info("Thumbnail callback called, success = " + response.success);
Ti.API.info("Thumbnail callback called, time = " + response.time);
Ti.API.info("Thumbnail callback called, code = " + response.code);
if(response.success) {
videoThumb = response.image;
}
});
but not in android.
It appears that at this time you cannot do this: it's broken. Here's the Jira ticket for the issue:
Android: Get image frames at times out of local video
It indicates that it's fixed in 6.1.0. Apparently now you can do this with a remote video, so if that fits your workflow you might be able to accomplish it.
Does this answer your question?
I am building a PhoneGap app, and currently have setup some datasources that have a JSON feed I pull from, to populate my app with data. Right now, it only pulls that data once, the first time the app is run.
I would like to download data everytime the app first opens, and then if it stays open for longer than 15 minutes, it updates again. The json feed can be queried with a last_mod_day in the URL so it pulls only the data that has changed.
What would be recommended to go about this, and how to check for a WiFi/Data Connection on the phone, and if not it fails quietly?
Below is the code for my current function to grab the feed.
function downloadFileError(evt) {
console.log('downloadFileError: ');
console.log(evt.target.error);
}
function downloadFile(ep) {
window.requestFileSystem(
LocalFileSystem.PERSISTENT,
0,
function onFileSystemSuccess(fileSystem) {
fileSystem.root.getFile("dummy.json", {
create: true,
exclusive: false
},
function gotFileEntry(fileEntry) {
var filename = cordova.file.dataDirectory + 'assets/json/' + ep + '.json';
var fileTransfer = new FileTransfer();
fileEntry.remove();
console.log('looking at ' + filename);
fileTransfer.download(
encodeURI("http://www.myURL.com/theApp?ep=" + ep),
filename,
function(theFile) {
console.log("download complete: " + theFile.toURL());
},
function(error) {
console.log("DLERR: src=" + error.source + " t=" + error.target);
}
);
},
function(evt) {
console.log(evt);
console.log('fn: ' + filename);
}
);
},
downloadFileError);
}
function downloadDynamicPages() {
var deferred = $.Deferred();
var pages = ['setOne','setTwo','setThree','setFour','setFive','setSix'];
var cnt = 0;
var total_pages = pages.length;
//checkConnection();
$.each(pages,function(k,v) {
console.log('looking at ' + v);
downloadFile(v);
cnt++;
if(cnt >= total_pages) {
deferred.resolve('all done with files');
}
});
return deferred.promise();
}
Any help on any part of these questions would help me greatly. If needed, I can answer any questions. Thank you Stack.
I'm implementing a Cordova application (3.2) where I want to use LeafletJS and a map tile provider together with a local filesystem cache of the tiles.
My approach in an overview is the following:
Extend the Leaflet TileLayer
Overwrite the _loadTile method to retrieve the tile either from local filesystem or from remote
My code:
var StorageTileLayer = L.TileLayer.extend({
log: function (text) {
if (this.options.log)
this.options.log(text);
else
console.log("[StorageTileLayer]: " + text);
},
_setUpTile: function (tile, key, value, cache) {
try {
tile._layer = this;
tile.onload = this._tileOnLoad;
tile.onerror = this._tileOnError;
this._adjustTilePoint(tile);
tile.src = value;
this.fire('tileloadstart', {
tile: tile,
url: tile.src
});
this.log("Setting url to " + tile.src);
}
catch (e) {
this.log("ERROR in setUpTile: " + e.message);
}
},
_loadTile: function (tile, tilePoint) {
this._adjustTilePoint(tilePoint);
var key = tilePoint.z + ',' + tilePoint.y + ',' + tilePoint.x;
var self = this;
var tileUrl = self.getTileUrl(tilePoint);
console.log(tileUrl);
console.log(typeof tileUrl);
if (this.options.storage) {
this.log("Load Tile with storage");
this.options.storage.get(key, tileUrl).then(function (value) {
self.log("Tile URL to load: " + value.url);
self._setUpTile(tile, key, value.url, true);
});
} else {
this.log("Load Tile without storage");
self._setUpTile(tile, key, tileUrl, false);
}
}
});
options.storage is a storage which has the method get(key, remoteUrl) and returns either the cached tile from local filestorage (this implementation actual works fine, so here is not the problem) or the remote url but downloads the tile in the background, so that it will be available from local file storage on the next call.
Unfortunately I can see on my device when I use Charles (Web Debugging Proxy) that although the local map tiles are loaded (I can see it from the logs) that there are still a couple of requests to the map tiles provider.
Does anyone have an idea what I am doing wrong and what else I have to overwrite in my StorageTileLayer to prevent the calls to the remote? The real problem is, that the map should work in offline mode as well, but it is not.
Thanks for your help.
Libraries in the environment:
Leaflet (0.7.3)
angularJS (1.2.16)
Cordova (3.2)
I basically fixed it with this code (angular js):
(function (window, L) {
var isDebug = false;
var StorageTileLayer = L.TileLayer.extend({
log: function (text) {
if (!isDebug)
return;
if (this.options.log)
this.options.log(text);
else
console.log("[StorageTileLayer]: " + text);
},
_setUpTile: function (tile, key, value, cache) {
try {
tile._layer = this;
tile.onload = this._tileOnLoad;
tile.onerror = this._tileOnError;
this._adjustTilePoint(tile);
tile.src = value;
this.fire('tileloadstart', {
tile: tile,
url: tile.src
});
}
catch (e) {
this.log("ERROR in setUpTile: " + e.message);
}
},
_loadTile: function (tile, tilePoint) {
this._adjustTilePoint(tilePoint);
var key = tilePoint.z + ',' + tilePoint.y + ',' + tilePoint.x;
var self = this;
var tileUrl = self.getTileUrl(tilePoint);
if (isNaN(tilePoint.x) || isNaN(tilePoint.y)) {
this.log("TilePoint x or y is nan: " + tilePoint.x + "-" + tilePoint.y);
return;
}
if (this.options.storage) {
this.options.storage.get(key, tileUrl).then(function (value) {
self.log("Tile URL to load: " + value.url);
self._setUpTile(tile, key, value.url, true);
});
} else {
this.log("Load Tile without storage");
self._setUpTile(tile, key, tileUrl, false);
}
}
});
window.StorageTileLayer = StorageTileLayer;
})(window, L);
Adding the tile layer to the leaflet map is the important part! you have to prevent the load balancer from getting different urls for each tile. I did it by setting the url of the tole layer to a fixed value:
var url = 'https://a.tiles.mapbox.com/v3/<<YOUR ACCESS CODE>>/{z}/{x}/{y}.png';
var layer = new StorageTileLayer(url, {
storage: TileStorage
});
Of course you still have to implement the TileStorage in my case it has a single method get(key, url) and returns a $q-defer which is resolved with either the local available file. If the file is not available in the local storage it will be downloaded and then the promise is resolved.
Unfortunately this TileStorage is not public available because its an in-house development of my company so I can't share it.
Nevertheless I hope this helps you.
I am trying to generate a PDF using the jsPDF library (https://github.com/MrRio/jsPDF) from within a mobile Cordova app. I am currently testing the app on an Android 4.0.4 device but it also needs to run on Windows mobile 8. The text in the PDF document is shown correctly however any images are scrambled. See image below
I did find this page (https://coderwall.com/p/nc8hia) that seemed to indicate there is a problem with jsPDF displaying images in Cordova (see comments) but the author never posted the follow-up. Has anyone been able to use jsPDF with Cordova and properly add images to the generated PDF? My code is below, any assistance or advice would be greatly appreciated.
function demoReceipt() {
var img = new Image();
img.onError = function() {
alert('Cannot load image: "' + url + '"');
};
img.onload = function() {
createPdf2(img);
};
img.src = 'img/testlogo.png';
}
function createPdf2(myLogo) {
// var doc = new jsPDF('p', 'pt', 'jontype');
var doc = new jsPDF('p', 'pt', 'letter');
doc.setProperties({
title : 'Fueling Receipt',
author : 'Jon Hoffman',
creater : 'Jon Hoffman'
});
doc.addImage(myLogo, 'PNG', 5, 5, 140, 30);
doc.setFontSize(12);
doc.text(10, 40, 'Sample PDF receipt');
doc.setFontSize(8);
doc.text(10, 45, 'Smaller text - new');
var pdfOutput = doc.output();
//NEXT SAVE IT TO THE DEVICE'S LOCAL FILE SYSTEM
//Requires cordova plugin add org.apache.cordova.file
console.log("file system...");
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem) {
console.log(fileSystem.name);
console.log(fileSystem.root.name);
console.log(fileSystem.root.fullPath);
fileSystem.root.getDirectory("myPDFs", {
create : true,
exclusive : false
}, function(dir) {
fileSystem.root.getFile("myPDFs/test.pdf", {
create : true
}, function(entry) {
var fileEntry = entry;
console.log(entry);
entry.createWriter(function(writer) {
writer.onwrite = function(evt) {
console.log("write success");
};
console.log("writing to file");
writer.write(pdfOutput);
}, function(error) {
console.log(error);
});
}, function(error) {
console.log(error);
});
}, function(error) {
});
}, function(event) {
console.log(evt.target.error.code);
});
}
I solved the issue with help from this blog post: https://coderwall.com/p/nc8hia. There does seems to be significant differences between the 0.90 version used in that post and the version that I am using from https://github.com/MrRio/jsPDF however the solution is pretty much the same.
First off, in the version from MyRio, you can get the PDF generation working without fixing the Blob issue noted in Igor’s post. All you need is to generate the PDF output by calling “doc.ouput()” and then save it using the Cordova filesystem plugin. So I thought I did not have to create the Blob (this is where I was wrong).
Igor (from the coderwall post) responded back to my question with some additional code but when I searched the jspdf.js file from MyRio version, I saw that the code (more compact version) was already in the code on lines 734 – 738:
var data = buildDocument(), len = data.length,
ab = new ArrayBuffer(len), u8 = new Uint8Array(ab);
while(len--) u8[len] = data.charCodeAt(len);
return new Blob([ab], { type : "application/pdf" });
But I also notice that the blob creation code that Igor fixed in his initial post was at the end of this block of code. So I commented out the “return new Blob([ab], { type : “application/pdf”});” line and put in the following code from Igor’s post with minor variable name changes:
try
{
var blob = new Blob([ab], {type: "application/pdf"});
console.debug("case 1");
return blob;
}
catch (e)
{
window.BlobBuilder = window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
if (e.name == 'TypeError' && window.BlobBuilder)
{
var bb = new BlobBuilder();
bb.append(ab);
console.debug("case 2");
return bb.getBlob("application/pdf");
}
else if (e.name == "InvalidStateError")
{
// InvalidStateError (tested on FF13 WinXP)
console.debug("case 3");
return new Blob([ab], {type: "application/pdf"});
}
else
{
// We're screwed, blob constructor unsupported entirely
console.debug("Errore");
}
}
Then when I generate that pdfOutput, in my code, I changed
var pdfOutput = doc.output();
to
var pdfOutput = doc.output(“blob”);
and it worked.
I hope this post is able to help out others experiencing the same issues.