Android: Trigger AJAX function not working - android

I have an AJAX function I am building which makes a call on scroll or click/touchtstart to append some content to my HTML. Everything works great for me in the desktop environment, both on click and on scroll events. However neither of them work on my Android 2.3.7 HTC EVO or my Nexus 7 on Android 4.1.1.
Here is my click JavaScript event handler:
$('.loadMore').each(function() {
//Set URL and start on the first page
var self = this;
var path2root = ".";
var loc = window.location.origin;
var url = loc + "/process.php";
var subcategoryId = $(this).parent().attr('data-subcategoryId');
var page = 1;
// Build the updated URL
$(self).bind('touchstart', function(e) {
e.preventDefault();
page++;
// AJAX function for processing JSON
$.ajax({
url: url + "?subcategoryId=" + subcategoryId + "&page=" + page,
type: "GET",
contentType: "application/json; charset=utf-8",
dataType: "json",
async: false,
success: function (data) {
var i = 0;
for(i = 0 ; i < data.length; i++) {
var articleId = data[i].articleResult.articleId;
var title = data[i].articleResult.title;
var subhead = data[i].articleResult.subhead;
var image = data[i].articleResult.image;
var response = "<td><div class='articleResult'><a href='" + path2root + "/article/article.php?articleId=" + articleId + "'><img src='" + image + "' width='120'/></a><br><h3><a href='" + path2root + "/article/article.php?articleId=" + articleId + "'>" + title.substring(0,25) + "</a></h3></div></td>";
$("table tr td:nth-last-child(2)").after(response);
};
}
});
});
});
My scroll function is very similar, only I bind the event to a different element, and on scroll:
// Collecting scroll info
$('.overthrow').each(function() {
// SAME VARIABLES
$(this).scroll(function () {
if ($(self).scrollLeft() >= parseInt($(document).width() - 50)) {
if (finished == 1) {
page++;
finished = 0;
// SAME AJAX FUNCTION
};
};
});
});
Keep in mind this is a mobile optimized webpage, not a native PhoneGap app, and I am using regular jQuery 1.8.0, not jQuery Mobile or PhoneGap.
I had found THIS issue over on Google Code in regards to Android 2.3 not receiving touchstart events very effectively, which led me to start building the on scroll function instead.

According to your link from Google Code the fix would be to include an e.preventDefault() at the start of your touchstart event like so:
$(self).bind('touchstart', function(e) { // include e as a reference to the event
e.preventDefault(); // prevents the default behaviour on this element
....
});
The above works on my Android 2.X devices; I hope it helps!

