Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0a4ed83 authored by David van Tonder's avatar David van Tonder Committed by elektroschmock
Browse files

Framework: Lock screen weather (Part 2 of 2)

This commit adds the framework side support for displaying the
current weather forecast on the lock screen.

Special thanks to Devatwork for his help with the update code,
Blunden for his help with the weather images and the AOKP team
for the original Yahoo! Weather parsing code.

Patch set 2  - Whitespace cleanup
Patch set 3  - Default show location, timestamp and metric to on
Patch set 4  - Adds AOKP copyright to util classes
Patch set 5  - Fixes derp introduced by Ps4
Patch set 6  - Fixes (incl music control widget size)
Patch set 7  - Minor tweak to the music control widget size
Patch set 8  - Better 'error' message on startup
Patch set 9  - Even better start up message and Cid image
Patch set 10 - German translation

Change-Id: Ib12951f163d4fe04cbfa2e4ae9fc59058b71f47e
parent 2dddab51
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -2394,6 +2394,48 @@ public final class Settings {
         */
        public static final String QUIET_HOURS_DIM = "quiet_hours_dim";

        /**
         * Show the weather on the lock screen
         * @hide
         */
        public static final String LOCKSCREEN_WEATHER = "lockscreen_weather";

        /**
         * Show the current weather location on the lock screen
         * @hide
         */
        public static final String WEATHER_SHOW_LOCATION = "weather_show_location";

        /**
         * Show the current weather location on the lock screen
         * @hide
         */
        public static final String WEATHER_SHOW_TIMESTAMP = "weather_show_timestamp";

        /**
         * Use the custom/manually configured weather location
         * @hide
         */
        public static final String WEATHER_USE_CUSTOM_LOCATION = "weather_use_custom_location";

        /**
         * Stores the custom/manually configured weather location
         * @hide
         */
        public static final String WEATHER_CUSTOM_LOCATION = "weather_custom_location";

        /**
         * Stores the weather update frequency
         * @hide
         */
        public static final String WEATHER_UPDATE_INTERVAL = "weather_update_interval";

        /**
         * Use Metric measurements (celcius, km/h) for weather data
         * @hide
         */
        public static final String WEATHER_USE_METRIC = "weather_use_metric";

        /**
         * Settings to backup. This is here so that it's in the same place as the settings
         * keys and easy to update.
+141 −0
Original line number Diff line number Diff line
/*
 * 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.android.internal.util.weather;

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 final String TAG = getClass().getSimpleName();
    private DefaultHttpClient client = new DefaultHttpClient();
    private HttpURLConnection httpConnection;

    public String retrieve(String url) {
        HttpGet get = new HttpGet(url);
        try {
            HttpResponse getResponse = client.execute(get);
            HttpEntity getResponseEntity = getResponse.getEntity();
            if (getResponseEntity != null) {
                String response = EntityUtils.toString(getResponseEntity);
                return response;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        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;
        }

        String strValue = 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");
            strValue += strLine + "\n";
        }

        // Release resource to system
        buffReader.close();
        inputStream.close();
        return strBuf.toString();
    }
}
+107 −0
Original line number Diff line number Diff line
/*
 * 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.android.internal.util.weather;

import android.content.Context;
import com.android.internal.R;

public class WeatherInfo {

    public static final String NODATA = "-";

    public String city, forecast_date, condition, condition_code, temp, temp_unit,
        humidity, wind, wind_dir, speed_unit, low, high;
    public long last_sync;

    public WeatherInfo() {
        this.city = NODATA;
        this.forecast_date = NODATA;
        this.condition = NODATA;
        this.condition_code = NODATA;
        this.temp = NODATA;
        this.temp_unit = NODATA;
        this.humidity = NODATA;
        this.wind = NODATA;
        this.wind_dir = NODATA;
        this.speed_unit = NODATA;
        this.low = NODATA;
        this.high = NODATA;
        this.last_sync = 0;
    }

    public WeatherInfo(Context context, String city, String fdate, String condition, String condition_code,
            String temp, String temp_unit, String humidity,
            String wind, String wind_dir, String speed_unit,
            String low, String high, long last_sync) {
        this.city = city;
        this.forecast_date = fdate;
        this.condition = condition;
        this.condition_code = condition_code;
        this.humidity = humidity + "%";
        this.wind = calcDirection(context, wind_dir) + " " + trimSpeed(wind) + speed_unit;
        this.speed_unit = speed_unit;
        this.last_sync = last_sync;
        // Only the current temperature gets the temp_unit added.
        this.temp_unit = temp_unit;
        this.temp = temp + "°" + temp_unit.toLowerCase();
        this.low = low + "°";
        this.high = high + "°";
    }

    /**
     * find the optimal weather string (helper function for translation)
     *
     * @param conditionCode condition code from Yahoo (this is the main
     *            identifier which will be used to find a matching translation
     *            in the project's resources
     * @param providedString
     * @return either the defaultString (which should be Yahoo's weather
     *         condition text), or the translated version from resources
     */
    public static String getTranslatedConditionString(Context context, int conditionCode,
            String providedString) {
        int resID = context.getResources().getIdentifier("weather_" + conditionCode, "string",
                context.getPackageName());
        return (resID != 0) ? context.getResources().getString(resID) : providedString;
    }

    private String calcDirection(Context context, String degrees) {
        int deg = Integer.parseInt(degrees);
        if (deg >= 338 || deg <= 22)
            return context.getResources().getString(R.string.weather_N);
        else if (deg < 68)
            return context.getResources().getString(R.string.weather_NE);
        else if (deg < 113)
            return context.getResources().getString(R.string.weather_E);
        else if (deg < 158)
            return context.getResources().getString(R.string.weather_SE);
        else if (deg < 203)
            return context.getResources().getString(R.string.weather_S);
        else if (deg < 248)
            return context.getResources().getString(R.string.weather_SW);
        else if (deg < 293)
            return context.getResources().getString(R.string.weather_W);
        else if (deg < 338)
            return context.getResources().getString(R.string.weather_NW);
        else
            return "";
    }

    private String trimSpeed(String speed) {
        return String.valueOf(Math.round(Float.parseFloat(speed)));
    }
}
+169 −0
Original line number Diff line number Diff line
/*
 * 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.android.internal.util.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;
    }

    public WeatherInfo parseWeatherResponse(Document docWeather) {
        if (docWeather == null) {
            Log.e(TAG, "Invalid doc weather");
            return null;
        }

        String strCity = null;
        String strDate = null;
        String strCondition = null;
        String strCondition_code = null;
        String strTemp = null;
        String strTempUnit = null;
        String strHumidity = null;
        String strWindSpeed = null;
        String strWindDir = null;
        String strSpeedUnit = null;
        String strHigh = null;
        String strLow = null;

        try {
            Element root = docWeather.getDocumentElement();
            root.normalize();

            NamedNodeMap locationNode = root.getElementsByTagName(PARAM_YAHOO_LOCATION).item(0)
                    .getAttributes();
            if (locationNode != null) {
                strCity = locationNode.getNamedItem(ATT_YAHOO_CITY).getNodeValue();
            }

            NamedNodeMap unitNode = root.getElementsByTagName(PARAM_YAHOO_UNIT).item(0)
                    .getAttributes();

            if (locationNode != null) {
                strTempUnit = unitNode.getNamedItem(ATT_YAHOO_TEMP_UNIT).getNodeValue();
                strSpeedUnit = unitNode.getNamedItem(ATT_YAHOO_SPEED).getNodeValue();
            }

            NamedNodeMap atmosNode = root.getElementsByTagName(PARAM_YAHOO_ATMOSPHERE).item(0)
                    .getAttributes();
            if (atmosNode != null) {
                strHumidity = atmosNode.getNamedItem(ATT_YAHOO_HUMIDITY).getNodeValue();
            }

            NamedNodeMap conditionNode = root.getElementsByTagName(PARAM_YAHOO_CONDITION).item(0)
                    .getAttributes();
            if (conditionNode != null) {
                strCondition = conditionNode.getNamedItem(ATT_YAHOO_TEXT).getNodeValue();
                strCondition_code = conditionNode.getNamedItem(ATT_YAHOO_CODE).getNodeValue();
                strCondition = WeatherInfo.getTranslatedConditionString(mContext, Integer.parseInt(strCondition_code), strCondition);
                strTemp = conditionNode.getNamedItem(ATT_YAHOO_TEMP).getNodeValue();
                strDate = conditionNode.getNamedItem(ATT_YAHOO_DATE).getNodeValue();
            }

            NamedNodeMap temNode = root.getElementsByTagName(PARAM_YAHOO_WIND).item(0)
                    .getAttributes();
            if (temNode != null) {
                strWindSpeed = temNode.getNamedItem(ATT_YAHOO_SPEED).getNodeValue();
                strWindDir = temNode.getNamedItem(ATT_YAHOO_DIRECTION).getNodeValue();
            }

            NamedNodeMap fcNode = root.getElementsByTagName(PARAM_YAHOO_FORECAST).item(0).getAttributes();
            if (fcNode != null) {
                strHigh = fcNode.getNamedItem(ATT_YAHOO_TODAY_HIGH).getNodeValue();
                strLow = fcNode.getNamedItem(ATT_YAHOO_TODAY_LOW).getNodeValue();
            }
        } catch (Exception e) {
            Log.e(TAG, "Something wrong with parser data: " + e.toString());
            return null;
        }

        /* Weather info */
        WeatherInfo yahooWeatherInfo = new WeatherInfo(mContext, strCity, strDate, strCondition, strCondition_code, strTemp,
                strTempUnit, strHumidity, strWindSpeed, strWindDir, strSpeedUnit, strLow, strHigh, System.currentTimeMillis());

        Log.d(TAG, "Weather updated for " + strCity + ": " + strDate + ", " + strCondition + "(" + strCondition_code
                + "), " + strTemp + strTempUnit + ", " + strHumidity + "% humidity, "  + ", wind: " + strWindDir + " at "
                + strWindSpeed + strSpeedUnit + ", low: " + strLow  + strTempUnit + " high: " + strHigh + strTempUnit);

        return yahooWeatherInfo;
    }

    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, e.toString());
        }
        return null;
    }
}
+41 −0
Original line number Diff line number Diff line
/*
 * 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.android.internal.util.weather;

import android.content.Context;

public class YahooPlaceFinder {

    private static final String YAHOO_API_BASE_REV_URL = "http://where.yahooapis.com/geocode?appid=jYkTZp64&q=%1$s,+%2$s&gflags=R";
    private static final String YAHOO_API_BASE_URL = "http://where.yahooapis.com/geocode?appid=jYkTZp64&q=%1$s";

    public static String reverseGeoCode(Context c, double latitude, double longitude) {

        String url = String.format(YAHOO_API_BASE_REV_URL, String.valueOf(latitude),
                String.valueOf(longitude));
        String response = new HttpRetriever().retrieve(url);
        return new WeatherXmlParser(c).parsePlaceFinderResponse(response);

    }

    public static String GeoCode(Context c, String location) {
        String url = String.format(YAHOO_API_BASE_URL, location).replace(' ', '+');
        String response = new HttpRetriever().retrieve(url);
        return new WeatherXmlParser(c).parsePlaceFinderResponse(response);
    }

}
Loading