mirror of
https://github.com/YTVanced/VancedMicroG
synced 2024-11-24 20:25:14 +00:00
Add basic support for location API (request and last location)
This commit is contained in:
parent
c57656d321
commit
3e573917ba
7 changed files with 337 additions and 43 deletions
|
@ -30,6 +30,8 @@ import static org.microg.gms.maps.Constants.ACTION_GMS_LOCATION_MANAGER_SERVICE_
|
|||
|
||||
public class GoogleLocationManagerService extends Service {
|
||||
private static final String TAG = "GmsLMS";
|
||||
|
||||
private GoogleLocationManagerServiceImpl impl = new GoogleLocationManagerServiceImpl(this);
|
||||
private AbstractGmsServiceBroker broker = new AbstractGmsServiceBroker() {
|
||||
@Override
|
||||
public void getGoogleLocationManagerService(IGmsCallbacks callback, int versionCode,
|
||||
|
@ -38,7 +40,6 @@ public class GoogleLocationManagerService extends Service {
|
|||
callback.onPostInitComplete(0, impl.asBinder(), null);
|
||||
}
|
||||
};
|
||||
private GoogleLocationManagerServiceImpl impl = new GoogleLocationManagerServiceImpl(this);
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
|
|
|
@ -6,58 +6,64 @@ import android.content.Context;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import com.google.android.gms.location.ILocationListener;
|
||||
import com.google.android.gms.location.LocationRequest;
|
||||
import com.google.android.gms.location.internal.ILocationListener;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static android.location.LocationManager.GPS_PROVIDER;
|
||||
import static android.location.LocationManager.NETWORK_PROVIDER;
|
||||
import static org.microg.gms.maps.Constants.KEY_MOCK_LOCATION;
|
||||
|
||||
public class GoogleLocationManager {
|
||||
private static final String MOCK_PROVIDER = KEY_MOCK_LOCATION;
|
||||
public class GoogleLocationManager implements LocationChangeListener {
|
||||
private static final String MOCK_PROVIDER = "mock";
|
||||
|
||||
private Context context;
|
||||
private LocationManager locationManager;
|
||||
private Map<String, Location> lastKnownLocaton = new HashMap<>();
|
||||
private RealLocationProvider gpsProvider;
|
||||
private RealLocationProvider networkProvider;
|
||||
private MockLocationProvider mockProvider;
|
||||
private List<LocationRequestHelper> currentRequests = new ArrayList<>();
|
||||
|
||||
public GoogleLocationManager(Context context) {
|
||||
this.context = context;
|
||||
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
updateLastKnownLocation();
|
||||
}
|
||||
|
||||
private void updateLastKnownLocation() {
|
||||
lastKnownLocaton.put(GPS_PROVIDER, locationManager.getLastKnownLocation(GPS_PROVIDER));
|
||||
lastKnownLocaton.put(NETWORK_PROVIDER,
|
||||
locationManager.getLastKnownLocation(NETWORK_PROVIDER));
|
||||
gpsProvider = new RealLocationProvider(locationManager, GPS_PROVIDER, this);
|
||||
networkProvider = new RealLocationProvider(locationManager, NETWORK_PROVIDER, this);
|
||||
mockProvider = new MockLocationProvider(this);
|
||||
}
|
||||
|
||||
public Location getLastLocation(String packageName) {
|
||||
if (lastKnownLocaton.get(KEY_MOCK_LOCATION) != null)
|
||||
return lastKnownLocaton.get(KEY_MOCK_LOCATION);
|
||||
if (hasFineLocationPermission()) {
|
||||
Location network = lastKnownLocaton.get(NETWORK_PROVIDER);
|
||||
Location gps = lastKnownLocaton.get(GPS_PROVIDER);
|
||||
return getLocation(hasFineLocationPermission(), hasCoarseLocationPermission());
|
||||
}
|
||||
|
||||
public Location getLocation(boolean gpsPermission, boolean networkPermission) {
|
||||
if (mockProvider.getLocation() != null)
|
||||
return mockProvider.getLocation();
|
||||
if (gpsPermission) {
|
||||
Location network = networkProvider.getLastLocation();
|
||||
Location gps = gpsProvider.getLastLocation();
|
||||
if (network == null)
|
||||
return gps;
|
||||
if (gps == null)
|
||||
return network;
|
||||
if (gps.getTime() > network.getTime())
|
||||
if (gps.getTime() > network.getTime() - 10000)
|
||||
return gps;
|
||||
return network;
|
||||
} else if (hasCoarseLocationPermission()) {
|
||||
return lastKnownLocaton.get(NETWORK_PROVIDER);
|
||||
} else if (networkPermission) {
|
||||
Location network = networkProvider.getLastLocation();
|
||||
if (network.getExtras() != null &&
|
||||
network.getExtras().getParcelable("no_gps_location") instanceof Location) {
|
||||
network = network.getExtras().getParcelable("no_gps_location");
|
||||
}
|
||||
return network;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean hasCoarseLocationPermission() {
|
||||
return context.checkCallingPermission(Manifest.permission.ACCESS_COARSE_LOCATION) ==
|
||||
PackageManager.PERMISSION_GRANTED;
|
||||
PackageManager.PERMISSION_GRANTED || hasFineLocationPermission();
|
||||
}
|
||||
|
||||
private boolean hasFineLocationPermission() {
|
||||
|
@ -70,37 +76,75 @@ public class GoogleLocationManager {
|
|||
PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
private void requestLocationUpdates(LocationRequestHelper request) {
|
||||
currentRequests.add(request);
|
||||
if (request.hasFinePermission &&
|
||||
request.locationRequest.getPriority() == LocationRequest.PRIORITY_HIGH_ACCURACY)
|
||||
gpsProvider.addRequest(request);
|
||||
if (request.hasCoarsePermission &&
|
||||
request.locationRequest.getPriority() != LocationRequest.PRIORITY_NO_POWER)
|
||||
networkProvider.addRequest(request);
|
||||
}
|
||||
|
||||
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
|
||||
String packageName) {
|
||||
|
||||
requestLocationUpdates(
|
||||
new LocationRequestHelper(context, request, hasFineLocationPermission(),
|
||||
hasCoarseLocationPermission(), packageName, listener));
|
||||
}
|
||||
|
||||
public void requestLocationUpdates(LocationRequest request, PendingIntent intent) {
|
||||
|
||||
public void requestLocationUpdates(LocationRequest request, PendingIntent intent,
|
||||
String packageName) {
|
||||
requestLocationUpdates(
|
||||
new LocationRequestHelper(context, request, hasFineLocationPermission(),
|
||||
hasCoarseLocationPermission(), packageName, intent));
|
||||
}
|
||||
|
||||
public void removeLocationUpdates(ILocationListener listener) {
|
||||
|
||||
private void removeLocationUpdates(LocationRequestHelper request) {
|
||||
currentRequests.remove(request);
|
||||
gpsProvider.removeRequest(request);
|
||||
networkProvider.removeRequest(request);
|
||||
}
|
||||
|
||||
public void removeLocationUpdates(PendingIntent intent) {
|
||||
public void removeLocationUpdates(ILocationListener listener, String packageName) {
|
||||
for (int i = 0; i < currentRequests.size(); i++) {
|
||||
if (currentRequests.get(i).respondsTo(listener)) {
|
||||
removeLocationUpdates(currentRequests.get(i));
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeLocationUpdates(PendingIntent intent, String packageName) {
|
||||
for (int i = 0; i < currentRequests.size(); i++) {
|
||||
if (currentRequests.get(i).respondsTo(intent)) {
|
||||
removeLocationUpdates(currentRequests.get(i));
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setMockMode(boolean mockMode) {
|
||||
if (!hasMockLocationPermission())
|
||||
return;
|
||||
if (!mockMode)
|
||||
lastKnownLocaton.put(MOCK_PROVIDER, null);
|
||||
mockProvider.setMockEnabled(mockMode);
|
||||
}
|
||||
|
||||
public void setMockLocation(Location mockLocation) {
|
||||
if (!hasMockLocationPermission())
|
||||
return;
|
||||
if (mockLocation.getExtras() == null) {
|
||||
mockLocation.setExtras(new Bundle());
|
||||
mockProvider.setLocation(mockLocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged() {
|
||||
for (int i = 0; i < currentRequests.size(); i++) {
|
||||
LocationRequestHelper request = currentRequests.get(i);
|
||||
if (!request
|
||||
.report(getLocation(request.hasFinePermission, request.hasCoarsePermission))) {
|
||||
removeLocationUpdates(request);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
mockLocation.getExtras().putBoolean(KEY_MOCK_LOCATION, false);
|
||||
lastKnownLocaton.put(MOCK_PROVIDER, mockLocation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import com.google.android.gms.location.LocationRequest;
|
|||
import com.google.android.gms.location.LocationStatus;
|
||||
import com.google.android.gms.location.internal.IGeofencerCallbacks;
|
||||
import com.google.android.gms.location.internal.IGoogleLocationManagerService;
|
||||
import com.google.android.gms.location.internal.ILocationListener;
|
||||
import com.google.android.gms.location.ILocationListener;
|
||||
import com.google.android.gms.location.internal.LocationRequestInternal;
|
||||
import com.google.android.gms.location.places.*;
|
||||
import com.google.android.gms.location.places.internal.IPlacesCallbacks;
|
||||
|
@ -89,21 +89,21 @@ public class GoogleLocationManagerServiceImpl extends IGoogleLocationManagerServ
|
|||
public void requestLocationUpdatesWithIntent(LocationRequest request,
|
||||
PendingIntent callbackIntent) throws RemoteException {
|
||||
Log.d(TAG, "requestLocationUpdatesWithIntent: " + request);
|
||||
getLocationManager().requestLocationUpdates(request, callbackIntent);
|
||||
getLocationManager().requestLocationUpdates(request, callbackIntent, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLocationUpdatesWithListener(ILocationListener listener)
|
||||
throws RemoteException {
|
||||
Log.d(TAG, "removeLocationUpdatesWithListener: " + listener);
|
||||
getLocationManager().removeLocationUpdates(listener);
|
||||
getLocationManager().removeLocationUpdates(listener, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLocationUpdatesWithIntent(PendingIntent callbackIntent)
|
||||
throws RemoteException {
|
||||
Log.d(TAG, "removeLocationUpdatesWithIntent: " + callbackIntent);
|
||||
getLocationManager().removeLocationUpdates(callbackIntent);
|
||||
getLocationManager().removeLocationUpdates(callbackIntent, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -154,9 +154,9 @@ public class GoogleLocationManagerServiceImpl extends IGoogleLocationManagerServ
|
|||
}
|
||||
|
||||
@Override
|
||||
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
|
||||
public void requestLocationUpdatesWithPackage(LocationRequest request, ILocationListener listener,
|
||||
String packageName) throws RemoteException {
|
||||
Log.d(TAG, "requestLocationUpdates: " + request);
|
||||
Log.d(TAG, "requestLocationUpdatesWithPackage: " + request);
|
||||
getLocationManager().requestLocationUpdates(request, listener, packageName);
|
||||
}
|
||||
|
||||
|
|
5
src/org/microg/gms/location/LocationChangeListener.java
Normal file
5
src/org/microg/gms/location/LocationChangeListener.java
Normal file
|
@ -0,0 +1,5 @@
|
|||
package org.microg.gms.location;
|
||||
|
||||
public interface LocationChangeListener {
|
||||
public void onLocationChanged();
|
||||
}
|
102
src/org/microg/gms/location/LocationRequestHelper.java
Normal file
102
src/org/microg/gms/location/LocationRequestHelper.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
package org.microg.gms.location;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import com.google.android.gms.location.ILocationListener;
|
||||
import com.google.android.gms.location.LocationRequest;
|
||||
|
||||
public class LocationRequestHelper {
|
||||
public static final String TAG = "GmsLocationRequestHelper";
|
||||
private final Context context;
|
||||
public final LocationRequest locationRequest;
|
||||
public final boolean hasFinePermission;
|
||||
public final boolean hasCoarsePermission;
|
||||
public final String packageName;
|
||||
private ILocationListener listener;
|
||||
private PendingIntent pendingIntent;
|
||||
|
||||
private Location lastReport;
|
||||
private int numReports = 0;
|
||||
|
||||
public LocationRequestHelper(Context context, LocationRequest locationRequest,
|
||||
boolean hasFinePermission, boolean hasCoarsePermission, String packageName,
|
||||
ILocationListener listener) {
|
||||
this.context = context;
|
||||
this.locationRequest = locationRequest;
|
||||
this.hasFinePermission = hasFinePermission;
|
||||
this.hasCoarsePermission = hasCoarsePermission;
|
||||
this.packageName = packageName;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public LocationRequestHelper(Context context, LocationRequest locationRequest,
|
||||
boolean hasFinePermission, boolean hasCoarsePermission, String packageName,
|
||||
PendingIntent pendingIntent) {
|
||||
this.context = context;
|
||||
this.locationRequest = locationRequest;
|
||||
this.hasFinePermission = hasFinePermission;
|
||||
this.hasCoarsePermission = hasCoarsePermission;
|
||||
this.packageName = packageName;
|
||||
this.pendingIntent = pendingIntent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether to continue sending reports to this {@link LocationRequestHelper}
|
||||
*/
|
||||
public boolean report(Location location) {
|
||||
if (lastReport != null) {
|
||||
if (location.getTime() - lastReport.getTime() < locationRequest.getFastestInterval()) {
|
||||
return true;
|
||||
}
|
||||
if (location.distanceTo(lastReport) < locationRequest.getSmallestDesplacement()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "sending Location: " + location);
|
||||
if (listener != null) {
|
||||
try {
|
||||
listener.onLocationChanged(location);
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
} else if (pendingIntent != null) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra("com.google.android.location.LOCATION", location);
|
||||
try {
|
||||
pendingIntent.send(context, 0, intent);
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
lastReport = location;
|
||||
numReports++;
|
||||
if (numReports >= locationRequest.getNumUpdates()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LocationRequestHelper{" +
|
||||
"locationRequest=" + locationRequest +
|
||||
", hasFinePermission=" + hasFinePermission +
|
||||
", hasCoarsePermission=" + hasCoarsePermission +
|
||||
", packageName='" + packageName + '\'' +
|
||||
", lastReport=" + lastReport +
|
||||
'}';
|
||||
}
|
||||
|
||||
public boolean respondsTo(ILocationListener listener) {
|
||||
return this.listener != null && listener != null &&
|
||||
this.listener.asBinder().equals(listener.asBinder());
|
||||
}
|
||||
|
||||
public boolean respondsTo(PendingIntent pendingIntent) {
|
||||
return this.pendingIntent != null && this.pendingIntent.equals(pendingIntent);
|
||||
}
|
||||
}
|
32
src/org/microg/gms/location/MockLocationProvider.java
Normal file
32
src/org/microg/gms/location/MockLocationProvider.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package org.microg.gms.location;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
|
||||
import static org.microg.gms.maps.Constants.KEY_MOCK_LOCATION;
|
||||
|
||||
public class MockLocationProvider {
|
||||
private boolean mockEnabled = false;
|
||||
private Location mockLocation = null;
|
||||
private final LocationChangeListener changeListener;
|
||||
|
||||
public MockLocationProvider(LocationChangeListener changeListener) {
|
||||
this.changeListener = changeListener;
|
||||
}
|
||||
|
||||
public void setMockEnabled(boolean mockEnabled) {
|
||||
this.mockEnabled = mockEnabled;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return mockEnabled ? mockLocation : null;
|
||||
}
|
||||
|
||||
public void setLocation(Location mockLocation) {
|
||||
if (mockLocation.getExtras() == null) {
|
||||
mockLocation.setExtras(new Bundle());
|
||||
}
|
||||
mockLocation.getExtras().putBoolean(KEY_MOCK_LOCATION, false);
|
||||
this.mockLocation = mockLocation;
|
||||
}
|
||||
}
|
110
src/org/microg/gms/location/RealLocationProvider.java
Normal file
110
src/org/microg/gms/location/RealLocationProvider.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
package org.microg.gms.location;
|
||||
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class RealLocationProvider {
|
||||
|
||||
public static final String TAG = "GmsRealLocationProvider";
|
||||
private Location lastLocation;
|
||||
private LocationManager locationManager;
|
||||
private String name;
|
||||
private final AtomicBoolean connected = new AtomicBoolean(false);
|
||||
private long connectedMinTime;
|
||||
private float connectedMinDistance;
|
||||
private List<LocationRequestHelper> requests = new ArrayList<>();
|
||||
private final LocationChangeListener changeListener;
|
||||
private LocationListener listener = new LocationListener() {
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
lastLocation = location;
|
||||
changeListener.onLocationChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String s, int i, Bundle bundle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String s) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
public RealLocationProvider(LocationManager locationManager, String name,
|
||||
LocationChangeListener changeListener) {
|
||||
this.locationManager = locationManager;
|
||||
this.name = name;
|
||||
this.changeListener = changeListener;
|
||||
updateLastLocation();
|
||||
}
|
||||
|
||||
private void updateLastLocation() {
|
||||
lastLocation = locationManager.getLastKnownLocation(name);
|
||||
}
|
||||
|
||||
public Location getLastLocation() {
|
||||
if (!connected.get()) {
|
||||
updateLastLocation();
|
||||
}
|
||||
return lastLocation;
|
||||
}
|
||||
|
||||
public void addRequest(LocationRequestHelper request) {
|
||||
Log.d(TAG, name + ": addRequest " + request);
|
||||
requests.add(request);
|
||||
updateConnection();
|
||||
}
|
||||
|
||||
public void removeRequest(LocationRequestHelper request) {
|
||||
Log.d(TAG, name + ": removeRequest " + request);
|
||||
requests.remove(request);
|
||||
updateConnection();
|
||||
}
|
||||
|
||||
private synchronized void updateConnection() {
|
||||
if (connected.get() && requests.isEmpty()) {
|
||||
Log.d(TAG, name + ": no longer requesting location update");
|
||||
locationManager.removeUpdates(listener);
|
||||
connected.set(false);
|
||||
} else if (!requests.isEmpty()) {
|
||||
long minTime = Long.MAX_VALUE;
|
||||
float minDistance = Float.MAX_VALUE;
|
||||
for (LocationRequestHelper request : requests) {
|
||||
minTime = Math.min(request.locationRequest.getInterval(), minTime);
|
||||
minDistance = Math
|
||||
.min(request.locationRequest.getSmallestDesplacement(), minDistance);
|
||||
}
|
||||
if (connected.get()) {
|
||||
if (connectedMinTime != minTime || connectedMinDistance != minDistance) {
|
||||
locationManager.removeUpdates(listener);
|
||||
locationManager.requestLocationUpdates(name, minTime, minDistance, listener,
|
||||
Looper.getMainLooper());
|
||||
}
|
||||
} else {
|
||||
locationManager.requestLocationUpdates(name, minTime, minDistance, listener,
|
||||
Looper.getMainLooper());
|
||||
}
|
||||
Log.d(TAG,
|
||||
name + ": requesting location updates. minTime=" + minTime + " minDistance=" +
|
||||
minDistance);
|
||||
connected.set(true);
|
||||
connectedMinTime = minTime;
|
||||
connectedMinDistance = minDistance;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue