PageRenderTime 35ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/Sky/src/org/jsharkey/sky/webservice/WebserviceHelper.java

http://android-sky.googlecode.com/
Java | 213 lines | 131 code | 34 blank | 48 comment | 18 complexity | 1aa963328e10318ba72d3ad8e7a91d4f MD5 | raw file
  1. /*
  2. * Copyright (C) 2009 Jeff Sharkey, http://jsharkey.org/
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.jsharkey.sky.webservice;
  17. import java.io.IOException;
  18. import java.io.InputStreamReader;
  19. import java.io.Reader;
  20. import java.util.List;
  21. import org.apache.http.HttpEntity;
  22. import org.apache.http.HttpResponse;
  23. import org.apache.http.StatusLine;
  24. import org.apache.http.client.HttpClient;
  25. import org.apache.http.client.methods.HttpGet;
  26. import org.apache.http.impl.client.DefaultHttpClient;
  27. import org.jsharkey.sky.ForecastProvider;
  28. import org.jsharkey.sky.ForecastUtils;
  29. import org.jsharkey.sky.R;
  30. import org.jsharkey.sky.ForecastProvider.AppWidgets;
  31. import org.jsharkey.sky.ForecastProvider.AppWidgetsColumns;
  32. import org.jsharkey.sky.ForecastProvider.Forecasts;
  33. import org.jsharkey.sky.ForecastProvider.ForecastsColumns;
  34. import org.jsharkey.sky.webservice.Forecast.ParseException;
  35. import android.content.ContentResolver;
  36. import android.content.ContentValues;
  37. import android.content.Context;
  38. import android.content.pm.PackageInfo;
  39. import android.content.pm.PackageManager;
  40. import android.content.pm.PackageManager.NameNotFoundException;
  41. import android.database.Cursor;
  42. import android.net.Uri;
  43. import android.text.format.DateUtils;
  44. import android.util.Log;
  45. /**
  46. * Helper class to handle querying a webservice for forecast details and parsing
  47. * results into {@link ForecastProvider}.
  48. */
  49. public class WebserviceHelper {
  50. private static final String TAG = "ForcastHelper";
  51. private static final String[] PROJECTION_APPWIDGET = {
  52. AppWidgetsColumns.LAT,
  53. AppWidgetsColumns.LON,
  54. AppWidgetsColumns.COUNTRY_CODE
  55. };
  56. private static final int COL_LAT = 0;
  57. private static final int COL_LON = 1;
  58. private static final int COL_COUNTRY_CODE = 2;
  59. public static final String COUNTRY_US = "US";
  60. /**
  61. * Timeout to wait for webservice to respond. Because we're in the
  62. * background, we don't mind waiting for good data.
  63. */
  64. static final long WEBSERVICE_TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
  65. /**
  66. * User-agent string to use when making requests. Should be filled using
  67. * {@link #prepareUserAgent(Context)} before making any other calls.
  68. */
  69. private static String sUserAgent = null;
  70. private static HttpClient sClient = new DefaultHttpClient();
  71. /**
  72. * Prepare the internal User-Agent string for use. This requires a
  73. * {@link Context} to pull the package name and version number for this
  74. * application.
  75. */
  76. public static void prepareUserAgent(Context context) {
  77. try {
  78. // Read package name and version number from manifest
  79. PackageManager manager = context.getPackageManager();
  80. PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
  81. sUserAgent = String.format(context.getString(R.string.template_user_agent),
  82. info.packageName, info.versionName);
  83. } catch(NameNotFoundException e) {
  84. Log.e(TAG, "Couldn't find package information in PackageManager", e);
  85. }
  86. }
  87. /**
  88. * Open a request to the given URL, returning a {@link Reader} across the
  89. * response from that API.
  90. */
  91. public static Reader queryApi(String url) throws ParseException {
  92. if (sUserAgent == null) {
  93. throw new ParseException("Must prepare user agent string");
  94. }
  95. Reader reader = null;
  96. HttpGet request = new HttpGet(url);
  97. request.setHeader("User-Agent", sUserAgent);
  98. try {
  99. HttpResponse response = sClient.execute(request);
  100. StatusLine status = response.getStatusLine();
  101. Log.d(TAG, "Request returned status " + status);
  102. HttpEntity entity = response.getEntity();
  103. reader = new InputStreamReader(entity.getContent());
  104. } catch (IOException e) {
  105. throw new ParseException("Problem calling forecast API", e);
  106. }
  107. return reader;
  108. }
  109. /**
  110. * Perform a webservice query to retrieve and store the forecast for the
  111. * given widget. This call blocks until request is finished and
  112. * {@link Forecasts#CONTENT_URI} has been updated.
  113. */
  114. public static void updateForecasts(Context context, Uri appWidgetUri, int days)
  115. throws ParseException {
  116. if (sUserAgent == null) {
  117. prepareUserAgent(context);
  118. }
  119. Uri appWidgetForecasts = Uri.withAppendedPath(appWidgetUri, AppWidgets.TWIG_FORECASTS);
  120. ContentResolver resolver = context.getContentResolver();
  121. Cursor cursor = null;
  122. double lat = Double.NaN;
  123. double lon = Double.NaN;
  124. String countryCode = null;
  125. // Pull exact forecast location from database
  126. try {
  127. cursor = resolver.query(appWidgetUri, PROJECTION_APPWIDGET, null, null, null);
  128. if (cursor != null && cursor.moveToFirst()) {
  129. lat = cursor.getDouble(COL_LAT);
  130. lon = cursor.getDouble(COL_LON);
  131. countryCode = cursor.getString(COL_COUNTRY_CODE);
  132. }
  133. } finally {
  134. if (cursor != null) {
  135. cursor.close();
  136. }
  137. }
  138. Log.d(TAG, "using country code=" + countryCode);
  139. // Query webservice for this location
  140. List<Forecast> forecasts = null;
  141. if (COUNTRY_US.equals(countryCode)) {
  142. forecasts = new NoaaSource().getForecasts(lat, lon, days);
  143. } else {
  144. forecasts = new MetarSource().getForecasts(lat, lon, days);
  145. }
  146. if (forecasts == null || forecasts.size() == 0) {
  147. throw new ParseException("No forecasts found from webservice query");
  148. }
  149. // Purge existing forecasts covered by incoming data, and anything
  150. // before today
  151. long lastMidnight = ForecastUtils.getLastMidnight();
  152. long earliest = Long.MAX_VALUE;
  153. for (Forecast forecast : forecasts) {
  154. earliest = Math.min(earliest, forecast.validStart);
  155. }
  156. resolver.delete(appWidgetForecasts,
  157. ForecastsColumns.VALID_START + " >= " + earliest + " OR " +
  158. ForecastsColumns.VALID_START + " <= " + lastMidnight, null);
  159. // Insert any new forecasts found
  160. ContentValues values = new ContentValues();
  161. for (Forecast forecast : forecasts) {
  162. Log.d(TAG, "inserting forecast with validStart=" + forecast.validStart);
  163. values.clear();
  164. values.put(ForecastsColumns.VALID_START, forecast.validStart);
  165. values.put(ForecastsColumns.TEMP_HIGH, forecast.tempHigh);
  166. values.put(ForecastsColumns.TEMP_LOW, forecast.tempLow);
  167. values.put(ForecastsColumns.CONDITIONS, forecast.conditions);
  168. values.put(ForecastsColumns.URL, forecast.url);
  169. if (forecast.alert) {
  170. values.put(ForecastsColumns.ALERT, ForecastsColumns.ALERT_TRUE);
  171. }
  172. resolver.insert(appWidgetForecasts, values);
  173. }
  174. // Mark widget cache as being updated
  175. values.clear();
  176. values.put(AppWidgetsColumns.LAST_UPDATED, System.currentTimeMillis());
  177. resolver.update(appWidgetUri, values, null, null);
  178. }
  179. }