/* * This source is part of the * _____ ___ ____ * __ / / _ \/ _ | / __/___ _______ _ * / // / , _/ __ |/ _/_/ _ \/ __/ _ `/ * \___/_/|_/_/ |_/_/ (_)___/_/ \_, / * /___/ * repository. * * Copyright (C) 2013 Benoit 'BoD' Lubek (BoD@JRAF.org) * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package org.jraf.android.bikey.backend.location; import android.content.Context; import android.location.Location; import android.location.LocationListener; import android.os.Bundle; import android.os.Handler; import org.jraf.android.bikey.app.Application; import org.jraf.android.util.handler.HandlerUtil; import org.jraf.android.util.listeners.Listeners; import org.jraf.android.util.log.Log; public class LocationManager { public static interface StatusListener { void onStatusChanged(boolean active); } public static interface ActivityRecognitionListener { void onActivityRecognized(int activityType, int confidence); } private static final LocationManager INSTANCE = new LocationManager(); private static final int INTERVAL_LOC_REQUEST = 1000; private static final int ALLOWED_LOC_MISSES = 8; protected static final float ACCURACY_THRESHOLD_M = 20; private static final int IGNORE_LOCATION_COUNT = 7; /** * Speeds below this value will be reported as 0 (because of GPS low precision). */ public static final float SPEED_MIN_THRESHOLD_M_S = 2.2f / 3.6f; public static LocationManager get() { return INSTANCE; } private final Context mContext; protected long mLastFixDate; private Handler mHandler; private boolean mActive = false; private int mIgnoreLocationCount = IGNORE_LOCATION_COUNT; private LocationManager() { mContext = Application.getApplication(); } /* * Location. */ public void addLocationListener(LocationListener listener) { mLocationListeners.add(listener); } public void removeLocationListener(LocationListener listener) { mLocationListeners.remove(listener); } private Listeners<LocationListener> mLocationListeners = new Listeners<LocationListener>() { @Override protected void onFirstListener() { Log.d("First location listener, start location listener"); startLocationListener(); } @Override protected void onNoMoreListeners() { Log.d("No more location listeners, stop location listener"); stopLocationListener(); } }; private void startLocationListener() { Log.d(); mIgnoreLocationCount = IGNORE_LOCATION_COUNT; HandlerUtil.getMainHandler().post(() -> { android.location.LocationManager locationManager = (android.location.LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); locationManager.removeUpdates(mGpsLocationListener); locationManager.requestLocationUpdates(android.location.LocationManager.GPS_PROVIDER, INTERVAL_LOC_REQUEST, 0, mGpsLocationListener); }); } private void stopLocationListener() { Log.d(); HandlerUtil.getMainHandler().post(() -> { android.location.LocationManager locationManager = (android.location.LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); locationManager.removeUpdates(mGpsLocationListener); }); } private android.location.LocationListener mGpsLocationListener = new android.location.LocationListener() { @Override public void onStatusChanged(String provider, int status, Bundle extras) { Log.d("status=" + status); } @Override public void onProviderEnabled(String provider) { Log.d("provider=" + provider); } @Override public void onProviderDisabled(String provider) { Log.d("provider=" + provider); } @Override public void onLocationChanged(Location location) { // For some reason, the time seems to have a 1 second precision. // Use the system time instead. location.setTime(System.currentTimeMillis()); mLocationListener.onLocationChanged(location); } }; private LocationListener mLocationListener = new LocationListener() { @Override public void onLocationChanged(Location location) { Log.d("location=" + location); if (location.hasAccuracy() && location.getAccuracy() > ACCURACY_THRESHOLD_M) { Log.d("Accuracy above threshold: ignore location"); return; } mIgnoreLocationCount--; if (mIgnoreLocationCount >= 0) { Log.d("Ignore first few locations"); return; } // Dispatch to listeners mLocationListeners.dispatch(listener -> listener.onLocationChanged(location)); } @Override public void onStatusChanged(String provider, int status, Bundle extras) {} @Override public void onProviderEnabled(String provider) {} @Override public void onProviderDisabled(String provider) {} }; /* * Gps status. */ public void addStatusListener(StatusListener listener) { mStatusListeners.add(listener); } public void removeStatusListener(StatusListener listener) { mStatusListeners.remove(listener); } private Listeners<StatusListener> mStatusListeners = new Listeners<StatusListener>() { @Override protected void onFirstListener() { Log.d("First status listener, start gps location listener"); startGpsLocationListener(); } @Override protected void onNoMoreListeners() { Log.d("No more status listeners, stop gps location listener"); stopGpsLocationListener(); } }; private void startGpsLocationListener() { Log.d(); setActive(false); HandlerUtil.getMainHandler().post(() -> { android.location.LocationManager locationManager = (android.location.LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); locationManager.removeUpdates(mGpsStatusLocationListener); locationManager.requestLocationUpdates(android.location.LocationManager.GPS_PROVIDER, INTERVAL_LOC_REQUEST, 0, mGpsStatusLocationListener); }); } private void stopGpsLocationListener() { Log.d(); HandlerUtil.getMainHandler().post(() -> { android.location.LocationManager locationManager = (android.location.LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); locationManager.removeUpdates(mGpsStatusLocationListener); }); } private android.location.LocationListener mGpsStatusLocationListener = new android.location.LocationListener() { @Override public void onStatusChanged(String provider, int status, Bundle extras) { Log.d("status=" + status); } @Override public void onProviderEnabled(String provider) { Log.d("provider=" + provider); } @Override public void onProviderDisabled(String provider) { Log.d("provider=" + provider); } @Override public void onLocationChanged(Location location) { mLastFixDate = System.currentTimeMillis(); // We just received a fix so we're active setActive(true); // Schedule to check if we're still active getHandler().removeCallbacks(mCheckForActiveRunnable); getHandler().postDelayed(mCheckForActiveRunnable, INTERVAL_LOC_REQUEST * ALLOWED_LOC_MISSES); } }; private Handler getHandler() { if (mHandler == null) { mHandler = new Handler(); } return mHandler; } protected Runnable mCheckForActiveRunnable = new Runnable() { @Override public void run() { if (System.currentTimeMillis() - mLastFixDate >= INTERVAL_LOC_REQUEST * ALLOWED_LOC_MISSES) { setActive(false); } } }; protected void setActive(boolean active) { if (mActive != active) { // Dispatch to listeners mStatusListeners.dispatch(listener -> listener.onStatusChanged(active)); } mActive = active; } }