diff options
| author | 2021-09-23 17:48:22 +0000 | |
|---|---|---|
| committer | 2021-09-23 17:48:22 +0000 | |
| commit | 6b21c0accce193ca4e6589f69718b07736a5e9f3 (patch) | |
| tree | 1e8bec1f6992f36c162374c0b8b7a60ad123fb50 /location/java/android | |
| parent | 83d7e36d540ef4bba4940f3a56e61c85a61b7007 (diff) | |
| parent | 03e0b8936267e58ff747219b183348372690108f (diff) | |
Merge "Add asynchronous geocoding API"
Diffstat (limited to 'location/java/android')
| -rw-r--r-- | location/java/android/location/Geocoder.java | 309 | ||||
| -rw-r--r-- | location/java/android/location/GeocoderParams.java | 20 |
2 files changed, 235 insertions, 94 deletions
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java index 51586d708018..e4a0d0caceca 100644 --- a/location/java/android/location/Geocoder.java +++ b/location/java/android/location/Geocoder.java @@ -16,8 +16,11 @@ package android.location; +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; -import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -27,24 +30,21 @@ import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** - * A class for handling geocoding and reverse geocoding. Geocoding is - * the process of transforming a street address or other description - * of a location into a (latitude, longitude) coordinate. Reverse - * geocoding is the process of transforming a (latitude, longitude) - * coordinate into a (partial) address. The amount of detail in a - * reverse geocoded location description may vary, for example one - * might contain the full street address of the closest building, while - * another might contain only a city name and postal code. + * A class for handling geocoding and reverse geocoding. Geocoding is the process of transforming a + * street address or other description of a location into a (latitude, longitude) coordinate. + * Reverse geocoding is the process of transforming a (latitude, longitude) coordinate into a + * (partial) address. The amount of detail in a reverse geocoded location description may vary, for + * example one might contain the full street address of the closest building, while another might + * contain only a city name and postal code. * - * The Geocoder class requires a backend service that is not included in - * the core android framework. The Geocoder query methods will return an - * empty list if there no backend service in the platform. Use the - * isPresent() method to determine whether a Geocoder implementation - * exists. + * The Geocoder class requires a backend service that is not included in the core android framework. + * The Geocoder query methods will return an empty list if there no backend service in the platform. + * Use the isPresent() method to determine whether a Geocoder implementation exists. * * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful or @@ -52,20 +52,26 @@ import java.util.concurrent.TimeUnit; */ public final class Geocoder { + /** A listener for asynchronous geocoding results. */ + public interface GeocodeListener { + /** Invoked when geocoding completes successfully. May return an empty list. */ + void onGeocode(@NonNull List<Address> addresses); + /** Invoked when geocoding fails, with a brief error message. */ + default void onError(@Nullable String errorMessage) {} + } + private static final long TIMEOUT_MS = 60000; private final GeocoderParams mParams; private final ILocationManager mService; /** - * Returns true if the Geocoder methods getFromLocation and - * getFromLocationName are implemented. Lack of network - * connectivity may still cause these methods to return null or - * empty lists. + * Returns true if there is a geocoder implementation present that may return results. If true, + * there is still no guarantee that any individual geocoding attempt will succeed. */ public static boolean isPresent() { - IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE); - ILocationManager lm = ILocationManager.Stub.asInterface(b); + ILocationManager lm = Objects.requireNonNull(ILocationManager.Stub.asInterface( + ServiceManager.getService(Context.LOCATION_SERVICE))); try { return lm.geocoderIsPresent(); } catch (RemoteException e) { @@ -74,40 +80,35 @@ public final class Geocoder { } /** - * Constructs a Geocoder whose responses will be localized for the - * given Locale. - * - * @param context the Context of the calling Activity - * @param locale the desired Locale for the query results - * - * @throws NullPointerException if Locale is null + * Constructs a Geocoder localized for the default locale. */ - public Geocoder(Context context, Locale locale) { - mParams = new GeocoderParams(context, locale); - mService = ILocationManager.Stub.asInterface( - ServiceManager.getService(Context.LOCATION_SERVICE)); + public Geocoder(@NonNull Context context) { + this(context, Locale.getDefault()); } /** - * Constructs a Geocoder whose responses will be localized for the - * default system Locale. - * - * @param context the Context of the calling Activity + * Constructs a Geocoder localized for the given locale. */ - public Geocoder(Context context) { - this(context, Locale.getDefault()); + public Geocoder(@NonNull Context context, @NonNull Locale locale) { + mParams = new GeocoderParams(context, locale); + mService = ILocationManager.Stub.asInterface( + ServiceManager.getService(Context.LOCATION_SERVICE)); } /** * Returns an array of Addresses that attempt to describe the area immediately surrounding the * given latitude and longitude. The returned addresses should be localized for the locale - * provided to this class's constructor. Results may be obtained by means of a network lookup - * and this method may take some time to return, and so should not be called on the main thread. + * provided to this class's constructor. * - * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on + * <p class="warning"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance - * purposes. + * purposes.</p> + * + * <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for + * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the + * asynchronous version of this API. If that is not possible, this should be run on a background + * thread to avoid blocking other operations.</p> * * @param latitude the latitude a point for the search * @param longitude the longitude a point for the search @@ -116,22 +117,51 @@ public final class Geocoder { * @return a list of Address objects. Returns null or empty list if no matches were * found or there is no backend service available. * - * @throws IllegalArgumentException if latitude is - * less than -90 or greater than 90 - * @throws IllegalArgumentException if longitude is - * less than -180 or greater than 180 - * @throws IOException if the network is unavailable or any other - * I/O problem occurs + * @throws IllegalArgumentException if latitude or longitude is invalid + * @throws IOException if there is a failure + * + * @deprecated Use {@link #getFromLocation(double, double, int, GeocodeListener)} instead to + * avoid blocking a thread waiting for results. */ - public List<Address> getFromLocation(double latitude, double longitude, int maxResults) + @Deprecated + public @Nullable List<Address> getFromLocation( + @FloatRange(from = -90D, to = 90D) double latitude, + @FloatRange(from = -180D, to = 180D)double longitude, + @IntRange int maxResults) throws IOException { + SynchronousGeocoder listener = new SynchronousGeocoder(); + getFromLocation(latitude, longitude, maxResults, listener); + return listener.getResults(); + } + + /** + * Provides an array of Addresses that attempt to describe the area immediately surrounding the + * given latitude and longitude. The returned addresses should be localized for the locale + * provided to this class's constructor. + * + * <p class="warning"><strong>Warning:</strong> Geocoding services may provide no guarantees on + * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful + * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance + * purposes.</p> + * + * @param latitude the latitude a point for the search + * @param longitude the longitude a point for the search + * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended + * @param listener a listener for receiving results + * + * @throws IllegalArgumentException if latitude or longitude is invalid + */ + public void getFromLocation( + @FloatRange(from = -90D, to = 90D) double latitude, + @FloatRange(from = -180D, to = 180D) double longitude, + @IntRange int maxResults, + @NonNull GeocodeListener listener) { Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude"); Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "longitude"); try { - GeocodeListener listener = new GeocodeListener(); - mService.getFromLocation(latitude, longitude, maxResults, mParams, listener); - return listener.getResults(); + mService.getFromLocation(latitude, longitude, maxResults, mParams, + new GeocoderImpl(listener)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -141,14 +171,17 @@ public final class Geocoder { * Returns an array of Addresses that attempt to describe the named location, which may be a * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain * View, CA", an airport code such as "SFO", and so forth. The returned addresses should be - * localized for the locale provided to this class's constructor. Results may be obtained by - * means of a network lookup and this method may take some time to return, and so should not be - * called on the main thread. + * localized for the locale provided to this class's constructor. * * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance - * purposes. + * purposes.</p> + * + * <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for + * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the + * asynchronous version of this API. If that is not possible, this should be run on a background + * thread to avoid blocking other operations.</p> * * @param locationName a user-supplied description of a location * @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended @@ -157,20 +190,47 @@ public final class Geocoder { * found or there is no backend service available. * * @throws IllegalArgumentException if locationName is null - * @throws IOException if the network is unavailable or any other - * I/O problem occurs + * @throws IOException if there is a failure + * + * @deprecated Use {@link #getFromLocationName(String, int, GeocodeListener)} instead to avoid + * blocking a thread waiting for results. */ - public List<Address> getFromLocationName(String locationName, int maxResults) throws IOException { + @Deprecated + public @Nullable List<Address> getFromLocationName( + @NonNull String locationName, + @IntRange int maxResults) throws IOException { return getFromLocationName(locationName, maxResults, 0, 0, 0, 0); } /** + * Provides an array of Addresses that attempt to describe the named location, which may be a + * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain + * View, CA", an airport code such as "SFO", and so forth. The returned addresses should be + * localized for the locale provided to this class's constructor. + * + * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on + * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful + * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance + * purposes.</p> + * + * @param locationName a user-supplied description of a location + * @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended + * @param listener a listener for receiving results + * + * @throws IllegalArgumentException if locationName is null + */ + public void getFromLocationName( + @NonNull String locationName, + @IntRange int maxResults, + @NonNull GeocodeListener listener) { + getFromLocationName(locationName, maxResults, 0, 0, 0, 0, listener); + } + + /** * Returns an array of Addresses that attempt to describe the named location, which may be a * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain * View, CA", an airport code such as "SFO", and so forth. The returned addresses should be - * localized for the locale provided to this class's constructor. Results may be obtained by - * means of a network lookup and this method may take some time to return, and so should not be - * called on the main thread. + * localized for the locale provided to this class's constructor. * * <p> You may specify a bounding box for the search results by including the latitude and * longitude of the lower left point and upper right point of the box. @@ -178,29 +238,78 @@ public final class Geocoder { * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance - * purposes. + * purposes.</p> * - * @param locationName a user-supplied description of a location - * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended - * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box - * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box - * @param upperRightLatitude the latitude of the upper right corner of the bounding box + * <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for + * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the + * asynchronous version of this API. If that is not possible, this should be run on a background + * thread to avoid blocking other operations.</p> + * + * @param locationName a user-supplied description of a location + * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are + * recommended + * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box + * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box + * @param upperRightLatitude the latitude of the upper right corner of the bounding box * @param upperRightLongitude the longitude of the upper right corner of the bounding box * * @return a list of Address objects. Returns null or empty list if no matches were * found or there is no backend service available. * * @throws IllegalArgumentException if locationName is null - * @throws IllegalArgumentException if any latitude is - * less than -90 or greater than 90 - * @throws IllegalArgumentException if any longitude is - * less than -180 or greater than 180 - * @throws IOException if the network is unavailable or any other - * I/O problem occurs + * @throws IllegalArgumentException if any latitude or longitude is invalid + * @throws IOException if there is a failure + * + * @deprecated Use {@link #getFromLocationName(String, int, double, double, double, double, + * GeocodeListener)} instead to avoid blocking a thread waiting for results. */ - public List<Address> getFromLocationName(String locationName, int maxResults, - double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, - double upperRightLongitude) throws IOException { + @Deprecated + public @Nullable List<Address> getFromLocationName( + @NonNull String locationName, + @IntRange int maxResults, + @FloatRange(from = -90D, to = 90D) double lowerLeftLatitude, + @FloatRange(from = -180D, to = 180D) double lowerLeftLongitude, + @FloatRange(from = -90D, to = 90D) double upperRightLatitude, + @FloatRange(from = -180D, to = 180D) double upperRightLongitude) throws IOException { + SynchronousGeocoder listener = new SynchronousGeocoder(); + getFromLocationName(locationName, maxResults, lowerLeftLatitude, lowerLeftLongitude, + upperRightLatitude, upperRightLongitude, listener); + return listener.getResults(); + } + + /** + * Returns an array of Addresses that attempt to describe the named location, which may be a + * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain + * View, CA", an airport code such as "SFO", and so forth. The returned addresses should be + * localized for the locale provided to this class's constructor. + * + * <p> You may specify a bounding box for the search results by including the latitude and + * longitude of the lower left point and upper right point of the box. + * + * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on + * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful + * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance + * purposes.</p> + * + * @param locationName a user-supplied description of a location + * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are + * recommended + * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box + * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box + * @param upperRightLatitude the latitude of the upper right corner of the bounding box + * @param upperRightLongitude the longitude of the upper right corner of the bounding box + * + * @throws IllegalArgumentException if locationName is null + * @throws IllegalArgumentException if any latitude or longitude is invalid + */ + public void getFromLocationName( + @NonNull String locationName, + @IntRange int maxResults, + @FloatRange(from = -90D, to = 90D) double lowerLeftLatitude, + @FloatRange(from = -180D, to = 180D) double lowerLeftLongitude, + @FloatRange(from = -90D, to = 90D) double upperRightLatitude, + @FloatRange(from = -180D, to = 180D) double upperRightLongitude, + @NonNull GeocodeListener listener) { Preconditions.checkArgument(locationName != null); Preconditions.checkArgumentInRange(lowerLeftLatitude, -90.0, 90.0, "lowerLeftLatitude"); Preconditions.checkArgumentInRange(lowerLeftLongitude, -180.0, 180.0, "lowerLeftLongitude"); @@ -209,27 +318,59 @@ public final class Geocoder { "upperRightLongitude"); try { - GeocodeListener listener = new GeocodeListener(); mService.getFromLocationName(locationName, lowerLeftLatitude, lowerLeftLongitude, - upperRightLatitude, upperRightLongitude, maxResults, mParams, listener); - return listener.getResults(); + upperRightLatitude, upperRightLongitude, maxResults, mParams, + new GeocoderImpl(listener)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - private static class GeocodeListener extends IGeocodeListener.Stub { + private static class GeocoderImpl extends IGeocodeListener.Stub { + + private GeocodeListener mListener; + + GeocoderImpl(GeocodeListener listener) { + mListener = Objects.requireNonNull(listener); + } + + @Override + public void onResults(String error, List<Address> addresses) throws RemoteException { + if (mListener == null) { + return; + } + + GeocodeListener listener = mListener; + mListener = null; + + if (error != null) { + listener.onError(error); + } else { + if (addresses == null) { + addresses = Collections.emptyList(); + } + listener.onGeocode(addresses); + } + } + } + + private static class SynchronousGeocoder implements GeocodeListener { private final CountDownLatch mLatch = new CountDownLatch(1); private String mError = null; private List<Address> mResults = Collections.emptyList(); - GeocodeListener() {} + SynchronousGeocoder() {} + + @Override + public void onGeocode(List<Address> addresses) { + mResults = addresses; + mLatch.countDown(); + } @Override - public void onResults(String error, List<Address> results) { - mError = error; - mResults = results; + public void onError(String errorMessage) { + mError = errorMessage; mLatch.countDown(); } diff --git a/location/java/android/location/GeocoderParams.java b/location/java/android/location/GeocoderParams.java index b00a9a99e75d..3ea6364e07c5 100644 --- a/location/java/android/location/GeocoderParams.java +++ b/location/java/android/location/GeocoderParams.java @@ -95,11 +95,11 @@ public class GeocoderParams implements Parcelable { new Parcelable.Creator<GeocoderParams>() { public GeocoderParams createFromParcel(Parcel in) { int uid = in.readInt(); - String packageName = in.readString(); - String attributionTag = in.readString(); - String language = in.readString(); - String country = in.readString(); - String variant = in.readString(); + String packageName = in.readString8(); + String attributionTag = in.readString8(); + String language = in.readString8(); + String country = in.readString8(); + String variant = in.readString8(); return new GeocoderParams(uid, packageName, attributionTag, new Locale(language, country, variant)); @@ -116,10 +116,10 @@ public class GeocoderParams implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mUid); - parcel.writeString(mPackageName); - parcel.writeString(mAttributionTag); - parcel.writeString(mLocale.getLanguage()); - parcel.writeString(mLocale.getCountry()); - parcel.writeString(mLocale.getVariant()); + parcel.writeString8(mPackageName); + parcel.writeString8(mAttributionTag); + parcel.writeString8(mLocale.getLanguage()); + parcel.writeString8(mLocale.getCountry()); + parcel.writeString8(mLocale.getVariant()); } } |