package com.droidwatcher.modules.location; import java.lang.ref.WeakReference; import java.util.Date; import org.acra.ACRA; import com.droidwatcher.Debug; import com.droidwatcher.receivers.ScreenStateReceiver; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; public class ScheduledLocationListener implements LocationListener { private LocationModule mModule; private ILocationResultListener mResultListner; private Location mNetworkLocation; private TimeoutHandler mTimeoutHandler; private Boolean mIsListeningGps; private Boolean mIsListeningNetwork; private AlarmManager mAlarmManager; private PendingIntent mPendingIntent; private Boolean mIsGpsActive; private long mInterval; private long mGpsRestUntil; private Location mLastLocation; private long mGpsTimeout; private static final int MAX_LOCATION_DIF = 100; /** 55 sec */ private static final long GPS_TIMEOUT_SMALL = 55 * 1000L; /** 2 min */ private static final long GPS_TIMEOUT_MEDIUM = 2 * 60 * 1000L; /** 30 minutes */ private static final long NETWORK_TIMEOUT = 30 * 60 * 1000L; /** 10 minutes */ private static final long GPS_REST_TIME = 10 * 60 * 1000L; public ScheduledLocationListener(LocationModule module, Context context){ mModule = module; mTimeoutHandler = new TimeoutHandler(this); mIsListeningGps = false; mIsListeningNetwork = false; mIsGpsActive = false; mGpsRestUntil = new Date().getTime(); mInterval = mModule.getSettingsManager().gpsInterval(); if (mInterval > GPS_TIMEOUT_MEDIUM) { mGpsTimeout = GPS_TIMEOUT_MEDIUM; } else { mGpsTimeout = GPS_TIMEOUT_SMALL; } mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, LocationTimerBroadcastReceiver.class); mPendingIntent = PendingIntent.getBroadcast(context, LocationTimerBroadcastReceiver.TIMER_REQUEST_CODE, intent, PendingIntent.FLAG_CANCEL_CURRENT); } @Override public synchronized void onLocationChanged(Location location) { if (mModule == null) { return; } try { Debug.i("[ScheduledLocationListener] Location changed; provider: " + location.getProvider()); if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) { if (acceptNewLocation(location)) { mResultListner.onLocationResult(location); } mIsGpsActive = false; stopListner(); } else{ if (!mIsListeningGps || !mModule.getLocationManager().isProviderEnabled(LocationManager.GPS_PROVIDER)) { mResultListner.onLocationResult(location); mIsGpsActive = false; stopListner(); } else{ if (mNetworkLocation == null || mNetworkLocation.getAccuracy() > location.getAccuracy() || location.getTime() - mNetworkLocation.getTime() >= mGpsTimeout) { mNetworkLocation = location; } } } } catch (Exception e) { Debug.exception(e); ACRA.getErrorReporter().handleSilentException(e); } } private Boolean acceptNewLocation(Location location){ if (!mModule.getSettingsManager().gpsOnlyNew() || mLastLocation == null) { mLastLocation = location; return true; } if (location.distanceTo(mLastLocation) >= MAX_LOCATION_DIF) { mLastLocation = location; return true; } return false; } public synchronized void getLocation(ILocationResultListener resultListner) { try { mResultListner = resultListner; mIsGpsActive = true; if (ScreenStateReceiver.getScreenState() == ScreenStateReceiver.SCREEN_STATE_OFF || !mModule.mIsGpsHidden) { startListner(); } mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, new Date().getTime() + mInterval, mInterval, mPendingIntent); } catch (Exception e) { Debug.exception(e); ACRA.getErrorReporter().handleSilentException(e); } } public synchronized void enableByAlarm(){ mIsGpsActive = true; if (ScreenStateReceiver.getScreenState() == ScreenStateReceiver.SCREEN_STATE_OFF || !mModule.mIsGpsHidden) { startListner(); } } public synchronized void startListner(){ if (mIsGpsActive) { if (new Date().getTime() < mGpsRestUntil) { return; } try { if (mModule.getLocationManager().isProviderEnabled(LocationManager.GPS_PROVIDER) && !mIsListeningGps) { mModule.getLocationManager().requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, mModule.getSettingsManager().gpsOnlyNew() ? 100 : 0, this); mIsListeningGps = true; } } catch (Exception e) { Debug.exception(e); ACRA.getErrorReporter().handleSilentException(e); mIsListeningGps = false; } try { if (mModule.getLocationManager().isProviderEnabled(LocationManager.NETWORK_PROVIDER) && !mIsListeningNetwork) { mModule.getLocationManager().requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, mModule.getSettingsManager().gpsOnlyNew() ? 100 : 0, this); mIsListeningNetwork = true; } } catch (Exception e) { Debug.exception(e); ACRA.getErrorReporter().handleSilentException(e); mIsListeningNetwork = false; } if (!mTimeoutHandler.hasMessages(TimeoutHandler.MSG_GPS_STOP)) { mTimeoutHandler.sendEmptyMessageDelayed(TimeoutHandler.MSG_GPS_STOP, mGpsTimeout); } } } public synchronized void stopListner(){ if (mIsListeningGps || mIsListeningNetwork) { try { mModule.getLocationManager().removeUpdates(this); } catch (Exception e) { Debug.exception(e); ACRA.getErrorReporter().handleSilentException(e); } } mIsListeningGps = false; mIsListeningNetwork = false; mTimeoutHandler.removeMessages(TimeoutHandler.MSG_GPS_STOP); } public synchronized void dispose() { try { try { mModule.getLocationManager().removeUpdates(this); } catch (Exception e) { Debug.exception(e); ACRA.getErrorReporter().handleSilentException(e); } mTimeoutHandler.removeMessages(TimeoutHandler.MSG_GPS_STOP); mAlarmManager.cancel(mPendingIntent); } catch (Exception e) { ACRA.getErrorReporter().handleSilentException(e); } finally { mModule = null; mTimeoutHandler = null; mPendingIntent = null; mAlarmManager = null; } } private void onGpsTimeout(){ long now = new Date().getTime(); if (mNetworkLocation != null && now - mNetworkLocation.getTime() <= NETWORK_TIMEOUT) { mResultListner.onLocationResult(mNetworkLocation); } else{ new GsmLocationListener(mModule.getContext()).getLocation(mResultListner); } mNetworkLocation = null; mIsGpsActive = false; stopListner(); mGpsRestUntil = now + GPS_REST_TIME; Debug.i("[RepeatingLocationListener] GPS TIMEOUT"); } private static class TimeoutHandler extends Handler { public static final int MSG_GPS_STOP = 1; private final WeakReference<ScheduledLocationListener> mListener; public TimeoutHandler(ScheduledLocationListener listener){ mListener = new WeakReference<ScheduledLocationListener>(listener); } @Override public synchronized void handleMessage(Message msg) { super.handleMessage(msg); ScheduledLocationListener listener = mListener.get(); if (listener == null) { return; } switch (msg.what) { case MSG_GPS_STOP: listener.onGpsTimeout(); break; default: break; } } } @Override public void onProviderDisabled(String provider) { } @Override public void onProviderEnabled(String provider) { } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } }