gov.wa.wsdot.android.wsdot.service.MountainPassesSyncService.java Source code

Java tutorial

Introduction

Here is the source code for gov.wa.wsdot.android.wsdot.service.MountainPassesSyncService.java

Source

/*
 * Copyright (c) 2012 Washington State Department of Transportation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 */

package gov.wa.wsdot.android.wsdot.service;

import gov.wa.wsdot.android.wsdot.R;
import gov.wa.wsdot.android.wsdot.provider.WSDOTContract.Caches;
import gov.wa.wsdot.android.wsdot.provider.WSDOTContract.MountainPasses;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

import org.json.JSONArray;
import org.json.JSONObject;

import android.annotation.SuppressLint;
import android.app.IntentService;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.text.format.DateUtils;
import android.util.Log;

public class MountainPassesSyncService extends IntentService {

    private static final String DEBUG_TAG = "MountainPassesSyncService";
    @SuppressLint("UseSparseArrays")
    private static HashMap<Integer, String[]> weatherPhrases = new HashMap<Integer, String[]>();
    @SuppressLint("UseSparseArrays")
    private static HashMap<Integer, String[]> weatherPhrasesNight = new HashMap<Integer, String[]>();
    private static DateFormat parseDateFormat = new SimpleDateFormat("yyyy,M,d,H,m"); //e.g. [2010, 11, 2, 8, 22, 32, 883, 0, 0]
    private static DateFormat displayDateFormat = new SimpleDateFormat("MMMM d, yyyy h:mm a");
    private static final String MOUNTAIN_PASS_URL = "http://data.wsdot.wa.gov/mobile/MountainPassConditions.js.gz";

