Major cleanup, Add support for Geocoder
Using spaces for indentation from now on Add support for forced locations (debug)
This commit is contained in:
parent
6606651255
commit
8ff926120c
|
@ -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
|
||||
|
||||
|
|
|
@ -1,94 +1,99 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
package="org.microg.nlp"
|
||||
android:versionName="1.0.1"
|
||||
android:versionCode="1001"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.microg.nlp"
|
||||
android:versionName="1.1.0"
|
||||
android:versionCode="1100">
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" />
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14" />
|
||||
|
||||
<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" />
|
||||
<permission
|
||||
android:name="org.microg.permission.FORCE_COARSE_LOCATION"
|
||||
android:protectionLevel="dangerous" />
|
||||
|
||||
<application
|
||||
android:icon="@drawable/nlp_app_icon"
|
||||
android:label="@string/nlp_app_name">
|
||||
<uses-library android:name="com.android.location.provider" />
|
||||
<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" />
|
||||
|
||||
<!-- Gingerbread / Ice Cream Sandwich -->
|
||||
<service
|
||||
android:name="org.microg.nlp.location.LocationServiceV1"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.location.NetworkLocationProvider" />
|
||||
</intent-filter>
|
||||
<application
|
||||
android:icon="@drawable/nlp_app_icon"
|
||||
android:label="@string/nlp_app_name">
|
||||
<uses-library android:name="com.android.location.provider" />
|
||||
|
||||
<meta-data
|
||||
android:name="serviceVersion"
|
||||
android:value="1" />
|
||||
<meta-data
|
||||
android:name="version"
|
||||
android:value="1" />
|
||||
</service>
|
||||
<!-- Gingerbread / Ice Cream Sandwich -->
|
||||
<service
|
||||
android:name="org.microg.nlp.location.LocationServiceV1"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.location.NetworkLocationProvider" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Jelly Bean / KitKat -->
|
||||
<service
|
||||
android:name="org.microg.nlp.location.LocationServiceV2"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<!-- KitKat changed the action name but nothing else, hence we handle it the same -->
|
||||
<action android:name="com.android.location.service.v3.NetworkLocationProvider" />
|
||||
<action android:name="com.android.location.service.v2.NetworkLocationProvider" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="serviceVersion"
|
||||
android:value="1" />
|
||||
<meta-data
|
||||
android:name="version"
|
||||
android:value="1" />
|
||||
</service>
|
||||
|
||||
<meta-data
|
||||
android:name="serviceVersion"
|
||||
android:value="2" />
|
||||
<meta-data
|
||||
android:name="serviceIsMultiuser"
|
||||
android:value="false" />
|
||||
</service>
|
||||
<!-- Jelly Bean / KitKat -->
|
||||
<service
|
||||
android:name="org.microg.nlp.location.LocationServiceV2"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<!-- KitKat changed the action name but nothing else, hence we handle it the same -->
|
||||
<action android:name="com.android.location.service.v3.NetworkLocationProvider" />
|
||||
<action android:name="com.android.location.service.v2.NetworkLocationProvider" />
|
||||
</intent-filter>
|
||||
|
||||
<service
|
||||
android:name="org.microg.nlp.geocode.GeocodeServiceV1"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<!-- Jelly Bean changed the action name but nothing else, hence we handle it the same -->
|
||||
<action android:name="com.android.location.service.GeocodeProvider" />
|
||||
<action android:name="com.google.android.location.GeocodeProvider" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="serviceVersion"
|
||||
android:value="2" />
|
||||
<meta-data
|
||||
android:name="serviceIsMultiuser"
|
||||
android:value="false" />
|
||||
</service>
|
||||
|
||||
<meta-data
|
||||
android:name="serviceVersion"
|
||||
android:value="2" />
|
||||
<meta-data
|
||||
android:name="serviceIsMultiuser"
|
||||
android:value="false" />
|
||||
</service>
|
||||
<service
|
||||
android:name="org.microg.nlp.geocode.GeocodeServiceV1"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<!-- Jelly Bean changed the action name but nothing else, hence we handle it the same -->
|
||||
<action android:name="com.android.location.service.GeocodeProvider" />
|
||||
<action android:name="com.google.android.location.GeocodeProvider" />
|
||||
</intent-filter>
|
||||
|
||||
<activity
|
||||
android:name="org.microg.nlp.ui.LocationBackendConfig"
|
||||
android:theme="@android:style/Theme.Holo.Light">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<meta-data
|
||||
android:name="serviceVersion"
|
||||
android:value="2" />
|
||||
<meta-data
|
||||
android:name="serviceIsMultiuser"
|
||||
android:value="false" />
|
||||
</service>
|
||||
|
||||
<receiver android:name="org.microg.nlp.PackageReceiver" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PACKAGE_ADDED" />
|
||||
<action android:name="android.intent.action.PACKAGE_CHANGED" />
|
||||
<action android:name="android.intent.action.PACKAGE_UPGRADED" />
|
||||
<action android:name="android.intent.action.PACKAGE_REMOVED" />
|
||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.PACKAGE_RESTARTED" />
|
||||
<activity
|
||||
android:name="org.microg.nlp.ui.LocationBackendConfig"
|
||||
android:theme="@android:style/Theme.Light">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
<receiver android:name="org.microg.nlp.PackageReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PACKAGE_ADDED" />
|
||||
<action android:name="android.intent.action.PACKAGE_CHANGED" />
|
||||
<action android:name="android.intent.action.PACKAGE_UPGRADED" />
|
||||
<action android:name="android.intent.action.PACKAGE_REMOVED" />
|
||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.PACKAGE_RESTARTED" />
|
||||
|
||||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -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)
|
||||
|
|
13
api/src/org/microg/nlp/api/GeocoderBackend.aidl
Normal file
13
api/src/org/microg/nlp/api/GeocoderBackend.aidl
Normal 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();
|
||||
}
|
|
@ -4,7 +4,7 @@ import org.microg.nlp.api.LocationCallback;
|
|||
import android.location.Location;
|
||||
|
||||
interface LocationBackend {
|
||||
void open(LocationCallback callback);
|
||||
Location update();
|
||||
void close();
|
||||
void open(LocationCallback callback);
|
||||
Location update();
|
||||
void close();
|
||||
}
|
||||
|
|
|
@ -8,91 +8,91 @@ import android.os.RemoteException;
|
|||
|
||||
public abstract class LocationBackendService extends Service {
|
||||
|
||||
private Backend backend = new Backend();
|
||||
private LocationCallback callback;
|
||||
private Location waiting;
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return backend;
|
||||
}
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a connection was setup
|
||||
*/
|
||||
protected void onOpen() {
|
||||
/**
|
||||
* Called after a connection was setup
|
||||
*/
|
||||
protected void onOpen() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before connection closure
|
||||
*/
|
||||
protected void onClose() {
|
||||
/**
|
||||
* Called before connection closure
|
||||
*/
|
||||
protected void onClose() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if we're an actively connected backend, false if not
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return callback != null;
|
||||
}
|
||||
/**
|
||||
* @return true if we're an actively connected backend, false if not
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return callback != null;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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 Location update() throws RemoteException {
|
||||
return LocationBackendService.this.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws RemoteException {
|
||||
onClose();
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void close() throws RemoteException {
|
||||
onClose();
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,5 +3,5 @@ package org.microg.nlp.api;
|
|||
import android.location.Location;
|
||||
|
||||
interface LocationCallback {
|
||||
void report(in Location location);
|
||||
void report(in Location location);
|
||||
}
|
||||
|
|
|
@ -6,78 +6,83 @@ import android.os.Bundle;
|
|||
import java.util.Collection;
|
||||
|
||||
public final class LocationHelper {
|
||||
private LocationHelper() {
|
||||
}
|
||||
private LocationHelper() {
|
||||
}
|
||||
|
||||
public static Location create(String source) {
|
||||
return new Location(source);
|
||||
}
|
||||
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 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, 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) {
|
||||
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, 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) {
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
package org.microg.nlp.api;
|
||||
|
||||
public class NlpApiConstants {
|
||||
public static final String ACTION_LOCATION_BACKEND = "org.microg.nlp.LOCATION_BACKEND";
|
||||
public static final String METADATA_BACKEND_SETTINGS_ACTIVITY = "org.microg.nlp.BACKEND_SETTINGS_ACTIVITY";
|
||||
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";
|
||||
}
|
||||
|
|
55
src/org/microg/nlp/AbstractBackendHelper.java
Normal file
55
src/org/microg/nlp/AbstractBackendHelper.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,11 +7,12 @@ import android.util.Log;
|
|||
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);
|
||||
Log.d(TAG, "Reloading location service...");
|
||||
LocationService.reloadLocationService(context);
|
||||
}
|
||||
private static final String TAG = PackageReceiver.class.getName();
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "Intent received: " + intent);
|
||||
Log.d(TAG, "Reloading location service...");
|
||||
LocationService.reloadLocationService(context);
|
||||
}
|
||||
}
|
||||
|
|
37
src/org/microg/nlp/Preferences.java
Normal file
37
src/org/microg/nlp/Preferences.java
Normal 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("\\|");
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ package org.microg.nlp;
|
|||
import android.os.IBinder;
|
||||
|
||||
public interface Provider {
|
||||
IBinder getBinder();
|
||||
IBinder getBinder();
|
||||
|
||||
void reload();
|
||||
void reload();
|
||||
}
|
||||
|
|
|
@ -4,51 +4,51 @@ 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.
|
||||
*
|
||||
* @param tag Used for debugging.
|
||||
*/
|
||||
public ProviderService(String tag) {
|
||||
super(tag);
|
||||
}
|
||||
/**
|
||||
* Creates an ProviderService. Invoked by your subclass's constructor.
|
||||
*
|
||||
* @param tag Used for debugging.
|
||||
*/
|
||||
public ProviderService(String tag) {
|
||||
super(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
provider = createProvider();
|
||||
}
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
provider = createProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return provider.getBinder();
|
||||
}
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return provider.getBinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
provider = null;
|
||||
}
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
provider = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link org.microg.nlp.Provider}.
|
||||
* This is most likely only called once
|
||||
*
|
||||
* @return a new {@link org.microg.nlp.Provider} instance
|
||||
*/
|
||||
protected abstract Provider createProvider();
|
||||
/**
|
||||
* Create a {@link org.microg.nlp.Provider}.
|
||||
* This is most likely only called once
|
||||
*
|
||||
* @return a new {@link org.microg.nlp.Provider} instance
|
||||
*/
|
||||
protected abstract T createProvider();
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
// Default implementation is to do nothing
|
||||
}
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
// Default implementation is to do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the currently used {@link org.microg.nlp.Provider} instance
|
||||
*/
|
||||
protected Provider getCurrentProvider() {
|
||||
return provider;
|
||||
}
|
||||
/**
|
||||
* @return the currently used {@link org.microg.nlp.Provider} instance
|
||||
*/
|
||||
protected T getCurrentProvider() {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
|
60
src/org/microg/nlp/geocode/BackendFuser.java
Normal file
60
src/org/microg/nlp/geocode/BackendFuser.java
Normal 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;
|
||||
}
|
||||
}
|
76
src/org/microg/nlp/geocode/BackendHelper.java
Normal file
76
src/org/microg/nlp/geocode/BackendHelper.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -2,5 +2,5 @@ package org.microg.nlp.geocode;
|
|||
|
||||
import org.microg.nlp.Provider;
|
||||
|
||||
public interface GeocodeProvider extends Provider {
|
||||
interface GeocodeProvider extends Provider {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
@Override
|
||||
public String onGetFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude,
|
||||
double upperRightLatitude, double upperRightLongitude, int maxResults,
|
||||
GeocoderParams params, List<Address> addresses) {
|
||||
return null;
|
||||
}
|
||||
public GeocodeProviderV1(Context context) {
|
||||
this.context = context;
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ package org.microg.nlp.geocode;
|
|||
|
||||
import org.microg.nlp.ProviderService;
|
||||
|
||||
public abstract class GeocodeService extends ProviderService {
|
||||
/**
|
||||
* Creates an GeocodeService. Invoked by your subclass's constructor.
|
||||
*
|
||||
* @param tag Used for debugging.
|
||||
*/
|
||||
public GeocodeService(String tag) {
|
||||
super(tag);
|
||||
}
|
||||
public abstract class GeocodeService extends ProviderService<GeocodeProvider> {
|
||||
/**
|
||||
* Creates an GeocodeService. Invoked by your subclass's constructor.
|
||||
*
|
||||
* @param tag Used for debugging.
|
||||
*/
|
||||
public GeocodeService(String tag) {
|
||||
super(tag);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
package org.microg.nlp.geocode;
|
||||
|
||||
import org.microg.nlp.Provider;
|
||||
|
||||
public class GeocodeServiceV1 extends GeocodeService {
|
||||
private static final String TAG = GeocodeServiceV1.class.getName();
|
||||
private static final String TAG = GeocodeServiceV1.class.getName();
|
||||
|
||||
public GeocodeServiceV1() {
|
||||
super(TAG);
|
||||
}
|
||||
public GeocodeServiceV1() {
|
||||
super(TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Provider createProvider() {
|
||||
return new GeocodeProviderV1();
|
||||
}
|
||||
@Override
|
||||
protected GeocodeProvider createProvider() {
|
||||
return new GeocodeProviderV1(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,147 +3,156 @@ 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 {
|
||||
private static final String TAG = BackendFuser.class.getName();
|
||||
import static org.microg.nlp.api.NlpApiConstants.*;
|
||||
|
||||
private final List<BackendHandler> backendHandlers;
|
||||
private final LocationProvider locationProvider;
|
||||
private boolean fusing = false;
|
||||
class BackendFuser {
|
||||
private static final String TAG = BackendFuser.class.getName();
|
||||
|
||||
public BackendFuser(Context context, List<BackendInfo> backends, 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));
|
||||
}
|
||||
}
|
||||
private final List<BackendHelper> backendHelpers;
|
||||
private final LocationProvider locationProvider;
|
||||
private Location forcedLocation;
|
||||
private boolean fusing = false;
|
||||
|
||||
@Override
|
||||
public void unbind() {
|
||||
for (BackendHandler handler : backendHandlers) {
|
||||
handler.unbind();
|
||||
}
|
||||
}
|
||||
public BackendFuser(Context context, LocationProvider provider) {
|
||||
locationProvider = provider;
|
||||
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 boolean bind() {
|
||||
fusing = false;
|
||||
boolean handlerBound = false;
|
||||
for (BackendHandler handler : backendHandlers) {
|
||||
if (handler.bind()) {
|
||||
handlerBound = true;
|
||||
}
|
||||
}
|
||||
return handlerBound;
|
||||
}
|
||||
public void unbind() {
|
||||
for (BackendHelper handler : backendHelpers) {
|
||||
handler.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location update() {
|
||||
fusing = true;
|
||||
for (BackendHandler handler : backendHandlers) {
|
||||
handler.update();
|
||||
}
|
||||
fusing = false;
|
||||
return getLastLocation();
|
||||
}
|
||||
public boolean bind() {
|
||||
fusing = false;
|
||||
boolean handlerBound = false;
|
||||
for (BackendHelper handler : backendHelpers) {
|
||||
if (handler.bind()) {
|
||||
handlerBound = true;
|
||||
}
|
||||
}
|
||||
return handlerBound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLastLocation() {
|
||||
List<Location> locations = new ArrayList<Location>();
|
||||
for (BackendHandler handler : backendHandlers) {
|
||||
locations.add(handler.getLastLocation());
|
||||
}
|
||||
if (locations.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
Collections.sort(locations, LocationComparator.INSTANCE);
|
||||
if (locations.get(0) != null) {
|
||||
locationProvider.reportLocation(locations.get(0));
|
||||
Log.v(TAG, "location=" + locations.get(0));
|
||||
}
|
||||
return locations.get(0);
|
||||
}
|
||||
}
|
||||
public Location update() {
|
||||
fusing = true;
|
||||
for (BackendHelper handler : backendHelpers) {
|
||||
handler.update();
|
||||
}
|
||||
fusing = false;
|
||||
return getLastLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
locationProvider.onEnable();
|
||||
}
|
||||
public Location getLastLocation() {
|
||||
List<Location> locations = new ArrayList<Location>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
locationProvider.onDisable();
|
||||
}
|
||||
private Location mergeLocations(List<Location> locations) {
|
||||
Collections.sort(locations, LocationComparator.INSTANCE);
|
||||
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 reportLocation(Location location) {
|
||||
if (fusing) return;
|
||||
getLastLocation();
|
||||
}
|
||||
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 Location getForcedLocation() {
|
||||
return forcedLocation;
|
||||
}
|
||||
|
||||
public static class BackendInfo {
|
||||
private final String packageName;
|
||||
private final String className;
|
||||
public static class LocationComparator implements Comparator<Location> {
|
||||
|
||||
public BackendInfo(String packageName, String className) {
|
||||
this.packageName = packageName;
|
||||
this.className = className;
|
||||
}
|
||||
}
|
||||
public static final LocationComparator INSTANCE = new LocationComparator();
|
||||
public static final long SWITCH_ON_FRESHNESS_CLIFF_MS = 30000; // 30 seconds TODO: make it a setting
|
||||
|
||||
public static class LocationComparator implements Comparator<Location> {
|
||||
|
||||
public static final LocationComparator INSTANCE = new LocationComparator();
|
||||
public static final long SWITCH_ON_FRESHNESS_CLIFF_MS = 30000; // 30 seconds TODO: make it a setting
|
||||
|
||||
/**
|
||||
* @return whether {@param lhs} is better than {@param rhs}
|
||||
*/
|
||||
@Override
|
||||
public int compare(Location lhs, Location rhs) {
|
||||
if (lhs == rhs)
|
||||
return 0;
|
||||
if (lhs == null) {
|
||||
return 1;
|
||||
}
|
||||
if (rhs == null) {
|
||||
return -1;
|
||||
}
|
||||
if (!lhs.hasAccuracy()) {
|
||||
return 1;
|
||||
}
|
||||
if (!rhs.hasAccuracy()) {
|
||||
return -1;
|
||||
}
|
||||
if (rhs.getTime() > lhs.getTime() + SWITCH_ON_FRESHNESS_CLIFF_MS) {
|
||||
return 1;
|
||||
}
|
||||
if (lhs.getTime() > rhs.getTime() + SWITCH_ON_FRESHNESS_CLIFF_MS) {
|
||||
return -1;
|
||||
}
|
||||
return (int) (lhs.getAccuracy() - rhs.getAccuracy());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return whether {@param lhs} is better than {@param rhs}
|
||||
*/
|
||||
@Override
|
||||
public int compare(Location lhs, Location rhs) {
|
||||
if (lhs == rhs)
|
||||
return 0;
|
||||
if (lhs == null) {
|
||||
return 1;
|
||||
}
|
||||
if (rhs == null) {
|
||||
return -1;
|
||||
}
|
||||
if (!lhs.hasAccuracy()) {
|
||||
return 1;
|
||||
}
|
||||
if (!rhs.hasAccuracy()) {
|
||||
return -1;
|
||||
}
|
||||
if (rhs.getTime() > lhs.getTime() + SWITCH_ON_FRESHNESS_CLIFF_MS) {
|
||||
return 1;
|
||||
}
|
||||
if (lhs.getTime() > rhs.getTime() + SWITCH_ON_FRESHNESS_CLIFF_MS) {
|
||||
return -1;
|
||||
}
|
||||
return (int) (lhs.getAccuracy() - rhs.getAccuracy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package org.microg.nlp.location;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
public interface BackendHandler {
|
||||
void unbind();
|
||||
|
||||
boolean bind();
|
||||
|
||||
Location update();
|
||||
|
||||
Location getLastLocation();
|
||||
}
|
|
@ -3,145 +3,119 @@ 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 {
|
||||
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 Callback callback = new Callback();
|
||||
private LocationBackend backend;
|
||||
private boolean updateWaiting;
|
||||
private Location lastLocation;
|
||||
private boolean bound;
|
||||
import static org.microg.nlp.api.NlpApiConstants.LOCATION_EXTRA_BACKEND_COMPONENT;
|
||||
import static org.microg.nlp.api.NlpApiConstants.LOCATION_EXTRA_BACKEND_PROVIDER;
|
||||
|
||||
public BackendHelper(Context context, LocationProvider provider, Intent serviceIntent) {
|
||||
this.context = context;
|
||||
this.provider = provider;
|
||||
this.serviceIntent = serviceIntent;
|
||||
}
|
||||
class BackendHelper extends AbstractBackendHelper {
|
||||
private static final String TAG = BackendHelper.class.getName();
|
||||
private final BackendFuser backendFuser;
|
||||
private final Callback callback = new Callback();
|
||||
private LocationBackend backend;
|
||||
private boolean updateWaiting;
|
||||
private Location lastLocation;
|
||||
|
||||
@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;
|
||||
}
|
||||
public BackendHelper(Context context, BackendFuser backendFuser, Intent serviceIntent) {
|
||||
super(TAG, context, serviceIntent);
|
||||
this.backendFuser = backendFuser;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
public Location getLastLocation() {
|
||||
return lastLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLastLocation() {
|
||||
return lastLocation;
|
||||
}
|
||||
public Location update() {
|
||||
if (backend == null) {
|
||||
Log.d(TAG, "Not (yet) bound.");
|
||||
updateWaiting = true;
|
||||
} else {
|
||||
updateWaiting = false;
|
||||
try {
|
||||
setLastLocation(backend.update());
|
||||
backendFuser.reportLocation();
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
unbind();
|
||||
}
|
||||
}
|
||||
return lastLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location update() {
|
||||
if (backend == null) {
|
||||
Log.d(TAG, "Not (yet) bound.");
|
||||
updateWaiting = true;
|
||||
} else {
|
||||
updateWaiting = false;
|
||||
try {
|
||||
setLastLocation(backend.update());
|
||||
provider.reportLocation(lastLocation);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
unbind();
|
||||
}
|
||||
}
|
||||
return lastLocation;
|
||||
}
|
||||
private Location setLastLocation(Location location) {
|
||||
if (location == null) {
|
||||
return lastLocation;
|
||||
}
|
||||
if (!location.hasAccuracy()) {
|
||||
return lastLocation;
|
||||
}
|
||||
if (location.getExtras() == null) {
|
||||
location.setExtras(new Bundle());
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (location.getTime() <= 0) {
|
||||
location.setTime(System.currentTimeMillis());
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
if (location.getElapsedRealtimeNanos() <= 0) {
|
||||
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
|
||||
}
|
||||
}
|
||||
location.getExtras()
|
||||
.putParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION, new Location(location));
|
||||
lastLocation = location;
|
||||
return lastLocation;
|
||||
}
|
||||
|
||||
private Location setLastLocation(Location location) {
|
||||
if (location == null) {
|
||||
return lastLocation;
|
||||
}
|
||||
if (!location.hasAccuracy()) {
|
||||
return lastLocation;
|
||||
}
|
||||
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");
|
||||
if (!location.hasAccuracy()) {
|
||||
location.setAccuracy(50000);
|
||||
}
|
||||
if (location.getTime() <= 0) {
|
||||
location.setTime(System.currentTimeMillis());
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
if (location.getElapsedRealtimeNanos() <= 0) {
|
||||
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
|
||||
}
|
||||
}
|
||||
location.getExtras().putParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION, new Location(location));
|
||||
lastLocation = location;
|
||||
return lastLocation;
|
||||
}
|
||||
@Override
|
||||
public void close() throws RemoteException {
|
||||
backend.close();
|
||||
}
|
||||
|
||||
private class Connection implements ServiceConnection {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
bound = true;
|
||||
backend = LocationBackend.Stub.asInterface(service);
|
||||
if (backend != null) {
|
||||
try {
|
||||
backend.open(callback);
|
||||
if (updateWaiting) {
|
||||
update();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
unbind();
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean hasBackend() {
|
||||
return backend != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
backend = null;
|
||||
bound = false;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
bound = true;
|
||||
backend = LocationBackend.Stub.asInterface(service);
|
||||
if (backend != null) {
|
||||
try {
|
||||
backend.open(callback);
|
||||
if (updateWaiting) {
|
||||
update();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
unbind();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Callback extends LocationCallback.Stub {
|
||||
@Override
|
||||
public void report(Location location) throws RemoteException {
|
||||
provider.reportLocation(setLastLocation(location));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
backend = null;
|
||||
bound = false;
|
||||
}
|
||||
|
||||
private class Callback extends LocationCallback.Stub {
|
||||
@Override
|
||||
public void report(Location location) throws RemoteException {
|
||||
setLastLocation(location);
|
||||
backendFuser.reportLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,12 @@ package org.microg.nlp.location;
|
|||
import android.location.Location;
|
||||
import org.microg.nlp.Provider;
|
||||
|
||||
public interface LocationProvider extends Provider {
|
||||
void onEnable();
|
||||
interface LocationProvider extends Provider {
|
||||
void onEnable();
|
||||
|
||||
void onDisable();
|
||||
void onDisable();
|
||||
|
||||
void reportLocation(Location location);
|
||||
void reportLocation(Location location);
|
||||
|
||||
void forceLocation(Location location);
|
||||
}
|
||||
|
|
|
@ -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,73 +13,79 @@ import com.android.location.provider.ProviderRequestUnbundled;
|
|||
|
||||
import static android.location.LocationProvider.AVAILABLE;
|
||||
|
||||
public class LocationProviderV2 extends LocationProviderBase implements LocationProvider {
|
||||
private static final String TAG = LocationProviderV2.class.getName();
|
||||
private static final ProviderPropertiesUnbundled props = ProviderPropertiesUnbundled.create(
|
||||
false, // requiresNetwork
|
||||
false, // requiresSatellite
|
||||
false, // requiresCell
|
||||
false, // hasMonetaryCost
|
||||
true, // supportsAltitude
|
||||
true, // supportsSpeed
|
||||
true, // supportsBearing
|
||||
Criteria.POWER_LOW, // powerRequirement
|
||||
Criteria.ACCURACY_COARSE); // accuracy
|
||||
private ThreadHelper helper;
|
||||
class LocationProviderV2 extends LocationProviderBase implements LocationProvider {
|
||||
private static final String TAG = LocationProviderV2.class.getName();
|
||||
private static final ProviderPropertiesUnbundled props = ProviderPropertiesUnbundled.create(
|
||||
false, // requiresNetwork
|
||||
false, // requiresSatellite
|
||||
false, // requiresCell
|
||||
false, // hasMonetaryCost
|
||||
true, // supportsAltitude
|
||||
true, // supportsSpeed
|
||||
true, // supportsBearing
|
||||
Criteria.POWER_LOW, // powerRequirement
|
||||
Criteria.ACCURACY_COARSE); // accuracy
|
||||
private final ThreadHelper helper;
|
||||
|
||||
public LocationProviderV2(Context context) {
|
||||
super(TAG, props);
|
||||
this.helper = new ThreadHelper(context, this);
|
||||
}
|
||||
public LocationProviderV2(Context context) {
|
||||
super(TAG, props);
|
||||
this.helper = new ThreadHelper(context, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
}
|
||||
@Override
|
||||
public void onDisable() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
helper.reload();
|
||||
}
|
||||
@Override
|
||||
public void forceLocation(Location location) {
|
||||
helper.forceLocation(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
}
|
||||
@Override
|
||||
public void reload() {
|
||||
helper.reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onGetStatus(Bundle extras) {
|
||||
return AVAILABLE;
|
||||
}
|
||||
@Override
|
||||
public void onEnable() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long onGetStatusUpdateTime() {
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int onGetStatus(Bundle extras) {
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetRequest(ProviderRequestUnbundled requests, WorkSource source) {
|
||||
Log.v(TAG, "onSetRequest: " + requests + " by " + source);
|
||||
@Override
|
||||
public long onGetStatusUpdateTime() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long autoTime = Long.MAX_VALUE;
|
||||
boolean autoUpdate = false;
|
||||
for (LocationRequestUnbundled request : requests.getLocationRequests()) {
|
||||
Log.v(TAG, "onSetRequest: request: " + request);
|
||||
if (autoTime > request.getInterval()) {
|
||||
autoTime = request.getInterval();
|
||||
}
|
||||
autoUpdate = true;
|
||||
}
|
||||
@Override
|
||||
public void onSetRequest(ProviderRequestUnbundled requests, WorkSource source) {
|
||||
Log.v(TAG, "onSetRequest: " + requests + " by " + source);
|
||||
|
||||
if (autoTime < 1500) {
|
||||
// Limit to 1.5s
|
||||
autoTime = 1500;
|
||||
}
|
||||
Log.v(TAG, "using autoUpdate=" + autoUpdate + " autoTime=" + autoTime);
|
||||
long autoTime = Long.MAX_VALUE;
|
||||
boolean autoUpdate = false;
|
||||
for (LocationRequestUnbundled request : requests.getLocationRequests()) {
|
||||
Log.v(TAG, "onSetRequest: request: " + request);
|
||||
if (autoTime > request.getInterval()) {
|
||||
autoTime = request.getInterval();
|
||||
}
|
||||
autoUpdate = true;
|
||||
}
|
||||
|
||||
if (autoTime < 1500) {
|
||||
// Limit to 1.5s
|
||||
autoTime = 1500;
|
||||
}
|
||||
Log.v(TAG, "using autoUpdate=" + autoUpdate + " autoTime=" + autoTime);
|
||||
|
||||
if (autoUpdate) {
|
||||
helper.setTime(autoTime);
|
||||
helper.enable();
|
||||
} else {
|
||||
helper.disable();
|
||||
}
|
||||
}
|
||||
|
||||
if (autoUpdate) {
|
||||
helper.setTime(autoTime);
|
||||
helper.enable();
|
||||
} else {
|
||||
helper.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,66 +1,61 @@
|
|||
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 static void reloadLocationService(Context context) {
|
||||
Intent intent = new Intent(LocationService.ACTION_RELOAD_SETTINGS);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
intent.setClass(context, LocationServiceV2.class);
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
context.startService(intent);
|
||||
}
|
||||
public abstract class LocationService extends ProviderService<LocationProvider> {
|
||||
public static void reloadLocationService(Context context) {
|
||||
Intent intent = new Intent(ACTION_RELOAD_SETTINGS);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
intent.setClass(context, LocationServiceV2.class);
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an LocationService. Invoked by your subclass's constructor.
|
||||
*
|
||||
* @param tag Used for debugging.
|
||||
*/
|
||||
public LocationService(String tag) {
|
||||
super(tag);
|
||||
}
|
||||
/**
|
||||
* Creates an LocationService. Invoked by your subclass's constructor.
|
||||
*
|
||||
* @param tag Used for debugging.
|
||||
*/
|
||||
public LocationService(String tag) {
|
||||
super(tag);
|
||||
}
|
||||
|
||||
@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 :)
|
||||
*/
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Location location = intent.getParcelableExtra("location");
|
||||
if (nlprovider != null && location != null) {
|
||||
nlprovider.reportLocation(location);
|
||||
}
|
||||
*/
|
||||
if (ACTION_RELOAD_SETTINGS.equals(intent.getAction())) {
|
||||
LocationProvider provider = getCurrentProvider();
|
||||
if (provider != null) {
|
||||
provider.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ACTION_RELOAD_SETTINGS.equals(intent.getAction())) {
|
||||
Provider provider = getCurrentProvider();
|
||||
if (provider != null) {
|
||||
provider.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUnbind(Intent intent) {
|
||||
Provider provider = getCurrentProvider();
|
||||
if (provider instanceof LocationProvider) {
|
||||
((LocationProvider) provider).onDisable();
|
||||
}
|
||||
return super.onUnbind(intent);
|
||||
}
|
||||
@Override
|
||||
public boolean onUnbind(Intent intent) {
|
||||
LocationProvider provider = getCurrentProvider();
|
||||
if (provider != null) {
|
||||
provider.onDisable();
|
||||
}
|
||||
return super.onUnbind(intent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package org.microg.nlp.location;
|
||||
|
||||
public class LocationServiceV2 extends LocationService {
|
||||
private static final String TAG = LocationServiceV2.class.getName();
|
||||
private static final String TAG = LocationServiceV2.class.getName();
|
||||
|
||||
public LocationServiceV2() {
|
||||
super(TAG);
|
||||
}
|
||||
public LocationServiceV2() {
|
||||
super(TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocationProvider createProvider() {
|
||||
return new LocationProviderV2(this);
|
||||
}
|
||||
@Override
|
||||
protected LocationProvider createProvider() {
|
||||
return new LocationProviderV2(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,78 +1,77 @@
|
|||
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 {
|
||||
private final Context context;
|
||||
private final LocationProvider locationProvider;
|
||||
private BackendHandler backendHandler;
|
||||
private ScheduledThreadPoolExecutor executor;
|
||||
private long time = 5000; // Initialize with 5s
|
||||
private boolean enabled;
|
||||
class ThreadHelper implements Runnable {
|
||||
private final Context context;
|
||||
private final LocationProvider locationProvider;
|
||||
private BackendFuser backendFuser;
|
||||
private ScheduledThreadPoolExecutor executor;
|
||||
private long time = 5000; // Initialize with 5s
|
||||
private boolean enabled;
|
||||
|
||||
public ThreadHelper(Context context, LocationProvider locationProvider) {
|
||||
this.context = context;
|
||||
this.locationProvider = locationProvider;
|
||||
updateBackendHandler();
|
||||
}
|
||||
public ThreadHelper(Context context, LocationProvider locationProvider) {
|
||||
this.context = context;
|
||||
this.locationProvider = locationProvider;
|
||||
updateBackendHandler();
|
||||
}
|
||||
|
||||
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]));
|
||||
}
|
||||
}
|
||||
backendHandler = new BackendFuser(context, backendList, locationProvider);
|
||||
}
|
||||
private void updateBackendHandler() {
|
||||
BackendFuser old = backendFuser;
|
||||
backendFuser = new BackendFuser(context, locationProvider);
|
||||
if (old != null) {
|
||||
backendFuser.forceLocation(old.getForcedLocation());
|
||||
}
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
disable();
|
||||
updateBackendHandler();
|
||||
enable();
|
||||
}
|
||||
public void forceLocation(Location location) {
|
||||
backendFuser.forceLocation(location);
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
if (executor != null) {
|
||||
executor.shutdownNow();
|
||||
executor = null;
|
||||
}
|
||||
if (enabled) {
|
||||
backendHandler.unbind();
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
public void reload() {
|
||||
disable();
|
||||
updateBackendHandler();
|
||||
enable();
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
public void disable() {
|
||||
if (executor != null) {
|
||||
executor.shutdownNow();
|
||||
executor = null;
|
||||
}
|
||||
if (enabled) {
|
||||
backendFuser.unbind();
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
if (executor != null) {
|
||||
executor.shutdownNow();
|
||||
executor = null;
|
||||
}
|
||||
executor = new ScheduledThreadPoolExecutor(1);
|
||||
executor.scheduleAtFixedRate(this, 0, time, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
if (!enabled) {
|
||||
backendHandler.bind();
|
||||
enabled = true;
|
||||
}
|
||||
reset();
|
||||
}
|
||||
public void reset() {
|
||||
if (executor != null) {
|
||||
executor.shutdownNow();
|
||||
executor = null;
|
||||
}
|
||||
executor = new ScheduledThreadPoolExecutor(1);
|
||||
executor.scheduleAtFixedRate(this, 0, time, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
backendHandler.update();
|
||||
}
|
||||
public void enable() {
|
||||
if (!enabled) {
|
||||
backendFuser.bind();
|
||||
enabled = true;
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
backendFuser.update();
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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,228 +27,235 @@ 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();
|
||||
private static final String TAG = LocationBackendConfig.class.getName();
|
||||
|
||||
private List<ServiceInfo> activeBackends;
|
||||
private Map<ServiceInfo, KnownBackend> knownBackends;
|
||||
private List<ServiceInfo> unusedBackends;
|
||||
private Adapter adapter;
|
||||
private DynamicListView listView;
|
||||
private View addButton;
|
||||
private PopupMenu popUp;
|
||||
private List<ServiceInfo> activeBackends;
|
||||
private Map<ServiceInfo, KnownBackend> knownBackends;
|
||||
private List<ServiceInfo> unusedBackends;
|
||||
private Adapter adapter;
|
||||
private DynamicListView listView;
|
||||
private View addButton;
|
||||
private PopupMenu popUp;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.pluginselection);
|
||||
listView = (DynamicListView) findViewById(android.R.id.list);
|
||||
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> view, View view2, int i, long l) {
|
||||
Log.d(TAG, "onItemClick: " + l);
|
||||
}
|
||||
});
|
||||
addButton = findViewById(android.R.id.button1);
|
||||
addButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
showAddPluginPopup(view);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.pluginselection);
|
||||
listView = (DynamicListView) findViewById(android.R.id.list);
|
||||
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> view, View view2, int i, long l) {
|
||||
Log.d(TAG, "onItemClick: " + l);
|
||||
}
|
||||
});
|
||||
addButton = findViewById(android.R.id.button1);
|
||||
addButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
showAddPluginPopup(view);
|
||||
}
|
||||
});
|
||||
|
||||
updateBackends();
|
||||
}
|
||||
updateBackends();
|
||||
}
|
||||
|
||||
private void updateBackends() {
|
||||
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);
|
||||
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("\\|")) {
|
||||
String[] parts = backend.split("/");
|
||||
if (parts.length == 2) {
|
||||
for (ServiceInfo serviceInfo : knownBackends.keySet()) {
|
||||
if (serviceInfo.packageName.equals(parts[0]) && serviceInfo.name.equals(parts[1])) {
|
||||
activeBackends.add(serviceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updateAddButton();
|
||||
resetAdapter();
|
||||
}
|
||||
private void updateBackends() {
|
||||
activeBackends = new ArrayList<ServiceInfo>();
|
||||
knownBackends = new HashMap<ServiceInfo, KnownBackend>();
|
||||
unusedBackends = new ArrayList<ServiceInfo>();
|
||||
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));
|
||||
}
|
||||
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])) {
|
||||
activeBackends.add(serviceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updateAddButton();
|
||||
resetAdapter();
|
||||
}
|
||||
|
||||
private void updateAddButton() {
|
||||
if (activeBackends.size() == knownBackends.size()) {
|
||||
if (knownBackends.isEmpty()) {
|
||||
// No backend installed
|
||||
// TODO: notify user about that
|
||||
}
|
||||
addButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
addButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
private void updateAddButton() {
|
||||
if (activeBackends.size() == knownBackends.size()) {
|
||||
if (knownBackends.isEmpty()) {
|
||||
// No backend installed
|
||||
// TODO: notify user about that
|
||||
}
|
||||
addButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
addButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetAdapter() {
|
||||
List<ServiceInfo> backends = activeBackends;
|
||||
adapter = new Adapter(backends);
|
||||
listView.setList(backends);
|
||||
listView.setAdapter(adapter);
|
||||
}
|
||||
private void resetAdapter() {
|
||||
List<ServiceInfo> backends = activeBackends;
|
||||
adapter = new Adapter(backends);
|
||||
listView.setList(backends);
|
||||
listView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void updateUnusedBackends() {
|
||||
unusedBackends.clear();
|
||||
for (KnownBackend backend : knownBackends.values()) {
|
||||
if (!activeBackends.contains(backend.serviceInfo)) {
|
||||
unusedBackends.add(backend.serviceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void updateUnusedBackends() {
|
||||
unusedBackends.clear();
|
||||
for (KnownBackend backend : knownBackends.values()) {
|
||||
if (!activeBackends.contains(backend.serviceInfo)) {
|
||||
unusedBackends.add(backend.serviceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enableBackend(ServiceInfo serviceInfo) {
|
||||
activeBackends.add(serviceInfo);
|
||||
onBackendsChanged();
|
||||
}
|
||||
private void enableBackend(ServiceInfo serviceInfo) {
|
||||
activeBackends.add(serviceInfo);
|
||||
onBackendsChanged();
|
||||
}
|
||||
|
||||
private void disabledBackend(ServiceInfo serviceInfo) {
|
||||
activeBackends.remove(serviceInfo);
|
||||
onBackendsChanged();
|
||||
}
|
||||
private void disabledBackend(ServiceInfo serviceInfo) {
|
||||
activeBackends.remove(serviceInfo);
|
||||
onBackendsChanged();
|
||||
}
|
||||
|
||||
private void onBackendsChanged() {
|
||||
updateAddButton();
|
||||
resetAdapter();
|
||||
getSharedPreferences("config", MODE_PRIVATE).edit().putString("location_backends", backendString(activeBackends)).commit();
|
||||
LocationService.reloadLocationService(this);
|
||||
}
|
||||
private void onBackendsChanged() {
|
||||
updateAddButton();
|
||||
resetAdapter();
|
||||
new Preferences(this).setLocationBackends(backendString(activeBackends));
|
||||
LocationService.reloadLocationService(this);
|
||||
}
|
||||
|
||||
private String backendString(List<ServiceInfo> backends) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (ServiceInfo backend : backends) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append("|");
|
||||
}
|
||||
sb.append(backend.packageName).append("/").append(backend.name);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
private String backendString(List<ServiceInfo> backends) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (ServiceInfo backend : backends) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append("|");
|
||||
}
|
||||
sb.append(backend.packageName).append("/").append(backend.name);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void showAddPluginPopup(View anchorView) {
|
||||
updateUnusedBackends();
|
||||
private void showAddPluginPopup(View anchorView) {
|
||||
updateUnusedBackends();
|
||||
|
||||
if (popUp != null) {
|
||||
popUp.dismiss();
|
||||
}
|
||||
popUp = new PopupMenu(this, anchorView);
|
||||
if (popUp != null) {
|
||||
popUp.dismiss();
|
||||
}
|
||||
popUp = new PopupMenu(this, anchorView);
|
||||
|
||||
for (int i = 0; i < unusedBackends.size(); i++) {
|
||||
KnownBackend backend = knownBackends.get(unusedBackends.get(i));
|
||||
String label = backend.simpleName;
|
||||
if (TextUtils.isEmpty(label)) {
|
||||
label = backend.serviceInfo.name;
|
||||
}
|
||||
popUp.getMenu().add(Menu.NONE, i, Menu.NONE, label);
|
||||
}
|
||||
popUp.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
popUp.dismiss();
|
||||
popUp = null;
|
||||
for (int i = 0; i < unusedBackends.size(); i++) {
|
||||
KnownBackend backend = knownBackends.get(unusedBackends.get(i));
|
||||
String label = backend.simpleName;
|
||||
if (TextUtils.isEmpty(label)) {
|
||||
label = backend.serviceInfo.name;
|
||||
}
|
||||
popUp.getMenu().add(Menu.NONE, i, Menu.NONE, label);
|
||||
}
|
||||
popUp.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
popUp.dismiss();
|
||||
popUp = null;
|
||||
|
||||
enableBackend(unusedBackends.get(menuItem.getItemId()));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
popUp.show();
|
||||
}
|
||||
enableBackend(unusedBackends.get(menuItem.getItemId()));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
popUp.show();
|
||||
}
|
||||
|
||||
private Intent createSettingsIntent(ComponentInfo componentInfo) {
|
||||
Intent settingsIntent = new Intent(Intent.ACTION_VIEW);
|
||||
settingsIntent.setPackage(componentInfo.packageName);
|
||||
settingsIntent.setClassName(componentInfo.packageName,
|
||||
componentInfo.metaData.getString(NlpApiConstants.METADATA_BACKEND_SETTINGS_ACTIVITY));
|
||||
return settingsIntent;
|
||||
}
|
||||
private Intent createSettingsIntent(ComponentInfo componentInfo) {
|
||||
Intent settingsIntent = new Intent(Intent.ACTION_VIEW);
|
||||
settingsIntent.setPackage(componentInfo.packageName);
|
||||
settingsIntent.setClassName(componentInfo.packageName,
|
||||
componentInfo.metaData.getString(METADATA_BACKEND_SETTINGS_ACTIVITY));
|
||||
return settingsIntent;
|
||||
}
|
||||
|
||||
private void showSettingsPopup(View anchorView, final KnownBackend backend) {
|
||||
if (popUp != null) {
|
||||
popUp.dismiss();
|
||||
}
|
||||
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) {
|
||||
popUp.getMenu().add(Menu.NONE, 1, Menu.NONE, "Settings"); // TODO label
|
||||
}
|
||||
}
|
||||
popUp.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
popUp.dismiss();
|
||||
popUp = null;
|
||||
private void showSettingsPopup(View anchorView, final KnownBackend backend) {
|
||||
if (popUp != null) {
|
||||
popUp.dismiss();
|
||||
}
|
||||
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(METADATA_BACKEND_SETTINGS_ACTIVITY) !=
|
||||
null) {
|
||||
if (getPackageManager().resolveActivity(createSettingsIntent(backend.serviceInfo), 0) !=
|
||||
null) {
|
||||
popUp.getMenu().add(Menu.NONE, 1, Menu.NONE, "Settings"); // TODO label
|
||||
}
|
||||
}
|
||||
popUp.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
popUp.dismiss();
|
||||
popUp = null;
|
||||
|
||||
if (item.getItemId() == 0) {
|
||||
disabledBackend(backend.serviceInfo);
|
||||
} else if (item.getItemId() == 1) {
|
||||
startActivity(createSettingsIntent(backend.serviceInfo));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
popUp.show();
|
||||
}
|
||||
if (item.getItemId() == 0) {
|
||||
disabledBackend(backend.serviceInfo);
|
||||
} else if (item.getItemId() == 1) {
|
||||
startActivity(createSettingsIntent(backend.serviceInfo));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
popUp.show();
|
||||
}
|
||||
|
||||
private class Adapter extends DynamicListView.StableArrayAdapter {
|
||||
public Adapter(List<ServiceInfo> backends) {
|
||||
super(LocationBackendConfig.this, R.layout.backend_list_entry, android.R.id.text2, backends);
|
||||
}
|
||||
private class Adapter extends DynamicListView.StableArrayAdapter {
|
||||
public Adapter(List<ServiceInfo> backends) {
|
||||
super(LocationBackendConfig.this, R.layout.backend_list_entry, android.R.id.text2,
|
||||
backends);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v = super.getView(position, convertView, parent);
|
||||
final KnownBackend backend = knownBackends.get(getItem(position));
|
||||
ImageView icon = (ImageView) v.findViewById(android.R.id.icon);
|
||||
icon.setImageDrawable(backend.icon);
|
||||
TextView title = (TextView) v.findViewById(android.R.id.text1);
|
||||
title.setText(backend.simpleName);
|
||||
TextView subtitle = (TextView) v.findViewById(android.R.id.text2);
|
||||
subtitle.setText(backend.serviceInfo.name);
|
||||
View overflow = v.findViewById(android.R.id.button1);
|
||||
overflow.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
showSettingsPopup(view, backend);
|
||||
}
|
||||
});
|
||||
return v;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v = super.getView(position, convertView, parent);
|
||||
final KnownBackend backend = knownBackends.get(getItem(position));
|
||||
ImageView icon = (ImageView) v.findViewById(android.R.id.icon);
|
||||
icon.setImageDrawable(backend.icon);
|
||||
TextView title = (TextView) v.findViewById(android.R.id.text1);
|
||||
title.setText(backend.simpleName);
|
||||
TextView subtitle = (TextView) v.findViewById(android.R.id.text2);
|
||||
subtitle.setText(backend.serviceInfo.name);
|
||||
View overflow = v.findViewById(android.R.id.button1);
|
||||
overflow.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
showSettingsPopup(view, backend);
|
||||
}
|
||||
});
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
private class KnownBackend {
|
||||
private ServiceInfo serviceInfo;
|
||||
private String simpleName;
|
||||
private Drawable icon;
|
||||
private class KnownBackend {
|
||||
private ServiceInfo serviceInfo;
|
||||
private String simpleName;
|
||||
private Drawable icon;
|
||||
|
||||
private KnownBackend(ServiceInfo serviceInfo, String simpleName, Drawable icon) {
|
||||
this.serviceInfo = serviceInfo;
|
||||
this.simpleName = simpleName;
|
||||
this.icon = icon;
|
||||
}
|
||||
private KnownBackend(ServiceInfo serviceInfo, String simpleName, Drawable icon) {
|
||||
this.serviceInfo = serviceInfo;
|
||||
this.simpleName = simpleName;
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return simpleName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return simpleName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue