Skip to content

Commit

Permalink
Copy from UnifiedNlp repo
Browse files Browse the repository at this point in the history
  • Loading branch information
mar-v-in committed Jan 3, 2015
0 parents commit bfe34fc
Show file tree
Hide file tree
Showing 13 changed files with 404 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Android.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := UnifiedNlpApi
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += src/org/microg/nlp/api/LocationBackend.aidl \
src/org/microg/nlp/api/GeocoderBackend.aidl \
src/org/microg/nlp/api/LocationCallback.aidl

include $(BUILD_STATIC_JAVA_LIBRARY)
4 changes: 4 additions & 0 deletions AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="org.microg.nlp.api">
<!-- This is not needed, it's just here to stop build systems from complaining -->
</manifest>
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
UnifiedNlpApi
=============
This library contains anything needed to build a backend for UnifiedNlp.

Writing the service
-------------------
### The easy way (Location)
Writing a service is fairly easy. Just create a class that extends `org.microg.nlp.api.LocationBackendService`, it provides several methods:

#### `update()`-method
You'll most likely want to override this method. It is called every time when an application requests a location.

However, as this method is blocking, you should not do heavy I/O operations (like network) in it.
If your backend uses a remote provider for location retrieval, do requests in an additional thread and return null in `update()`.
On request success, use `report()` to send the new location to the requesting application.

See JavaDoc for additional information.

#### `onOpen()`-method and `onClose()`-method
These might be interesting to override too. `onOpen()` is called after UnifiedNlp connected to this backend and `onClose()` is called before connection closure.
This is a good place to initialize or respectively destroy whatever you need during `update()` calls.

#### `report(Location)`-method
You can call this method every time to report the given location as soon as possible.

### The easy way (Geocoding)
Providing a Geocoder is even simpler than a LocationProvider. Extend `org.microg.nlp.api.GeocoderBackendService` and implement the methods `getFromLocation` and `getFromLocationName`.
Both methods reflect a call to the corresponding method in `android.location.Geocoder`.

### The flexible way
Instead of using the `LocationBackendService` helper class you can do it by hand.
It's important that your service overrides the `onBind()` method and responds with a `Binder` to the `LocationBackend` interface.

Advertise your service
----------------------
To let UnifiedNlp see your service you need to advertise it by providing the `org.microg.nlp.LOCATION_BACKEND` action.

For security reasons, you should add an `android:permission` restriction to `android.permission.ACCESS_COARSE_LOCATION`. This ensures only application with access to coarse locations will be able to connect to your service.

You may want to set `android:icon` and `android:label` to something reasonable, else your applications icon/label are used.
If your backend has settings you can advertise it's activity using the `org.microg.nlp.BACKEND_SETTINGS_ACTIVITY` meta-data so that it is callable from the UnifiedNlp settings.

A service entry for a backend service could be:

<service
android:name=".SampleService"
android:exported="true"
android:permission="android.permission.ACCESS_COARSE_LOCATION"
android:label="A very nice Backend">
<intent-filter>
<action android:name="org.microg.nlp.LOCATION_BACKEND" />
</intent-filter>
<meta-data
android:name="org.microg.nlp.BACKEND_SETTINGS_ACTIVITY"
android:value="org.microg.nlp.api.sample.SampleActivity" />
</service>
24 changes: 24 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0'
}
}

apply plugin: 'com.android.library'

android {
compileSdkVersion 21
buildToolsVersion "21.0.2"
lintOptions.abortOnError false

sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
aidl.srcDirs = ['src']
}
}
}
1 change: 1 addition & 0 deletions project.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
android.library=true
29 changes: 29 additions & 0 deletions src/org/microg/nlp/api/AbstractBackendService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.microg.nlp.api;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public abstract class AbstractBackendService extends Service {

@Override
public IBinder onBind(Intent intent) {
return getBackend();
}

/**
* Called after a connection was setup
*/
protected void onOpen() {

}

/**
* Called before connection closure
*/
protected void onClose() {

}

protected abstract IBinder getBackend();
}
13 changes: 13 additions & 0 deletions src/org/microg/nlp/api/GeocoderBackend.aidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.microg.nlp.api;

import android.location.Location;
import android.location.Address;

interface GeocoderBackend {
void open();
List<Address> getFromLocation(double latitude, double longitude, int maxResults, String locale);
List<Address> getFromLocationName(String locationName, int maxResults, double lowerLeftLatitude,
double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude,
String locale);
void close();
}
64 changes: 64 additions & 0 deletions src/org/microg/nlp/api/GeocoderBackendService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.microg.nlp.api;

import android.location.Address;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.List;

public abstract class GeocoderBackendService extends AbstractBackendService {

private Backend backend = new Backend();

@Override
protected IBinder getBackend() {
return backend;
}

/**
* @param locale The locale, formatted as a String with underscore (eg. en_US) the resulting
* address should be localized in
* @see android.location.Geocoder#getFromLocation(double, double, int)
*/
protected abstract List<Address> getFromLocation(double latitude, double longitude,
int maxResults, String locale);

/**
* @param locale The locale, formatted as a String with underscore (eg. en_US) the resulting
* address should be localized in
* @see android.location.Geocoder#getFromLocationName(String, int, double, double, double, double)
*/
protected abstract List<Address> getFromLocationName(String locationName, int maxResults,
double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude,
double upperRightLongitude, String locale);

private class Backend extends GeocoderBackend.Stub {

@Override
public void open() throws RemoteException {
onOpen();
}

@Override
public List<Address> getFromLocation(double latitude, double longitude, int maxResults,
String locale) throws RemoteException {
return GeocoderBackendService.this
.getFromLocation(latitude, longitude, maxResults, locale);
}

@Override
public List<Address> getFromLocationName(String locationName, int maxResults,
double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude,
double upperRightLongitude, String locale) throws RemoteException {
return GeocoderBackendService.this
.getFromLocationName(locationName, maxResults, lowerLeftLatitude,
lowerLeftLongitude, upperRightLatitude, upperRightLongitude, locale);
}

@Override
public void close() throws RemoteException {
onClose();
backend = null;
}
}
}
10 changes: 10 additions & 0 deletions src/org/microg/nlp/api/LocationBackend.aidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.microg.nlp.api;

import org.microg.nlp.api.LocationCallback;
import android.location.Location;

interface LocationBackend {
void open(LocationCallback callback);
Location update();
void close();
}
82 changes: 82 additions & 0 deletions src/org/microg/nlp/api/LocationBackendService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.microg.nlp.api;

import android.location.Location;
import android.os.IBinder;
import android.os.RemoteException;

public abstract class LocationBackendService extends AbstractBackendService {

private Backend backend = new Backend();
private LocationCallback callback;
private Location waiting;

/**
* Called, whenever an app requires a location update. This can be a single or a repeated request.
* <p/>
* You may return null if your backend has no newer location available then the last one.
* Do not send the same {@link android.location.Location} twice, if it's not based on updated/refreshed data.
* <p/>
* You can completely ignore this method (means returning null) if you use {@link #report(android.location.Location)}.
*
* @return a new {@link android.location.Location} instance or null if not available.
*/
protected Location update() {
return null;
}

/**
* Directly report a {@link android.location.Location} to the requesting apps. Use this if your updates are based
* on environment changes (eg. cell id change).
*
* @param location the new {@link android.location.Location} instance to be send
*/
public void report(Location location) {
if (callback != null) {
try {
callback.report(location);
} catch (android.os.DeadObjectException e) {
waiting = location;
callback = null;
} catch (RemoteException e) {
waiting = location;
}
} else {
waiting = location;
}
}

/**
* @return true if we're an actively connected backend, false if not
*/
public boolean isConnected() {
return callback != null;
}

@Override
protected IBinder getBackend() {
return backend;
}

private class Backend extends LocationBackend.Stub {
@Override
public void open(LocationCallback callback) throws RemoteException {
LocationBackendService.this.callback = callback;
if (waiting != null) {
callback.report(waiting);
waiting = null;
}
onOpen();
}

@Override
public Location update() throws RemoteException {
return LocationBackendService.this.update();
}

@Override
public void close() throws RemoteException {
onClose();
callback = null;
}
}
}
7 changes: 7 additions & 0 deletions src/org/microg/nlp/api/LocationCallback.aidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.microg.nlp.api;

import android.location.Location;

interface LocationCallback {
void report(in Location location);
}
88 changes: 88 additions & 0 deletions src/org/microg/nlp/api/LocationHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.microg.nlp.api;

import android.location.Location;
import android.os.Bundle;

import java.util.Collection;

public final class LocationHelper {
private LocationHelper() {
}

public static Location create(String source) {
return new Location(source);
}

public static Location create(String source, double latitude, double longitude,
float accuracy) {
Location location = create(source);
location.setLatitude(latitude);
location.setLongitude(longitude);
location.setAccuracy(accuracy);
return location;
}

public static Location create(String source, double latitude, double longitude, float altitude,
Bundle extras) {
Location location = create(source, latitude, longitude, altitude);
location.setExtras(extras);
return location;
}

public static Location create(String source, double latitude, double longitude, double altitude,
float accuracy) {
Location location = create(source, latitude, longitude, accuracy);
location.setAltitude(altitude);
return location;
}

public static Location create(String source, double latitude, double longitude, double altitude,
float accuracy, Bundle extras) {
Location location = create(source, latitude, longitude, altitude, accuracy);
location.setExtras(extras);
return location;
}

public static Location create(String source, long time) {
Location location = create(source);
location.setTime(time);
return location;
}

public static Location create(String source, long time, Bundle extras) {
Location location = create(source, time);
location.setExtras(extras);
return location;
}

public static Location average(String source, Collection<Location> locations) {
if (locations == null || locations.size() == 0) {
return null;
}
int num = locations.size();
double latitude = 0;
double longitude = 0;
float accuracy = 0;
int altitudes = 0;
double altitude = 0;
for (Location value : locations) {
if (value != null) {
latitude += value.getLatitude();
longitude += value.getLongitude();
accuracy += value.getAccuracy();
if (value.hasAltitude()) {
altitude += value.getAltitude();
altitudes++;
}
}
}
Bundle extras = new Bundle();
extras.putInt("AVERAGED_OF", num);
if (altitudes > 0) {
return create(source, latitude / num, longitude / num, altitude / altitudes,
accuracy / num, extras);
} else {
return create(source, latitude / num, longitude / num, accuracy / num, extras);
}
}
}
Loading

0 comments on commit bfe34fc

Please sign in to comment.