Home | History | Annotate | Download | only in facade
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under 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  */
     81 public class LocationFacade extends RpcReceiver {
     82   private final EventFacade mEventFacade;
     83   private final Service mService;
     84   private final Map<String, Location> mLocationUpdates;
     85   private final LocationManager mLocationManager;
     86   private final Geocoder mGeocoder;
     87 
     88   private final LocationListener mLocationListener = new LocationListener() {
     89     @Override
     90     public synchronized void onLocationChanged(Location location) {
     91       mLocationUpdates.put(location.getProvider(), location);
     92       Map<String, Location> copy = Maps.newHashMap();
     93       for (Entry<String, Location> entry : mLocationUpdates.entrySet()) {
     94         copy.put(entry.getKey(), entry.getValue());
     95       }
     96       mEventFacade.postEvent("location", copy);
     97     }
     98 
     99     @Override
    100     public void onProviderDisabled(String provider) {
    101     }
    102 
    103     @Override
    104     public void onProviderEnabled(String provider) {
    105     }
    106 
    107     @Override
    108     public void onStatusChanged(String provider, int status, Bundle extras) {
    109     }
    110   };
    111 
    112   public LocationFacade(FacadeManager manager) {
    113     super(manager);
    114     mService = manager.getService();
    115     mEventFacade = manager.getReceiver(EventFacade.class);
    116     mGeocoder = new Geocoder(mService);
    117     mLocationManager = (LocationManager) mService.getSystemService(Context.LOCATION_SERVICE);
    118     mLocationUpdates = new HashMap<String, Location>();
    119   }
    120 
    121   @Override
    122   public void shutdown() {
    123     stopLocating();
    124   }
    125 
    126   @Rpc(description = "Returns availables providers on the phone")
    127   public List<String> locationProviders() {
    128     return mLocationManager.getAllProviders();
    129   }
    130 
    131   @Rpc(description = "Ask if provider is enabled")
    132   public boolean locationProviderEnabled(
    133       @RpcParameter(name = "provider", description = "Name of location provider") String provider) {
    134     return mLocationManager.isProviderEnabled(provider);
    135   }
    136 
    137   @Rpc(description = "Starts collecting location data.")
    138   @RpcStartEvent("location")
    139   public void startLocating(
    140       @RpcParameter(name = "minDistance", description = "minimum time between updates in milliseconds") @RpcDefault("60000") Integer minUpdateTime,
    141       @RpcParameter(name = "minUpdateDistance", description = "minimum distance between updates in meters") @RpcDefault("30") Integer minUpdateDistance) {
    142     for (String provider : mLocationManager.getAllProviders()) {
    143       mLocationManager.requestLocationUpdates(provider, minUpdateTime, minUpdateDistance,
    144           mLocationListener, mService.getMainLooper());
    145     }
    146   }
    147 
    148   @Rpc(description = "Returns the current location as indicated by all available providers.", returns = "A map of location information by provider.")
    149   public Map<String, Location> readLocation() {
    150     return mLocationUpdates;
    151   }
    152 
    153   @Rpc(description = "Stops collecting location data.")
    154   @RpcStopEvent("location")
    155   public synchronized void stopLocating() {
    156     mLocationManager.removeUpdates(mLocationListener);
    157     mLocationUpdates.clear();
    158   }
    159 
    160   @Rpc(description = "Returns the last known location of the device.", returns = "A map of location information by provider.")
    161   public Map<String, Location> getLastKnownLocation() {
    162     Map<String, Location> location = new HashMap<String, Location>();
    163     for (String provider : mLocationManager.getAllProviders()) {
    164       location.put(provider, mLocationManager.getLastKnownLocation(provider));
    165     }
    166     return location;
    167   }
    168 
    169   @Rpc(description = "Returns a list of addresses for the given latitude and longitude.", returns = "A list of addresses.")
    170   public List<Address> geocode(
    171       @RpcParameter(name = "latitude") Double latitude,
    172       @RpcParameter(name = "longitude") Double longitude,
    173       @RpcParameter(name = "maxResults", description = "maximum number of results") @RpcDefault("1") Integer maxResults)
    174       throws IOException {
    175     return mGeocoder.getFromLocation(latitude, longitude, maxResults);
    176   }
    177 }
    178