1 /* 2 * Copyright (C) 2016 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.googlecode.android_scripting.facade; 18 19 import android.app.Service; 20 import android.content.Context; 21 import android.location.Address; 22 import android.location.Geocoder; 23 import android.location.Location; 24 import android.location.LocationListener; 25 import android.location.LocationManager; 26 import android.os.Bundle; 27 28 import com.google.common.collect.Maps; 29 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 30 import com.googlecode.android_scripting.rpc.Rpc; 31 import com.googlecode.android_scripting.rpc.RpcDefault; 32 import com.googlecode.android_scripting.rpc.RpcParameter; 33 import com.googlecode.android_scripting.rpc.RpcStartEvent; 34 import com.googlecode.android_scripting.rpc.RpcStopEvent; 35 36 import java.io.IOException; 37 import java.util.HashMap; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Map.Entry; 41 42 /** 43 * This facade exposes the LocationManager related functionality.<br> 44 * <br> 45 * <b>Overview</b><br> 46 * Once activated by 'startLocating' the LocationFacade attempts to return location data collected 47 * via GPS or the cell network. If neither are available the last known location may be retrieved. 48 * If both are available the format of the returned data is:<br> 49 * {u'network': {u'altitude': 0, u'provider': u'network', u'longitude': -0.38509020000000002, 50 * u'time': 1297079691231L, u'latitude': 52.410557300000001, u'speed': 0, u'accuracy': 75}, u'gps': 51 * {u'altitude': 51, u'provider': u'gps', u'longitude': -0.38537094593048096, u'time': 52 * 1297079709000L, u'latitude': 52.41076922416687, u'speed': 0, u'accuracy': 24}}<br> 53 * If neither are available {} is returned. <br> 54 * Example (python):<br> 55 * 56 * <pre> 57 * import android, time 58 * droid = android.Android() 59 * droid.startLocating() 60 * time.sleep(15) 61 * loc = droid.readLocation().result 62 * if loc = {}: 63 * loc = getLastKnownLocation().result 64 * if loc != {}: 65 * try: 66 * n = loc['gps'] 67 * except KeyError: 68 * n = loc['network'] 69 * la = n['latitude'] 70 * lo = n['longitude'] 71 * address = droid.geocode(la, lo).result 72 * droid.stopLocating() 73 * </pre> 74 * 75 * The address format is:<br> 76 * [{u'thoroughfare': u'Some Street', u'locality': u'Some Town', u'sub_admin_area': u'Some Borough', 77 * u'admin_area': u'Some City', u'feature_name': u'House Numbers', u'country_code': u'GB', 78 * u'country_name': u'United Kingdom', u'postal_code': u'ST1 1'}] 79 * 80 * @author Damon Kohler (damonkohler@gmail.com) 81 * @author Felix Arends (felix.arends@gmail.com) 82 */ 83 public class LocationFacade extends RpcReceiver { 84 private final EventFacade mEventFacade; 85 private final Service mService; 86 private final Map<String, Location> mLocationUpdates; 87 private final LocationManager mLocationManager; 88 private final Geocoder mGeocoder; 89 90 private final LocationListener mLocationListener = new LocationListener() { 91 @Override 92 public synchronized void onLocationChanged(Location location) { 93 mLocationUpdates.put(location.getProvider(), location); 94 Map<String, Location> copy = Maps.newHashMap(); 95 for (Entry<String, Location> entry : mLocationUpdates.entrySet()) { 96 copy.put(entry.getKey(), entry.getValue()); 97 } 98 mEventFacade.postEvent("location", copy); 99 } 100 101 @Override 102 public void onProviderDisabled(String provider) { 103 } 104 105 @Override 106 public void onProviderEnabled(String provider) { 107 } 108 109 @Override 110 public void onStatusChanged(String provider, int status, Bundle extras) { 111 } 112 }; 113 LocationFacade(FacadeManager manager)114 public LocationFacade(FacadeManager manager) { 115 super(manager); 116 mService = manager.getService(); 117 mEventFacade = manager.getReceiver(EventFacade.class); 118 mGeocoder = new Geocoder(mService); 119 mLocationManager = (LocationManager) mService.getSystemService(Context.LOCATION_SERVICE); 120 mLocationUpdates = new HashMap<String, Location>(); 121 } 122 123 @Override shutdown()124 public void shutdown() { 125 stopLocating(); 126 } 127 128 @Rpc(description = "Returns availables providers on the phone") locationProviders()129 public List<String> locationProviders() { 130 return mLocationManager.getAllProviders(); 131 } 132 133 @Rpc(description = "Ask if provider is enabled") locationProviderEnabled( @pcParametername = "provider", description = "Name of location provider") String provider)134 public boolean locationProviderEnabled( 135 @RpcParameter(name = "provider", description = "Name of location provider") String provider) { 136 return mLocationManager.isProviderEnabled(provider); 137 } 138 139 @Rpc(description = "Starts collecting location data.") 140 @RpcStartEvent("location") startLocating( @pcParametername = "minDistance", description = "minimum time between updates in milliseconds") @pcDefault"60000") Integer minUpdateTime, @RpcParameter(name = "minUpdateDistance", description = "minimum distance between updates in meters") @RpcDefault("30") Integer minUpdateDistance)141 public void startLocating( 142 @RpcParameter(name = "minDistance", description = "minimum time between updates in milliseconds") @RpcDefault("60000") Integer minUpdateTime, 143 @RpcParameter(name = "minUpdateDistance", description = "minimum distance between updates in meters") @RpcDefault("30") Integer minUpdateDistance) { 144 for (String provider : mLocationManager.getAllProviders()) { 145 mLocationManager.requestLocationUpdates(provider, minUpdateTime, minUpdateDistance, 146 mLocationListener, mService.getMainLooper()); 147 } 148 } 149 150 @Rpc(description = "Returns the current location as indicated by all available providers.", returns = "A map of location information by provider.") readLocation()151 public Map<String, Location> readLocation() { 152 return mLocationUpdates; 153 } 154 155 @Rpc(description = "Stops collecting location data.") 156 @RpcStopEvent("location") stopLocating()157 public synchronized void stopLocating() { 158 mLocationManager.removeUpdates(mLocationListener); 159 mLocationUpdates.clear(); 160 } 161 162 @Rpc(description = "Returns the last known location of the device.", returns = "A map of location information by provider.") getLastKnownLocation()163 public Map<String, Location> getLastKnownLocation() { 164 Map<String, Location> location = new HashMap<String, Location>(); 165 for (String provider : mLocationManager.getAllProviders()) { 166 location.put(provider, mLocationManager.getLastKnownLocation(provider)); 167 } 168 return location; 169 } 170 171 @Rpc(description = "Returns a list of addresses for the given latitude and longitude.", returns = "A list of addresses.") geocode( @pcParametername = "latitude") Double latitude, @RpcParameter(name = "longitude") Double longitude, @RpcParameter(name = "maxResults", description = "maximum number of results") @RpcDefault("1") Integer maxResults)172 public List<Address> geocode( 173 @RpcParameter(name = "latitude") Double latitude, 174 @RpcParameter(name = "longitude") Double longitude, 175 @RpcParameter(name = "maxResults", description = "maximum number of results") @RpcDefault("1") Integer maxResults) 176 throws IOException { 177 return mGeocoder.getFromLocation(latitude, longitude, maxResults); 178 } 179 } 180