/* * Copyright (C) 2015-2016 VersoBit * * This file is part of Weather Doge. * * Weather Doge is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Weather Doge is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Weather Doge. If not, see <http://www.gnu.org/licenses/>. */ package com.versobit.weatherdoge; import android.content.Context; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.util.Log; import java.math.BigDecimal; import java.math.RoundingMode; final class LocationApi implements LocationListener { private static final String TAG = "FossLocationApi"; // Accurate to about 110 meters private static final int COORD_FUZZ = 3; private static final int LOC_ACCURACY = 110; private final LocationReceiver receiver; private final LocationManager locationManager; LocationApi(Context ctx, LocationReceiver receiver) { this.receiver = receiver; locationManager = (LocationManager)ctx.getSystemService(Context.LOCATION_SERVICE); } void connect() { try { locationManager.requestLocationUpdates(getBestProvider(), 0, 0, this); } catch (SecurityException ex) { Log.wtf(TAG, ex); return; } receiver.onConnected(); } void disconnect() { try { locationManager.removeUpdates(this); } catch (SecurityException ex) { Log.wtf(TAG, ex); } } boolean isConnected() { return true; } boolean isConnecting() { return false; } Location getLocation() { try { return fuzzLocation(locationManager.getLastKnownLocation(getBestProvider())); } catch (SecurityException ex) { Log.wtf(TAG, ex); } return null; } static boolean isAvailable(Context ctx) { LocationManager lm = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE); return lm.isProviderEnabled(LocationManager.GPS_PROVIDER) || lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER); } private String getBestProvider() { // The network provider should be fastest and has enough accuracy for weather if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) { return LocationManager.NETWORK_PROVIDER; } if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { return LocationManager.GPS_PROVIDER; } // Fall back to network and hope it works return LocationManager.NETWORK_PROVIDER; } private static Location fuzzLocation(Location location) { if (location == null) { return null; } // Because we are receiving precise locations (unlike the Google Play flavor) we need to do // the fuzzing on our own before sending the locations off to the APIs. Truncating the // coordinates to three decimals places will give similar accuracy. It's probably much // simpler than whatever Play Services is doing but hopefully it has a similar effect. location.setLatitude(BigDecimal.valueOf(location.getLatitude()).setScale(COORD_FUZZ, RoundingMode.DOWN).doubleValue()); location.setLongitude(BigDecimal.valueOf(location.getLongitude()).setScale(COORD_FUZZ, BigDecimal.ROUND_DOWN).doubleValue()); location.setAccuracy(LOC_ACCURACY); return location; } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } @Override public void onProviderEnabled(String provider) { } @Override public void onProviderDisabled(String provider) { } @Override public void onLocationChanged(Location location) { receiver.onLocation(fuzzLocation(location)); } }