    public MountainPassesSyncService() {
        super("MountainPassesSyncService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        ContentResolver resolver = getContentResolver();
        Cursor cursor = null;
        long now = System.currentTimeMillis();
        boolean shouldUpdate = true;
        String responseString = "";

        /** 
         * Check the cache table for the last time data was downloaded. If we are within
         * the allowed time period, don't sync, otherwise get fresh data from the server.
         */
        try {
            cursor = resolver.query(Caches.CONTENT_URI, new String[] { Caches.CACHE_LAST_UPDATED },
                    Caches.CACHE_TABLE_NAME + " LIKE ?", new String[] { "mountain_passes" }, null);

            if (cursor != null && cursor.moveToFirst()) {
                long lastUpdated = cursor.getLong(0);
                //long deltaMinutes = (now - lastUpdated) / DateUtils.MINUTE_IN_MILLIS;
                //Log.d(DEBUG_TAG, "Delta since last update is " + deltaMinutes + " min");
                shouldUpdate = (Math.abs(now - lastUpdated) > (15 * DateUtils.MINUTE_IN_MILLIS));
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        // Ability to force a refresh of camera data.
        boolean forceUpdate = intent.getBooleanExtra("forceUpdate", false);

        if (shouldUpdate || forceUpdate) {
            List<Integer> starred = new ArrayList<Integer>();

            starred = getStarred();
            buildWeatherPhrases();

            try {
                URL url = new URL(MOUNTAIN_PASS_URL);
                URLConnection urlConn = url.openConnection();

                BufferedInputStream bis = new BufferedInputStream(urlConn.getInputStream());
                GZIPInputStream gzin = new GZIPInputStream(bis);
                InputStreamReader is = new InputStreamReader(gzin);
                BufferedReader in = new BufferedReader(is);

                String mDateUpdated = "";
                String jsonFile = "";
                String line;
                while ((line = in.readLine()) != null)
                    jsonFile += line;
                in.close();

                JSONObject obj = new JSONObject(jsonFile);
                JSONObject result = obj.getJSONObject("GetMountainPassConditionsResult");
                JSONArray passConditions = result.getJSONArray("PassCondition");
                String weatherCondition;
                Integer weather_image;
                Integer forecast_weather_image;
                List<ContentValues> passes = new ArrayList<ContentValues>();

                int numConditions = passConditions.length();
                for (int j = 0; j < numConditions; j++) {
                    JSONObject pass = passConditions.getJSONObject(j);
                    ContentValues passData = new ContentValues();
                    weatherCondition = pass.getString("WeatherCondition");
                    weather_image = getWeatherImage(weatherPhrases, weatherCondition);

                    String tempDate = pass.getString("DateUpdated");

                    try {
                        tempDate = tempDate.replace("[", "");
                        tempDate = tempDate.replace("]", "");

                        String[] a = tempDate.split(",");
                        StringBuilder sb = new StringBuilder();
                        for (int m = 0; m < 5; m++) {
                            sb.append(a[m]);
                            sb.append(",");
                        }
                        tempDate = sb.toString().trim();
                        tempDate = tempDate.substring(0, tempDate.length() - 1);
                        Date date = parseDateFormat.parse(tempDate);
                        mDateUpdated = displayDateFormat.format(date);
                    } catch (Exception e) {
                        Log.e(DEBUG_TAG, "Error parsing date: " + tempDate, e);
                        mDateUpdated = "N/A";
                    }

                    JSONArray forecasts = pass.getJSONArray("Forecast");
                    JSONArray forecastItems = new JSONArray();

                    int numForecasts = forecasts.length();
                    for (int l = 0; l < numForecasts; l++) {
                        JSONObject forecast = forecasts.getJSONObject(l);

                        if (isNight(forecast.getString("Day"))) {
                            forecast_weather_image = getWeatherImage(weatherPhrasesNight,
                                    forecast.getString("ForecastText"));
                        } else {
                            forecast_weather_image = getWeatherImage(weatherPhrases,
                                    forecast.getString("ForecastText"));
                        }

                        forecast.put("weather_icon", forecast_weather_image);

                        if (l == 0) {
                            if (weatherCondition.equals("")) {
                                weatherCondition = forecast.getString("ForecastText").split("\\.")[0] + ".";
                                weather_image = forecast_weather_image;
                            }
                        }

                        forecastItems.put(forecast);
                    }

                    passData.put(MountainPasses.MOUNTAIN_PASS_ID, pass.getString("MountainPassId"));
                    passData.put(MountainPasses.MOUNTAIN_PASS_NAME, pass.getString("MountainPassName"));
                    passData.put(MountainPasses.MOUNTAIN_PASS_WEATHER_ICON, weather_image);
                    passData.put(MountainPasses.MOUNTAIN_PASS_FORECAST, forecastItems.toString());
                    passData.put(MountainPasses.MOUNTAIN_PASS_WEATHER_CONDITION, weatherCondition);
                    passData.put(MountainPasses.MOUNTAIN_PASS_DATE_UPDATED, mDateUpdated);
                    passData.put(MountainPasses.MOUNTAIN_PASS_CAMERA, pass.getString("Cameras"));
                    passData.put(MountainPasses.MOUNTAIN_PASS_ELEVATION, pass.getString("ElevationInFeet"));
                    passData.put(MountainPasses.MOUNTAIN_PASS_TRAVEL_ADVISORY_ACTIVE,
                            pass.getString("TravelAdvisoryActive"));
                    passData.put(MountainPasses.MOUNTAIN_PASS_ROAD_CONDITION, pass.getString("RoadCondition"));
                    passData.put(MountainPasses.MOUNTAIN_PASS_TEMPERATURE,
                            pass.getString("TemperatureInFahrenheit"));
                    JSONObject restrictionOne = pass.getJSONObject("RestrictionOne");
                    passData.put(MountainPasses.MOUNTAIN_PASS_RESTRICTION_ONE,
                            restrictionOne.getString("RestrictionText"));
                    passData.put(MountainPasses.MOUNTAIN_PASS_RESTRICTION_ONE_DIRECTION,
                            restrictionOne.getString("TravelDirection"));
                    JSONObject restrictionTwo = pass.getJSONObject("RestrictionTwo");
                    passData.put(MountainPasses.MOUNTAIN_PASS_RESTRICTION_TWO,
                            restrictionTwo.getString("RestrictionText"));
                    passData.put(MountainPasses.MOUNTAIN_PASS_RESTRICTION_TWO_DIRECTION,
                            restrictionTwo.getString("TravelDirection"));

                    if (starred.contains(Integer.parseInt(pass.getString("MountainPassId")))) {
                        passData.put(MountainPasses.MOUNTAIN_PASS_IS_STARRED, 1);
                    }

                    passes.add(passData);

                }

                // Purge existing mountain passes covered by incoming data
                resolver.delete(MountainPasses.CONTENT_URI, null, null);
                // Bulk insert all the new mountain passes
                resolver.bulkInsert(MountainPasses.CONTENT_URI, passes.toArray(new ContentValues[passes.size()]));
                // Update the cache table with the time we did the update
                ContentValues values = new ContentValues();
                values.put(Caches.CACHE_LAST_UPDATED, System.currentTimeMillis());
                resolver.update(Caches.CONTENT_URI, values, Caches.CACHE_TABLE_NAME + "=?",
                        new String[] { "mountain_passes" });

                responseString = "OK";
            } catch (Exception e) {
                Log.e(DEBUG_TAG, "Error: " + e.getMessage());
                responseString = e.getMessage();
            }

        } else {
            responseString = "NOP";
        }

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction("gov.wa.wsdot.android.wsdot.intent.action.MOUNTAIN_PASSES_RESPONSE");
        broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
        broadcastIntent.putExtra("responseString", responseString);
        sendBroadcast(broadcastIntent);
    }

    private void buildWeatherPhrases() {
        String[] weather_clear = { "fair", "sunny", "clear" };
        String[] weather_few_clouds = { "few clouds", "scattered clouds", "mostly sunny", "mostly clear" };
        String[] weather_partly_cloudy = { "partly cloudy", "partly sunny" };
        String[] weather_cloudy = { "cloudy" };
        String[] weather_mostly_cloudy = { "broken", "mostly cloudy" };
        String[] weather_overcast = { "overcast" };
        String[] weather_light_rain = { "light rain", "showers" };
        String[] weather_rain = { "rain", "heavy rain", "raining" };
        String[] weather_snow = { "snow", "snowing", "light snow", "heavy snow" };
        String[] weather_fog = { "fog" };
        String[] weather_sleet = { "rain snow", "light rain snow", "heavy rain snow", "rain and snow" };
        String[] weather_hail = { "ice pellets", "light ice pellets", "heavy ice pellets", "hail" };
        String[] weather_thunderstorm = { "thunderstorm", "thunderstorms" };

        weatherPhrases.put(R.drawable.ic_list_sunny, weather_clear);
        weatherPhrases.put(R.drawable.ic_list_cloudy_1, weather_few_clouds);
        weatherPhrases.put(R.drawable.ic_list_cloudy_2, weather_partly_cloudy);
        weatherPhrases.put(R.drawable.ic_list_cloudy_3, weather_cloudy);
        weatherPhrases.put(R.drawable.ic_list_cloudy_4, weather_mostly_cloudy);
        weatherPhrases.put(R.drawable.ic_list_overcast, weather_overcast);
        weatherPhrases.put(R.drawable.ic_list_light_rain, weather_light_rain);
        weatherPhrases.put(R.drawable.ic_list_shower_3, weather_rain);
        weatherPhrases.put(R.drawable.ic_list_snow_4, weather_snow);
        weatherPhrases.put(R.drawable.ic_list_fog, weather_fog);
        weatherPhrases.put(R.drawable.ic_list_sleet, weather_sleet);
        weatherPhrases.put(R.drawable.ic_list_hail, weather_hail);
        weatherPhrases.put(R.drawable.ic_list_tstorm_3, weather_thunderstorm);

        weatherPhrasesNight.put(R.drawable.ic_list_sunny_night, weather_clear);
        weatherPhrasesNight.put(R.drawable.ic_list_cloudy_1_night, weather_few_clouds);
        weatherPhrasesNight.put(R.drawable.ic_list_cloudy_2_night, weather_partly_cloudy);
        weatherPhrasesNight.put(R.drawable.ic_list_cloudy_3_night, weather_cloudy);
        weatherPhrasesNight.put(R.drawable.ic_list_cloudy_4_night, weather_mostly_cloudy);
        weatherPhrasesNight.put(R.drawable.ic_list_overcast, weather_overcast);
        weatherPhrasesNight.put(R.drawable.ic_list_light_rain, weather_light_rain);
        weatherPhrasesNight.put(R.drawable.ic_list_shower_3, weather_rain);
        weatherPhrasesNight.put(R.drawable.ic_list_snow_4, weather_snow);
        weatherPhrasesNight.put(R.drawable.ic_list_fog_night, weather_fog);
        weatherPhrasesNight.put(R.drawable.ic_list_sleet, weather_sleet);
        weatherPhrasesNight.put(R.drawable.ic_list_hail, weather_hail);
        weatherPhrasesNight.put(R.drawable.ic_list_tstorm_3, weather_thunderstorm);

        return;
    }

    private static Integer getWeatherImage(HashMap<Integer, String[]> weatherPhrases, String weather) {
        Integer image = R.drawable.weather_na;
        Set<Entry<Integer, String[]>> set = weatherPhrases.entrySet();
        Iterator<Entry<Integer, String[]>> i = set.iterator();

        if (weather.equals(""))
            return image;

        String s0 = weather.split("\\.")[0]; // Pattern match on first sentence only.

        while (i.hasNext()) {
            Entry<Integer, String[]> me = i.next();
            for (String phrase : (String[]) me.getValue()) {
                if (s0.toLowerCase().startsWith(phrase)) {
                    image = (Integer) me.getKey();
                    return image;
                }
            }
        }

        return image;
    }

    private static boolean isNight(String text) {
        String patternStr = "night|tonight";
        Pattern pattern = Pattern.compile(patternStr, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(text);
        boolean matchFound = matcher.find();

        return matchFound;
    }

    /** 
     * Check the mountain pass table for any starred entries. If we find some, save them
     * to a list so we can re-star those passes after we flush the database.
     */
    private List<Integer> getStarred() {
        ContentResolver resolver = getContentResolver();
        Cursor cursor = null;
        List<Integer> starred = new ArrayList<Integer>();

        try {
            cursor = resolver.query(MountainPasses.CONTENT_URI, new String[] { MountainPasses.MOUNTAIN_PASS_ID },
                    MountainPasses.MOUNTAIN_PASS_IS_STARRED + "=?", new String[] { "1" }, null);

            if (cursor != null && cursor.moveToFirst()) {
                while (!cursor.isAfterLast()) {
                    starred.add(cursor.getInt(0));
                    cursor.moveToNext();
                }
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        return starred;
    }

}