diff options
Diffstat (limited to 'src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java')
-rwxr-xr-x | src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java | 154 |
1 files changed, 115 insertions, 39 deletions
diff --git a/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java b/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java index 94fca71..f38c046 100755 --- a/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java +++ b/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java @@ -26,19 +26,19 @@ import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; import android.os.AsyncTask; import android.os.Bundle; import android.os.IBinder; import android.os.PowerManager; import android.os.PowerManager.WakeLock; +import android.os.SystemClock; import android.text.TextUtils; import android.util.Log; import com.cyanogenmod.lockclock.ClockWidgetProvider; import com.cyanogenmod.lockclock.misc.Constants; import com.cyanogenmod.lockclock.misc.Preferences; +import com.cyanogenmod.lockclock.misc.WidgetUtils; import java.util.Date; @@ -47,6 +47,15 @@ public class WeatherUpdateService extends Service { private static final boolean D = Constants.DEBUG; public static final String ACTION_FORCE_UPDATE = "com.cyanogenmod.lockclock.action.FORCE_WEATHER_UPDATE"; + private static final String ACTION_CANCEL_LOCATION_UPDATE = + "com.cyanogenmod.lockclock.action.CANCEL_LOCATION_UPDATE"; + + // Broadcast action for end of update + public static final String ACTION_UPDATE_FINISHED = "com.cyanogenmod.lockclock.action.WEATHER_UPDATE_FINISHED"; + public static final String EXTRA_UPDATE_CANCELLED = "update_cancelled"; + + private static final long LOCATION_REQUEST_TIMEOUT = 5L * 60L * 1000L; // request for at most 5 minutes + private static final long OUTDATED_LOCATION_THRESHOLD_MILLIS = 10L * 60L * 1000L; // 10 minutes private WeatherUpdateTask mTask; @@ -62,18 +71,26 @@ public class WeatherUpdateService extends Service { public int onStartCommand(Intent intent, int flags, int startId) { if (D) Log.v(TAG, "Got intent " + intent); - if (mTask != null && mTask.getStatus() != AsyncTask.Status.FINISHED) { + boolean active = mTask != null && mTask.getStatus() != AsyncTask.Status.FINISHED; + + if (ACTION_CANCEL_LOCATION_UPDATE.equals(intent.getAction())) { + WeatherLocationListener.cancel(this); + if (!active) { + stopSelf(); + } + return START_NOT_STICKY; + } + + if (active) { if (D) Log.v(TAG, "Weather update is still active, not starting new update"); return START_REDELIVER_INTENT; } boolean force = ACTION_FORCE_UPDATE.equals(intent.getAction()); - if (force) { - Preferences.setCachedWeatherInfo(this, 0, null); - } if (!shouldUpdate(force)) { Log.d(TAG, "Service started, but shouldn't update ... stopping"); stopSelf(); + sendCancelledBroadcast(); return START_NOT_STICKY; } @@ -83,6 +100,12 @@ public class WeatherUpdateService extends Service { return START_REDELIVER_INTENT; } + private void sendCancelledBroadcast() { + Intent finishedIntent = new Intent(ACTION_UPDATE_FINISHED); + finishedIntent.putExtra(EXTRA_UPDATE_CANCELLED, true); + sendBroadcast(finishedIntent); + } + @Override public IBinder onBind(Intent intent) { return null; @@ -97,25 +120,16 @@ public class WeatherUpdateService extends Service { } private boolean shouldUpdate(boolean force) { - ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); - NetworkInfo info = cm.getActiveNetworkInfo(); - - if (info == null || !info.isConnected()) { - if (D) Log.d(TAG, "No network connection is available for weather update"); - return false; - } - - if (!Preferences.showWeather(this)) { - if (D) Log.v(TAG, "Weather isn't shown, skip update"); - return false; - } - long interval = Preferences.weatherRefreshIntervalInMs(this); if (interval == 0 && !force) { if (D) Log.v(TAG, "Interval set to manual and update not forced, skip update"); return false; } + if (force) { + Preferences.setCachedWeatherInfo(this, 0, null); + } + long now = System.currentTimeMillis(); long lastUpdate = Preferences.lastWeatherUpdateTimestamp(this); long due = lastUpdate + interval; @@ -127,7 +141,7 @@ public class WeatherUpdateService extends Service { return false; } - return true; + return WidgetUtils.isNetworkAvailable(this); } private class WeatherUpdateTask extends AsyncTask<Void, Void, WeatherInfo> { @@ -138,6 +152,7 @@ public class WeatherUpdateService extends Service { if (D) Log.d(TAG, "Starting weather update task"); PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + mWakeLock.setReferenceCounted(false); mContext = WeatherUpdateService.this; } @@ -151,12 +166,32 @@ public class WeatherUpdateService extends Service { LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE); Location location = lm.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER); if (D) Log.v(TAG, "Current location is " + location); + + // If lastKnownLocation is not present (because none of the apps in the + // device has requested the current location to the system yet) or outdated, + // then try to get the current location use the provider that best matches the criteria. + boolean needsUpdate = location == null; + if (location != null) { + long delta = System.currentTimeMillis() - location.getTime(); + needsUpdate = delta > OUTDATED_LOCATION_THRESHOLD_MILLIS; + } + if (needsUpdate) { + if (D) Log.d(TAG, "Getting best location provider"); + String locationProvider = lm.getBestProvider(sLocationCriteria, true); + if (TextUtils.isEmpty(locationProvider)) { + Log.e(TAG, "No available location providers matching criteria."); + } else { + WeatherLocationListener.registerIfNeeded(mContext, locationProvider); + } + } + return location; } @Override protected WeatherInfo doInBackground(Void... params) { - WeatherProvider provider = new YahooWeatherProvider(mContext); + WeatherProvider provider = Preferences.weatherProvider(mContext); + boolean metric = Preferences.useMetricUnits(mContext); String customLocationId = null, customLocationName = null; if (Preferences.useCustomWeatherLocation(mContext)) { @@ -165,31 +200,22 @@ public class WeatherUpdateService extends Service { } if (customLocationId != null) { - return provider.getWeatherInfo(customLocationId, customLocationName); + return provider.getWeatherInfo(customLocationId, customLocationName, metric); } Location location = getCurrentLocation(); if (location != null) { - WeatherInfo info = provider.getWeatherInfo(location); + WeatherInfo info = provider.getWeatherInfo(location, metric); if (info != null) { return info; } } + // work with cached location from last request for now + // a listener to update it is already scheduled if possible WeatherInfo cachedInfo = Preferences.getCachedWeatherInfo(mContext); if (cachedInfo != null) { - return provider.getWeatherInfo(cachedInfo.getId(), cachedInfo.getCity()); - } - // If lastKnownLocation is not present because none of the apps in the - // device has requested the current location to the system yet, then try to - // get the current location use the provider that best matches the criteria. - if (D) Log.d(TAG, "Getting best location provider"); - LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE); - String locationProvider = lm.getBestProvider(sLocationCriteria, true); - if (TextUtils.isEmpty(locationProvider)) { - Log.e(TAG, "No available location providers matching criteria."); - } else { - WeatherLocationListener.registerIfNeeded(mContext, locationProvider); + return provider.getWeatherInfo(cachedInfo.getId(), cachedInfo.getCity(), metric); } return null; @@ -224,6 +250,10 @@ public class WeatherUpdateService extends Service { scheduleUpdate(mContext, interval, false); } + Intent finishedIntent = new Intent(ACTION_UPDATE_FINISHED); + finishedIntent.putExtra(EXTRA_UPDATE_CANCELLED, result == null); + sendBroadcast(finishedIntent); + if (D) Log.d(TAG, "RELEASING WAKELOCK"); mWakeLock.release(); stopSelf(); @@ -232,6 +262,7 @@ public class WeatherUpdateService extends Service { private static class WeatherLocationListener implements LocationListener { private Context mContext; + private PendingIntent mTimeoutIntent; private static WeatherLocationListener sInstance = null; static void registerIfNeeded(Context context, String provider) { @@ -248,35 +279,79 @@ public class WeatherUpdateService extends Service { // Check whether the provider is supported. // NOTE!!! Actually only WeatherUpdateService class is calling this function // with the NETWORK_PROVIDER, so setting the instance is safe. We must - // change this if this call receive differents providers + // change this if this call receive different providers LocationProvider lp = locationManager.getProvider(provider); if (lp != null) { if (D) Log.d(TAG, "LocationManager - Requesting single update"); locationManager.requestSingleUpdate(provider, sInstance, appContext.getMainLooper()); + sInstance.setTimeoutAlarm(); } } } } + static void cancel(Context context) { + synchronized (WeatherLocationListener.class) { + if (sInstance != null) { + final Context appContext = context.getApplicationContext(); + final LocationManager locationManager = + (LocationManager) appContext.getSystemService(Context.LOCATION_SERVICE); + if (D) Log.d(TAG, "Aborting location request after timeout"); + locationManager.removeUpdates(sInstance); + sInstance.cancelTimeoutAlarm(); + sInstance = null; + } + } + } + private WeatherLocationListener(Context context) { super(); mContext = context; } + private void setTimeoutAlarm() { + Intent intent = new Intent(mContext, WeatherUpdateService.class); + intent.setAction(ACTION_CANCEL_LOCATION_UPDATE); + + mTimeoutIntent = PendingIntent.getService(mContext, 0, intent, + PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT); + + AlarmManager am = (AlarmManager) mContext.getSystemService(ALARM_SERVICE); + long elapseTime = SystemClock.elapsedRealtime() + LOCATION_REQUEST_TIMEOUT; + am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, elapseTime, mTimeoutIntent); + } + + private void cancelTimeoutAlarm() { + if (mTimeoutIntent != null) { + AlarmManager am = (AlarmManager) mContext.getSystemService(ALARM_SERVICE); + am.cancel(mTimeoutIntent); + mTimeoutIntent = null; + } + } + @Override public void onLocationChanged(Location location) { // Now, we have a location to use. Schedule a weather update right now. if (D) Log.d(TAG, "The location has changed, schedule an update "); synchronized (WeatherLocationListener.class) { WeatherUpdateService.scheduleUpdate(mContext, 0, true); + cancelTimeoutAlarm(); sInstance = null; } } @Override public void onStatusChanged(String provider, int status, Bundle extras) { - // Not used + // Now, we have a location to use. Schedule a weather update right now. + if (D) Log.d(TAG, "The location service has become available, schedule an update "); + if (status == LocationProvider.AVAILABLE) { + synchronized (WeatherLocationListener.class) { + WeatherUpdateService.scheduleUpdate(mContext, 0, true); + cancelTimeoutAlarm(); + sInstance = null; + } + } } @Override @@ -298,9 +373,9 @@ public class WeatherUpdateService extends Service { am.set(AlarmManager.RTC_WAKEUP, due, getUpdateIntent(context, force)); } - public static void scheduleNextUpdate(Context context) { + public static void scheduleNextUpdate(Context context, boolean force) { long lastUpdate = Preferences.lastWeatherUpdateTimestamp(context); - if (lastUpdate == 0) { + if (lastUpdate == 0 || force) { scheduleUpdate(context, 0, true); } else { long interval = Preferences.weatherRefreshIntervalInMs(context); @@ -320,5 +395,6 @@ public class WeatherUpdateService extends Service { AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); am.cancel(getUpdateIntent(context, true)); am.cancel(getUpdateIntent(context, false)); + WeatherLocationListener.cancel(context); } } |