[android] Added WiFi location support for Kindle Fire, fixed location bugs

This commit is contained in:
Alex Zolotarev 2011-12-15 12:46:10 +03:00 committed by Alex Zolotarev
parent bf9aca0441
commit 29fce40e56
5 changed files with 242 additions and 15 deletions

View file

@ -14,6 +14,9 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
<activity android:name=".MWMActivity"

View file

@ -25,6 +25,7 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService
private final static String PACKAGE_NAME = "com.mapswithme.maps";
private int m_locationIconRes;
private boolean m_locationStarted = false;
private LocationService m_locationService = null;
@ -96,7 +97,8 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService
@Override
protected void onPause()
{
m_locationService.stopUpdate(this);
if (m_locationStarted)
m_locationService.stopUpdate(this);
super.onPause();
}
@ -104,7 +106,8 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService
@Override
protected void onResume()
{
m_locationService.startUpdate(this);
if (m_locationStarted)
m_locationService.startUpdate(this, this);
super.onResume();
}
@ -132,10 +135,11 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService
switch (item.getItemId())
{
case R.id.my_position:
if (m_locationService.isSubscribed(this))
if (m_locationStarted)
m_locationService.stopUpdate(this);
else
m_locationService.startUpdate(this);
m_locationService.startUpdate(this, this);
m_locationStarted = !m_locationStarted;
return true;
case R.id.download_maps:

View file

@ -13,10 +13,11 @@ import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.util.Log;
public class LocationService implements LocationListener, SensorEventListener
public class LocationService implements LocationListener, SensorEventListener, WifiLocation.Listener
{
private static final String TAG = "LocationService";
@ -36,8 +37,11 @@ public class LocationService implements LocationListener, SensorEventListener
private HashSet<Listener> m_observers = new HashSet<Listener>(2);
// Used to filter locations from different providers
private Location m_lastLocation = null;
private WifiLocation m_wifiScanner = null;
private LocationManager m_locationManager;
private SensorManager m_sensorManager;
private Sensor m_compassSensor;
@ -77,23 +81,34 @@ public class LocationService implements LocationListener, SensorEventListener
while (it.hasNext())
it.next().onCompassUpdated(time, magneticNorth, trueNorth, accuracy);
}
public boolean isSubscribed(Listener observer)
{
return m_observers.contains(observer);
}
public void startUpdate(Listener observer)
public void startUpdate(Listener observer, Context c)
{
m_observers.add(observer);
if (!m_isActive)
{
// @TODO Add WiFi provider
final List<String> enabledProviders = m_locationManager.getProviders(true);
List<String> enabledProviders = m_locationManager.getProviders(true);
// Remove passive provider, we don't use it in the current implementation
for (int i = 0; i < enabledProviders.size(); ++i)
if (enabledProviders.get(i).equals(LocationManager.PASSIVE_PROVIDER))
{
enabledProviders.remove(i);
break;
}
if (enabledProviders.size() == 0)
{
observer.onLocationStatusChanged(DISABLED_BY_USER);
// Use WiFi BSSIDS and Google Internet location service if no other options are available
// But only if connection is available
if (com.mapswithme.util.ConnectionState.isConnected(c))
{
observer.onLocationStatusChanged(STARTED);
if (m_wifiScanner == null)
m_wifiScanner = new WifiLocation();
m_wifiScanner.StartScan(c, this);
}
else
observer.onLocationStatusChanged(DISABLED_BY_USER);
}
else
{
@ -108,7 +123,9 @@ public class LocationService implements LocationListener, SensorEventListener
m_locationManager.requestLocationUpdates(provider, 0, 0, this);
// Send last known location for faster startup.
// It should pass filter in the handler below.
onLocationChanged(m_locationManager.getLastKnownLocation(provider));
final Location lastKnown = m_locationManager.getLastKnownLocation(provider);
if (lastKnown != null)
onLocationChanged(lastKnown);
}
}
if (m_sensorManager != null)
@ -253,4 +270,11 @@ public class LocationService implements LocationListener, SensorEventListener
{
Log.d(TAG, String.format("Status changed for location provider: %s to %d", provider, status));
}
@Override
public void onWifiLocationUpdated(Location l)
{
if (l != null)
onLocationChanged(l);
}
}

View file

@ -0,0 +1,142 @@
package com.mapswithme.maps.location;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Location;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
public class WifiLocation extends BroadcastReceiver
{
private final String MWM_GEOLOCATION_SERVER = "http://geolocation.server/";
public interface Listener
{
public void onWifiLocationUpdated(Location l);
}
// @TODO support multiple listeners
private Listener m_observer = null;
private WifiManager m_wifi = null;
public WifiLocation()
{
}
// @TODO support multiple listeners
// Returns true if was started successfully
public boolean StartScan(Context c, Listener l)
{
m_observer = l;
if (m_wifi == null)
{
m_wifi = (WifiManager) c.getSystemService(Context.WIFI_SERVICE);
c.registerReceiver(this, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
return m_wifi.startScan();
}
return true;
}
@Override
public void onReceive(Context c, Intent intent)
{
c.unregisterReceiver(this);
// Prepare JSON request with BSSIDs
StringBuilder json = new StringBuilder("{\"version\":\"1.1.0\"");
boolean wifiHeaderAdded = false;
List<ScanResult> results = m_wifi.getScanResults();
for (ScanResult r : results)
{
if (r.BSSID != null)
{
if (!wifiHeaderAdded)
{
json.append(",\"wifi_towers\":[");
wifiHeaderAdded = true;
}
json.append("{\"mac_address\":\"");
json.append(r.BSSID);
json.append("\",\"ssid\":\"");
json.append(r.SSID == null ? " " : r.SSID);
json.append("\",\"signal_strength\":");
json.append(String.valueOf(r.level));
json.append("},");
}
}
if (wifiHeaderAdded)
{
json.deleteCharAt(json.length() - 1);
json.append("]");
}
json.append("}");
// Result for Listener
Location location = null;
// Send http POST to google location service
URL url;
OutputStreamWriter wr = null;
BufferedReader rd = null;
try
{
url = new URL(MWM_GEOLOCATION_SERVER);
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(json.toString());
wr.flush();
// Get the response
rd = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String line = null;
String response = "";
while ((line = rd.readLine()) != null) {
response += line;
}
final JSONObject jRoot = new JSONObject(response);
final JSONObject jLocation = jRoot.getJSONObject("location");
final double lat = jLocation.getDouble("latitude");
final double lon = jLocation.getDouble("longitude");
final double acc = jLocation.getDouble("accuracy");
location = new Location("wifiscanner");
location.setAccuracy((float)acc);
location.setLatitude(lat);
location.setLongitude(lon);
location.setTime(java.lang.System.currentTimeMillis());
wr.close();
rd.close();
} catch (MalformedURLException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} catch (JSONException e)
{
e.printStackTrace();
}
if (m_observer != null)
m_observer.onWifiLocationUpdated(location);
m_wifi = null;
}
}

View file

@ -0,0 +1,54 @@
package com.mapswithme.util;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
public class ConnectionState
{
public static final int NOT_CONNECTED = 0;
public static final int CONNECTED_BY_3G = 1;
public static final int CONNECTED_BY_WIFI = 2;
public static final int CONNECTED_BY_WIFI_AND_3G = CONNECTED_BY_3G & CONNECTED_BY_WIFI;
public static int getState(Context c)
{
boolean haveConnectedWifi = false;
boolean haveConnectedMobile = false;
ConnectivityManager cm = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo[] netInfo = cm.getAllNetworkInfo();
for (NetworkInfo ni : netInfo)
{
if (ni.getTypeName().equalsIgnoreCase("WIFI"))
if (ni.isConnected())
haveConnectedWifi = true;
if (ni.getTypeName().equalsIgnoreCase("MOBILE"))
if (ni.isConnected())
haveConnectedMobile = true;
}
if (haveConnectedWifi && haveConnectedMobile)
return CONNECTED_BY_WIFI_AND_3G;
else if (haveConnectedMobile)
return CONNECTED_BY_3G;
else if (haveConnectedWifi)
return CONNECTED_BY_WIFI;
return NOT_CONNECTED;
}
public static boolean is3GConnected(Context c)
{
return (getState(c) & CONNECTED_BY_3G) == CONNECTED_BY_3G;
}
public static boolean isWifiConnected(Context c)
{
return (getState(c) & CONNECTED_BY_WIFI) == CONNECTED_BY_WIFI;
}
public static boolean isConnected(Context c)
{
return !(getState(c) == NOT_CONNECTED);
}
}