Change-Id: I3dbb40210d87708e0bff46729f707d4ab8e29e42 Signed-off-by: Mike Lockwood <lockwood@android.com>
1417 lines
54 KiB
Java
1417 lines
54 KiB
Java
/*
|
|
* Copyright (C) 2007 The Android Open Source Project
|
|
*
|
|
* 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 android.location;
|
|
|
|
import android.app.PendingIntent;
|
|
import android.content.Intent;
|
|
import android.os.Bundle;
|
|
import android.os.Looper;
|
|
import android.os.RemoteException;
|
|
import android.os.Handler;
|
|
import android.os.Message;
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.location.DummyLocationProvider;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* This class provides access to the system location services. These
|
|
* services allow applications to obtain periodic updates of the
|
|
* device's geographical location, or to fire an application-specified
|
|
* {@link Intent} when the device enters the proximity of a given
|
|
* geographical location.
|
|
*
|
|
* <p>You do not
|
|
* instantiate this class directly; instead, retrieve it through
|
|
* {@link android.content.Context#getSystemService
|
|
* Context.getSystemService(Context.LOCATION_SERVICE)}.
|
|
*/
|
|
public class LocationManager {
|
|
private static final String TAG = "LocationManager";
|
|
private ILocationManager mService;
|
|
private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
|
|
new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
|
|
private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
|
|
new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
|
|
private final GpsStatus mGpsStatus = new GpsStatus();
|
|
|
|
/**
|
|
* Name of the network location provider. This provider determines location based on
|
|
* availability of cell tower and WiFi access points. Results are retrieved
|
|
* by means of a network lookup.
|
|
*
|
|
* Requires either of the permissions android.permission.ACCESS_COARSE_LOCATION
|
|
* or android.permission.ACCESS_FINE_LOCATION.
|
|
*/
|
|
public static final String NETWORK_PROVIDER = "network";
|
|
|
|
/**
|
|
* Name of the GPS location provider. This provider determines location using
|
|
* satellites. Depending on conditions, this provider may take a while to return
|
|
* a location fix.
|
|
*
|
|
* Requires the permission android.permission.ACCESS_FINE_LOCATION.
|
|
*
|
|
* <p> The extras Bundle for the GPS location provider can contain the
|
|
* following key/value pairs:
|
|
*
|
|
* <ul>
|
|
* <li> satellites - the number of satellites used to derive the fix
|
|
* </ul>
|
|
*/
|
|
public static final String GPS_PROVIDER = "gps";
|
|
|
|
/**
|
|
* A special location provider for receiving locations without actually initiating
|
|
* a location fix. This provider can be used to passively receive location updates
|
|
* when other applications or services request them without actually requesting
|
|
* the locations yourself. This provider will return locations generated by other
|
|
* providers. You can query the {@link Location#getProvider()} method to determine
|
|
* the origin of the location update.
|
|
*
|
|
* Requires the permission android.permission.ACCESS_FINE_LOCATION, although if the GPS
|
|
* is not enabled this provider might only return coarse fixes.
|
|
*/
|
|
public static final String PASSIVE_PROVIDER = "passive";
|
|
|
|
/**
|
|
* Key used for the Bundle extra holding a boolean indicating whether
|
|
* a proximity alert is entering (true) or exiting (false)..
|
|
*/
|
|
public static final String KEY_PROXIMITY_ENTERING = "entering";
|
|
|
|
/**
|
|
* Key used for a Bundle extra holding an Integer status value
|
|
* when a status change is broadcast using a PendingIntent.
|
|
*/
|
|
public static final String KEY_STATUS_CHANGED = "status";
|
|
|
|
/**
|
|
* Key used for a Bundle extra holding an Boolean status value
|
|
* when a provider enabled/disabled event is broadcast using a PendingIntent.
|
|
*/
|
|
public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
|
|
|
|
/**
|
|
* Key used for a Bundle extra holding a Location value
|
|
* when a location change is broadcast using a PendingIntent.
|
|
*/
|
|
public static final String KEY_LOCATION_CHANGED = "location";
|
|
|
|
/**
|
|
* Broadcast intent action indicating that the GPS has either been
|
|
* enabled or disabled. An intent extra provides this state as a boolean,
|
|
* where {@code true} means enabled.
|
|
* @see #EXTRA_GPS_ENABLED
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public static final String GPS_ENABLED_CHANGE_ACTION =
|
|
"android.location.GPS_ENABLED_CHANGE";
|
|
|
|
/**
|
|
* Broadcast intent action indicating that the GPS has either started or
|
|
* stopped receiving GPS fixes. An intent extra provides this state as a
|
|
* boolean, where {@code true} means that the GPS is actively receiving fixes.
|
|
* @see #EXTRA_GPS_ENABLED
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public static final String GPS_FIX_CHANGE_ACTION =
|
|
"android.location.GPS_FIX_CHANGE";
|
|
|
|
/**
|
|
* The lookup key for a boolean that indicates whether GPS is enabled or
|
|
* disabled. {@code true} means GPS is enabled. Retrieve it with
|
|
* {@link android.content.Intent#getBooleanExtra(String,boolean)}.
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public static final String EXTRA_GPS_ENABLED = "enabled";
|
|
|
|
// Map from LocationListeners to their associated ListenerTransport objects
|
|
private HashMap<LocationListener,ListenerTransport> mListeners =
|
|
new HashMap<LocationListener,ListenerTransport>();
|
|
|
|
private class ListenerTransport extends ILocationListener.Stub {
|
|
private static final int TYPE_LOCATION_CHANGED = 1;
|
|
private static final int TYPE_STATUS_CHANGED = 2;
|
|
private static final int TYPE_PROVIDER_ENABLED = 3;
|
|
private static final int TYPE_PROVIDER_DISABLED = 4;
|
|
|
|
private LocationListener mListener;
|
|
private final Handler mListenerHandler;
|
|
|
|
ListenerTransport(LocationListener listener, Looper looper) {
|
|
mListener = listener;
|
|
|
|
if (looper == null) {
|
|
mListenerHandler = new Handler() {
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
_handleMessage(msg);
|
|
}
|
|
};
|
|
} else {
|
|
mListenerHandler = new Handler(looper) {
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
_handleMessage(msg);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
public void onLocationChanged(Location location) {
|
|
Message msg = Message.obtain();
|
|
msg.what = TYPE_LOCATION_CHANGED;
|
|
msg.obj = location;
|
|
mListenerHandler.sendMessage(msg);
|
|
}
|
|
|
|
public void onStatusChanged(String provider, int status, Bundle extras) {
|
|
Message msg = Message.obtain();
|
|
msg.what = TYPE_STATUS_CHANGED;
|
|
Bundle b = new Bundle();
|
|
b.putString("provider", provider);
|
|
b.putInt("status", status);
|
|
if (extras != null) {
|
|
b.putBundle("extras", extras);
|
|
}
|
|
msg.obj = b;
|
|
mListenerHandler.sendMessage(msg);
|
|
}
|
|
|
|
public void onProviderEnabled(String provider) {
|
|
Message msg = Message.obtain();
|
|
msg.what = TYPE_PROVIDER_ENABLED;
|
|
msg.obj = provider;
|
|
mListenerHandler.sendMessage(msg);
|
|
}
|
|
|
|
public void onProviderDisabled(String provider) {
|
|
Message msg = Message.obtain();
|
|
msg.what = TYPE_PROVIDER_DISABLED;
|
|
msg.obj = provider;
|
|
mListenerHandler.sendMessage(msg);
|
|
}
|
|
|
|
private void _handleMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case TYPE_LOCATION_CHANGED:
|
|
Location location = new Location((Location) msg.obj);
|
|
mListener.onLocationChanged(location);
|
|
break;
|
|
case TYPE_STATUS_CHANGED:
|
|
Bundle b = (Bundle) msg.obj;
|
|
String provider = b.getString("provider");
|
|
int status = b.getInt("status");
|
|
Bundle extras = b.getBundle("extras");
|
|
mListener.onStatusChanged(provider, status, extras);
|
|
break;
|
|
case TYPE_PROVIDER_ENABLED:
|
|
mListener.onProviderEnabled((String) msg.obj);
|
|
break;
|
|
case TYPE_PROVIDER_DISABLED:
|
|
mListener.onProviderDisabled((String) msg.obj);
|
|
break;
|
|
}
|
|
try {
|
|
mService.locationCallbackFinished(this);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "locationCallbackFinished: RemoteException", e);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* @hide - hide this constructor because it has a parameter
|
|
* of type ILocationManager, which is a system private class. The
|
|
* right way to create an instance of this class is using the
|
|
* factory Context.getSystemService.
|
|
*/
|
|
public LocationManager(ILocationManager service) {
|
|
if (false) {
|
|
Log.d(TAG, "Constructor: service = " + service);
|
|
}
|
|
mService = service;
|
|
}
|
|
|
|
private LocationProvider createProvider(String name, Bundle info) {
|
|
DummyLocationProvider provider =
|
|
new DummyLocationProvider(name);
|
|
provider.setRequiresNetwork(info.getBoolean("network"));
|
|
provider.setRequiresSatellite(info.getBoolean("satellite"));
|
|
provider.setRequiresCell(info.getBoolean("cell"));
|
|
provider.setHasMonetaryCost(info.getBoolean("cost"));
|
|
provider.setSupportsAltitude(info.getBoolean("altitude"));
|
|
provider.setSupportsSpeed(info.getBoolean("speed"));
|
|
provider.setSupportsBearing(info.getBoolean("bearing"));
|
|
provider.setPowerRequirement(info.getInt("power"));
|
|
provider.setAccuracy(info.getInt("accuracy"));
|
|
return provider;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of the names of all known location providers. All
|
|
* providers are returned, including ones that are not permitted to be
|
|
* accessed by the calling activity or are currently disabled.
|
|
*
|
|
* @return list of Strings containing names of the providers
|
|
*/
|
|
public List<String> getAllProviders() {
|
|
if (false) {
|
|
Log.d(TAG, "getAllProviders");
|
|
}
|
|
try {
|
|
return mService.getAllProviders();
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "getAllProviders: RemoteException", ex);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of the names of location providers. Only providers that
|
|
* are permitted to be accessed by the calling activity will be returned.
|
|
*
|
|
* @param enabledOnly if true then only the providers which are currently
|
|
* enabled are returned.
|
|
* @return list of Strings containing names of the providers
|
|
*/
|
|
public List<String> getProviders(boolean enabledOnly) {
|
|
try {
|
|
return mService.getProviders(enabledOnly);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "getProviders: RemoteException", ex);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns the information associated with the location provider of the
|
|
* given name, or null if no provider exists by that name.
|
|
*
|
|
* @param name the provider name
|
|
* @return a LocationProvider, or null
|
|
*
|
|
* @throws IllegalArgumentException if name is null
|
|
* @throws SecurityException if the caller is not permitted to access the
|
|
* given provider.
|
|
*/
|
|
public LocationProvider getProvider(String name) {
|
|
if (name == null) {
|
|
throw new IllegalArgumentException("name==null");
|
|
}
|
|
try {
|
|
Bundle info = mService.getProviderInfo(name);
|
|
if (info == null) {
|
|
return null;
|
|
}
|
|
return createProvider(name, info);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "getProvider: RemoteException", ex);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of the names of LocationProviders that satisfy the given
|
|
* criteria, or null if none do. Only providers that are permitted to be
|
|
* accessed by the calling activity will be returned.
|
|
*
|
|
* @param criteria the criteria that the returned providers must match
|
|
* @param enabledOnly if true then only the providers which are currently
|
|
* enabled are returned.
|
|
* @return list of Strings containing names of the providers
|
|
*/
|
|
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
|
|
List<String> goodProviders = Collections.emptyList();
|
|
List<String> providers = getProviders(enabledOnly);
|
|
for (String providerName : providers) {
|
|
LocationProvider provider = getProvider(providerName);
|
|
if (provider != null && provider.meetsCriteria(criteria)) {
|
|
if (goodProviders.isEmpty()) {
|
|
goodProviders = new ArrayList<String>();
|
|
}
|
|
goodProviders.add(providerName);
|
|
}
|
|
}
|
|
return goodProviders;
|
|
}
|
|
|
|
/**
|
|
* Returns the next looser power requirement, in the sequence:
|
|
*
|
|
* POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
|
|
*/
|
|
private int nextPower(int power) {
|
|
switch (power) {
|
|
case Criteria.POWER_LOW:
|
|
return Criteria.POWER_MEDIUM;
|
|
case Criteria.POWER_MEDIUM:
|
|
return Criteria.POWER_HIGH;
|
|
case Criteria.POWER_HIGH:
|
|
return Criteria.NO_REQUIREMENT;
|
|
case Criteria.NO_REQUIREMENT:
|
|
default:
|
|
return Criteria.NO_REQUIREMENT;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the next looser accuracy requirement, in the sequence:
|
|
*
|
|
* ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
|
|
*/
|
|
private int nextAccuracy(int accuracy) {
|
|
if (accuracy == Criteria.ACCURACY_FINE) {
|
|
return Criteria.ACCURACY_COARSE;
|
|
} else {
|
|
return Criteria.NO_REQUIREMENT;
|
|
}
|
|
}
|
|
|
|
private abstract class LpComparator implements Comparator<LocationProvider> {
|
|
|
|
public int compare(int a1, int a2) {
|
|
if (a1 < a2) {
|
|
return -1;
|
|
} else if (a1 > a2) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public int compare(float a1, float a2) {
|
|
if (a1 < a2) {
|
|
return -1;
|
|
} else if (a1 > a2) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
private class LpPowerComparator extends LpComparator {
|
|
public int compare(LocationProvider l1, LocationProvider l2) {
|
|
int a1 = l1.getPowerRequirement();
|
|
int a2 = l2.getPowerRequirement();
|
|
return compare(a1, a2); // Smaller is better
|
|
}
|
|
|
|
public boolean equals(LocationProvider l1, LocationProvider l2) {
|
|
int a1 = l1.getPowerRequirement();
|
|
int a2 = l2.getPowerRequirement();
|
|
return a1 == a2;
|
|
}
|
|
}
|
|
|
|
private class LpAccuracyComparator extends LpComparator {
|
|
public int compare(LocationProvider l1, LocationProvider l2) {
|
|
int a1 = l1.getAccuracy();
|
|
int a2 = l2.getAccuracy();
|
|
return compare(a1, a2); // Smaller is better
|
|
}
|
|
|
|
public boolean equals(LocationProvider l1, LocationProvider l2) {
|
|
int a1 = l1.getAccuracy();
|
|
int a2 = l2.getAccuracy();
|
|
return a1 == a2;
|
|
}
|
|
}
|
|
|
|
private class LpCapabilityComparator extends LpComparator {
|
|
|
|
private static final int ALTITUDE_SCORE = 4;
|
|
private static final int BEARING_SCORE = 4;
|
|
private static final int SPEED_SCORE = 4;
|
|
|
|
private int score(LocationProvider p) {
|
|
return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
|
|
(p.supportsBearing() ? BEARING_SCORE : 0) +
|
|
(p.supportsSpeed() ? SPEED_SCORE : 0);
|
|
}
|
|
|
|
public int compare(LocationProvider l1, LocationProvider l2) {
|
|
int a1 = score(l1);
|
|
int a2 = score(l2);
|
|
return compare(-a1, -a2); // Bigger is better
|
|
}
|
|
|
|
public boolean equals(LocationProvider l1, LocationProvider l2) {
|
|
int a1 = score(l1);
|
|
int a2 = score(l2);
|
|
return a1 == a2;
|
|
}
|
|
}
|
|
|
|
private LocationProvider best(List<String> providerNames) {
|
|
List<LocationProvider> providers = new ArrayList<LocationProvider>(providerNames.size());
|
|
for (String name : providerNames) {
|
|
providers.add(getProvider(name));
|
|
}
|
|
|
|
if (providers.size() < 2) {
|
|
return providers.get(0);
|
|
}
|
|
|
|
// First, sort by power requirement
|
|
Collections.sort(providers, new LpPowerComparator());
|
|
int power = providers.get(0).getPowerRequirement();
|
|
if (power < providers.get(1).getPowerRequirement()) {
|
|
return providers.get(0);
|
|
}
|
|
|
|
int idx, size;
|
|
|
|
List<LocationProvider> tmp = new ArrayList<LocationProvider>();
|
|
idx = 0;
|
|
size = providers.size();
|
|
while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
|
|
tmp.add(providers.get(idx));
|
|
idx++;
|
|
}
|
|
|
|
// Next, sort by accuracy
|
|
Collections.sort(tmp, new LpAccuracyComparator());
|
|
int acc = tmp.get(0).getAccuracy();
|
|
if (acc < tmp.get(1).getAccuracy()) {
|
|
return tmp.get(0);
|
|
}
|
|
|
|
List<LocationProvider> tmp2 = new ArrayList<LocationProvider>();
|
|
idx = 0;
|
|
size = tmp.size();
|
|
while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
|
|
tmp2.add(tmp.get(idx));
|
|
idx++;
|
|
}
|
|
|
|
// Finally, sort by capability "score"
|
|
Collections.sort(tmp2, new LpCapabilityComparator());
|
|
return tmp2.get(0);
|
|
}
|
|
|
|
/**
|
|
* Returns the name of the provider that best meets the given criteria. Only providers
|
|
* that are permitted to be accessed by the calling activity will be
|
|
* returned. If several providers meet the criteria, the one with the best
|
|
* accuracy is returned. If no provider meets the criteria,
|
|
* the criteria are loosened in the following sequence:
|
|
*
|
|
* <ul>
|
|
* <li> power requirement
|
|
* <li> accuracy
|
|
* <li> bearing
|
|
* <li> speed
|
|
* <li> altitude
|
|
* </ul>
|
|
*
|
|
* <p> Note that the requirement on monetary cost is not removed
|
|
* in this process.
|
|
*
|
|
* @param criteria the criteria that need to be matched
|
|
* @param enabledOnly if true then only a provider that is currently enabled is returned
|
|
* @return name of the provider that best matches the requirements
|
|
*/
|
|
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
|
|
List<String> goodProviders = getProviders(criteria, enabledOnly);
|
|
if (!goodProviders.isEmpty()) {
|
|
return best(goodProviders).getName();
|
|
}
|
|
|
|
// Make a copy of the criteria that we can modify
|
|
criteria = new Criteria(criteria);
|
|
|
|
// Loosen power requirement
|
|
int power = criteria.getPowerRequirement();
|
|
while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
|
|
power = nextPower(power);
|
|
criteria.setPowerRequirement(power);
|
|
goodProviders = getProviders(criteria, enabledOnly);
|
|
}
|
|
if (!goodProviders.isEmpty()) {
|
|
return best(goodProviders).getName();
|
|
}
|
|
|
|
// // Loosen response time requirement
|
|
// int responseTime = criteria.getPreferredResponseTime();
|
|
// while (goodProviders.isEmpty() &&
|
|
// (responseTime != Criteria.NO_REQUIREMENT)) {
|
|
// responseTime += 1000;
|
|
// if (responseTime > 60000) {
|
|
// responseTime = Criteria.NO_REQUIREMENT;
|
|
// }
|
|
// criteria.setPreferredResponseTime(responseTime);
|
|
// goodProviders = getProviders(criteria);
|
|
// }
|
|
// if (!goodProviders.isEmpty()) {
|
|
// return best(goodProviders);
|
|
// }
|
|
|
|
// Loosen accuracy requirement
|
|
int accuracy = criteria.getAccuracy();
|
|
while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
|
|
accuracy = nextAccuracy(accuracy);
|
|
criteria.setAccuracy(accuracy);
|
|
goodProviders = getProviders(criteria, enabledOnly);
|
|
}
|
|
if (!goodProviders.isEmpty()) {
|
|
return best(goodProviders).getName();
|
|
}
|
|
|
|
// Remove bearing requirement
|
|
criteria.setBearingRequired(false);
|
|
goodProviders = getProviders(criteria, enabledOnly);
|
|
if (!goodProviders.isEmpty()) {
|
|
return best(goodProviders).getName();
|
|
}
|
|
|
|
// Remove speed requirement
|
|
criteria.setSpeedRequired(false);
|
|
goodProviders = getProviders(criteria, enabledOnly);
|
|
if (!goodProviders.isEmpty()) {
|
|
return best(goodProviders).getName();
|
|
}
|
|
|
|
// Remove altitude requirement
|
|
criteria.setAltitudeRequired(false);
|
|
goodProviders = getProviders(criteria, enabledOnly);
|
|
if (!goodProviders.isEmpty()) {
|
|
return best(goodProviders).getName();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Registers the current activity to be notified periodically by
|
|
* the named provider. Periodically, the supplied LocationListener will
|
|
* be called with the current Location or with status updates.
|
|
*
|
|
* <p> It may take a while to receive the most recent location. If
|
|
* an immediate location is required, applications may use the
|
|
* {@link #getLastKnownLocation(String)} method.
|
|
*
|
|
* <p> In case the provider is disabled by the user, updates will stop,
|
|
* and the {@link LocationListener#onProviderDisabled(String)}
|
|
* method will be called. As soon as the provider is enabled again,
|
|
* the {@link LocationListener#onProviderEnabled(String)} method will
|
|
* be called and location updates will start again.
|
|
*
|
|
* <p> The frequency of notification may be controlled using the
|
|
* minTime and minDistance parameters. If minTime is greater than 0,
|
|
* the LocationManager could potentially rest for minTime milliseconds
|
|
* between location updates to conserve power. If minDistance is greater than 0,
|
|
* a location will only be broadcasted if the device moves by minDistance meters.
|
|
* To obtain notifications as frequently as possible, set both parameters to 0.
|
|
*
|
|
* <p> Background services should be careful about setting a sufficiently high
|
|
* minTime so that the device doesn't consume too much power by keeping the
|
|
* GPS or wireless radios on all the time. In particular, values under 60000ms
|
|
* are not recommended.
|
|
*
|
|
* <p> The calling thread must be a {@link android.os.Looper} thread such as
|
|
* the main thread of the calling Activity.
|
|
*
|
|
* @param provider the name of the provider with which to register
|
|
* @param minTime the minimum time interval for notifications, in
|
|
* milliseconds. This field is only used as a hint to conserve power, and actual
|
|
* time between location updates may be greater or lesser than this value.
|
|
* @param minDistance the minimum distance interval for notifications,
|
|
* in meters
|
|
* @param listener a {#link LocationListener} whose
|
|
* {@link LocationListener#onLocationChanged} method will be called for
|
|
* each location update
|
|
*
|
|
* @throws IllegalArgumentException if provider is null or doesn't exist
|
|
* @throws IllegalArgumentException if listener is null
|
|
* @throws RuntimeException if the calling thread has no Looper
|
|
* @throws SecurityException if no suitable permission is present for the provider.
|
|
*/
|
|
public void requestLocationUpdates(String provider,
|
|
long minTime, float minDistance, LocationListener listener) {
|
|
if (provider == null) {
|
|
throw new IllegalArgumentException("provider==null");
|
|
}
|
|
if (listener == null) {
|
|
throw new IllegalArgumentException("listener==null");
|
|
}
|
|
_requestLocationUpdates(provider, minTime, minDistance, listener, null);
|
|
}
|
|
|
|
/**
|
|
* Registers the current activity to be notified periodically by
|
|
* the named provider. Periodically, the supplied LocationListener will
|
|
* be called with the current Location or with status updates.
|
|
*
|
|
* <p> It may take a while to receive the most recent location. If
|
|
* an immediate location is required, applications may use the
|
|
* {@link #getLastKnownLocation(String)} method.
|
|
*
|
|
* <p> In case the provider is disabled by the user, updates will stop,
|
|
* and the {@link LocationListener#onProviderDisabled(String)}
|
|
* method will be called. As soon as the provider is enabled again,
|
|
* the {@link LocationListener#onProviderEnabled(String)} method will
|
|
* be called and location updates will start again.
|
|
*
|
|
* <p> The frequency of notification may be controlled using the
|
|
* minTime and minDistance parameters. If minTime is greater than 0,
|
|
* the LocationManager could potentially rest for minTime milliseconds
|
|
* between location updates to conserve power. If minDistance is greater than 0,
|
|
* a location will only be broadcasted if the device moves by minDistance meters.
|
|
* To obtain notifications as frequently as possible, set both parameters to 0.
|
|
*
|
|
* <p> Background services should be careful about setting a sufficiently high
|
|
* minTime so that the device doesn't consume too much power by keeping the
|
|
* GPS or wireless radios on all the time. In particular, values under 60000ms
|
|
* are not recommended.
|
|
*
|
|
* <p> The supplied Looper is used to implement the callback mechanism.
|
|
*
|
|
* @param provider the name of the provider with which to register
|
|
* @param minTime the minimum time interval for notifications, in
|
|
* milliseconds. This field is only used as a hint to conserve power, and actual
|
|
* time between location updates may be greater or lesser than this value.
|
|
* @param minDistance the minimum distance interval for notifications,
|
|
* in meters
|
|
* @param listener a {#link LocationListener} whose
|
|
* {@link LocationListener#onLocationChanged} method will be called for
|
|
* each location update
|
|
* @param looper a Looper object whose message queue will be used to
|
|
* implement the callback mechanism.
|
|
*
|
|
* @throws IllegalArgumentException if provider is null or doesn't exist
|
|
* @throws IllegalArgumentException if listener is null
|
|
* @throws IllegalArgumentException if looper is null
|
|
* @throws SecurityException if no suitable permission is present for the provider.
|
|
*/
|
|
public void requestLocationUpdates(String provider,
|
|
long minTime, float minDistance, LocationListener listener,
|
|
Looper looper) {
|
|
if (provider == null) {
|
|
throw new IllegalArgumentException("provider==null");
|
|
}
|
|
if (listener == null) {
|
|
throw new IllegalArgumentException("listener==null");
|
|
}
|
|
if (looper == null) {
|
|
throw new IllegalArgumentException("looper==null");
|
|
}
|
|
_requestLocationUpdates(provider, minTime, minDistance, listener, looper);
|
|
}
|
|
|
|
private void _requestLocationUpdates(String provider,
|
|
long minTime, float minDistance, LocationListener listener,
|
|
Looper looper) {
|
|
if (minTime < 0L) {
|
|
minTime = 0L;
|
|
}
|
|
if (minDistance < 0.0f) {
|
|
minDistance = 0.0f;
|
|
}
|
|
|
|
try {
|
|
synchronized (mListeners) {
|
|
ListenerTransport transport = mListeners.get(listener);
|
|
if (transport == null) {
|
|
transport = new ListenerTransport(listener, looper);
|
|
}
|
|
mListeners.put(listener, transport);
|
|
mService.requestLocationUpdates(provider, minTime, minDistance, transport);
|
|
}
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers the current activity to be notified periodically by
|
|
* the named provider. Periodically, the supplied PendingIntent will
|
|
* be broadcast with the current Location or with status updates.
|
|
*
|
|
* <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
|
|
*
|
|
* <p> It may take a while to receive the most recent location. If
|
|
* an immediate location is required, applications may use the
|
|
* {@link #getLastKnownLocation(String)} method.
|
|
*
|
|
* <p> The frequency of notification or new locations may be controlled using the
|
|
* minTime and minDistance parameters. If minTime is greater than 0,
|
|
* the LocationManager could potentially rest for minTime milliseconds
|
|
* between location updates to conserve power. If minDistance is greater than 0,
|
|
* a location will only be broadcast if the device moves by minDistance meters.
|
|
* To obtain notifications as frequently as possible, set both parameters to 0.
|
|
*
|
|
* <p> Background services should be careful about setting a sufficiently high
|
|
* minTime so that the device doesn't consume too much power by keeping the
|
|
* GPS or wireless radios on all the time. In particular, values under 60000ms
|
|
* are not recommended.
|
|
*
|
|
* <p> In case the provider is disabled by the user, updates will stop,
|
|
* and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
|
|
* of false. If the provider is re-enabled, an intent will be sent with an
|
|
* extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
|
|
* start again.
|
|
*
|
|
* <p> If the provider's status changes, an intent will be sent with an extra with key
|
|
* KEY_STATUS_CHANGED and an integer value indicating the new status. Any extras associated
|
|
* with the status update will be sent as well.
|
|
*
|
|
* @param provider the name of the provider with which to register
|
|
* @param minTime the minimum time interval for notifications, in
|
|
* milliseconds. This field is only used as a hint to conserve power, and actual
|
|
* time between location updates may be greater or lesser than this value.
|
|
* @param minDistance the minimum distance interval for notifications,
|
|
* in meters
|
|
* @param intent a {#link PendingIntet} to be sent for each location update
|
|
*
|
|
* @throws IllegalArgumentException if provider is null or doesn't exist
|
|
* @throws IllegalArgumentException if intent is null
|
|
* @throws SecurityException if no suitable permission is present for the provider.
|
|
*/
|
|
public void requestLocationUpdates(String provider,
|
|
long minTime, float minDistance, PendingIntent intent) {
|
|
if (provider == null) {
|
|
throw new IllegalArgumentException("provider==null");
|
|
}
|
|
if (intent == null) {
|
|
throw new IllegalArgumentException("intent==null");
|
|
}
|
|
_requestLocationUpdates(provider, minTime, minDistance, intent);
|
|
}
|
|
|
|
private void _requestLocationUpdates(String provider,
|
|
long minTime, float minDistance, PendingIntent intent) {
|
|
if (minTime < 0L) {
|
|
minTime = 0L;
|
|
}
|
|
if (minDistance < 0.0f) {
|
|
minDistance = 0.0f;
|
|
}
|
|
|
|
try {
|
|
mService.requestLocationUpdatesPI(provider, minTime, minDistance, intent);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "requestLocationUpdates: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes any current registration for location updates of the current activity
|
|
* with the given LocationListener. Following this call, updates will no longer
|
|
* occur for this listener.
|
|
*
|
|
* @param listener {#link LocationListener} object that no longer needs location updates
|
|
* @throws IllegalArgumentException if listener is null
|
|
*/
|
|
public void removeUpdates(LocationListener listener) {
|
|
if (listener == null) {
|
|
throw new IllegalArgumentException("listener==null");
|
|
}
|
|
if (false) {
|
|
Log.d(TAG, "removeUpdates: listener = " + listener);
|
|
}
|
|
try {
|
|
ListenerTransport transport = mListeners.remove(listener);
|
|
if (transport != null) {
|
|
mService.removeUpdates(transport);
|
|
}
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "removeUpdates: DeadObjectException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes any current registration for location updates of the current activity
|
|
* with the given PendingIntent. Following this call, updates will no longer
|
|
* occur for this intent.
|
|
*
|
|
* @param intent {#link PendingIntent} object that no longer needs location updates
|
|
* @throws IllegalArgumentException if intent is null
|
|
*/
|
|
public void removeUpdates(PendingIntent intent) {
|
|
if (intent == null) {
|
|
throw new IllegalArgumentException("intent==null");
|
|
}
|
|
if (false) {
|
|
Log.d(TAG, "removeUpdates: intent = " + intent);
|
|
}
|
|
try {
|
|
mService.removeUpdatesPI(intent);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "removeUpdates: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a proximity alert for the location given by the position
|
|
* (latitude, longitude) and the given radius. When the device
|
|
* detects that it has entered or exited the area surrounding the
|
|
* location, the given PendingIntent will be used to create an Intent
|
|
* to be fired.
|
|
*
|
|
* <p> The fired Intent will have a boolean extra added with key
|
|
* {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
|
|
* entering the proximity region; if false, it is exiting.
|
|
*
|
|
* <p> Due to the approximate nature of position estimation, if the
|
|
* device passes through the given area briefly, it is possible
|
|
* that no Intent will be fired. Similarly, an Intent could be
|
|
* fired if the device passes very close to the given area but
|
|
* does not actually enter it.
|
|
*
|
|
* <p> After the number of milliseconds given by the expiration
|
|
* parameter, the location manager will delete this proximity
|
|
* alert and no longer monitor it. A value of -1 indicates that
|
|
* there should be no expiration time.
|
|
*
|
|
* <p> In case the screen goes to sleep, checks for proximity alerts
|
|
* happen only once every 4 minutes. This conserves battery life by
|
|
* ensuring that the device isn't perpetually awake.
|
|
*
|
|
* <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
|
|
* and {@link #GPS_PROVIDER}.
|
|
*
|
|
* @param latitude the latitude of the central point of the
|
|
* alert region
|
|
* @param longitude the longitude of the central point of the
|
|
* alert region
|
|
* @param radius the radius of the central point of the
|
|
* alert region, in meters
|
|
* @param expiration time for this proximity alert, in milliseconds,
|
|
* or -1 to indicate no expiration
|
|
* @param intent a PendingIntent that will be used to generate an Intent to
|
|
* fire when entry to or exit from the alert region is detected
|
|
*
|
|
* @throws SecurityException if no permission exists for the required
|
|
* providers.
|
|
*/
|
|
public void addProximityAlert(double latitude, double longitude,
|
|
float radius, long expiration, PendingIntent intent) {
|
|
if (false) {
|
|
Log.d(TAG, "addProximityAlert: latitude = " + latitude +
|
|
", longitude = " + longitude + ", radius = " + radius +
|
|
", expiration = " + expiration +
|
|
", intent = " + intent);
|
|
}
|
|
try {
|
|
mService.addProximityAlert(latitude, longitude, radius,
|
|
expiration, intent);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "addProximityAlert: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes the proximity alert with the given PendingIntent.
|
|
*
|
|
* @param intent the PendingIntent that no longer needs to be notified of
|
|
* proximity alerts
|
|
*/
|
|
public void removeProximityAlert(PendingIntent intent) {
|
|
if (false) {
|
|
Log.d(TAG, "removeProximityAlert: intent = " + intent);
|
|
}
|
|
try {
|
|
mService.removeProximityAlert(intent);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "removeProximityAlert: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the current enabled/disabled status of the given provider. If the
|
|
* user has enabled this provider in the Settings menu, true is returned
|
|
* otherwise false is returned
|
|
*
|
|
* @param provider the name of the provider
|
|
* @return true if the provider is enabled
|
|
*
|
|
* @throws SecurityException if no suitable permission is present for the provider.
|
|
* @throws IllegalArgumentException if provider is null or doesn't exist
|
|
*/
|
|
public boolean isProviderEnabled(String provider) {
|
|
if (provider == null) {
|
|
throw new IllegalArgumentException("provider==null");
|
|
}
|
|
try {
|
|
return mService.isProviderEnabled(provider);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "isProviderEnabled: RemoteException", ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a Location indicating the data from the last known
|
|
* location fix obtained from the given provider. This can be done
|
|
* without starting the provider. Note that this location could
|
|
* be out-of-date, for example if the device was turned off and
|
|
* moved to another location.
|
|
*
|
|
* <p> If the provider is currently disabled, null is returned.
|
|
*
|
|
* @param provider the name of the provider
|
|
* @return the last known location for the provider, or null
|
|
*
|
|
* @throws SecurityException if no suitable permission is present for the provider.
|
|
* @throws IllegalArgumentException if provider is null or doesn't exist
|
|
*/
|
|
public Location getLastKnownLocation(String provider) {
|
|
if (provider == null) {
|
|
throw new IllegalArgumentException("provider==null");
|
|
}
|
|
try {
|
|
return mService.getLastKnownLocation(provider);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "getLastKnowLocation: RemoteException", ex);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Mock provider support
|
|
|
|
/**
|
|
* Creates a mock location provider and adds it to the set of active providers.
|
|
*
|
|
* @param name the provider name
|
|
* @param requiresNetwork
|
|
* @param requiresSatellite
|
|
* @param requiresCell
|
|
* @param hasMonetaryCost
|
|
* @param supportsAltitude
|
|
* @param supportsSpeed
|
|
* @param supportsBearing
|
|
* @param powerRequirement
|
|
* @param accuracy
|
|
*
|
|
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
|
|
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
|
|
* Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled
|
|
* @throws IllegalArgumentException if a provider with the given name already exists
|
|
*/
|
|
public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
|
|
boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
|
|
boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
|
|
try {
|
|
mService.addTestProvider(name, requiresNetwork, requiresSatellite, requiresCell,
|
|
hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing, powerRequirement,
|
|
accuracy);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "addTestProvider: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes the mock location provider with the given name.
|
|
*
|
|
* @param provider the provider name
|
|
*
|
|
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
|
|
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
|
|
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
|
|
* @throws IllegalArgumentException if no provider with the given name exists
|
|
*/
|
|
public void removeTestProvider(String provider) {
|
|
try {
|
|
mService.removeTestProvider(provider);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "removeTestProvider: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a mock location for the given provider. This location will be used in place
|
|
* of any actual location from the provider.
|
|
*
|
|
* @param provider the provider name
|
|
* @param loc the mock location
|
|
*
|
|
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
|
|
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
|
|
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
|
|
* @throws IllegalArgumentException if no provider with the given name exists
|
|
*/
|
|
public void setTestProviderLocation(String provider, Location loc) {
|
|
try {
|
|
mService.setTestProviderLocation(provider, loc);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "setTestProviderLocation: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes any mock location associated with the given provider.
|
|
*
|
|
* @param provider the provider name
|
|
*
|
|
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
|
|
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
|
|
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
|
|
* @throws IllegalArgumentException if no provider with the given name exists
|
|
*/
|
|
public void clearTestProviderLocation(String provider) {
|
|
try {
|
|
mService.clearTestProviderLocation(provider);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "clearTestProviderLocation: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a mock enabled value for the given provider. This value will be used in place
|
|
* of any actual value from the provider.
|
|
*
|
|
* @param provider the provider name
|
|
* @param enabled the mock enabled value
|
|
*
|
|
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
|
|
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
|
|
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
|
|
* @throws IllegalArgumentException if no provider with the given name exists
|
|
*/
|
|
public void setTestProviderEnabled(String provider, boolean enabled) {
|
|
try {
|
|
mService.setTestProviderEnabled(provider, enabled);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "setTestProviderEnabled: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes any mock enabled value associated with the given provider.
|
|
*
|
|
* @param provider the provider name
|
|
*
|
|
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
|
|
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
|
|
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
|
|
* @throws IllegalArgumentException if no provider with the given name exists
|
|
*/
|
|
public void clearTestProviderEnabled(String provider) {
|
|
try {
|
|
mService.clearTestProviderEnabled(provider);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "clearTestProviderEnabled: RemoteException", ex);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Sets mock status values for the given provider. These values will be used in place
|
|
* of any actual values from the provider.
|
|
*
|
|
* @param provider the provider name
|
|
* @param status the mock status
|
|
* @param extras a Bundle containing mock extras
|
|
* @param updateTime the mock update time
|
|
*
|
|
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
|
|
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
|
|
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
|
|
* @throws IllegalArgumentException if no provider with the given name exists
|
|
*/
|
|
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
|
|
try {
|
|
mService.setTestProviderStatus(provider, status, extras, updateTime);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "setTestProviderStatus: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes any mock status values associated with the given provider.
|
|
*
|
|
* @param provider the provider name
|
|
*
|
|
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
|
|
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
|
|
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
|
|
* @throws IllegalArgumentException if no provider with the given name exists
|
|
*/
|
|
public void clearTestProviderStatus(String provider) {
|
|
try {
|
|
mService.clearTestProviderStatus(provider);
|
|
} catch (RemoteException ex) {
|
|
Log.e(TAG, "clearTestProviderStatus: RemoteException", ex);
|
|
}
|
|
}
|
|
|
|
// GPS-specific support
|
|
|
|
// This class is used to send GPS status events to the client's main thread.
|
|
private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
|
|
|
|
private final GpsStatus.Listener mListener;
|
|
private final GpsStatus.NmeaListener mNmeaListener;
|
|
|
|
// This must not equal any of the GpsStatus event IDs
|
|
private static final int NMEA_RECEIVED = 1000;
|
|
|
|
private class Nmea {
|
|
long mTimestamp;
|
|
String mNmea;
|
|
|
|
Nmea(long timestamp, String nmea) {
|
|
mTimestamp = timestamp;
|
|
mNmea = nmea;
|
|
}
|
|
}
|
|
private ArrayList<Nmea> mNmeaBuffer;
|
|
|
|
GpsStatusListenerTransport(GpsStatus.Listener listener) {
|
|
mListener = listener;
|
|
mNmeaListener = null;
|
|
}
|
|
|
|
GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
|
|
mNmeaListener = listener;
|
|
mListener = null;
|
|
mNmeaBuffer = new ArrayList<Nmea>();
|
|
}
|
|
|
|
public void onGpsStarted() {
|
|
if (mListener != null) {
|
|
Message msg = Message.obtain();
|
|
msg.what = GpsStatus.GPS_EVENT_STARTED;
|
|
mGpsHandler.sendMessage(msg);
|
|
}
|
|
}
|
|
|
|
public void onGpsStopped() {
|
|
if (mListener != null) {
|
|
Message msg = Message.obtain();
|
|
msg.what = GpsStatus.GPS_EVENT_STOPPED;
|
|
mGpsHandler.sendMessage(msg);
|
|
}
|
|
}
|
|
|
|
public void onFirstFix(int ttff) {
|
|
if (mListener != null) {
|
|
mGpsStatus.setTimeToFirstFix(ttff);
|
|
Message msg = Message.obtain();
|
|
msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
|
|
mGpsHandler.sendMessage(msg);
|
|
}
|
|
}
|
|
|
|
public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
|
|
float[] elevations, float[] azimuths, int ephemerisMask,
|
|
int almanacMask, int usedInFixMask) {
|
|
if (mListener != null) {
|
|
mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
|
|
ephemerisMask, almanacMask, usedInFixMask);
|
|
|
|
Message msg = Message.obtain();
|
|
msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
|
|
// remove any SV status messages already in the queue
|
|
mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
|
|
mGpsHandler.sendMessage(msg);
|
|
}
|
|
}
|
|
|
|
public void onNmeaReceived(long timestamp, String nmea) {
|
|
if (mNmeaListener != null) {
|
|
synchronized (mNmeaBuffer) {
|
|
mNmeaBuffer.add(new Nmea(timestamp, nmea));
|
|
}
|
|
Message msg = Message.obtain();
|
|
msg.what = NMEA_RECEIVED;
|
|
// remove any NMEA_RECEIVED messages already in the queue
|
|
mGpsHandler.removeMessages(NMEA_RECEIVED);
|
|
mGpsHandler.sendMessage(msg);
|
|
}
|
|
}
|
|
|
|
private final Handler mGpsHandler = new Handler() {
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
if (msg.what == NMEA_RECEIVED) {
|
|
synchronized (mNmeaBuffer) {
|
|
int length = mNmeaBuffer.size();
|
|
for (int i = 0; i < length; i++) {
|
|
Nmea nmea = mNmeaBuffer.get(i);
|
|
mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
|
|
}
|
|
mNmeaBuffer.clear();
|
|
}
|
|
} else {
|
|
// synchronize on mGpsStatus to ensure the data is copied atomically.
|
|
synchronized(mGpsStatus) {
|
|
mListener.onGpsStatusChanged(msg.what);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Adds a GPS status listener.
|
|
*
|
|
* @param listener GPS status listener object to register
|
|
*
|
|
* @return true if the listener was successfully added
|
|
*
|
|
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
|
|
*/
|
|
public boolean addGpsStatusListener(GpsStatus.Listener listener) {
|
|
boolean result;
|
|
|
|
if (mGpsStatusListeners.get(listener) != null) {
|
|
// listener is already registered
|
|
return true;
|
|
}
|
|
try {
|
|
GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
|
|
result = mService.addGpsStatusListener(transport);
|
|
if (result) {
|
|
mGpsStatusListeners.put(listener, transport);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Removes a GPS status listener.
|
|
*
|
|
* @param listener GPS status listener object to remove
|
|
*/
|
|
public void removeGpsStatusListener(GpsStatus.Listener listener) {
|
|
try {
|
|
GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
|
|
if (transport != null) {
|
|
mService.removeGpsStatusListener(transport);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds an NMEA listener.
|
|
*
|
|
* @param listener a {#link GpsStatus.NmeaListener} object to register
|
|
*
|
|
* @return true if the listener was successfully added
|
|
*
|
|
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
|
|
*/
|
|
public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
|
|
boolean result;
|
|
|
|
if (mNmeaListeners.get(listener) != null) {
|
|
// listener is already registered
|
|
return true;
|
|
}
|
|
try {
|
|
GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
|
|
result = mService.addGpsStatusListener(transport);
|
|
if (result) {
|
|
mNmeaListeners.put(listener, transport);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Removes an NMEA listener.
|
|
*
|
|
* @param listener a {#link GpsStatus.NmeaListener} object to remove
|
|
*/
|
|
public void removeNmeaListener(GpsStatus.NmeaListener listener) {
|
|
try {
|
|
GpsStatusListenerTransport transport = mNmeaListeners.remove(listener);
|
|
if (transport != null) {
|
|
mService.removeGpsStatusListener(transport);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves information about the current status of the GPS engine.
|
|
* This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
|
|
* callback to ensure that the data is copied atomically.
|
|
*
|
|
* The caller may either pass in a {@link GpsStatus} object to set with the latest
|
|
* status information, or pass null to create a new {@link GpsStatus} object.
|
|
*
|
|
* @param status object containing GPS status details, or null.
|
|
* @return status object containing updated GPS status.
|
|
*/
|
|
public GpsStatus getGpsStatus(GpsStatus status) {
|
|
if (status == null) {
|
|
status = new GpsStatus();
|
|
}
|
|
status.setStatus(mGpsStatus);
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Sends additional commands to a location provider.
|
|
* Can be used to support provider specific extensions to the Location Manager API
|
|
*
|
|
* @param provider name of the location provider.
|
|
* @param command name of the command to send to the provider.
|
|
* @param extras optional arguments for the command (or null).
|
|
* The provider may optionally fill the extras Bundle with results from the command.
|
|
*
|
|
* @return true if the command succeeds.
|
|
*/
|
|
public boolean sendExtraCommand(String provider, String command, Bundle extras) {
|
|
try {
|
|
return mService.sendExtraCommand(provider, command, extras);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "RemoteException in sendExtraCommand: ", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used by NetInitiatedActivity to report user response
|
|
* for network initiated GPS fix requests.
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public boolean sendNiResponse(int notifId, int userResponse) {
|
|
try {
|
|
return mService.sendNiResponse(notifId, userResponse);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "RemoteException in sendNiResponse: ", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|