From 211a49cde4397caa5f894f5466c88eccd86de0fe Mon Sep 17 00:00:00 2001 From: joeDiHare Date: Thu, 21 Apr 2016 22:08:07 +0100 Subject: [PATCH] added icons to weather forecast module --- res/drawable/clear_day.xml | 11 + res/drawable/clear_night.xml | 11 + res/drawable/cloudy.xml | 11 + res/drawable/fog.xml | 17 ++ res/drawable/home.xml | 11 + res/drawable/news_ap.xml | 11 + res/drawable/partly_cloudy_day.xml | 14 + res/drawable/partly_cloudy_night.xml | 14 + res/drawable/rain.xml | 29 +++ res/drawable/sleet.xml | 47 ++++ res/drawable/snow.xml | 65 +++++ res/drawable/umbrella.xml | 15 ++ res/drawable/wind.xml | 17 ++ .../magicmirror/modules/ForecastModule.java | 245 ++++++++++-------- 14 files changed, 406 insertions(+), 112 deletions(-) create mode 100644 res/drawable/clear_day.xml create mode 100644 res/drawable/clear_night.xml create mode 100644 res/drawable/cloudy.xml create mode 100644 res/drawable/fog.xml create mode 100644 res/drawable/home.xml create mode 100644 res/drawable/news_ap.xml create mode 100644 res/drawable/partly_cloudy_day.xml create mode 100644 res/drawable/partly_cloudy_night.xml create mode 100644 res/drawable/rain.xml create mode 100644 res/drawable/sleet.xml create mode 100644 res/drawable/snow.xml create mode 100644 res/drawable/umbrella.xml create mode 100644 res/drawable/wind.xml diff --git a/res/drawable/clear_day.xml b/res/drawable/clear_day.xml new file mode 100644 index 0000000..16df169 --- /dev/null +++ b/res/drawable/clear_day.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/res/drawable/clear_night.xml b/res/drawable/clear_night.xml new file mode 100644 index 0000000..b5bdd2c --- /dev/null +++ b/res/drawable/clear_night.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/res/drawable/cloudy.xml b/res/drawable/cloudy.xml new file mode 100644 index 0000000..50ed366 --- /dev/null +++ b/res/drawable/cloudy.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/res/drawable/fog.xml b/res/drawable/fog.xml new file mode 100644 index 0000000..354aa54 --- /dev/null +++ b/res/drawable/fog.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/res/drawable/home.xml b/res/drawable/home.xml new file mode 100644 index 0000000..f6f9952 --- /dev/null +++ b/res/drawable/home.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/res/drawable/news_ap.xml b/res/drawable/news_ap.xml new file mode 100644 index 0000000..e0ba481 --- /dev/null +++ b/res/drawable/news_ap.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/res/drawable/partly_cloudy_day.xml b/res/drawable/partly_cloudy_day.xml new file mode 100644 index 0000000..69f77c5 --- /dev/null +++ b/res/drawable/partly_cloudy_day.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/res/drawable/partly_cloudy_night.xml b/res/drawable/partly_cloudy_night.xml new file mode 100644 index 0000000..391b3b9 --- /dev/null +++ b/res/drawable/partly_cloudy_night.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/res/drawable/rain.xml b/res/drawable/rain.xml new file mode 100644 index 0000000..0b638a3 --- /dev/null +++ b/res/drawable/rain.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + diff --git a/res/drawable/sleet.xml b/res/drawable/sleet.xml new file mode 100644 index 0000000..fbce61a --- /dev/null +++ b/res/drawable/sleet.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/res/drawable/snow.xml b/res/drawable/snow.xml new file mode 100644 index 0000000..9c51230 --- /dev/null +++ b/res/drawable/snow.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/drawable/umbrella.xml b/res/drawable/umbrella.xml new file mode 100644 index 0000000..bd5ca28 --- /dev/null +++ b/res/drawable/umbrella.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/res/drawable/wind.xml b/res/drawable/wind.xml new file mode 100644 index 0000000..7b98389 --- /dev/null +++ b/res/drawable/wind.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/src/com/ineptech/magicmirror/modules/ForecastModule.java b/src/com/ineptech/magicmirror/modules/ForecastModule.java index 043bfb7..5259e6b 100644 --- a/src/com/ineptech/magicmirror/modules/ForecastModule.java +++ b/src/com/ineptech/magicmirror/modules/ForecastModule.java @@ -1,8 +1,12 @@ package com.ineptech.magicmirror.modules; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -16,47 +20,52 @@ import android.content.Context; import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.text.InputType; +import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; +import android.text.style.ImageSpan; +import android.util.Log; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import com.ineptech.magicmirror.MainApplication; +import com.ineptech.magicmirror.R; import com.ineptech.magicmirror.Utils; public class ForecastModule extends Module { - - String apikey = "30cc6bf19bbb41dd5b36b0fbcadb4864"; - double latitude, latitude_def = 45.5200; - double longitude, longitude_def = -122.6819; + + String apikey;// fetched from sensitive-data in resources + double latitude, latitude_def = 40.852676; + double longitude, longitude_def = 14.267968; private static final long timeBetweenCalls = 10 * 60 * 1000; // 10 minutes long lastRan = 0; int consecFails = 0; String cast = ""; - Boolean useCelsius = false; + int cast_show; + Boolean useCelsius = true; CheckBox cbCelsius; - + Context ctx; + + public ForecastModule(Context context) { super("Weather"); desc = "Shows today's temperatures: \"current (high | low)\" as reported by forecast.io. " + "This will not work until you go to forecast.io and register to get a (free) api key " + "and enter it in to the box below. To do so, visit developer.forecast.io and click on Register. " - + "Then enter your latitude and longitude (be sure to get the signs right). I hope to add " - + "snow/rain/etc icons at some point, haven't gotten to it yet..."; + + "Then enter your latitude and longitude (be sure to get the signs right)."; defaultTextSize = 72; - sampleString = "100° (90° | 110°)"; + sampleString = "100\u00B0 (90° | 110°)"; + ctx = context; + apikey = ctx.getResources().getString(R.string.forecast_api); loadConfig(); } - + public void update() { - if (Utils.isNewDay()) { - consecFails = 0; // try to retry every day in case of temporary network failures - tv.setText(""); // clear old text - } if (consecFails > 9) { tv.setText(""); tv.setVisibility(TextView.GONE); @@ -64,105 +73,103 @@ public void update() { new ForecastTask(this).execute(); } } - - private void loadConfig() { - latitude = latitude_def; - longitude = longitude_def; - String latitude_s = prefs.get(name+"_latitude", ""+latitude_def); - String longitude_s = prefs.get(name+"_longitude", ""+longitude_def); - try { - latitude = Double.parseDouble(latitude_s); - longitude = Double.parseDouble(longitude_s); - } catch (Exception e) { } - apikey = prefs.get(name+"_apikey", apikey); - useCelsius = prefs.get(name+"_useCelsius", false); - } - - @Override - public void saveConfig() { - super.saveConfig(); - prefs.set(name+"_latitude", eLat.getText().toString()); - prefs.set(name+"_longitude", eLong.getText().toString()); - prefs.set(name+"_apikey", eApikey.getText().toString()); - useCelsius = cbCelsius.isChecked(); - prefs.set(name+"_useCelsius", useCelsius); - } - - private EditText eLong; - private EditText eLat; - private EditText eApikey; - - @Override - public void makeConfigLayout() { - super.makeConfigLayout(); - LinearLayout holder = new LinearLayout(MainApplication.getContext()); - holder.setOrientation(LinearLayout.VERTICAL); - TextView tv1 = new TextView(MainApplication.getContext()); - tv1.setText("Latitude: "); - holder.addView(tv1); - eLat = new EditText(MainApplication.getContext()); - eLat.setBackgroundColor(Color.LTGRAY); - holder.addView(eLat); - TextView tv2 = new TextView(MainApplication.getContext()); - tv2.setText("Longitude: "); - holder.addView(tv2); - eLong = new EditText(MainApplication.getContext()); - eLong.setBackgroundColor(Color.LTGRAY); - holder.addView(eLong); - TextView tv3 = new TextView(MainApplication.getContext()); - tv3.setText("Api key (get from forecast.io and copy-paste here): "); - holder.addView(tv3); - eApikey = new EditText(MainApplication.getContext()); - eApikey.setBackgroundColor(Color.LTGRAY); - holder.addView(eApikey); - configLayout.addView(holder); - - eLat.setText(latitude+""); - eLong.setText(longitude+""); - eApikey.setText(apikey); - - eLong.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL|InputType.TYPE_NUMBER_FLAG_DECIMAL); - eLat.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL|InputType.TYPE_NUMBER_FLAG_DECIMAL); - - cbCelsius = new CheckBox(MainApplication.getContext()); - cbCelsius.setText("Use Celsius for temperature?"); - cbCelsius.setChecked(useCelsius); - holder.addView(cbCelsius); - } - + + private void loadConfig() { + latitude = latitude_def; + longitude = longitude_def; + String latitude_s = prefs.get(name+"_latitude", ""+latitude_def); + String longitude_s = prefs.get(name+"_longitude", ""+longitude_def); + try { + latitude = Double.parseDouble(latitude_s); + longitude = Double.parseDouble(longitude_s); + } catch (Exception e) { } + apikey = prefs.get(name+"_apikey", apikey); + useCelsius = prefs.get(name+"_useCelsius", false); + } + + @Override + public void saveConfig() { + super.saveConfig(); + prefs.set(name+"_latitude", eLat.getText().toString()); + prefs.set(name+"_longitude", eLong.getText().toString()); + prefs.set(name+"_apikey", eApikey.getText().toString()); + useCelsius = cbCelsius.isChecked(); + prefs.set(name+"_useCelsius", useCelsius); + } + + private EditText eLong; + private EditText eLat; + private EditText eApikey; + + @Override + public void makeConfigLayout() { + super.makeConfigLayout(); + LinearLayout holder = new LinearLayout(MainApplication.getContext()); + holder.setOrientation(LinearLayout.VERTICAL); + TextView tv1 = new TextView(MainApplication.getContext()); + tv1.setText("Latitude: "); + holder.addView(tv1); + eLat = new EditText(MainApplication.getContext()); + eLat.setBackgroundColor(Color.LTGRAY); + holder.addView(eLat); + TextView tv2 = new TextView(MainApplication.getContext()); + tv2.setText("Longitude: "); + holder.addView(tv2); + eLong = new EditText(MainApplication.getContext()); + eLong.setBackgroundColor(Color.LTGRAY); + holder.addView(eLong); + TextView tv3 = new TextView(MainApplication.getContext()); + tv3.setText("Api key (get from forecast.io and copy-paste here): "); + holder.addView(tv3); + eApikey = new EditText(MainApplication.getContext()); + eApikey.setBackgroundColor(Color.LTGRAY); + holder.addView(eApikey); + configLayout.addView(holder); + + eLat.setText(latitude+""); + eLong.setText(longitude+""); + eApikey.setText(apikey); + + eLong.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL|InputType.TYPE_NUMBER_FLAG_DECIMAL); + eLat.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL|InputType.TYPE_NUMBER_FLAG_DECIMAL); + + cbCelsius = new CheckBox(MainApplication.getContext()); + cbCelsius.setText("Use Celsius units?"); + cbCelsius.setChecked(useCelsius); + holder.addView(cbCelsius); + + } + void set() { - // TODO: add icons for snow and rain. To do so: - // First, update ___ to include a key word, e.g. "umbrella", to the forecast display test. - // Then, trandform it into an icon here with in this method with code like so: - // String keyword = "umbrella"; - // int i = cast.indexOf(keyword); - // if (i >= 0) - // span.setSpan(isUmbrella, i, i+keyword.length, 0); - - SpannableString span = new SpannableString(cast); - SpannableStringBuilder builder = new SpannableStringBuilder(); - builder.append(span); - tv.setText(builder); + + Drawable myIcon = ctx.getResources().getDrawable(cast_show); + myIcon.setBounds(0, 0, tv.getLineHeight(), tv.getLineHeight()); + + // add weather icon before temperature + ImageSpan is = new ImageSpan(myIcon); + final Spannable text = new SpannableString(" " + cast); + text.setSpan(is, 0,1, 0); + tv.setText(text, TextView.BufferType.SPANNABLE); lastRan = Calendar.getInstance().getTimeInMillis(); } - + } class ForecastTask extends AsyncTask { private ForecastModule module; String sampleResponse = "{\"latitude\":45.52,\"longitude\":122.6819,\"timezone\":\"Asia/Harbin\",\"offset\":8,\"currently\":{\"time\":1449289114,\"summary\":\"Clear\",\"icon\":\"clear-day\",\"precipType\":\"snow\",\"temperature\":22.35,\"apparentTemperature\":11.63,\"dewPoint\":2.76,\"humidity\":0.42,\"windSpeed\":10.22,\"windBearing\":304,\"visibility\":10,\"cloudCover\":0,\"pressure\":1026.52},\"daily\":{\"data\":[{\"time\":1449244800,\"summary\":\"Clear throughout the day.\",\"icon\":\"clear-day\",\"sunriseTime\":1449270846,\"sunsetTime\":1449302882,\"moonPhase\":0.8,\"precipType\":\"snow\",\"temperatureMin\":10.48,\"temperatureMinTime\":1449320400,\"temperatureMax\":22.73,\"temperatureMaxTime\":1449291600,\"apparentTemperatureMin\":4.25,\"apparentTemperatureMinTime\":1449316800,\"apparentTemperatureMax\":12.86,\"apparentTemperatureMaxTime\":1449291600,\"dewPoint\":3.95,\"humidity\":0.58,\"windSpeed\":5.86,\"windBearing\":292,\"visibility\":8.33,\"pressure\":1024.88}]},\"flags\":{\"sources\":[\"isd\"],\"isd-stations\":[\"508440-99999\",\"509490-99999\",\"540260-99999\",\"540490-99999\"],\"units\":\"us\"}}"; - String sampleForecastURL = "https://api.forecast.io/forecast/api_key_goes_here/45.5200,-122.6819,2015-12-04T20:18:34-0800?units=us&exclude=minutely,hourly"; - + //String sampleForecastURL = "https://api.forecast.io/forecast/api_key_goes_here/45.5200,-122.6819,2015-12-04T20:18:34-0800?units=us&exclude=minutely,hourly"; + public ForecastTask(ForecastModule _module) { module = _module; } - + @Override protected String doInBackground(Void... params) { if (Utils.debug) return sampleResponse; - + HttpClient httpClient = new DefaultHttpClient(); HttpContext localContext = new BasicHttpContext(); String forecastTime = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); @@ -187,10 +194,11 @@ protected String doInBackground(Void... params) { protected void onPostExecute(String results) { if (results!=null) { - // TODO: improve parsing of the forecast results try { System.out.println(results); - module.cast = parseForecast(results); + List tmp = parseForecast(results); + module.cast = tmp.get(0); + module.cast_show = iconResources.get(tmp.get(1)); module.set(); } catch (Exception e) { module.consecFails++; @@ -200,28 +208,41 @@ protected void onPostExecute(String results) { module.consecFails++; } } - + + private final Map iconResources = new HashMap() {{ + put("clear-day", R.drawable.clear_day); + put("clear-night", R.drawable.clear_night); + put("cloudy", R.drawable.cloudy); + put("fog", R.drawable.fog); + put("partly-cloudy-day", R.drawable.partly_cloudy_day); + put("partly-cloudy-night", R.drawable.partly_cloudy_night); + put("rain", R.drawable.rain); + put("sleet", R.drawable.sleet); + put("snow", R.drawable.snow); + put("wind", R.drawable.wind); + }}; + String temp = "", high = "", low = "", icon = ""; - String parseForecast (String s) throws Exception { - String re = ".*temperature\":([\\-0-9\\.]+),.*temperatureMin\":([\\-0-9\\.]+),.*temperatureMax\":([\\-0-9\\.]+),.*"; + List parseForecast (String s) throws Exception { + String re = ".*icon\":\"([\\a-z\\.]+)\",.*temperature\":([\\-0-9\\.]+),.*temperatureMin\":([\\-0-9\\.]+),.*temperatureMax\":([\\-0-9\\.]+),.*"; Pattern r = Pattern.compile(re); Matcher m = r.matcher(s); if (m.find()) { - temp = m.group(1); - low = m.group(2); - high = m.group(3); + icon = m.group(1); + temp = m.group(2); + low = m.group(3); + high = m.group(4); } - int ftemp = Math.round(Float.parseFloat(temp)); - int flow = Math.round(Float.parseFloat(low)); + int ftemp = Math.round(Float.parseFloat(temp)); + int flow = Math.round(Float.parseFloat(low)); int fhigh = Math.round(Float.parseFloat(high)); - String cast = ftemp +"° ("+fhigh+"° | "+flow+"°)"; - + + List cast = Arrays.asList(""+ftemp +"℃ (min:"+flow+"° | max:"+fhigh+"°)", icon); + // TODO: Check for weather alerts and display something suitable? if (s.contains("Alert")) { // do something... } - // TODO: Add icons for snow, rain, etc - return cast; - } + } }