aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java')
-rwxr-xr-xsrc/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java154
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);
}
}