Geolocation with Titanium - javascript

I want to develop an application with Appcelerator where I have a fleet of bikes, all with smartphone. I want to know the location of each one. Can someone give me a help by using localization by smartphone android GPS?

I'm assuming you want to get it periodically, so you can track where the bikes are at a given moment...
What you would need is to use Titanium Geolocation. you can read about it here: http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.Geolocation
and call getCurrentPosition method (http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.Geolocation-method-getCurrentPosition) that accepts a callback function with the location.
Inside that callback function send the information to your server (http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.Network-method-createHTTPClient)
and just call that function every X interval you with using a setInterval or setTimeout.
keep in mind that the app will need to run in order for it to keep working, unless you write an android service (not possible on iOS).

I will suggest you to do like this:
first create service.js under android folder, and there write something like:
startSendigGpsPositions();
function startSendingGpsPosition(){
Ti.Geolocation.getCurrentPosition(function(e){
var postData = {};
postData["localizationSystems"] = [];
postData["localizationSystems"].push({
"localizationSystem_id":"gps",
"entries":[ {
"lat": e.coords.latitude,
"lon": e.coords.longitude,
"accuracy": e.coords.accuracy
} ]
});
callHttpFunction(postData, {
onSuccess: function(){
//if needed you can set a timeout so the battery does not die immediately
setTimeOut(function(){
startSendingGpsPosition();
}, 1000)
},
onError: function(){
// decide if you want to show the error to the user or just ignore it and recall startSendingGpsPosition
}
})
});
}
function callHttpFunction(postData, callback){
var xhr = Titanium.Network.createHTTPClient();
xhr.open(POST, url);
xhr.onload = function(e) {
var res = JSON.parse(this.responseText);
callback.onSuccess(res);
};
xhr.onerror = function(e) {
var res = JSON.parse(this.responseText);
callback.onError(res);
};
if (Titanium.Network.online) {
xhr.send(postData);
} else {
if (callback.error) { callback.onError(); }
};
}
From index.js you call it like this:
function startService(){
var intent = Ti.Android.createServiceIntent({
url : 'service.js'
});
if(!Ti.Android.isServiceRunning(intent)){
Ti.Android.startService(intent);
}
}
Another way is to create a native module where you use onLocationChanged function, so any time your location will change it will give you back the geo point without having to ask any 1 second for the gps position. This is how it should look like:
LocationListener locationListener = null;
...
locationListener = new LocationListener() {
#Override
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
// TODO Auto-generated method stub
}
#Override
public void onProviderEnabled(String arg0) {
// TODO Auto-generated method stub
}
#Override
public void onProviderDisabled(String arg0) {
// TODO Auto-generated method stub
}
#Override
public void onLocationChanged(Location gpsLocation) {
HashMap<String , String> map = new HashMap<String, String>();
HashMap<String , String> container = new HashMap<String, String>();
map.put("success", "true");
container.put("latitude", ""+gpsLocation.getLatitude());
container.put("longitude", ""+gpsLocation.getLongitude());
container.put("accuracy", ""+gpsLocation.getAccuracy());
container.put("altitude", ""+gpsLocation.getAltitude());
container.put("provider", ""+gpsLocation.getProvider());
container.put("speed", ""+gpsLocation.getSpeed());
map.put("container", (new JSONObject(container)).toString());
callbackLocation.call(getKrollObject(), map);
}
};
Once you have the data in the callback you can do the http request from javascript :)

I'd recommend NOT using Titanium, especially if you plan to make another App. I started with Titanium and although I found Javascript straightforward and enjoyable, the framework is severely limiting when you start trying to do more advanced things. With native you have much greater control, and it's very much worth the hump of learning.

Related

How to call SseEmitter just once from spring endpoint

