VancedMicroG/play-services-location-core/src/main/java/org/microg/gms/location/RealLocationProvider.java

168 lines
6.2 KiB
Java

/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@SuppressWarnings("MissingPermission")
public class RealLocationProvider {
public static final String TAG = "GmsLocProviderReal";
private static final int MIN_GPS_TIME = 10000;
private final LocationManager locationManager;
private final String name;
private final AtomicBoolean connected = new AtomicBoolean(false);
private final LocationChangeListener changeListener;
private long connectedMinTime;
private float connectedMinDistance;
private Location lastLocation;
private final List<LocationRequestHelper> requests = new ArrayList<LocationRequestHelper>();
private LocationListener listener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
lastLocation = new Location(location);
try {
lastLocation.getExtras().keySet(); // call to unparcel()
} catch (Exception e) {
// Sometimes we need to define the correct ClassLoader before unparcel(). Ignore those.
}
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() {
Location newLocation = locationManager.getLastKnownLocation(name);
if (newLocation != null) lastLocation = newLocation;
}
public void invokeOnceReady(Runnable runnable) {
// Always ready
runnable.run();
}
public Location getLastLocation() {
if (!connected.get()) {
updateLastLocation();
}
if (lastLocation == null) {
Log.d(TAG, "uh-ok: last location for " + name + " is null!");
}
return lastLocation;
}
public void addRequest(LocationRequestHelper request) {
Log.d(TAG, name + ": addRequest " + request);
for (int i = 0; i < requests.size(); i++) {
LocationRequestHelper req = requests.get(i);
if (req.respondsTo(request.pendingIntent) || req.respondsTo(request.listener) || req.respondsTo(request.callback)) {
requests.remove(i);
i--;
}
}
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;
StringBuilder sb = new StringBuilder();
for (LocationRequestHelper request : requests) {
minTime = Math.min(request.locationRequest.getInterval(), minTime);
minDistance = Math.min(request.locationRequest.getSmallestDisplacement(), minDistance);
if (sb.length() != 0) sb.append(", ");
sb.append(request.packageName).append(":").append(request.locationRequest.getInterval()).append("ms");
}
if (minTime > MIN_GPS_TIME && name.equals("gps")) {
Log.d(TAG, name + ": ignoring request as " + minTime + "ms (" + sb + "), is less than " + MIN_GPS_TIME);
locationManager.removeUpdates(listener);
connected.set(false);
return;
}
Log.d(TAG, name + ": requesting location updates with interval " + minTime + "ms (" + sb + "), minDistance=" + 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());
}
connected.set(true);
connectedMinTime = minTime;
connectedMinDistance = minDistance;
}
}
public void dump(PrintWriter writer) {
if (writer != null) {
writer.println(name + " provider:");
writer.println(" last location: " + lastLocation);
writer.println(" active: " + connected.get());
if (connected.get()) {
writer.println(" interval: " + connectedMinTime);
writer.println(" distance: " + connectedMinDistance);
}
}
}
}