Major cleanup, Add support for Geocoder

Using spaces for indentation from now on
Add support for forced locations (debug)
This commit is contained in:
mar-v-in 2014-12-25 12:49:33 +01:00
parent 6606651255
commit 8ff926120c
30 changed files with 1693 additions and 1428 deletions

View file

@ -18,7 +18,6 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, api/src)
# For some reason framework has to be added here else GeocoderParams is not found,
# this way everything else is duplicated, but atleast compiles...
@ -81,5 +80,5 @@ LOCAL_AAPT_FLAGS := --rename-manifest-package com.google.android.location
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
include $(LOCAL_PATH)/api/Android.mk

View file

@ -1,17 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.microg.nlp"
android:versionName="1.0.1"
android:versionCode="1001"
xmlns:android="http://schemas.android.com/apk/res/android">
android:versionName="1.1.0"
android:versionCode="1100">
<uses-sdk android:minSdkVersion="14" />
<uses-sdk
android:minSdkVersion="14" />
<permission
android:name="org.microg.permission.FORCE_COARSE_LOCATION"
android:protectionLevel="dangerous" />
<uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
<uses-permission android:name="android.permission.ACCESS_COARSE_UPDATES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="org.microg.permission.FORCE_COARSE_LOCATION" />
<application
android:icon="@drawable/nlp_app_icon"
@ -71,7 +76,7 @@
<activity
android:name="org.microg.nlp.ui.LocationBackendConfig"
android:theme="@android:style/Theme.Holo.Light">
android:theme="@android:style/Theme.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View file

@ -4,6 +4,7 @@ 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)

View file

@ -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, double lowerLeftLatitude,
double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude,
int maxResults, String locale);
void close();
}

View file

@ -13,7 +13,8 @@ public final class LocationHelper {
return new Location(source);
}
public static Location create(String source, double latitude, double longitude, float accuracy) {
public static Location create(String source, double latitude, double longitude,
float accuracy) {
Location location = create(source);
location.setLatitude(latitude);
location.setLongitude(longitude);
@ -21,19 +22,22 @@ public final class LocationHelper {
return location;
}
public static Location create(String source, double latitude, double longitude, float altitude, Bundle extras) {
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) {
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) {
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;
@ -75,7 +79,8 @@ public final class LocationHelper {
Bundle extras = new Bundle();
extras.putInt("AVERAGED_OF", num);
if (altitudes > 0) {
return create(source, latitude / num, longitude / num, altitude / altitudes, accuracy / num, extras);
return create(source, latitude / num, longitude / num, altitude / altitudes,
accuracy / num, extras);
} else {
return create(source, latitude / num, longitude / num, accuracy / num, extras);
}

View file

@ -2,5 +2,13 @@ package org.microg.nlp.api;
public class NlpApiConstants {
public static final String ACTION_LOCATION_BACKEND = "org.microg.nlp.LOCATION_BACKEND";
public static final String ACTION_GEOCODER_BACKEND = "org.microg.nlp.GEOCODER_BACKEND";
public static final String ACTION_RELOAD_SETTINGS = "org.microg.nlp.RELOAD_SETTINGS";
public static final String ACTION_FORCE_LOCATION = "org.microg.nlp.FORCE_LOCATION";
public static final String PERMISSION_FORCE_LOCATION = "org.microg.permission.FORCE_COARSE_LOCATION";
public static final String INTENT_EXTRA_LOCATION = "location";
public static final String LOCATION_EXTRA_BACKEND_PROVIDER = "SERVICE_BACKEND_PROVIDER";
public static final String LOCATION_EXTRA_BACKEND_COMPONENT = "SERVICE_BACKEND_COMPONENT";
public static final String LOCATION_EXTRA_OTHER_BACKENDS = "OTHER_BACKEND_RESULTS";
public static final String METADATA_BACKEND_SETTINGS_ACTIVITY = "org.microg.nlp.BACKEND_SETTINGS_ACTIVITY";
}

View file

@ -0,0 +1,55 @@
package org.microg.nlp;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.RemoteException;
import android.util.Log;
public abstract class AbstractBackendHelper implements ServiceConnection {
protected final Context context;
protected final Intent serviceIntent;
protected boolean bound;
private final String TAG;
public AbstractBackendHelper(String tag, Context context, Intent serviceIntent) {
TAG = tag;
this.context = context;
this.serviceIntent = serviceIntent;
}
public abstract void close() throws RemoteException;
public abstract boolean hasBackend();
public void unbind() {
if (bound) {
if (hasBackend()) {
try {
close();
} catch (Exception e) {
Log.w(TAG, e);
}
}
try {
context.unbindService(this);
} catch (Exception e) {
Log.w(TAG, e);
}
bound = false;
}
}
public boolean bind() {
if (!bound) {
try {
context.bindService(serviceIntent, this, Context.BIND_AUTO_CREATE);
} catch (Exception e) {
Log.w(TAG, e);
return false;
}
}
return true;
}
}

View file

@ -8,6 +8,7 @@ import org.microg.nlp.location.LocationService;
public class PackageReceiver extends BroadcastReceiver {
private static final String TAG = PackageReceiver.class.getName();
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Intent received: " + intent);

View file

@ -0,0 +1,37 @@
package org.microg.nlp;
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings;
public class Preferences {
private static final String DEFAULT_LOCATION_BACKENDS = "default_location_backends";
private static final String LOCATION_BACKENDS = "location_backends";
private static final String DEFAULT_GEOCODER_BACKENDS = "default_geocoder_backends";
private static final String GEOCODER_BACKENDS = "geocoder_backends";
private final Context context;
public Preferences(Context context) {
this.context = context;
}
private SharedPreferences getSharedPreferences() {
return context.getSharedPreferences("config", Context.MODE_PRIVATE);
}
public String[] getLocationBackends() {
String defBackends = Settings.System.getString(context.getContentResolver(), DEFAULT_LOCATION_BACKENDS);
String backends = getSharedPreferences().getString(LOCATION_BACKENDS, defBackends);
return backends.split("\\|");
}
public void setLocationBackends(String backends) {
getSharedPreferences().edit().putString(LOCATION_BACKENDS, backends).commit();
}
public String[] getGeocoderBackends() {
String defBackends = Settings.System.getString(context.getContentResolver(), DEFAULT_GEOCODER_BACKENDS);
String backends = getSharedPreferences().getString(GEOCODER_BACKENDS, defBackends);
return backends.split("\\|");
}
}

View file

@ -4,8 +4,8 @@ import android.app.IntentService;
import android.content.Intent;
import android.os.IBinder;
public abstract class ProviderService extends IntentService {
private Provider provider;
public abstract class ProviderService<T extends Provider> extends IntentService {
private T provider;
/**
* Creates an ProviderService. Invoked by your subclass's constructor.
@ -38,7 +38,7 @@ public abstract class ProviderService extends IntentService {
*
* @return a new {@link org.microg.nlp.Provider} instance
*/
protected abstract Provider createProvider();
protected abstract T createProvider();
@Override
protected void onHandleIntent(Intent intent) {
@ -48,7 +48,7 @@ public abstract class ProviderService extends IntentService {
/**
* @return the currently used {@link org.microg.nlp.Provider} instance
*/
protected Provider getCurrentProvider() {
protected T getCurrentProvider() {
return provider;
}
}

View file

@ -0,0 +1,60 @@
package org.microg.nlp.geocode;
import android.content.Context;
import android.content.Intent;
import android.location.Address;
import org.microg.nlp.Preferences;
import java.util.ArrayList;
import java.util.List;
import static org.microg.nlp.api.NlpApiConstants.ACTION_GEOCODER_BACKEND;
class BackendFuser {
private final List<BackendHelper> backendHelpers;
public BackendFuser(Context context) {
backendHelpers = new ArrayList<BackendHelper>();
for (String backend : new Preferences(context).getGeocoderBackends()) {
String[] parts = backend.split("/");
if (parts.length == 2) {
Intent intent = new Intent(ACTION_GEOCODER_BACKEND);
intent.setPackage(parts[0]);
intent.setClassName(parts[0], parts[1]);
backendHelpers.add(new BackendHelper(context, intent));
}
}
}
public List<Address> getFromLocation(double latitude, double longitude, int maxResults,
String locale) {
if (backendHelpers.isEmpty())
return null;
ArrayList<Address> result = new ArrayList<Address>();
for (BackendHelper backendHelper : backendHelpers) {
List<Address> backendResult = backendHelper
.getFromLocation(latitude, longitude, maxResults, locale);
if (backendResult != null) {
result.addAll(backendResult);
}
}
return result;
}
public List<Address> getFromLocationName(String locationName, double lowerLeftLatitude,
double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude,
int maxResults, String locale) {
if (backendHelpers.isEmpty())
return null;
ArrayList<Address> result = new ArrayList<Address>();
for (BackendHelper backendHelper : backendHelpers) {
List<Address> backendResult = backendHelper.getFromLocationName(
locationName, lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude,
upperRightLongitude, maxResults, locale);
if (backendResult != null) {
result.addAll(backendResult);
}
}
return result;
}
}

View file

@ -0,0 +1,76 @@
package org.microg.nlp.geocode;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.location.Address;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import org.microg.nlp.AbstractBackendHelper;
import org.microg.nlp.api.GeocoderBackend;
import java.util.List;
class BackendHelper extends AbstractBackendHelper {
private static final String TAG = BackendHelper.class.getName();
private GeocoderBackend backend;
public BackendHelper(Context context, Intent serviceIntent) {
super(TAG, context, serviceIntent);
}
public List<Address> getFromLocation(double latitude, double longitude, int maxResults,
String locale) {
try {
return backend.getFromLocation(latitude, longitude, maxResults, locale);
} catch (Exception e) {
Log.w(TAG, e);
unbind();
return null;
}
}
public List<Address> getFromLocationName(String locationName, double lowerLeftLatitude,
double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude,
int maxResults, String locale) {
try {
return backend.getFromLocationName(locationName, lowerLeftLatitude, lowerLeftLongitude,
upperRightLatitude, upperRightLongitude, maxResults, locale);
} catch (Exception e) {
Log.w(TAG, e);
unbind();
return null;
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bound = true;
backend = GeocoderBackend.Stub.asInterface(service);
if (backend != null) {
try {
backend.open();
} catch (Exception e) {
Log.w(TAG, e);
unbind();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
backend = null;
bound = false;
}
@Override
public void close() throws RemoteException {
backend.close();
}
@Override
public boolean hasBackend() {
return backend != null;
}
}

View file

@ -2,5 +2,5 @@ package org.microg.nlp.geocode;
import org.microg.nlp.Provider;
public interface GeocodeProvider extends Provider {
interface GeocodeProvider extends Provider {
}

View file

@ -1,27 +1,52 @@
package org.microg.nlp.geocode;
import android.content.Context;
import android.location.Address;
import android.location.GeocoderParams;
import com.android.location.provider.GeocodeProvider;
import java.util.List;
public class GeocodeProviderV1 extends GeocodeProvider implements org.microg.nlp.geocode.GeocodeProvider {
@Override
public String onGetFromLocation(double latitude, double longitude, int maxResults, GeocoderParams params,
List<Address> addresses) {
return null;
class GeocodeProviderV1 extends GeocodeProvider implements org.microg.nlp.geocode.GeocodeProvider {
private BackendFuser backendFuser;
private Context context;
public GeocodeProviderV1(Context context) {
this.context = context;
reload();
}
@Override
public String onGetFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude,
double upperRightLatitude, double upperRightLongitude, int maxResults,
public String onGetFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, List<Address> addresses) {
List<Address> fuserResult = backendFuser
.getFromLocation(latitude, longitude, maxResults, params.getLocale().toString());
return handleResult(addresses, fuserResult);
}
@Override
public String onGetFromLocationName(String locationName, double lowerLeftLatitude,
double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude,
int maxResults, GeocoderParams params, List<Address> addresses) {
List<Address> fuserResult = backendFuser.getFromLocationName(locationName,
lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
maxResults, params.getLocale().toString());
return handleResult(addresses, fuserResult);
}
private String handleResult(List<Address> realResult, List<Address> fuserResult) {
if (fuserResult == null) {
return "no backend";
} else if (fuserResult.isEmpty()) {
return "no result";
} else {
realResult.addAll(fuserResult);
return null;
}
}
@Override
public void reload() {
backendFuser = new BackendFuser(context);
}
}

View file

@ -2,7 +2,7 @@ package org.microg.nlp.geocode;
import org.microg.nlp.ProviderService;
public abstract class GeocodeService extends ProviderService {
public abstract class GeocodeService extends ProviderService<GeocodeProvider> {
/**
* Creates an GeocodeService. Invoked by your subclass's constructor.
*

View file

@ -1,7 +1,5 @@
package org.microg.nlp.geocode;
import org.microg.nlp.Provider;
public class GeocodeServiceV1 extends GeocodeService {
private static final String TAG = GeocodeServiceV1.class.getName();
@ -10,7 +8,7 @@ public class GeocodeServiceV1 extends GeocodeService {
}
@Override
protected Provider createProvider() {
return new GeocodeProviderV1();
protected GeocodeProvider createProvider() {
return new GeocodeProviderV1(this);
}
}

View file

@ -3,45 +3,50 @@ package org.microg.nlp.location;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.IBinder;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import org.microg.nlp.api.NlpApiConstants;
import org.microg.nlp.Preferences;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class BackendFuser implements BackendHandler, LocationProvider {
import static org.microg.nlp.api.NlpApiConstants.*;
class BackendFuser {
private static final String TAG = BackendFuser.class.getName();
private final List<BackendHandler> backendHandlers;
private final List<BackendHelper> backendHelpers;
private final LocationProvider locationProvider;
private Location forcedLocation;
private boolean fusing = false;
public BackendFuser(Context context, List<BackendInfo> backends, LocationProvider provider) {
public BackendFuser(Context context, LocationProvider provider) {
locationProvider = provider;
backendHandlers = new ArrayList<BackendHandler>();
for (BackendInfo backendInfo : backends) {
Intent intent = new Intent(NlpApiConstants.ACTION_LOCATION_BACKEND);
intent.setPackage(backendInfo.packageName);
intent.setClassName(backendInfo.packageName, backendInfo.className);
backendHandlers.add(new BackendHelper(context, this, intent));
backendHelpers = new ArrayList<BackendHelper>();
for (String backend : new Preferences(context).getLocationBackends()) {
String[] parts = backend.split("/");
if (parts.length == 2) {
Intent intent = new Intent(ACTION_LOCATION_BACKEND);
intent.setPackage(parts[0]);
intent.setClassName(parts[0], parts[1]);
backendHelpers.add(new BackendHelper(context, this, intent));
}
}
}
@Override
public void unbind() {
for (BackendHandler handler : backendHandlers) {
for (BackendHelper handler : backendHelpers) {
handler.unbind();
}
}
@Override
public boolean bind() {
fusing = false;
boolean handlerBound = false;
for (BackendHandler handler : backendHandlers) {
for (BackendHelper handler : backendHelpers) {
if (handler.bind()) {
handlerBound = true;
}
@ -49,68 +54,72 @@ public class BackendFuser implements BackendHandler, LocationProvider {
return handlerBound;
}
@Override
public Location update() {
fusing = true;
for (BackendHandler handler : backendHandlers) {
for (BackendHelper handler : backendHelpers) {
handler.update();
}
fusing = false;
return getLastLocation();
}
@Override
public Location getLastLocation() {
List<Location> locations = new ArrayList<Location>();
for (BackendHandler handler : backendHandlers) {
for (BackendHelper handler : backendHelpers) {
locations.add(handler.getLastLocation());
}
if (forcedLocation != null) {
locations.add(forcedLocation);
}
if (locations.isEmpty()) {
return null;
} else {
Location location = mergeLocations(locations);
if (location != null) {
location.setProvider(LocationManager.NETWORK_PROVIDER);
locationProvider.reportLocation(location);
Log.v(TAG, "location=" + location);
}
return location;
}
}
private Location mergeLocations(List<Location> locations) {
Collections.sort(locations, LocationComparator.INSTANCE);
if (locations.get(0) != null) {
locationProvider.reportLocation(locations.get(0));
Log.v(TAG, "location=" + locations.get(0));
}
if (locations.isEmpty() || locations.get(0) == null)
return null;
if (locations.size() == 1)
return locations.get(0);
Location location = new Location(locations.get(0));
ArrayList<Location> backendResults = new ArrayList<Location>();
for (Location backendResult : locations) {
if (locations.get(0) == backendResult)
continue;
if (backendResult != null)
backendResults.add(backendResult);
}
if (!backendResults.isEmpty()) {
location.getExtras()
.putParcelableArrayList(LOCATION_EXTRA_OTHER_BACKENDS, backendResults);
}
return location;
}
@Override
public void onEnable() {
locationProvider.onEnable();
}
@Override
public void onDisable() {
locationProvider.onDisable();
}
@Override
public void reportLocation(Location location) {
if (fusing) return;
public void reportLocation() {
if (fusing)
return;
getLastLocation();
}
@Override
public IBinder getBinder() {
return locationProvider.getBinder();
public void forceLocation(Location location) {
forcedLocation = location;
Bundle extras = new Bundle();
extras.putString(LOCATION_EXTRA_BACKEND_PROVIDER, "forced");
location.setExtras(extras);
}
@Override
public void reload() {
locationProvider.reload();
}
public static class BackendInfo {
private final String packageName;
private final String className;
public BackendInfo(String packageName, String className) {
this.packageName = packageName;
this.className = className;
}
public Location getForcedLocation() {
return forcedLocation;
}
public static class LocationComparator implements Comparator<Location> {

View file

@ -1,13 +0,0 @@
package org.microg.nlp.location;
import android.location.Location;
public interface BackendHandler {
void unbind();
boolean bind();
Location update();
Location getLastLocation();
}

View file

@ -3,70 +3,34 @@ package org.microg.nlp.location;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Location;
import android.os.*;
import android.util.Log;
import com.android.location.provider.LocationProviderBase;
import org.microg.nlp.AbstractBackendHelper;
import org.microg.nlp.api.LocationBackend;
import org.microg.nlp.api.LocationCallback;
public class BackendHelper implements BackendHandler {
import static org.microg.nlp.api.NlpApiConstants.LOCATION_EXTRA_BACKEND_COMPONENT;
import static org.microg.nlp.api.NlpApiConstants.LOCATION_EXTRA_BACKEND_PROVIDER;
class BackendHelper extends AbstractBackendHelper {
private static final String TAG = BackendHelper.class.getName();
private final Context context;
private final LocationProvider provider;
private final Intent serviceIntent;
private final Connection connection = new Connection();
private final BackendFuser backendFuser;
private final Callback callback = new Callback();
private LocationBackend backend;
private boolean updateWaiting;
private Location lastLocation;
private boolean bound;
public BackendHelper(Context context, LocationProvider provider, Intent serviceIntent) {
this.context = context;
this.provider = provider;
this.serviceIntent = serviceIntent;
public BackendHelper(Context context, BackendFuser backendFuser, Intent serviceIntent) {
super(TAG, context, serviceIntent);
this.backendFuser = backendFuser;
}
@Override
public boolean bind() {
if (!bound) {
try {
context.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE);
} catch (Exception e) {
Log.w(TAG, e);
return false;
}
}
return true;
}
@Override
public void unbind() {
if (bound) {
if (backend != null) {
try {
backend.close();
} catch (Exception e) {
Log.w(TAG, e);
}
}
try {
context.unbindService(connection);
} catch (Exception e) {
Log.w(TAG, e);
}
bound = false;
}
}
@Override
public Location getLastLocation() {
return lastLocation;
}
@Override
public Location update() {
if (backend == null) {
Log.d(TAG, "Not (yet) bound.");
@ -75,7 +39,7 @@ public class BackendHelper implements BackendHandler {
updateWaiting = false;
try {
setLastLocation(backend.update());
provider.reportLocation(lastLocation);
backendFuser.reportLocation();
} catch (Exception e) {
Log.w(TAG, e);
unbind();
@ -94,9 +58,9 @@ public class BackendHelper implements BackendHandler {
if (location.getExtras() == null) {
location.setExtras(new Bundle());
}
location.getExtras().putString("SERVICE_BACKEND_PROVIDER", location.getProvider());
location.getExtras().putString("SERVICE_BACKEND_COMPONENT", serviceIntent.getComponent().flattenToShortString());
location.setProvider("network");
location.getExtras().putString(LOCATION_EXTRA_BACKEND_PROVIDER, location.getProvider());
location.getExtras().putString(LOCATION_EXTRA_BACKEND_COMPONENT,
serviceIntent.getComponent().flattenToShortString());
if (!location.hasAccuracy()) {
location.setAccuracy(50000);
}
@ -108,12 +72,22 @@ public class BackendHelper implements BackendHandler {
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}
}
location.getExtras().putParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION, new Location(location));
location.getExtras()
.putParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION, new Location(location));
lastLocation = location;
return lastLocation;
}
private class Connection implements ServiceConnection {
@Override
public void close() throws RemoteException {
backend.close();
}
@Override
public boolean hasBackend() {
return backend != null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bound = true;
@ -136,12 +110,12 @@ public class BackendHelper implements BackendHandler {
backend = null;
bound = false;
}
}
private class Callback extends LocationCallback.Stub {
@Override
public void report(Location location) throws RemoteException {
provider.reportLocation(setLastLocation(location));
setLastLocation(location);
backendFuser.reportLocation();
}
}
}

View file

@ -3,10 +3,12 @@ package org.microg.nlp.location;
import android.location.Location;
import org.microg.nlp.Provider;
public interface LocationProvider extends Provider {
interface LocationProvider extends Provider {
void onEnable();
void onDisable();
void reportLocation(Location location);
void forceLocation(Location location);
}

View file

@ -2,6 +2,7 @@ package org.microg.nlp.location;
import android.content.Context;
import android.location.Criteria;
import android.location.Location;
import android.os.Bundle;
import android.os.WorkSource;
import android.util.Log;
@ -12,7 +13,7 @@ import com.android.location.provider.ProviderRequestUnbundled;
import static android.location.LocationProvider.AVAILABLE;
public class LocationProviderV2 extends LocationProviderBase implements LocationProvider {
class LocationProviderV2 extends LocationProviderBase implements LocationProvider {
private static final String TAG = LocationProviderV2.class.getName();
private static final ProviderPropertiesUnbundled props = ProviderPropertiesUnbundled.create(
false, // requiresNetwork
@ -24,7 +25,7 @@ public class LocationProviderV2 extends LocationProviderBase implements Location
true, // supportsBearing
Criteria.POWER_LOW, // powerRequirement
Criteria.ACCURACY_COARSE); // accuracy
private ThreadHelper helper;
private final ThreadHelper helper;
public LocationProviderV2(Context context) {
super(TAG, props);
@ -35,6 +36,11 @@ public class LocationProviderV2 extends LocationProviderBase implements Location
public void onDisable() {
}
@Override
public void forceLocation(Location location) {
helper.forceLocation(location);
}
@Override
public void reload() {
helper.reload();
@ -81,4 +87,5 @@ public class LocationProviderV2 extends LocationProviderBase implements Location
helper.disable();
}
}
}

View file

@ -1,20 +1,17 @@
package org.microg.nlp.location;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import org.microg.nlp.Provider;
import org.microg.nlp.ProviderService;
public abstract class LocationService extends ProviderService {
public static final String ACTION_RELOAD_SETTINGS = "org.microg.nlp.RELOAD_SETTINGS";
import static org.microg.nlp.api.NlpApiConstants.*;
public abstract class LocationService extends ProviderService<LocationProvider> {
public static void reloadLocationService(Context context) {
Intent intent = new Intent(LocationService.ACTION_RELOAD_SETTINGS);
Intent intent = new Intent(ACTION_RELOAD_SETTINGS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
intent.setClass(context, LocationServiceV2.class);
} else {
@ -34,21 +31,19 @@ public abstract class LocationService extends ProviderService {
@Override
protected void onHandleIntent(Intent intent) {
/*
* There is an undocumented way to send locations via intent in Google's LocationService.
* This intent based location is not secure, that's why it's not active here,
* but maybe we will add it in the future, could be a nice debugging feature :)
*/
/*
Location location = intent.getParcelableExtra("location");
if (nlprovider != null && location != null) {
nlprovider.reportLocation(location);
if (ACTION_FORCE_LOCATION.equals(intent.getAction())) {
if (checkCallingPermission(PERMISSION_FORCE_LOCATION) ==
PackageManager.PERMISSION_GRANTED) {
LocationProvider provider = getCurrentProvider();
if (provider != null && intent.hasExtra(INTENT_EXTRA_LOCATION)) {
provider.forceLocation(
(Location) intent.getParcelableExtra(INTENT_EXTRA_LOCATION));
}
}
}
*/
if (ACTION_RELOAD_SETTINGS.equals(intent.getAction())) {
Provider provider = getCurrentProvider();
LocationProvider provider = getCurrentProvider();
if (provider != null) {
provider.reload();
}
@ -57,9 +52,9 @@ public abstract class LocationService extends ProviderService {
@Override
public boolean onUnbind(Intent intent) {
Provider provider = getCurrentProvider();
if (provider instanceof LocationProvider) {
((LocationProvider) provider).onDisable();
LocationProvider provider = getCurrentProvider();
if (provider != null) {
provider.onDisable();
}
return super.onUnbind(intent);
}

View file

@ -1,16 +1,15 @@
package org.microg.nlp.location;
import android.content.Context;
import android.location.Location;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadHelper implements Runnable {
class ThreadHelper implements Runnable {
private final Context context;
private final LocationProvider locationProvider;
private BackendHandler backendHandler;
private BackendFuser backendFuser;
private ScheduledThreadPoolExecutor executor;
private long time = 5000; // Initialize with 5s
private boolean enabled;
@ -22,15 +21,15 @@ public class ThreadHelper implements Runnable {
}
private void updateBackendHandler() {
List<BackendFuser.BackendInfo> backendList = new ArrayList<BackendFuser.BackendInfo>();
String backends = context.getSharedPreferences("config", Context.MODE_PRIVATE).getString("location_backends", "");
for (String backend : backends.split("\\|")) {
String[] parts = backend.split("/");
if (parts.length == 2) {
backendList.add(new BackendFuser.BackendInfo(parts[0], parts[1]));
BackendFuser old = backendFuser;
backendFuser = new BackendFuser(context, locationProvider);
if (old != null) {
backendFuser.forceLocation(old.getForcedLocation());
}
}
backendHandler = new BackendFuser(context, backendList, locationProvider);
public void forceLocation(Location location) {
backendFuser.forceLocation(location);
}
public void reload() {
@ -45,7 +44,7 @@ public class ThreadHelper implements Runnable {
executor = null;
}
if (enabled) {
backendHandler.unbind();
backendFuser.unbind();
enabled = false;
}
}
@ -65,7 +64,7 @@ public class ThreadHelper implements Runnable {
public void enable() {
if (!enabled) {
backendHandler.bind();
backendFuser.bind();
enabled = true;
}
reset();
@ -73,6 +72,6 @@ public class ThreadHelper implements Runnable {
@Override
public void run() {
backendHandler.update();
backendFuser.update();
}
}

View file

@ -489,7 +489,9 @@ public class DynamicListView extends ListView {
public void setList(List<ServiceInfo> list) {
this.mList = list;
} /**
}
/**
* This scroll listener is added to the listview in order to handle cell swapping
* when the cell is either at the top or bottom edge of the listview. If the hover
* cell is at either edge of the listview, the listview will begin scrolling. As
@ -581,7 +583,8 @@ public class DynamicListView extends ListView {
HashMap<ServiceInfo, Integer> mIdMap = new HashMap<ServiceInfo, Integer>();
public StableArrayAdapter(Context context, int rootViewResourceId, int textViewResourceId, List<ServiceInfo> objects) {
public StableArrayAdapter(Context context, int rootViewResourceId, int textViewResourceId,
List<ServiceInfo> objects) {
super(context, rootViewResourceId, textViewResourceId, objects);
init(objects);
}
@ -607,5 +610,4 @@ public class DynamicListView extends ListView {
}
}
}

View file

@ -18,8 +18,8 @@ import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import org.microg.nlp.Preferences;
import org.microg.nlp.R;
import org.microg.nlp.api.NlpApiConstants;
import org.microg.nlp.location.LocationService;
import java.util.ArrayList;
@ -27,6 +27,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.microg.nlp.api.NlpApiConstants.ACTION_LOCATION_BACKEND;
import static org.microg.nlp.api.NlpApiConstants.METADATA_BACKEND_SETTINGS_ACTIVITY;
public class LocationBackendConfig extends Activity {
private static final String TAG = LocationBackendConfig.class.getName();
@ -64,20 +67,21 @@ public class LocationBackendConfig extends Activity {
activeBackends = new ArrayList<ServiceInfo>();
knownBackends = new HashMap<ServiceInfo, KnownBackend>();
unusedBackends = new ArrayList<ServiceInfo>();
Intent intent = new Intent(NlpApiConstants.ACTION_LOCATION_BACKEND);
List<ResolveInfo> resolveInfos = getPackageManager().queryIntentServices(intent, PackageManager.GET_META_DATA);
Intent intent = new Intent(ACTION_LOCATION_BACKEND);
List<ResolveInfo> resolveInfos = getPackageManager()
.queryIntentServices(intent, PackageManager.GET_META_DATA);
for (ResolveInfo info : resolveInfos) {
ServiceInfo serviceInfo = info.serviceInfo;
String simpleName = String.valueOf(serviceInfo.loadLabel(getPackageManager()));
Drawable icon = serviceInfo.loadIcon(getPackageManager());
knownBackends.put(serviceInfo, new KnownBackend(serviceInfo, simpleName, icon));
}
String activeBackendString = getSharedPreferences("config", MODE_PRIVATE).getString("location_backends", "");
for (String backend : activeBackendString.split("\\|")) {
for (String backend : new Preferences(this).getLocationBackends()) {
String[] parts = backend.split("/");
if (parts.length == 2) {
for (ServiceInfo serviceInfo : knownBackends.keySet()) {
if (serviceInfo.packageName.equals(parts[0]) && serviceInfo.name.equals(parts[1])) {
if (serviceInfo.packageName.equals(parts[0]) &&
serviceInfo.name.equals(parts[1])) {
activeBackends.add(serviceInfo);
}
}
@ -128,7 +132,7 @@ public class LocationBackendConfig extends Activity {
private void onBackendsChanged() {
updateAddButton();
resetAdapter();
getSharedPreferences("config", MODE_PRIVATE).edit().putString("location_backends", backendString(activeBackends)).commit();
new Preferences(this).setLocationBackends(backendString(activeBackends));
LocationService.reloadLocationService(this);
}
@ -176,7 +180,7 @@ public class LocationBackendConfig extends Activity {
Intent settingsIntent = new Intent(Intent.ACTION_VIEW);
settingsIntent.setPackage(componentInfo.packageName);
settingsIntent.setClassName(componentInfo.packageName,
componentInfo.metaData.getString(NlpApiConstants.METADATA_BACKEND_SETTINGS_ACTIVITY));
componentInfo.metaData.getString(METADATA_BACKEND_SETTINGS_ACTIVITY));
return settingsIntent;
}
@ -187,8 +191,10 @@ public class LocationBackendConfig extends Activity {
popUp = new PopupMenu(this, anchorView);
popUp.getMenu().add(Menu.NONE, 0, Menu.NONE, "Remove"); // TODO label
if (backend.serviceInfo.metaData != null &&
backend.serviceInfo.metaData.getString(NlpApiConstants.METADATA_BACKEND_SETTINGS_ACTIVITY) != null) {
if (getPackageManager().resolveActivity(createSettingsIntent(backend.serviceInfo), 0) != null) {
backend.serviceInfo.metaData.getString(METADATA_BACKEND_SETTINGS_ACTIVITY) !=
null) {
if (getPackageManager().resolveActivity(createSettingsIntent(backend.serviceInfo), 0) !=
null) {
popUp.getMenu().add(Menu.NONE, 1, Menu.NONE, "Settings"); // TODO label
}
}
@ -211,7 +217,8 @@ public class LocationBackendConfig extends Activity {
private class Adapter extends DynamicListView.StableArrayAdapter {
public Adapter(List<ServiceInfo> backends) {
super(LocationBackendConfig.this, R.layout.backend_list_entry, android.R.id.text2, backends);
super(LocationBackendConfig.this, R.layout.backend_list_entry, android.R.id.text2,
backends);
}
@Override