aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDanny Baumann <dannybaumann@web.de>2013-10-09 15:29:06 +0200
committerDanny Baumann <dannybaumann@web.de>2013-10-13 13:37:00 +0200
commitd0ea3088d7b866315e9a54e875de6caae71b48b1 (patch)
treeb78a178e620c8e01555e0e4ca67bd7ba530b90eb /src
parent1a961f9d5780f1c97270703dad20eace4c475bf6 (diff)
downloadandroid_packages_apps_LockClock-d0ea3088d7b866315e9a54e875de6caae71b48b1.tar.gz
android_packages_apps_LockClock-d0ea3088d7b866315e9a54e875de6caae71b48b1.tar.bz2
android_packages_apps_LockClock-d0ea3088d7b866315e9a54e875de6caae71b48b1.zip
Improve weather retrieval code.
- add weather provider abstraction - centralize yahoo specific bits at one place - switch to parsing JSON instead of XML - switch to using places instead of placefinder, which allows localized city names and custom location selection Change-Id: I5acfa084fda5024bd1af940a866fcdf77ef2895a
Diffstat (limited to 'src')
-rwxr-xr-xsrc/com/cyanogenmod/lockclock/misc/Constants.java5
-rw-r--r--src/com/cyanogenmod/lockclock/misc/Preferences.java20
-rw-r--r--src/com/cyanogenmod/lockclock/preference/WeatherPreferences.java103
-rwxr-xr-xsrc/com/cyanogenmod/lockclock/weather/HttpRetriever.java153
-rwxr-xr-xsrc/com/cyanogenmod/lockclock/weather/WeatherInfo.java40
-rw-r--r--src/com/cyanogenmod/lockclock/weather/WeatherProvider.java37
-rwxr-xr-xsrc/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java93
-rw-r--r--src/com/cyanogenmod/lockclock/weather/WeatherXmlParser.java154
-rwxr-xr-xsrc/com/cyanogenmod/lockclock/weather/YahooPlaceFinder.java61
-rw-r--r--src/com/cyanogenmod/lockclock/weather/YahooWeatherProvider.java204
10 files changed, 411 insertions, 459 deletions
diff --git a/src/com/cyanogenmod/lockclock/misc/Constants.java b/src/com/cyanogenmod/lockclock/misc/Constants.java
index bb783c3..535dcb0 100755
--- a/src/com/cyanogenmod/lockclock/misc/Constants.java
+++ b/src/com/cyanogenmod/lockclock/misc/Constants.java
@@ -35,14 +35,15 @@ public class Constants {
public static final String SHOW_WEATHER = "show_weather";
public static final String WEATHER_USE_CUSTOM_LOCATION = "weather_use_custom_location";
- public static final String WEATHER_CUSTOM_LOCATION_STRING = "weather_custom_location_string";
+ public static final String WEATHER_CUSTOM_LOCATION_ID = "weather_custom_location_id";
+ public static final String WEATHER_CUSTOM_LOCATION_CITY = "weather_custom_location_city";
public static final String WEATHER_SHOW_LOCATION = "weather_show_location";
public static final String WEATHER_SHOW_TIMESTAMP = "weather_show_timestamp";
public static final String WEATHER_USE_METRIC = "weather_use_metric";
public static final String WEATHER_INVERT_LOWHIGH = "weather_invert_lowhigh";
public static final String WEATHER_REFRESH_INTERVAL = "weather_refresh_interval";
public static final String WEATHER_USE_ALTERNATE_ICONS = "weather_use_alternate_icons";
- public static final String WEATHER_WOEID = "weather_woeid";
+ public static final String WEATHER_LOCATION_ID = "weather_woeid";
public static final String WEATHER_SHOW_WHEN_MINIMIZED = "weather_show_when_minimized";
public static final String WEATHER_FONT_COLOR = "weather_font_color";
public static final String WEATHER_TIMESTAMP_FONT_COLOR = "weather_timestamp_font_color";
diff --git a/src/com/cyanogenmod/lockclock/misc/Preferences.java b/src/com/cyanogenmod/lockclock/misc/Preferences.java
index 9979b50..4c20ace 100644
--- a/src/com/cyanogenmod/lockclock/misc/Preferences.java
+++ b/src/com/cyanogenmod/lockclock/misc/Preferences.java
@@ -150,8 +150,16 @@ public class Preferences {
return getPrefs(context).getBoolean(Constants.WEATHER_USE_CUSTOM_LOCATION, false);
}
- public static String customWeatherLocation(Context context) {
- return getPrefs(context).getString(Constants.WEATHER_CUSTOM_LOCATION_STRING, null);
+ public static String customWeatherLocationId(Context context) {
+ return getPrefs(context).getString(Constants.WEATHER_CUSTOM_LOCATION_ID, null);
+ }
+
+ public static void setCustomWeatherLocationId(Context context, String id) {
+ getPrefs(context).edit().putString(Constants.WEATHER_CUSTOM_LOCATION_ID, id).apply();
+ }
+
+ public static String customWeatherLocationCity(Context context) {
+ return getPrefs(context).getString(Constants.WEATHER_CUSTOM_LOCATION_CITY, null);
}
public static void setCachedWeatherInfo(Context context, long timestamp, WeatherInfo data) {
@@ -172,12 +180,12 @@ public class Preferences {
getPrefs(context).getString(Constants.WEATHER_DATA, null));
}
- public static String getCachedWoeid(Context context) {
- return getPrefs(context).getString(Constants.WEATHER_WOEID, null);
+ public static String getCachedLocationId(Context context) {
+ return getPrefs(context).getString(Constants.WEATHER_LOCATION_ID, null);
}
- public static void setCachedWoeid(Context context, String woeid) {
- getPrefs(context).edit().putString(Constants.WEATHER_WOEID, woeid).apply();
+ public static void setCachedLocationId(Context context, String id) {
+ getPrefs(context).edit().putString(Constants.WEATHER_LOCATION_ID, id).apply();
}
public static Set<String> calendarsToDisplay(Context context) {
diff --git a/src/com/cyanogenmod/lockclock/preference/WeatherPreferences.java b/src/com/cyanogenmod/lockclock/preference/WeatherPreferences.java
index f1468df..dde2e9e 100644
--- a/src/com/cyanogenmod/lockclock/preference/WeatherPreferences.java
+++ b/src/com/cyanogenmod/lockclock/preference/WeatherPreferences.java
@@ -45,7 +45,10 @@ import com.cyanogenmod.lockclock.R;
import com.cyanogenmod.lockclock.misc.Constants;
import com.cyanogenmod.lockclock.misc.Preferences;
import com.cyanogenmod.lockclock.weather.WeatherUpdateService;
-import com.cyanogenmod.lockclock.weather.YahooPlaceFinder;
+import com.cyanogenmod.lockclock.weather.WeatherProvider.LocationResult;
+import com.cyanogenmod.lockclock.weather.YahooWeatherProvider;
+
+import java.util.List;
public class WeatherPreferences extends PreferenceFragment implements
OnPreferenceClickListener, OnSharedPreferenceChangeListener {
@@ -53,7 +56,7 @@ public class WeatherPreferences extends PreferenceFragment implements
private static final String[] LOCATION_PREF_KEYS = new String[] {
Constants.WEATHER_USE_CUSTOM_LOCATION,
- Constants.WEATHER_CUSTOM_LOCATION_STRING
+ Constants.WEATHER_CUSTOM_LOCATION_CITY
};
private static final String[] WEATHER_REFRESH_KEYS = new String[] {
Constants.SHOW_WEATHER,
@@ -80,7 +83,7 @@ public class WeatherPreferences extends PreferenceFragment implements
// Load items that need custom summaries etc.
mUseCustomLoc = (CheckBoxPreference) findPreference(Constants.WEATHER_USE_CUSTOM_LOCATION);
mUseCustomLoc.setOnPreferenceClickListener(this);
- mCustomWeatherLoc = (EditTextPreference) findPreference(Constants.WEATHER_CUSTOM_LOCATION_STRING);
+ mCustomWeatherLoc = (EditTextPreference) findPreference(Constants.WEATHER_CUSTOM_LOCATION_CITY);
mCustomWeatherLoc.setOnPreferenceClickListener(this);
updateLocationSummary();
@@ -133,8 +136,8 @@ public class WeatherPreferences extends PreferenceFragment implements
for (String k : LOCATION_PREF_KEYS) {
if (TextUtils.equals(key, k)) {
- // location pref has changed -> clear out woeid cache
- Preferences.setCachedWoeid(mContext, null);
+ // location pref has changed -> clear out location id cache
+ Preferences.setCachedLocationId(mContext, null);
forceWeatherUpdate = true;
break;
}
@@ -167,14 +170,14 @@ public class WeatherPreferences extends PreferenceFragment implements
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference == mCustomWeatherLoc) {
- String location = com.cyanogenmod.lockclock.misc.Preferences.customWeatherLocation(mContext);
+ String location = Preferences.customWeatherLocationCity(mContext);
if (location != null) {
mCustomWeatherLoc.getEditText().setText(location);
mCustomWeatherLoc.getEditText().setSelection(location.length());
}
- mCustomWeatherLoc.getDialog().findViewById(android.R.id.button1)
- .setOnClickListener(new View.OnClickListener() {
+ final View okButton = mCustomWeatherLoc.getDialog().findViewById(android.R.id.button1);
+ okButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final ProgressDialog d = new ProgressDialog(mContext);
@@ -185,22 +188,84 @@ public class WeatherPreferences extends PreferenceFragment implements
final String location = mCustomWeatherLoc.getEditText().getText().toString();
final WeatherLocationTask task = new WeatherLocationTask() {
@Override
- protected void onPostExecute(String woeid) {
- if (woeid == null) {
+ protected void onPostExecute(final List<LocationResult> results) {
+ if (results == null || results.isEmpty()) {
Toast.makeText(mContext,
mContext.getString(R.string.weather_retrieve_location_dialog_title),
Toast.LENGTH_SHORT)
.show();
+ } else if (results.size() > 1) {
+ handleResultDisambiguation(results);
} else {
- mCustomWeatherLoc.setText(location);
- mCustomWeatherLoc.setSummary(location);
- mCustomWeatherLoc.getDialog().dismiss();
+ applyLocation(results.get(0));
}
d.dismiss();
}
};
task.execute(location);
}
+
+ private void handleResultDisambiguation(final List<LocationResult> results) {
+ CharSequence[] items = buildItemList(results);
+ new AlertDialog.Builder(mContext)
+ .setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ applyLocation(results.get(which));
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .setTitle(R.string.weather_select_location)
+ .show();
+ }
+
+ private CharSequence[] buildItemList(List<LocationResult> results) {
+ boolean needCountry = false, needPostal = false;
+ String countryId = results.get(0).countryId;
+ String postalCity = null, postalCountryId = null;
+
+ for (LocationResult result : results) {
+ if (!TextUtils.equals(result.countryId, countryId)) {
+ needCountry = true;
+ }
+ if (TextUtils.equals(result.city, postalCity)
+ && TextUtils.equals(result.countryId, postalCountryId)) {
+ needPostal = true;
+ }
+ if (postalCity == null) {
+ postalCity = result.city;
+ postalCountryId = result.countryId;
+ }
+ if (needPostal && needCountry) {
+ break;
+ }
+ }
+
+ int count = results.size();
+ CharSequence[] items = new CharSequence[count];
+ for (int i = 0; i < count; i++) {
+ LocationResult result = results.get(i);
+ StringBuilder builder = new StringBuilder();
+ if (needPostal && result.postal != null) {
+ builder.append(result.postal).append(" ");
+ }
+ builder.append(result.city);
+ if (needCountry) {
+ String country = result.country != null
+ ? result.country : result.countryId;
+ builder.append(" (").append(country).append(")");
+ }
+ items[i] = builder.toString();
+ }
+ return items;
+ }
+ private void applyLocation(final LocationResult result) {
+ Preferences.setCustomWeatherLocationId(mContext, result.id);
+ mCustomWeatherLoc.setText(result.city);
+ mCustomWeatherLoc.setSummary(result.city);
+ mCustomWeatherLoc.getDialog().dismiss();
+ }
});
return true;
}
@@ -211,24 +276,22 @@ public class WeatherPreferences extends PreferenceFragment implements
// Utility classes and supporting methods
//===============================================================================================
- private class WeatherLocationTask extends AsyncTask<String, Void, String> {
+ private class WeatherLocationTask extends AsyncTask<String, Void, List<LocationResult>> {
@Override
- protected String doInBackground(String... input) {
- String woeid = null;
-
+ protected List<LocationResult> doInBackground(String... input) {
try {
- woeid = YahooPlaceFinder.geoCode(mContext, input[0]);
+ return new YahooWeatherProvider(mContext).getLocations(input[0]);
} catch (Exception e) {
Log.e(TAG, "Could not resolve location", e);
}
- return woeid;
+ return null;
}
}
private void updateLocationSummary() {
if (mUseCustomLoc.isChecked()) {
- String location = com.cyanogenmod.lockclock.misc.Preferences.customWeatherLocation(mContext);
+ String location = Preferences.customWeatherLocationCity(mContext);
if (location == null) {
location = getResources().getString(R.string.unknown);
}
diff --git a/src/com/cyanogenmod/lockclock/weather/HttpRetriever.java b/src/com/cyanogenmod/lockclock/weather/HttpRetriever.java
index c735735..957d6dc 100755
--- a/src/com/cyanogenmod/lockclock/weather/HttpRetriever.java
+++ b/src/com/cyanogenmod/lockclock/weather/HttpRetriever.java
@@ -1,150 +1,45 @@
-/******************************************************************************
- * Class : HttpConnectHelper.java *
- * Main Weather activity, in this demo apps i use API from yahoo, you can *
- * use other weather web service which you prefer *
- * *
- * Version : v1.0 *
- * Date : May 09, 2011 *
- * Copyright (c)-2011 DatNQ some right reserved *
- * You can distribute, modify or what ever you want but WITHOUT ANY WARRANTY *
- * Be honest by keep credit of this file *
- * *
- * If you have any concern, feel free to contact with me via email, i will *
- * check email in free time *
- * Email: nguyendatnq@gmail.com *
- * ---------------------------------------------------------------------------*
- * Modification Logs: *
- * KEYCHANGE DATE AUTHOR DESCRIPTION *
- * ---------------------------------------------------------------------------*
- * ------- May 09, 2011 DatNQ Create new *
- ******************************************************************************/
-
-/**
- * Modification into Android-internal HttpRetreiver.java
- * Copyright (C) 2012 The AOKP Project
+/*
+ * Copyright (C) 2013 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.util.Log;
+
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-
-import org.w3c.dom.Document;
-
-import android.util.Log;
public class HttpRetriever {
+ private static final String TAG = "HttpRetriever";
- private final String TAG = getClass().getSimpleName();
- private DefaultHttpClient client = new DefaultHttpClient();
- private HttpURLConnection httpConnection;
-
- public String retrieve(String url) {
- HttpGet get = new HttpGet(url);
+ public static String retrieve(String url) {
+ HttpGet request = new HttpGet(url);
try {
- HttpResponse getResponse = client.execute(get);
- HttpEntity getResponseEntity = getResponse.getEntity();
- if (getResponseEntity != null) {
- String response = EntityUtils.toString(getResponseEntity);
- return response;
+ HttpResponse response = new DefaultHttpClient().execute(request);
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ return EntityUtils.toString(entity);
}
} catch (IOException e) {
- e.printStackTrace();
+ Log.e(TAG, "Couldn't retrieve data", e);
}
return null;
}
-
- private void requestConnectServer(String strURL) throws IOException {
- httpConnection = (HttpURLConnection) new URL(strURL).openConnection();
- httpConnection.connect();
-
- if (httpConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
- Log.e(TAG, "Something wrong with connection");
- httpConnection.disconnect();
- throw new IOException("Error in connection: " + httpConnection.getResponseCode());
- }
- }
-
- private void requestDisconnect() {
- if (httpConnection != null) {
- httpConnection.disconnect();
- }
- }
-
- public Document getDocumentFromURL(String strURL) throws IOException {
- if (strURL == null) {
- Log.e(TAG, "Invalid input URL");
- return null;
- }
-
- // Connect to server, get data and close
- requestConnectServer(strURL);
- String strDocContent = getDataFromConnection();
- requestDisconnect();
-
- if (strDocContent == null) {
- Log.e(TAG, "Cannot get XML content");
- return null;
- }
-
- int strContentSize = strDocContent.length();
- StringBuffer strBuff = new StringBuffer();
- strBuff.setLength(strContentSize + 1);
- strBuff.append(strDocContent);
- ByteArrayInputStream is = new ByteArrayInputStream(strDocContent.getBytes());
-
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- DocumentBuilder db;
- Document docData = null;
-
- try {
- db = dbf.newDocumentBuilder();
- docData = db.parse(is);
- } catch (Exception e) {
- Log.e(TAG, "Parser data error");
- return null;
- }
- return docData;
- }
-
- private String getDataFromConnection() throws IOException {
- if (httpConnection == null) {
- Log.e(TAG, "Connection is null");
- return null;
- }
-
- InputStream inputStream = httpConnection.getInputStream();
- if (inputStream == null) {
- Log.e(TAG, "Input stream error");
- return null;
- }
-
- StringBuffer strBuf = new StringBuffer();
- BufferedReader buffReader = new BufferedReader(new InputStreamReader(inputStream));
- String strLine = "";
-
- while ((strLine = buffReader.readLine()) != null) {
- strBuf.append(strLine + "\n");
- }
-
- // Release resource to system
- buffReader.close();
- inputStream.close();
- return strBuf.toString();
- }
}
diff --git a/src/com/cyanogenmod/lockclock/weather/WeatherInfo.java b/src/com/cyanogenmod/lockclock/weather/WeatherInfo.java
index fb7c5c5..0d7a089 100755
--- a/src/com/cyanogenmod/lockclock/weather/WeatherInfo.java
+++ b/src/com/cyanogenmod/lockclock/weather/WeatherInfo.java
@@ -31,6 +31,7 @@ public class WeatherInfo {
private Context mContext;
+ private String id;
private String city;
private String forecastDate;
private String condition;
@@ -45,11 +46,12 @@ public class WeatherInfo {
private String speedUnit;
private long timestamp;
- public WeatherInfo(Context context,
+ public WeatherInfo(Context context, String id,
String city, String fdate, String condition, int conditionCode,
float temp, float low, float high, String tempUnit, float humidity,
float wind, int windDir, String speedUnit, long timestamp) {
- this.mContext = context;
+ this.mContext = context.getApplicationContext();
+ this.id = id;
this.city = city;
this.forecastDate = fdate;
this.condition = condition;
@@ -83,6 +85,10 @@ public class WeatherInfo {
return WidgetUtils.getOverlaidBitmap(mContext, resId, color);
}
+ public String getId() {
+ return id;
+ }
+
public String getCity() {
return city;
}
@@ -152,7 +158,9 @@ public class WeatherInfo {
StringBuilder builder = new StringBuilder();
builder.append("WeatherInfo for ");
builder.append(city);
- builder.append("@ ");
+ builder.append(" (");
+ builder.append(id);
+ builder.append(") @ ");
builder.append(getTimestamp());
builder.append(": ");
builder.append(getCondition());
@@ -175,6 +183,7 @@ public class WeatherInfo {
public String toSerializedString() {
StringBuilder builder = new StringBuilder();
+ builder.append(id).append('|');
builder.append(city).append('|');
builder.append(forecastDate).append('|');
builder.append(condition).append('|');
@@ -197,7 +206,7 @@ public class WeatherInfo {
}
String[] parts = input.split("\\|");
- if (parts == null || parts.length != 13) {
+ if (parts == null || parts.length != 14) {
return null;
}
@@ -206,21 +215,22 @@ public class WeatherInfo {
float temperature, low, high, humidity, wind;
try {
- conditionCode = Integer.parseInt(parts[3]);
- temperature = Float.parseFloat(parts[4]);
- low = Float.parseFloat(parts[5]);
- high = Float.parseFloat(parts[6]);
- humidity = Float.parseFloat(parts[8]);
- wind = Float.parseFloat(parts[9]);
- windDirection = Integer.parseInt(parts[10]);
- timestamp = Long.parseLong(parts[12]);
+ conditionCode = Integer.parseInt(parts[4]);
+ temperature = Float.parseFloat(parts[5]);
+ low = Float.parseFloat(parts[6]);
+ high = Float.parseFloat(parts[7]);
+ humidity = Float.parseFloat(parts[9]);
+ wind = Float.parseFloat(parts[10]);
+ windDirection = Integer.parseInt(parts[11]);
+ timestamp = Long.parseLong(parts[13]);
} catch (NumberFormatException e) {
return null;
}
return new WeatherInfo(context,
- /* city */ parts[0], /* date */ parts[1], /* condition */ parts[2],
- conditionCode, temperature, low, high, /* tempUnit */ parts[7],
- humidity, wind, windDirection, /* speedUnit */ parts[11], timestamp);
+ /* id */ parts[0], /* city */ parts[1], /* date */ parts[2],
+ /* condition */ parts[3], conditionCode, temperature, low, high,
+ /* tempUnit */ parts[8], humidity, wind, windDirection,
+ /* speedUnit */ parts[12], timestamp);
}
}
diff --git a/src/com/cyanogenmod/lockclock/weather/WeatherProvider.java b/src/com/cyanogenmod/lockclock/weather/WeatherProvider.java
new file mode 100644
index 0000000..15c8aff
--- /dev/null
+++ b/src/com/cyanogenmod/lockclock/weather/WeatherProvider.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 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.location.Location;
+
+import java.util.List;
+
+public interface WeatherProvider {
+ public class LocationResult {
+ public String id;
+ public String city;
+ public String postal;
+ public String countryId;
+ public String country;
+ };
+
+ List<LocationResult> getLocations(String input);
+
+ WeatherInfo getWeatherInfo(String id, String localizedCityName);
+
+ WeatherInfo getWeatherInfo(Location location);
+};
diff --git a/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java b/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java
index 720d904..2d1e688 100755
--- a/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java
+++ b/src/com/cyanogenmod/lockclock/weather/WeatherUpdateService.java
@@ -38,17 +38,12 @@ 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;
@@ -140,27 +135,6 @@ public class WeatherUpdateService extends Service {
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);
@@ -168,63 +142,38 @@ public class WeatherUpdateService extends Service {
return location;
}
- private String getCachedLocation() {
- WeatherInfo weatherInfo = Preferences.getCachedWeatherInfo(mContext);
- String location = (weatherInfo != null) ? weatherInfo.getCity() : null;
- if (D) Log.v(TAG, "Last known, cached 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 = null;
+ WeatherProvider provider = new YahooWeatherProvider(mContext);
+ String customLocationId = null, customLocationName = null;
if (Preferences.useCustomWeatherLocation(mContext)) {
- customLocation = Preferences.customWeatherLocation(mContext);
+ customLocationId = Preferences.customWeatherLocationId(mContext);
+ customLocationName = Preferences.customWeatherLocationCity(mContext);
}
- if (customLocation != null) {
- woeid = getWoeidForCustomLocation(customLocation);
- } else {
- Location location = getCurrentLocation();
- if (location != null) {
- woeid = getWoeidForCurrentLocation(location);
- } else {
- // work with cached location from last request for now
- customLocation = getCachedLocation();
- if (customLocation != null) {
- woeid = getWoeidForCustomLocation(customLocation);
- }
- // 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 an non-accuracy/network provider.
- WeatherLocationListener.registerIfNeeded(mContext, LocationManager.NETWORK_PROVIDER);
- }
+ if (customLocationId != null) {
+ return provider.getWeatherInfo(customLocationId, customLocationName);
}
- if (woeid == null || isCancelled()) {
- return null;
+ Location location = getCurrentLocation();
+ if (location != null) {
+ WeatherInfo info = provider.getWeatherInfo(location);
+ if (info != null) {
+ return info;
+ }
}
-
- Document doc = getDocument(woeid);
- if (doc == null || isCancelled()) {
- return null;
+ // work with cached location from last request for now
+ 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 an non-accuracy/network provider.
+ WeatherLocationListener.registerIfNeeded(mContext, LocationManager.NETWORK_PROVIDER);
- return new WeatherXmlParser(mContext).parseWeatherResponse(doc);
+ return null;
}
@Override
diff --git a/src/com/cyanogenmod/lockclock/weather/WeatherXmlParser.java b/src/com/cyanogenmod/lockclock/weather/WeatherXmlParser.java
deleted file mode 100644
index 552ffb4..0000000
--- a/src/com/cyanogenmod/lockclock/weather/WeatherXmlParser.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/******************************************************************************
- * Class : YahooWeatherHelper.java *
- * Parser helper for Yahoo *
- * *
- * Version : v1.0 *
- * Date : May 06, 2011 *
- * Copyright (c)-2011 DatNQ some right reserved *
- * You can distribute, modify or what ever you want but WITHOUT ANY WARRANTY *
- * Be honest by keep credit of this file *
- * *
- * If you have any concern, feel free to contact with me via email, i will *
- * check email in free time *
- * Email: nguyendatnq@gmail.com *
- * ---------------------------------------------------------------------------*
- * Modification Logs: *
- * KEYCHANGE DATE AUTHOR DESCRIPTION *
- * ---------------------------------------------------------------------------*
- * ------- May 06, 2011 DatNQ Create new *
- ******************************************************************************/
-/*
- * Modification into Android-internal WeatherXmlParser.java
- * Copyright (C) 2012 The AOKP Project
- */
-
-package com.cyanogenmod.lockclock.weather;
-
-import java.io.StringReader;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-
-import android.content.Context;
-import android.util.Log;
-
-public class WeatherXmlParser {
-
- protected static final String TAG = "WeatherXmlParser";
-
- /** Yahoo attributes */
- private static final String PARAM_YAHOO_LOCATION = "yweather:location";
- private static final String PARAM_YAHOO_UNIT = "yweather:units";
- private static final String PARAM_YAHOO_ATMOSPHERE = "yweather:atmosphere";
- private static final String PARAM_YAHOO_CONDITION = "yweather:condition";
- private static final String PARAM_YAHOO_WIND = "yweather:wind";
- private static final String PARAM_YAHOO_FORECAST = "yweather:forecast";
-
- private static final String ATT_YAHOO_CITY = "city";
- private static final String ATT_YAHOO_TEMP = "temp";
- private static final String ATT_YAHOO_CODE = "code";
- private static final String ATT_YAHOO_TEMP_UNIT = "temperature";
- private static final String ATT_YAHOO_HUMIDITY = "humidity";
- private static final String ATT_YAHOO_TEXT = "text";
- private static final String ATT_YAHOO_DATE = "date";
- private static final String ATT_YAHOO_SPEED = "speed";
- private static final String ATT_YAHOO_DIRECTION = "direction";
- private static final String ATT_YAHOO_TODAY_HIGH = "high";
- private static final String ATT_YAHOO_TODAY_LOW = "low";
-
- private Context mContext;
-
- public WeatherXmlParser(Context context) {
- mContext = context;
- }
-
- private String getValueForAttribute(Element root, String tagName, String attributeName) {
- NamedNodeMap node = root.getElementsByTagName(tagName).item(0).getAttributes();
- if (node == null) {
- return null;
- }
- return node.getNamedItem(attributeName).getNodeValue();
- }
-
- private float getFloatForAttribute(Element root, String tagName, String attributeName)
- throws NumberFormatException {
- String value = getValueForAttribute(root, tagName, attributeName);
- if (value == null || value.equals("")) {
- return Float.NaN;
- }
- return Float.parseFloat(value);
- }
-
- private int getIntForAttribute(Element root, String tagName, String attributeName)
- throws NumberFormatException {
- String value = getValueForAttribute(root, tagName, attributeName);
- if (value == null || value.equals("")) {
- return -1;
- }
- return Integer.parseInt(value);
- }
-
- public WeatherInfo parseWeatherResponse(Document docWeather) {
- if (docWeather == null) {
- Log.e(TAG, "Invalid doc weather");
- return null;
- }
-
- try {
- Element root = docWeather.getDocumentElement();
- root.normalize();
-
- WeatherInfo w = new WeatherInfo(mContext,
- /* city */ getValueForAttribute(root, PARAM_YAHOO_LOCATION, ATT_YAHOO_CITY),
- /* forecastDate */ getValueForAttribute(root, PARAM_YAHOO_CONDITION, ATT_YAHOO_DATE),
- /* condition */ getValueForAttribute(root, PARAM_YAHOO_CONDITION, ATT_YAHOO_TEXT),
- /* conditionCode */ getIntForAttribute(root, PARAM_YAHOO_CONDITION, ATT_YAHOO_CODE),
- /* temperature */ getFloatForAttribute(root, PARAM_YAHOO_CONDITION, ATT_YAHOO_TEMP),
- /* low */ getFloatForAttribute(root, PARAM_YAHOO_FORECAST, ATT_YAHOO_TODAY_LOW),
- /* high */ getFloatForAttribute(root, PARAM_YAHOO_FORECAST, ATT_YAHOO_TODAY_HIGH),
- /* tempUnit */ getValueForAttribute(root, PARAM_YAHOO_UNIT, ATT_YAHOO_TEMP_UNIT),
- /* humidity */ getFloatForAttribute(root, PARAM_YAHOO_ATMOSPHERE, ATT_YAHOO_HUMIDITY),
- /* wind */ getFloatForAttribute(root, PARAM_YAHOO_WIND, ATT_YAHOO_SPEED),
- /* windDir */ getIntForAttribute(root, PARAM_YAHOO_WIND, ATT_YAHOO_DIRECTION),
- /* speedUnit */ getValueForAttribute(root, PARAM_YAHOO_UNIT, ATT_YAHOO_SPEED),
- System.currentTimeMillis());
-
- Log.d(TAG, "Weather updated: " + w);
- return w;
- } catch (Exception e) {
- Log.e(TAG, "Couldn't parse Yahoo weather XML", e);
- return null;
- }
- }
-
- public String parsePlaceFinderResponse(String response) {
- try {
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- DocumentBuilder db = dbf.newDocumentBuilder();
- Document doc = db.parse(new InputSource(new StringReader(response)));
-
- NodeList resultNodes = doc.getElementsByTagName("Result");
-
- Node resultNode = resultNodes.item(0);
- NodeList attrsList = resultNode.getChildNodes();
-
- for (int i = 0; i < attrsList.getLength(); i++) {
- Node node = attrsList.item(i);
- Node firstChild = node.getFirstChild();
- if ("woeid".equalsIgnoreCase(node.getNodeName()) && firstChild != null) {
- return firstChild.getNodeValue();
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "Couldn't parse Yahoo place finder XML", e);
- }
- return null;
- }
-}
diff --git a/src/com/cyanogenmod/lockclock/weather/YahooPlaceFinder.java b/src/com/cyanogenmod/lockclock/weather/YahooPlaceFinder.java
deleted file mode 100755
index b08fc13..0000000
--- a/src/com/cyanogenmod/lockclock/weather/YahooPlaceFinder.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2013 The CyanogenMod Project (DvTonder)
- * Copyright (C) 2012 The AOKP 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.content.Context;
-import android.net.Uri;
-
-import com.cyanogenmod.lockclock.misc.Preferences;
-
-public class YahooPlaceFinder {
-
- private static final String YAHOO_API_BASE_URL = "http://query.yahooapis.com/v1/public/yql?q=" +
- Uri.encode("select woeid from geo.placefinder where text =");
-
- public static String reverseGeoCode(Context c, double latitude, double longitude) {
- String formattedCoordinates = String.format("\"%s %s\" and gflags=\"R\"",
- String.valueOf(latitude), String.valueOf(longitude));
- String url = YAHOO_API_BASE_URL + Uri.encode(formattedCoordinates);
- String response = new HttpRetriever().retrieve(url);
- if (response == null) {
- return null;
- }
-
- String woeid = new WeatherXmlParser(c).parsePlaceFinderResponse(response);
- if (woeid != null) {
- // cache the result for potential reuse - the placefinder service API is rate limited
- Preferences.setCachedWoeid(c, woeid);
- }
- return woeid;
- }
-
- public static String geoCode(Context c, String location) {
- String url = YAHOO_API_BASE_URL + Uri.encode(String.format("\"%s\"",location));
- String response = new HttpRetriever().retrieve(url);
- if (response == null) {
- return null;
- }
-
- String woeid = new WeatherXmlParser(c).parsePlaceFinderResponse(response);
- if (woeid != null) {
- // cache the result for potential reuse - the placefinder service API is rate limited
- Preferences.setCachedWoeid(c, woeid);
- }
- return woeid;
- }
-}
diff --git a/src/com/cyanogenmod/lockclock/weather/YahooWeatherProvider.java b/src/com/cyanogenmod/lockclock/weather/YahooWeatherProvider.java
new file mode 100644
index 0000000..2a07e5b
--- /dev/null
+++ b/src/com/cyanogenmod/lockclock/weather/YahooWeatherProvider.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2013 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.content.Context;
+import android.location.Location;
+import android.net.Uri;
+import android.util.Log;
+
+import com.cyanogenmod.lockclock.misc.Preferences;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class YahooWeatherProvider implements WeatherProvider {
+ private static final String TAG = "YahooWeatherProvider";
+
+ private static final String URL_WEATHER =
+ "http://query.yahooapis.com/v1/public/yql?format=json&q=" +
+ Uri.encode("select * from weather.forecast where woeid =");
+ private static final String URL_LOCATION =
+ "http://query.yahooapis.com/v1/public/yql?format=json&q=" +
+ Uri.encode("select woeid, postal, admin1, admin2, admin3, " +
+ "locality1, locality2, country from geo.places where text =");
+
+ private static final String[] LOCALITY_NAMES = new String[] {
+ "locality2", "locality1", "admin3", "admin2", "admin1"
+ };
+
+ private Context mContext;
+
+ public YahooWeatherProvider(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public List<LocationResult> getLocations(String input) {
+ String locale = mContext.getResources().getConfiguration().locale.getCountry();
+ String params = "\"" + input + "\" and lang = \"" + locale + "\"";
+ String url = URL_LOCATION + Uri.encode(params);
+ JSONArray places = fetchPlaceList(url);
+
+ if (places == null) {
+ return null;
+ }
+
+ ArrayList<LocationResult> results = new ArrayList<LocationResult>();
+ for (int i = 0; i < places.length(); i++) {
+ try {
+ LocationResult result = parsePlace(places.getJSONObject(i), true);
+ if (result != null) {
+ results.add(result);
+ }
+ } catch (JSONException e) {
+ Log.w(TAG, "Found invalid JSON place record, ignoring.", e);
+ }
+ }
+
+ return results;
+ }
+
+ public WeatherInfo getWeatherInfo(String id, String localizedCityName) {
+ String unit = Preferences.useMetricUnits(mContext) ? "c" : "f";
+ String params = "\"" + id + "\" and u = \"" + unit + "\"";
+ String url = URL_WEATHER + Uri.encode(params);
+ String response = HttpRetriever.retrieve(url);
+
+ if (response == null) {
+ return null;
+ }
+
+ try {
+ JSONObject rootObject = new JSONObject(response);
+ JSONObject results = rootObject.getJSONObject("query").getJSONObject("results");
+ JSONObject data = results.getJSONObject("channel");
+
+ String city = localizedCityName != null
+ ? localizedCityName : data.getJSONObject("location").getString("city");
+
+ JSONObject wind = data.getJSONObject("wind");
+ JSONObject units = data.getJSONObject("units");
+ JSONObject item = data.getJSONObject("item");
+ JSONObject conditions = item.getJSONObject("condition");
+ JSONObject forecast = item.getJSONArray("forecast").getJSONObject(0);
+
+ WeatherInfo w = new WeatherInfo(mContext, id, city,
+ /* forecastDate */ conditions.getString("date"),
+ /* condition */ conditions.getString("text"),
+ /* conditionCode */ conditions.getInt("code"),
+ /* temperature */ (float) conditions.getDouble("temp"),
+ /* low */ (float) forecast.getDouble("low"),
+ /* high */ (float) forecast.getDouble("high"),
+ /* tempUnit */ units.getString("temperature"),
+ /* humidity */ (float) data.getJSONObject("atmosphere").getDouble("humidity"),
+ /* wind */ (float) wind.getDouble("speed"),
+ /* windDir */ wind.getInt("direction"),
+ /* speedUnit */ units.getString("speed"),
+ System.currentTimeMillis());
+
+ Log.d(TAG, "Weather updated: " + w);
+ return w;
+ } catch (JSONException e) {
+ Log.w(TAG, "Weather condition data is invalid.", e);
+ }
+
+ return null;
+ }
+
+ public WeatherInfo getWeatherInfo(Location location) {
+ String formattedCoordinates = String.format(Locale.US, "\"%f %f\"",
+ location.getLatitude(), location.getLongitude());
+ String url = URL_LOCATION + Uri.encode(formattedCoordinates);
+ JSONArray places = fetchPlaceList(url);
+ if (places == null) {
+ return null;
+ }
+
+ for (int i = 0; i < places.length(); i++) {
+ LocationResult result = null;
+ try {
+ result = parsePlace(places.getJSONObject(i), false);
+ } catch (JSONException e) {
+ Log.w(TAG, "Found invalid JSON place record, ignoring.", e);
+ }
+ if (result != null) {
+ Log.d(TAG, "Looking up weather for " + result.city + " (" + result.id + ")");
+ WeatherInfo info = getWeatherInfo(result.id, result.city);
+ if (info != null) {
+ // cache the result for potential reuse
+ // (the placefinder service API is rate limited)
+ Preferences.setCachedLocationId(mContext, result.id);
+ return info;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private LocationResult parsePlace(JSONObject place, boolean requireFullDataSet)
+ throws JSONException {
+ LocationResult result = new LocationResult();
+ JSONObject country = place.getJSONObject("country");
+
+ result.id = place.getString("woeid");
+ result.country = country.getString("content");
+ result.countryId = country.getString("code");
+ if (!place.isNull("postal")) {
+ result.postal = place.getJSONObject("postal").getString("content");
+ }
+
+ for (String locName : LOCALITY_NAMES) {
+ if (!place.isNull(locName)) {
+ result.city = place.getJSONObject(locName).getString("content");
+ break;
+ }
+ }
+
+ if (result.id == null || result.city == null) {
+ return null;
+ }
+ if (requireFullDataSet && (result.countryId == null || result.postal == null)) {
+ return null;
+ }
+
+ return result;
+ }
+
+ private JSONArray fetchPlaceList(String url) {
+ String response = HttpRetriever.retrieve(url);
+ if (response == null) {
+ return null;
+ }
+
+ try {
+ JSONObject rootObject = new JSONObject(response);
+ JSONObject results = rootObject.getJSONObject("query").getJSONObject("results");
+ return results.getJSONArray("place");
+ } catch (JSONException e) {
+ Log.w(TAG, "Received malformed places data", e);
+ }
+
+ return null;
+ }
+};