I'm listening to my /score endpoint with JavaScript like so:
var sse = new EventSource('/score');
sse.onmessage = function (evt) {
var el = document.getElementById('scores');
el.appendChild(document.createTextNode(evt.data));
el.appendChild(document.createElement('br'));
};
But for some reason it's like the endpoint it's called every second.
EventSource.onmessage documantation says:
Is an EventHandler called when a message event is received, that is when a message is coming from the source
This is my /score endpoint:
private ExecutorService executorService = Executors.newCachedThreadPool();
#GetMapping("/score")
public SseEmitter getScore() {
final SseEmitter sseEmitter = new SseEmitter();
executorService.submit(() -> {
try {
//System.out.println(text);
//sseEmitter.send(text);
sseEmitter.send("ok");
sseEmitter.complete();
} catch (Exception e) {
e.printStackTrace();
}
});
return sseEmitter;
}
How can i trigger it only when i send a request manually?
After a lot of reading i managed to trigger the /score endpoint on every request, but i had to change the server side a lot.
#Autowired
private MessageProcessor processor;
#GetMapping(path = "/score", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<MyObject> receive() {
return Flux.create(sink -> {
processor.register(sink::next);
});
}
Now I'm returning a Flux object instead of a SseEmitter , because with Emitter i need constantly to send responses to the client.
I also created another endpoint /send , where i send my object with POST
#PostMapping("/send")
public String send(#RequestBody MyObject event) {
LOGGER.info("Received '{}'", event);
processor.process(event);
return "Done";
}
Nothing is changed on the Client side, the pipe between /receive and EventSource shouldn't be terminated from the client. I only added a JSON parsing because now i've got a custom object.
eventSource = new EventSource("/score");
eventSource.onmessage = function (evt) {
var obj =JSON.parse (evt.data);
var el = document.getElementById('scores');
el.appendChild(document.createTextNode(obj.id+' '+obj.name+' '+obj.desc));
el.appendChild(document.createElement('br'));
};
The key part was to use a MessageProccessor
#Service
public class MessageProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageProcessor.class);
private List < Consumer < MyObject >> listeners = new CopyOnWriteArrayList < > ();
public void register(Consumer < MyObject > listener) {
listeners.add(listener);
LOGGER.info("Added a listener, for a total of {} listener{}", listeners.size(), listeners.size() > 1 ? "s" : "");
}
// TODO FBE implement unregister
public void process(MyObject event) {
System.out.println("Processing: " + event);
listeners.forEach(c -> c.accept(event));
}
}
Another example of Webflux can be found here
Output
Client
Server (The 4 objects i posted)
That's because you never called EventSource.close() to close the connection. Since your eventSource doesn't have a terminating condition, your browser will repeatably call /score.

Parallel file upload XMLHttpRequest requests and why they won't work

