aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java
diff options
context:
space:
mode:
authorDvTonder <david.vantonder@gmail.com>2013-01-08 16:25:09 -0500
committerDanny Baumann <dannybaumann@web.de>2013-01-18 09:27:54 +0100
commit1dc51e521b868dcda27fad1d93a041913b0efa38 (patch)
tree84a66c94e1f372990be3de05af2d831f0db2a3f9 /src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java
parentf9c9c2850954bca9023993bf22a1355a40779835 (diff)
downloadandroid_packages_apps_LockClock-1dc51e521b868dcda27fad1d93a041913b0efa38.tar.gz
android_packages_apps_LockClock-1dc51e521b868dcda27fad1d93a041913b0efa38.tar.bz2
android_packages_apps_LockClock-1dc51e521b868dcda27fad1d93a041913b0efa38.zip
Chronus: Optimize widget loading/display
Completely refactors the Calendar handling and changes the refresh interval from every minute to the minimum of either the next weather refresh or the next event start/end. It also now detects more events (new calendar entry, deleted calendar entry, location change, timezone change etc) Patch set 7 : Change the startup of the provider to only update the required panels on enabling the appwigdet and updates This further reduces the number of times the widget refreshes itself, now limited only to actual events Patch set 8 : Change the weather refresh time to be absolute, not relative to current time Patch set 9 : Refactor the calendar to use a CalendarInfo class that maintains a static list of events. This allows the refreshing of the lock screen widget without querying the calendar provider every time the screen turns on Patch set 10: Simplified onReceive loading/updating of widget with additional tweaks to handling deleted widgets and a fix to the refresh timer calculations including a check of the lookahead window. Patch set 11: Change the alarmservice to wake the CPU and do the update if the device is sleeping. This way, since the updates are so infrequent now, it makes sense to ensure things are updated (if needed) when the user turns the screen on after a while. Patch set 12: Change application ID to an ID registered for Chronus Patch set 13: Store the returned WOEID in sharedPreferences it if its valid and retrieve the previously stored one if not. This allows for the querying of weather data even though the Yahoo Placefinder service API limit has been exceeded and it returns an invalid XML result on geocode query. Patch set 14: Add a flag to determine when we want the real placefinder result or the cached result is also OK Patch set 15: Final comments and code formatting cleanup Patch set 16: Factor out weather fetching to separate service to decouple weather fetching from widget update. Also optimize a lot of code. Patch set 17: Fix alarms refresh and optimize calendar query and weather refresh behaviour. Patch set 18: Unify debug flags Change-Id: I0496dad356c92fb26ad7289268327b27b365b6cd
Diffstat (limited to 'src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java')
-rw-r--r--src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java b/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java
new file mode 100644
index 0000000..3e24dd7
--- /dev/null
+++ b/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cyanogenmod.lockclock.weather;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Location;
+import android.location.LocationManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.AsyncTask;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import com.cyanogenmod.lockclock.ClockWidgetProvider;
+import com.cyanogenmod.lockclock.misc.Constants;
+import com.cyanogenmod.lockclock.misc.Preferences;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.w3c.dom.Document;
+
+public class WeatherUpdateService extends Service {
+ private static final String TAG = "WeatherUpdateService";
+ private static final boolean D = Constants.DEBUG;
+
+ private static final String URL_YAHOO_API_WEATHER = "http://weather.yahooapis.com/forecastrss?w=%s&u=";
+
+ public static final String ACTION_FORCE_UPDATE = "com.cyanogenmod.lockclock.action.FORCE_WEATHER_UPDATE";
+
+ private WeatherUpdateTask mTask;
+
+ @Override
+ 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) {
+ 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();
+ return START_NOT_STICKY;
+ }
+
+ mTask = new WeatherUpdateTask();
+ mTask.execute();
+
+ return START_REDELIVER_INTENT;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mTask != null && mTask.getStatus() != AsyncTask.Status.FINISHED) {
+ mTask.cancel(true);
+ mTask = null;
+ }
+ }
+
+ 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;
+ }
+
+ long now = System.currentTimeMillis();
+ long lastUpdate = Preferences.lastWeatherUpdateTimestamp(this);
+ long due = lastUpdate + interval;
+
+ if (D) Log.d(TAG, "Now " + now + " due " + due + "(" + new Date(due) + ")");
+
+ if (lastUpdate != 0 && now < due) {
+ if (D) Log.v(TAG, "Weather update is not due yet");
+ return false;
+ }
+
+ return true;
+ }
+
+ private class WeatherUpdateTask extends AsyncTask<Void, Void, WeatherInfo> {
+ private WakeLock mWakeLock;
+ private Context mContext;
+
+ private static final int RESULT_SUCCESS = 0;
+ private static final int RESULT_FAILURE = 1;
+ private static final int RESULT_CANCELLED = 2;
+
+ public WeatherUpdateTask() {
+ PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mContext = WeatherUpdateService.this;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ mWakeLock.acquire();
+ }
+
+ private String getWoeidForCustomLocation(String location) {
+ // first try with the cached woeid, no need to constantly query constant information
+ String woeid = Preferences.getCachedWoeid(mContext);
+ if (woeid == null) {
+ woeid = YahooPlaceFinder.geoCode(mContext, location);
+ }
+ if (D) Log.v(TAG, "Yahoo location code for " + location + " is " + woeid);
+ return woeid;
+ }
+
+ private String getWoeidForCurrentLocation(Location location) {
+ String woeid = YahooPlaceFinder.reverseGeoCode(mContext,
+ location.getLatitude(), location.getLongitude());
+ if (woeid == null) {
+ // we couldn't fetch up-to-date information, fall back to cache
+ woeid = Preferences.getCachedWoeid(mContext);
+ }
+ if (D) Log.v(TAG, "Yahoo location code for current geolocation " + location + " is " + woeid);
+ return woeid;
+ }
+
+ private Location getCurrentLocation() {
+ LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ Location location = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
+ if (D) Log.v(TAG, "Current location is " + location);
+ return location;
+ }
+
+ private Document getDocument(String woeid) {
+ boolean celcius = Preferences.useMetricUnits(mContext);
+ String urlWithUnit = URL_YAHOO_API_WEATHER + (celcius ? "c" : "f");
+
+ try {
+ return new HttpRetriever().getDocumentFromURL(String.format(urlWithUnit, woeid));
+ } catch (IOException e) {
+ Log.e(TAG, "Couldn't fetch weather data", e);
+ }
+ return null;
+ }
+
+ @Override
+ protected WeatherInfo doInBackground(Void... params) {
+ String customLocation = null;
+ String woeid;
+
+ if (Preferences.useCustomWeatherLocation(mContext)) {
+ customLocation = Preferences.customWeatherLocation(mContext);
+ }
+
+ if (customLocation != null) {
+ woeid = getWoeidForCustomLocation(customLocation);
+ } else {
+ Location location = getCurrentLocation();
+ woeid = getWoeidForCurrentLocation(location);
+ }
+
+ if (woeid == null || isCancelled()) {
+ return null;
+ }
+
+ Document doc = getDocument(woeid);
+ if (doc == null || isCancelled()) {
+ return null;
+ }
+
+ return new WeatherXmlParser(mContext).parseWeatherResponse(doc);
+ }
+
+ @Override
+ protected void onPostExecute(WeatherInfo result) {
+ finish(result);
+ }
+
+ @Override
+ protected void onCancelled() {
+ finish(null);
+ }
+
+ private void finish(WeatherInfo result) {
+ if (result != null) {
+ long now = System.currentTimeMillis();
+ Preferences.setCachedWeatherInfo(mContext, now, result);
+ scheduleUpdate(mContext, Preferences.weatherRefreshIntervalInMs(mContext));
+
+ Intent updateIntent = new Intent(mContext, ClockWidgetProvider.class);
+ sendBroadcast(updateIntent);
+ } else if (isCancelled()) {
+ /* cancelled, likely due to lost network - we'll get restarted
+ * when network comes back */
+ } else {
+ /* failure, schedule next download in 30 minutes */
+ long interval = 30 * 60 * 1000;
+ scheduleUpdate(mContext, interval);
+ }
+
+ mWakeLock.release();
+ stopSelf();
+ }
+ }
+
+ private static void scheduleUpdate(Context context, long timeFromNow) {
+ AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ long due = System.currentTimeMillis() + timeFromNow;
+
+ if (D) Log.v(TAG, "Scheduling next update at " + new Date(due));
+ am.set(AlarmManager.RTC_WAKEUP, due, getUpdateIntent(context, false));
+ }
+
+ public static void scheduleNextUpdate(Context context) {
+ long lastUpdate = Preferences.lastWeatherUpdateTimestamp(context);
+ if (lastUpdate == 0) {
+ scheduleUpdate(context, 0);
+ } else {
+ long interval = Preferences.weatherRefreshIntervalInMs(context);
+ scheduleUpdate(context, lastUpdate + interval - System.currentTimeMillis());
+ }
+ }
+
+ public static PendingIntent getUpdateIntent(Context context, boolean force) {
+ Intent i = new Intent(context, WeatherUpdateService.class);
+ if (force) {
+ i.setAction(ACTION_FORCE_UPDATE);
+ }
+ return PendingIntent.getService(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ public static void cancelUpdates(Context context) {
+ AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ am.cancel(getUpdateIntent(context, true));
+ am.cancel(getUpdateIntent(context, false));
+ }
+}