So I was able to get the AJAX call functioning properly for the on scroll event. I have abandoned the click/touchstart event all together as that seems to be buggy in Android.
For whatever reason, Android was not reading dynamic URL correctly when uploaded to the webserver:
var loc = window.location.origin;
var url = loc + "/process.php";
So when I was building my ajax call with the compiled URL:
$.ajax({
url: url + "?subcategoryId=" + subcategoryId + "&page=" + page,
the window.origin.location was being misinterpreted. Instead I needed to hard-code the location of my process.php file:
url: "process.php?subcategoryId=" + subcategoryId + "&page=" + page,
I discovered my solution through another SO post HERE
The only catch being my code was working on iPhone, but not on Android. If anyone knows the reason behind this, that would be great information to have.

Related

Cordova fileTransfer works perfect on iOS, throws error code = 1 on Android

I'm developing a mobile app for iOS and Android using Cordova and Ionic Framework. There needs to be 'Send Photo' and related functionality, and I'm using Cordova's FileTransfer to do this.
It works perfectly on iOS simulator, but throws "error code = 1" on Android device.
I know this means file_not_found or similar.
Note it happens if I take a picture from camera, or choose one from gallery.
Here is my code:
$scope.takePic = function() {
var options = {
quality: 50,
destinationType: navigator.camera.DestinationType.FILE_URI,
sourceType: 0, // 0:Photo Library, 1=Camera, 2=Saved Photo Album
encodingType: 0 // 0=JPG 1=PNG
}
navigator.camera.getPicture(onSuccess, onFail, options);
}
var onSuccess = function(FILE_URI) {
window.resolveLocalFileSystemURL(FILE_URI, function(fileEntry) {
alert("full: " + JSON.stringify(fileEntry));
var realUrl = fileEntry.toURL();
$scope.picData = realUrl;
$scope.$apply();
console.log("real URL", realUrl);
});
};
var onFail = function(e) {
console.log("On fail " + e);
}
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
Flash.success("Wysłano");
var response = JSON.parse(r.response);
$scope.attachment_id = response.data;
$scope.$apply();
$http.post($rootScope.baseServerUrl + 'Members/changeAvatar', {attachment_id: response.data}).success( function (response){
console.log(response);
});
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
$scope.send = function() {
Flash.warning('wysyłam');
var myImg = $scope.picData;
alert(myImg);
var options = new FileUploadOptions();
options.headers = {
Accept: "application/json",
Connection: "close"
}
options.fileKey="file";
options.fileName=$scope.picData.substr($scope.picData.lastIndexOf('/')+1);
options.mimeType="image/jpeg";
options.chunkedMode = false;
var ft = new FileTransfer();
ft.upload(myImg, encodeURI($rootScope.baseServerUrl + 'media/Attachments/add'), win, fail, options);
}
$scope.takePic and send are called by button clicks. There are a lot of alerts and console because I'm trying to find why its not working.
After picking a picture from the gallery on android I get:
file:///storage/sdcard0/download/file-name.jpg
on iOS simulator:
file:///Users//Library/Application%20Support/iPhone%20Simulator/7.1/Applications/B5FB2081-54E7-4335-8856-84C6499E6B07/tmp/cdv_photo_038.jpg
and by using this path I can show this picture by using <img src="{{picData}}"> this works on both platforms.
But if I try to send it on an Android device I get error Code = 1. On iOS sim it sends, photo, gets proper response, changes avatar...everything.
Both Cordova and plugins File and FileTransfer are up to date.
It looks like you might have a path error, file:///storage.sdcard0/download/file-name.jpg should be file:///storage/sdcard0/download/file-name.jpg if I'm not mistaken.
From perusing your code, it doesn't appear that you are parsing anything incorrectly. Maybe you want to try using an older more stable version of the file plugin if it is returning the wrong URI (and maybe file a bug report)? I haven't used the file plugin since they released 1.0, but from personal experience there have been bugs/regressions in the bleeding edge releases before.
You can target specific plugin versions from the cordova-cli using # like cordova plugin add org.apache.cordova.file#1.0.0
As well as specific tags/releases from github using # like cordova plugin add https://github.com/apache/cordova-plugin-file-transfer#r0.4.2
Maybe late but I've kinda fixed it.
In my view file I use:
<input id="file" name="file" type="file" onchange="angular.element(this).scope().addFile(this)" class="upload" accept="image/*" capture="camera"/>
so it fires $scope.addFile() from my controller as soon as you pick up file from galery:
$scope.addFile = function(item){
function uploadComplete(evt) {
/* This event is raised when the server send back a response */
$scope.imgId = JSON.parse(evt.target.responseText).data;
$scope.$apply();
$http.post($rootScope.baseServerUrl + 'Members/changeAvatar', {attachment_id: $scope.imgId}).success( function (response){
console.log(response);
$scope.User.attachment_id = $scope.imgId;
$scope.$apply();
});
}
function uploadFailed(evt) {
alert("There was an error attempting to upload the file.")
};
var updateImage = function (element) {
$scope.$apply(function() {
$scope.theFile = element.files[0];
var formData = new FormData();
formData.append("file", $scope.theFile);
var xhr = new XMLHttpRequest()
xhr.addEventListener("load", uploadComplete, false)
xhr.addEventListener("error", uploadFailed, false)
xhr.open("POST", $scope.baseServerUrl + "media/Attachments/add")
xhr.setRequestHeader("Accept","application/json")
$scope.progressVisible = true
xhr.send(formData);
});
};
updateImage(item)
}
Works for all Android devices I tested, above 4.0 excluding 4.4 because of input type="file" bug, works on iOS simulator and devices with 8.1 system (should also on older but I didn't test it).
It's not a perfect solution, because you can use only pictures you already got on your phone. I couldnt figure it out how to use Cordova FileTransfer with our server authentication way: I was always getting "please log in" in response, even when I tried adding all needed headers, tokens, anything...
So even though this solution is far from what I wanted to achieve - it works. Hope it helps anyone.

LeafletJS - Cached tiles with local filesystem

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.

AJAX not being called on load in PhoneGap 2.9.1

I am trying to load and parse an XML file onto a page in my PhoneGap application. My XML file is in the correct directory.
When I run page in Google Chrome using a Local Apache Web Server, the file is loaded and Parsed, but when I run the application on my Device. (Samsung Galaxy SII GT-i9100p) The script doesn't run. I have tried everything I can think of. The thing that has stumped is that it was working a few days ago, up until I changed the script to parse the data into a jQuery Mobile Collapsible.
Any help would be much appreciated.
Here is my code:
$(function loadXml() {
load();
});
$(document).on('pagebeforecreate orientationchange', Application.updateIcons);
function load() {
$.ajax({
type: "GET",
url: "schooldata.xml",
dataType: "xml",
success: function (xml) {
var regionName;
var schoolName;
var schoolDescription;
var liHtml;
$(xml).find("REGION").each(function (index, Region) {
liHtml = "";
$(Region).find("SCHOOL").each(function (index, School) {
regionName = $(Region).find("REGIONNAME").text();
schoolName = $(School).find("NAME").text();
schoolDescription = $(School).find("DESCRIPTION").text();
liHtml +=
"<li>" +
schoolName +
"<br>" +
schoolDescription +
"</li>"
})
$('#schoolcontainer').append
(
"<div data-role='collapsible' data-theme='b'>" +
"<h3>" + regionName + "</h3>" +
"<ul data-role='listview' data-inset='false'>" +
liHtml +
"</ul>" +
"</div>"
)
});
$('#schoolcontainer').collapsibleset("refresh");
}
});
}
$(document).one('deviceready', Application.initApplication);

wikitude 3.01: adding some features between examples did not work properly

i downloaded the SDK of Wikitude 3.1, installed the samples into my Eclipse workspace and on my Gnexus everything works. I divide the questions in points so could be easier to answer:
I added the "onMarkerSelectedFn" method into the "limitingVisiblePois.js" file because i wanted to have my POIs clickable and when clicked, appear the information page like the 5.1 example. I added the method but it doesn't work and i haven't understand where i'm making mistakes. Each other file is the same for the 5.x samples.
Code of "limitingVisiblePois.js" edited by me
var World = {
markerDrawable_idle: new AR.ImageResource("assets/marker_idle.png"),
markerDrawable_selected: new AR.ImageResource("assets/marker_selected.png"),
markerDrawable_directionIndicator: new AR.ImageResource("assets/indi.png"),
markerList: [],
// called to inject new POI data
loadPoisFromJsonData: function loadPoisFromJsonDataFn(poiData) {
PoiRadar.show();
document.getElementById("statusElement").innerHTML = 'Loading JSON objects';
var poiImage = new AR.ImageResource("img/marker.png", {
onError: World.errorLoadingImage
});
// TODO: call single POI-creation statement instead
for (var i = 0; i < poiData.length; i++) {
var singlePoi = {
//EDIT BRUS: adding the ID of each POIs
"id": poiData[i].id,
"latitude": parseFloat(poiData[i].latitude),
"longitude": parseFloat(poiData[i].longitude),
"altitude": parseFloat(poiData[i].altitude),
"title": poiData[i].name,
"description": poiData[i].description
};
World.markerList.push(new Marker(singlePoi));
}
document.getElementById("statusElement").innerHTML = 'JSON objects loaded properly';
},
// user's latest known location, accessible via userLocation.latitude, userLocation.longitude, userLocation.altitude
userLocation: null,
// location updates
locationChanged: function locationChangedFn(lat, lon, alt, acc) {
World.userLocation = {
'latitude': lat,
'longitude': lon,
'altitude': alt,
'accuracy': acc
};
},
//EDIT BRUS: Adding onMarkerSelected function
onMarkerSelected: function onMarkerSelectedFn(marker) {
// notify native environment
document.location = "architectsdk://markerselected?id=" + marker.poiData.id;
},
// called from slider.js every time the slider value changes
onSliderChanged: function onSliderChangedFn(value) {
if (value > 0) {
var valueMeters = value * 1000;
PoiRadar.setMaxDistance(valueMeters);
AR.context.scene.cullingDistance = valueMeters;
}
}
};
// forward locationChanges to custom function
AR.context.onLocationChanged = World.locationChanged;
2) I wasn't able to understand where the POIs latlong coordinates where declareated. In the same code posted ahead, there is the function
loadPoisFromJsonData: function loadPoisFromJsonDataFn(poiData)
but i don't understand how the poiData are taken.
I used the last 3.1 SDK in Android and phonegap.
thanks in advance,
Kind regards
Brus
PhoneGap Plugin samples are yet not in sync with those from the native SDK.
Whereat the current "HelloWorld" sample in PhoneGap Plugin requests POI-Data from a webservice the sample you pointed out is from the Android SDK and passes POI-data from native Android via "architectView.callJavaScript('loadPoisFromJsonData(...)')".
Both are making use of the same method to parse POI data, PhoneGap sample uses it e.g. that way
// request POI data
requestDataFromServer: function requestDataFromServerFn(lat, lon) {
var serverUrl = ServerInformation.POIDATA_SERVER + "?" + ServerInformation.POIDATA_SERVER_ARG_LAT + "=" + lat + "&" + ServerInformation.POIDATA_SERVER_ARG_LON + "=" + lon + "&" + ServerInformation.POIDATA_SERVER_ARG_NR_POIS + "=20";
var jqxhr = $.getJSON(serverUrl, function(data) {
World.loadPoisFromJsonData(data);
})
.error(function(err) {
alert("JSON error occured! " + err.message);
})
.complete(function() {});
}
You may just add these lines in your locationChanged implementation to use places from a dummy-webserver (don't forget to define 'alreadyRequestedData= false;' )
if (!World.alreadyRequestedData) {
World.requestDataFromServer(lat, lon);
World.alreadyRequestedData = true;
}
Kind regards,
Andreas

Android + JQM+ jsonp not working consistently

I am crunntly working on Hybrid Android App using JQM + PhoneGap
I am doing a JSONP request with AJAX
It works well on all Chrome PC browser, also works well on my android application when WiFi is connected,
but when changing network connection to 3G the AJAX request is not responding.
I found #BruceHill related post that wrote the following :
"mobile operators do content modification before delivering it to the phone and this modification breaks jQuery"
Jquery mobile page not loading correctly on Netherlands 3G connections
Although I am not living in Holland, I tried doing what he suggests by locating all the JS files on a remote server and called it via CDN, but unfortunately it didn't help.
I will be happy to get some help on this one...
this is my AJAX request:
$.mobile.allowCrossDomainPages = true;
$('#expertsListPage').live('pageshow', function (event) {
$.mobile.showPageLoadingMsg();
getExpertsList();
});
var catId;
var catName
function getExpertsList() {
$('#expertsList li').remove();
catId = getUrlVars()["id"];
catName = getUrlVars()["cat"] ;
$('h1').text( unescape(catName) );
var url = 'http://apis.mydomain.com/mydata.svc/jsonp'
$.ajax({
cache: true,
type: 'GET',
url: url,
dataType: 'jsonp' ,
jsonp: 'callback',
success:api_do
});
}
var expertss;
function api_do(obj) {
$('#expertsList li').remove();
expertss = obj.experts;
$.each(expertss, function (index, expert) {
$('#expertsList').append('<li><a href="ExpertDetails.html?id=' + expert.id + '&catid=' + catId +'&catName=' + catName + '">' +
'<img style="width:160px;height:160px;" src="' + expert.thumbnail + '"/>' +
'<h4>' + expert.name + '</h4>' +
'<p>' + expert.description + '</p>' +
'</a></li>');
});
$('#expertsList').listview('refresh');
$.mobile.hidePageLoadingMsg();
}
function getUrlVars() {
var varsExperts = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
varsExperts.push(hash[0]);
varsExperts[hash[0]] = hash[1];
}
return varsExperts;
}
Try adding this code to your javascript, might help.
$( document ).on( "mobileinit", function() {
// Make your jQuery Mobile framework configuration changes here!
$.support.cors = true;
$.mobile.allowCrossDomainPages = true;
});

Categories

Resources