I am trying to upload many (3 for now) files in parallel using XMLHttpRequest. If have some code that pulls them from a list of many dropped files and makes sure that at each moment I am sending 3 files (if available).
Here is my code, which is standard as far as I know:
var xhr = item._xhr = new XMLHttpRequest();
var form = new FormData();
var that = this;
angular.forEach(item.formData, function(obj) {
angular.forEach(obj, function(value, key) {
form.append(key, value);
});
});
form.append(item.alias, item._file, item.file.name);
xhr.upload.onprogress = function(event) {
// ...
};
xhr.onload = function() {
// ...
};
xhr.onerror = function() {
// ...
};
xhr.onabort = function() {
// ...
};
xhr.open(item.method, item.url, true);
xhr.withCredentials = item.withCredentials;
angular.forEach(item.headers, function(value, name) {
xhr.setRequestHeader(name, value);
});
xhr.send(form);
Looking at the network monitor in Opera's developer tools, I see that this kinda works and I get 3 files "in progress" at all times:
However, if I look the way the requests are progressing, I see that 2 of the 3 uploads (here, the seemingly long-running ones) are being put in status "Pending" and only 1 of the 3 requests is truly active at a time. This gets reflected in the upload times as well, since no time improvement appears to happen due to this parallelism.
I have placed console logs all over my code and it seems like this is not a problem with my code.
Are there any browser limitations to uploading files in parallel that I should know about? As far as I know, the AJAX limitations are quite higher in number of requests than what I use here... Is adding a file to the request changing things?
This turned out to be ASP.NET causing the issue.
Multiple requests coming from the same SessionId get serialized, because they lock the session object.
See here.
My fix was to make the session read-only for this particular action. That way, no locking was required.
This is my code (original code taken from here):
public class CustomControllerFactory : DefaultControllerFactory
{
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return SessionStateBehavior.Default;
}
var actionName = requestContext.RouteData.Values["action"].ToString();
MethodInfo actionMethodInfo;
var methods = controllerType.GetMethods(BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
actionMethodInfo = methods.FirstOrDefault(x => x.Name == actionName && x.GetCustomAttribute<ActionSessionStateAttribute>() != null);
if (actionMethodInfo != null)
{
var actionSessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
.OfType<ActionSessionStateAttribute>()
.FirstOrDefault();
if (actionSessionStateAttr != null)
{
return actionSessionStateAttr.Behavior;
}
}
return base.GetControllerSessionBehavior(requestContext, controllerType);
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ActionSessionStateAttribute : Attribute
{
public SessionStateBehavior Behavior { get; private set; }
public ActionSessionStateAttribute(SessionStateBehavior behavior)
{
this.Behavior = behavior;
}
}
// In your Global.asax.cs
protected void Application_Start(object sender, EventArgs e)
{
// .........
ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
}
// You use it on the controller action like that:
[HttpPost]
[Authorize(Roles = "Administrators")]
[ValidateAntiForgeryToken]
[ActionSessionState(SessionStateBehavior.ReadOnly)]
public async Task<ActionResult> AngularUpload(HttpPostedFileBase file){}
And here is the glorious result:
The HTTP/1.1 RFC
Section 8.1.4 of the HTTP/1.1 RFC says a “single-user client SHOULD NOT maintain more than 2 connections with any server or proxy.
Read more here: Roundup on Parallel Connections

Calling server side function from signalR in Android

SignalR connected in Android, But I want to call a function which is available on server,
I tried the following code,
String host = "Host URL";
HubConnection hubConnection = new HubConnection(host, "", false, new Logger() {
#Override
public void log(String message, LogLevel level) {
System.out.println("message - " + message);
}
});
hubProxy = hubConnection.createHubProxy("Task");
SignalRFuture<Void> signalRFuture = hubConnection.start().done(addSession("Session ID"));
And
private Action<Void> addSession(String sessionID) {
//hubProxy. [server is not available here]
}
In javascript, I tried like following,
$.connection.hub.start().done(function () {
addSession(sessionId)
});
function addSession(sessionId) {
proxy.server.addSession(sessionId)
.done(function () {
isHubConnected = true;
}
);
}
In javascript this works perfectly, But in android :( ,
Update
By trying like #AnikIslamAbhi's answer,
signalRFuture = hubConnection.start().done(addSession("sessionID"));
private Action<Void> addSession(String sessionID) {
System.out.println("addSession : " + sessionID);
hubProxy.invoke("addSession", sessionID);
return null;
}
I received following error message,
InvalidStateException: The operation is not allowed in the 'Connecting' state
In javascript you are using Auto proxy.
But in android you are using manual proxy
Both have differences in their behavior.
Like
To call serverside method using auto proxy
proxy.server.addSession(sessionId)
To call serverside method using manual proxy
proxy.invoke("addSession", sessionId)
You can find more on this link

Create application based on Website

I searched and tried a lot to develop an application which uses the content of a Website. I just saw the StackExchange app, which looks like I want to develop my application. The difference between web and application is here:
Browser:
App:
As you can see, there are some differences between the Browser and the App.
I hope somebody knows how to create an app like that, because after hours of searching I just found the solution of using a simple WebView (which is just a 1:1 like the browser) or to use Javascript in the app to remove some content (which is actually a bit buggy...).
To repeat: the point is, I want to get the content of a website (on start of the app) and to put it inside my application.
Cheers.
What you want to do is to scrape the websites in question by getting their html code and sorting it using some form of logic - I recomend xPath for this. then you can implement this data into some nice native interface.
You need however to be very aware that the data you get is not allways formated the way you want so all of your algorithems have to be very flexible.
the proccess can be cut into steps like this
retrive data from website (DefaultHttpClient and AsyncTask)
analyse and retrive relevant data (your relevant algorithm)
show data to user (Your interface implementation)
UPDATE
Bellow is some example code to fetch some data of a website it implements html-cleaner libary and you will need to implement this in your project.
class GetStationsClass extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
HttpClient httpclient = new DefaultHttpClient();
httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
httpclient.getParams().setParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET, "iso-8859-1");
HttpPost httppost = new HttpPost("http://ntlive.dk/rt/route?id=786");
httppost.setHeader("Accept-Charset", "iso-8859-1, unicode-1-1;q=0.8");
try {
// Add your data
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(3);
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "utf-8"));
// Execute HTTP Post Request
HttpResponse response = httpclient.execute(httppost);
int status = response.getStatusLine().getStatusCode();
String data = "";
if (status != HttpStatus.SC_OK) {
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
response.getEntity().writeTo(ostream);
data = ostream.toString();
} else {
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(),
"iso-8859-1"));
String line = null;
while ((line = reader.readLine()) != null) {
data += line;
}
XPath xpath = XPathFactory.newInstance().newXPath();
try {
Document document = readDocument(data);
NodeList nodes = (NodeList) xpath.evaluate("//*[#id=\"container\"]/ul/li", document,
XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
Node thisNode = nodes.item(i);
Log.v("",thisNode.getTextContent().trim);
}
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
//update user interface here
}
}
private Document readDocument(String content) {
Long timeStart = new Date().getTime();
TagNode tagNode = new HtmlCleaner().clean(content);
Document doc = null;
try {
doc = new DomSerializer(new CleanerProperties()).createDOM(tagNode);
return doc;
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return doc;
}
to run the code above use
new getStationsClass.execute();

Android - Is it possible to fire a native intent from an application wrapped in PhoneGap

I am developing an application on Sencha Touch 2.0.1 & PhoneGap.
I need to catch and transfer an event firing inside Sencha Touch to the native Android environment.
i.e: Some sencha touch-controlled-buttons need to fire an intent on click to start another activity (non-PhoneGap activities).
So far I have found various examples like webintents and this. But as far as I see, these are inapplicable in my case.
I seek to either drop PhoneGap and work with another wrapper, or somehow circumvent this issue. Thanks in advance!
I think you'll need to make your own phonegap plugin that launches the native activity from inside it's execute method.
There's a ContactView plugin you should be able to use as a guide for writing your own.
https://github.com/phonegap/phonegap-plugins/blob/master/Android/ContactView/ContactView.java
Specifically these two methods
#Override
public PluginResult execute(String action, JSONArray args, String callbackId) {
startContactActivity();
PluginResult mPlugin = new PluginResult(PluginResult.Status.NO_RESULT);
mPlugin.setKeepCallback(true);
this.callback = callbackId;
return mPlugin;
}
public void startContactActivity() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
this.ctx.startActivityForResult((Plugin) this, intent, PICK_CONTACT);
}
Take a look at this, the explicit and implicit intent sections (1.2, 1.3):
http://www.vogella.de/articles/AndroidIntent/article.html
Then take a look at the source code for WebIntent.java, in particular the startActivity function:
https://github.com/phonegap/phonegap-plugins/blob/master/Android/WebIntent/WebIntent.java
void startActivity(String action, Uri uri, String type, Map<String, String> extras) {
Intent i = (uri != null ? new Intent(action, uri) : new Intent(action));
And then the intent constructors here (search for Constructors):
http://developer.android.com/reference/android/content/Intent.html
WebIntent does not support the Intent constructor that that takes an Android class.
But you can extend the function to have it work with an explicit intent (code below is quick and dirty and untested):
void startActivity(String action, Uri uri, String type, String className, Map<String, String> extras) {
Intent i;
if (uri != null)
i = new Intent(action, uri)
else if (className != null)
i = new Intent(this.ctx, Class.forName(className));
else
new Intent(action));
Above, in the execute function, you must also parse out the new parameter in the "Parse the arguments" section
// Parse the arguments
JSONObject obj = args.getJSONObject(0);
String type = obj.has("type") ? obj.getString("type") : null;
Uri uri = obj.has("url") ? Uri.parse(obj.getString("url")) : null;
String className = obj.has("className") ? obj.getString("className") : null;
JSONObject extras = obj.has("extras") ? obj.getJSONObject("extras") : null;
and then pass the new className string in the call to startActivity a few lines below:
startActivity(obj.getString("action"), uri, type, className, extrasMap);
Then you should be able to call an android activity by classname using something like:
Android.callByClassName = function(className) {
var extras = {};
extras[WebIntent.EXTRA_CUSTOM] = "my_custom";
extras[WebIntent.EXTRA_CUSTOM2] = "my_custom2";
window.plugins.webintent.startActivity({
className: className,
extras: extras
},
function() {},
function() {
alert('Failed to send call class by classname');
}
);
};
Where the classname is something like: com.company.ActivityName
DISCLAIMER: Rough code, not tested.

Categories

Resources