diff --git a/.idea/dictionaries/weatherstation.xml b/.idea/dictionaries/weatherstation.xml
new file mode 100644
index 0000000..fce3e16
--- /dev/null
+++ b/.idea/dictionaries/weatherstation.xml
@@ -0,0 +1,12 @@
+
+
+
+ appid
+ dateandtime
+ openweathermap
+ ssid
+ weseng
+ wifiweatherstation
+
+
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 37a7509..85d6e17 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,40 @@
+
+
+
+
+
+
diff --git a/app/build.gradle b/app/build.gradle
index fa756fd..ba0cbfb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,8 +6,8 @@ android {
applicationId "de.weseng.wifiweatherstation"
minSdkVersion 26
targetSdkVersion 28
- versionCode 20190310
- versionName "2019.3.10"
+ versionCode 20190317
+ versionName "2019.3.17"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -18,6 +18,10 @@ android {
}
}
+repositories {
+ maven { url 'https://jitpack.io' }
+}
+
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
@@ -27,4 +31,5 @@ dependencies {
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:design:28.0.0'
+ implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0-alpha'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2241dd7..458aecc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,11 +2,10 @@
-
-
-
+
+
@@ -24,7 +23,7 @@
+ android:noHistory="true">
@@ -33,7 +32,8 @@
+ android:label="@string/app_name"
+ android:configChanges="orientation|screenSize">
@@ -43,6 +43,11 @@
+
+
+
+
+
diff --git a/app/src/main/java/de/weseng/wifiweatherstation/CenteredImageSpan.java b/app/src/main/java/de/weseng/wifiweatherstation/CenteredImageSpan.java
new file mode 100644
index 0000000..8d5bcd6
--- /dev/null
+++ b/app/src/main/java/de/weseng/wifiweatherstation/CenteredImageSpan.java
@@ -0,0 +1,79 @@
+package de.weseng.wifiweatherstation;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.text.style.ImageSpan;
+
+import java.lang.ref.WeakReference;
+
+public class CenteredImageSpan extends ImageSpan {
+ private WeakReference mDrawableRef;
+
+ CenteredImageSpan(Context context, final int drawableRes) {
+ super(context, drawableRes);
+ }
+
+ @Override
+ public int getSize(@NonNull Paint paint, CharSequence text,
+ int start, int end,
+ Paint.FontMetricsInt fm) {
+ Drawable d = getCachedDrawable();
+ Rect rect = d.getBounds();
+
+ float drawableHeight = rect.height();
+ if (fm != null) {
+ Paint.FontMetricsInt pfm = paint.getFontMetricsInt();
+ float fontHeight = pfm.descent - pfm.ascent;
+ int paintOversizeHalf = (int) (drawableHeight/2-fontHeight*1.25);
+
+ // keep it the same as paint's fm plus some space
+ fm.ascent = pfm.ascent - paintOversizeHalf;
+ fm.descent = pfm.descent + paintOversizeHalf;
+ fm.top = pfm.top;
+ fm.bottom = pfm.bottom;
+ }
+
+ return rect.right;
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas, CharSequence text,
+ int start, int end, float x,
+ int top, int y, int bottom, @NonNull Paint paint) {
+ Drawable b = getCachedDrawable();
+ canvas.save();
+
+ int drawableHeight = b.getIntrinsicHeight();
+ int fontAscent = paint.getFontMetricsInt().ascent;
+ int fontDescent = paint.getFontMetricsInt().descent;
+
+ Paint.FontMetricsInt pfm = paint.getFontMetricsInt();
+ int fontHeightRelativeCorrection = (int) ((pfm.descent - pfm.ascent)/5.0);
+
+ int transY = bottom - b.getBounds().bottom + // align bottom to bottom
+ (drawableHeight - fontDescent + fontAscent) / 2 - fontHeightRelativeCorrection; // align center to center
+ canvas.translate(x, transY);
+ b.draw(canvas);
+ canvas.restore();
+ }
+
+ // Redefined locally because it is a private member from DynamicDrawableSpan
+ private Drawable getCachedDrawable() {
+ WeakReference wr = mDrawableRef;
+ Drawable d = null;
+
+ if (wr != null)
+ d = wr.get();
+
+ if (d == null) {
+ d = getDrawable();
+ mDrawableRef = new WeakReference<>(d);
+ }
+
+ return d;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/weseng/wifiweatherstation/DetailActivity.java b/app/src/main/java/de/weseng/wifiweatherstation/DetailActivity.java
new file mode 100644
index 0000000..044e192
--- /dev/null
+++ b/app/src/main/java/de/weseng/wifiweatherstation/DetailActivity.java
@@ -0,0 +1,287 @@
+package de.weseng.wifiweatherstation;
+
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.github.mikephil.charting.charts.LineChart;
+import com.github.mikephil.charting.components.Legend;
+import com.github.mikephil.charting.components.XAxis;
+import com.github.mikephil.charting.components.YAxis;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.data.LineData;
+import com.github.mikephil.charting.data.LineDataSet;
+import com.github.mikephil.charting.formatter.IFillFormatter;
+import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
+import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
+import com.github.mikephil.charting.utils.ColorTemplate;
+
+import java.util.ArrayList;
+
+public class DetailActivity extends AppCompatActivity {
+ private final String TAG = getClass().getSimpleName();
+ private LineChart chart;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_detail);
+
+ Intent intent = getIntent();
+ ArrayList dataList1 = intent.getStringArrayListExtra("temperature");
+ ArrayList dataList2 = intent.getStringArrayListExtra("humidity");
+ Log.d(TAG, "dataList1: " + dataList1);
+ Log.d(TAG, "dataList2: " + dataList2);
+
+ chart = findViewById(R.id.chart1);
+
+ // background color for the whole view
+ //chart.setBackgroundColor(Color.TRANSPARENT);
+
+ // background color for the chart area
+ //chart.setDrawGridBackground(true);
+ //chart.setGridBackgroundColor(Color.TRANSPARENT);
+
+ // disable description text
+ chart.getDescription().setEnabled(false);
+
+ // enable touch gestures, default true
+ //chart.setTouchEnabled(false);
+
+ // enable dragging (default true) and scaling (default true)
+ //chart.setDragEnabled(false);
+ //chart.setScaleEnabled(false);
+ //chart.setScaleXEnabled(false);
+ chart.setScaleYEnabled(false);
+
+ // force pinch zoom along both axis (default false)
+ //chart.setPinchZoom(true);
+
+ // animates the rendering of the chart
+ chart.animateX(1000);
+ //chart.animateY(1500);
+ //chart.animateXY(1500, 1500);
+
+ { // x axis
+
+ XAxis xAxis = chart.getXAxis();
+ //xAxis.setTypeface(tfLight);
+ //xAxis.setTextSize(11f);
+ xAxis.setDrawLabels(false);
+ //xAxis.setTextColor(Color.LTGRAY);
+ xAxis.setDrawGridLines(false);
+ xAxis.setDrawAxisLine(false);
+ }
+
+ { // y axis
+
+ //YAxis yAxis = chart.getAxisLeft();
+
+ // disable dual axis (only use LEFT axis)
+ //chart.getAxisRight().setEnabled(false);
+
+ //leftAxis.setTypeface(tfLight);
+ //yAxis.setTextColor(ColorTemplate.getHoloBlue());
+ //yAxis.setAxisMinimum(12f);
+ //yAxis.setAxisMaximum(30f);
+ //yAxis.setDrawGridLines(true);
+
+ // horizontal grid lines
+ //yAxis.enableGridDashedLine(10f, 10f, 0f);
+
+ //yAxis.setGranularityEnabled(true);
+ }
+
+
+ { // y axis left
+
+ YAxis leftAxis = chart.getAxisLeft();
+ leftAxis.setTypeface(Typeface.DEFAULT_BOLD);
+ leftAxis.setTextColor(MainNativeActivity.red.toArgb());
+ leftAxis.setAxisMinimum(12);
+ leftAxis.setAxisMaximum(30);
+ leftAxis.setDrawGridLines(true);
+ leftAxis.setGranularityEnabled(true);
+ }
+
+ { // y axis right
+ YAxis rightAxis = chart.getAxisRight();
+ rightAxis.setTypeface(Typeface.DEFAULT_BOLD);
+ rightAxis.setTextColor(ColorTemplate.getHoloBlue());
+ rightAxis.setAxisMinimum(20);
+ rightAxis.setAxisMaximum(80);
+ //rightAxis.setDrawGridLines(false);
+ //rightAxis.setDrawZeroLine(false);
+ //rightAxis.setGranularityEnabled(false);
+ }
+
+ setData(dataList1, getString(R.string.temperature), dataList2, getString(R.string.humidity));
+
+ { // ### Legend ###
+
+ // get the legend (only possible after setting data)
+ Legend l = chart.getLegend();
+
+ // draw legend entries as lines, default square
+ l.setForm(Legend.LegendForm.LINE);
+
+ l.setTypeface(Typeface.DEFAULT_BOLD);
+ //l.setTextSize(11f);
+ l.setTextColor(Color.LTGRAY);
+
+ // position, default: bottom left
+ //l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
+ //l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
+
+ // default horizontal
+ //l.setOrientation(Legend.LegendOrientation.HORIZONTAL);
+
+ // default false
+ //l.setDrawInside(true);
+
+ //l.setYOffset(11f);
+ }
+
+ }
+
+ private void setData(ArrayList dataList1, String label1,
+ ArrayList dataList2, String label2) {
+ int n;
+
+ n = dataList1.size();
+ ArrayList values1 = new ArrayList<>();
+ if (n > 0) {
+ for (int i = 0; i < n; i++) {
+ float val = Float.parseFloat(dataList1.get(i));
+ values1.add(new Entry(i, val));
+ }
+ }
+
+ n = dataList2.size();
+ ArrayList values2 = new ArrayList<>();
+ if (n > 0) {
+ for (int i = 0; i < n; i++) {
+ float val = Float.parseFloat(dataList2.get(i));
+ values2.add(new Entry(i, val));
+ }
+ }
+
+
+ // create a DataSet and give it a type
+ LineDataSet set1 = new LineDataSet(values1, label1);
+ set1.setAxisDependency(YAxis.AxisDependency.LEFT);
+
+ { // ### Lines ###
+
+ //set1.setColor(Color.BLACK);
+ set1.setColor(MainNativeActivity.red.toArgb());
+ //set1.setColor(ColorTemplate.getHoloBlue());
+
+ // line thickness (min = 0.2f, max = 10f, default 1f)
+ //set1.setLineWidth(1f);
+
+ // draw dashed line, see also legend
+ //set1.enableDashedLine(10f, 5f, 0f);
+
+ // set interpolation
+ //set1.setMode(LineDataSet.Mode.STEPPED);
+ //set1.setMode(LineDataSet.Mode.LINEAR);
+ //set1.setMode(LineDataSet.Mode.CUBIC_BEZIER);
+ //set1.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
+ }
+
+ { // ### Points ###
+
+ // points, circles over the data point, def: true
+ //set1.setDrawCircles(false);
+
+ //set1.setCircleColor(Color.BLACK);
+ set1.setCircleColor(MainNativeActivity.red.toArgb());
+
+ // point size (default radius = 4f, min = 1f)
+ //set1.setCircleRadius(3f);
+
+ // icons over the data point, def: false
+ //set1.setDrawIcons(false);
+
+ // false to draw points as solid circles (default true)
+ set1.setDrawCircleHole(false);
+ }
+
+ { // ### Values ###
+
+ // values over the data point, def: true
+ set1.setDrawValues(false);
+
+ // text size of values
+ //set1.setValueTextSize(9f);
+ }
+
+ { // ### Legend ###
+
+ //set1.setFormLineWidth(1f);
+ //set1.setFormLineDashEffect(new DashPathEffect(new float[]{10f, 5f}, 0f));
+ //set1.setFormSize(15.f);
+ }
+
+ // draw selection line as dashed
+ set1.setHighLightColor(Color.LTGRAY);
+ //set1.enableDashedHighlightLine(10f, 5f, 0f);
+
+ { // ### Filled area ###
+
+ // set the filled area
+ //set1.setDrawFilled(true);
+ set1.setFillFormatter(new IFillFormatter() {
+ @Override
+ public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) {
+ return chart.getAxisLeft().getAxisMinimum();
+ }
+ });
+
+ // set color of filled area
+ set1.setFillColor(MainNativeActivity.red.toArgb());
+ //if (Utils.getSDKInt() >= 18) {
+ // // drawables only supported on api level 18 and above
+ // Drawable drawable = ContextCompat.getDrawable(this, R.drawable.fade_red);
+ // set1.setFillDrawable(drawable);
+ //} else {
+ // set1.setFillColor(Color.BLACK);
+ //}
+ }
+
+
+ LineDataSet set2 = new LineDataSet(values2, label2);
+ set2.setAxisDependency(YAxis.AxisDependency.RIGHT);
+ set2.setColor(ColorTemplate.getHoloBlue());
+ set2.setCircleColor(ColorTemplate.getHoloBlue());
+ //set2.setCircleColor(MainNativeActivity.red.toArgb());
+ set2.setDrawCircleHole(false);
+ set2.setDrawValues(false);
+ set2.setHighLightColor(Color.LTGRAY);
+ //set2.setDrawFilled(true);
+ set2.setFillFormatter(new IFillFormatter() {
+ @Override
+ public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) {
+ return chart.getAxisRight().getAxisMinimum();
+ }
+ });
+
+
+ ArrayList dataSets = new ArrayList<>();
+ dataSets.add(set1); // add the data sets
+ dataSets.add(set2); // add the data sets
+
+ // create a data object with the data sets
+ LineData data = new LineData(dataSets);
+
+ //data.setValueTextColor(Color.WHITE);
+ //data.setValueTextSize(9f);
+
+ // set data
+ chart.setData(data);
+ }
+}
diff --git a/app/src/main/java/de/weseng/wifiweatherstation/MainNativeActivity.java b/app/src/main/java/de/weseng/wifiweatherstation/MainNativeActivity.java
index 3a5e57e..ec11d8f 100644
--- a/app/src/main/java/de/weseng/wifiweatherstation/MainNativeActivity.java
+++ b/app/src/main/java/de/weseng/wifiweatherstation/MainNativeActivity.java
@@ -1,6 +1,8 @@
package de.weseng.wifiweatherstation;
+import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
@@ -8,6 +10,9 @@ import android.os.AsyncTask;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
@@ -22,20 +27,38 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Locale;
+import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import static de.weseng.wifiweatherstation.SettingsActivity.PREFS_NAME;
+import static java.lang.Math.pow;
public class MainNativeActivity extends AppCompatActivity {
private final String TAG = getClass().getSimpleName();
+
+ static Color green = Color.valueOf(0x71ff5f);
+ static Color red = Color.valueOf(0xffd1655d);
+ static Color blue = Color.valueOf(Color.rgb(113, 157, 195));
+
private int pBarLockCount = 0;
// In Android 4.4 (API 19) AsyncTask MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; (maximum number of threads)
ArrayList> endlessTasks = new ArrayList<>();
+ ArrayList dataListStringTemp1 = new ArrayList<>();
+ ArrayList dataListStringTemp2 = new ArrayList<>();
+ ArrayList dataListStringTemp3 = new ArrayList<>();
+ ArrayList dataListStringHumidity1 = new ArrayList<>();
+ ArrayList dataListStringHumidity2 = new ArrayList<>();
+ ArrayList dataListStringHumidity3 = new ArrayList<>();
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -45,36 +68,48 @@ public class MainNativeActivity extends AppCompatActivity {
String urlLocal = settings.getString("urlLocal", getString(R.string.url_local));
String urlGlobal = settings.getString("urlGlobal", getString(R.string.url_global));
- if (savedInstanceState == null) {
- TableLayout tr1 = findViewById(R.id.tableRow1);
- tr1.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO: do your logic here
- }
- });
- TableLayout tr2 = findViewById(R.id.tableRow2);
- tr2.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO: do your logic here
- }
- });
- TableLayout tr3 = findViewById(R.id.tableRow3);
- tr3.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO: do your logic here
- }
- });
+ TableLayout tr1 = findViewById(R.id.tableRow1);
+ tr1.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(MainNativeActivity.this, DetailActivity.class);
+ intent.putExtra("temperature", dataListStringTemp1);
+ intent.putExtra("humidity", dataListStringHumidity1);
+ startActivity(intent);
+ }
+ });
+ TableLayout tr2 = findViewById(R.id.tableRow2);
+ tr2.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(MainNativeActivity.this, DetailActivity.class);
+ intent.putExtra("temperature", dataListStringTemp2);
+ intent.putExtra("humidity", dataListStringHumidity2);
+ startActivity(intent);
+ }
+ });
+ TableLayout tr3 = findViewById(R.id.tableRow3);
+ tr3.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(MainNativeActivity.this, DetailActivity.class);
+ intent.putExtra("temperature", dataListStringTemp3);
+ intent.putExtra("humidity", dataListStringHumidity3);
+ startActivity(intent);
+ }
+ });
+ TextView tv = findViewById(R.id.textViewWeatherOutdoor);
+ tv.setText(Html.fromHtml(getString(R.string.weather_outdoor), Html.FROM_HTML_MODE_COMPACT));
+
+ if (savedInstanceState == null) {
String urlPre = "";
if (SettingsActivity.isLocal(getApplicationContext())) {
//Toast.makeText(getApplicationContext(), "intern", Toast.LENGTH_LONG).show();
if (urlLocal != null && urlLocal.length() > 0)
urlPre = urlLocal.substring(urlLocal.length() - 1).equals("/") ? urlLocal : urlLocal + "/";
} else {
- //Toast.makeText(getApplicationContext(), "extern", Toast.LENGTH_LONG).show();
+ //Toast.makeText(getApplicationContext(), "external", Toast.LENGTH_LONG).show();
if (urlGlobal != null && urlGlobal.length() > 0)
urlPre = urlGlobal.substring(urlGlobal.length() - 1).equals("/") ? urlGlobal : urlGlobal + "/";
}
@@ -85,18 +120,20 @@ public class MainNativeActivity extends AppCompatActivity {
executeAsyncTask(new GetData(R.id.textViewTemperatureValue3, R.id.textViewHumidityValue3),
urlPre + "api.php?host=192.168.1.73&last");
+ executeAsyncTask(new OpenWeatherMap(R.id.textViewWeatherOutdoor), urlPre + "openweathermap/api.php");
+
endlessTasks.add(new GetData(R.id.textViewTemperatureValue1, R.id.textViewHumidityValue1,
R.id.textViewTemperatureMin1, R.id.textViewTemperatureMax1, R.id.textViewTemperatureDelta1,
R.id.textViewHumidityMin1, R.id.textViewHumidityMax1, R.id.textViewHumidityDelta1,
- true));
+ true, dataListStringTemp1, dataListStringHumidity1));
endlessTasks.add(new GetData(R.id.textViewTemperatureValue2, R.id.textViewHumidityValue2,
R.id.textViewTemperatureMin2, R.id.textViewTemperatureMax2, R.id.textViewTemperatureDelta2,
R.id.textViewHumidityMin2, R.id.textViewHumidityMax2, R.id.textViewHumidityDelta2,
- true));
+ true, dataListStringTemp2, dataListStringHumidity2));
endlessTasks.add(new GetData(R.id.textViewTemperatureValue3, R.id.textViewHumidityValue3,
R.id.textViewTemperatureMin3, R.id.textViewTemperatureMax3, R.id.textViewTemperatureDelta3,
R.id.textViewHumidityMin3, R.id.textViewHumidityMax3, R.id.textViewHumidityDelta3,
- true));
+ true, dataListStringTemp3, dataListStringHumidity3));
executeAsyncTask(endlessTasks.get(0), urlPre + "api.php?host=192.168.1.71&day");
executeAsyncTask(endlessTasks.get(1), urlPre + "api.php?host=192.168.1.72&day");
executeAsyncTask(endlessTasks.get(2), urlPre + "api.php?host=192.168.1.73&day");
@@ -148,6 +185,8 @@ public class MainNativeActivity extends AppCompatActivity {
return super.onKeyDown(keyCode, event);
}
+ @SuppressLint("ObsoleteSdkInt")
+ @SafeVarargs
@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
public static void executeAsyncTask(AsyncTask asyncTask, T... params) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
@@ -159,6 +198,7 @@ public class MainNativeActivity extends AppCompatActivity {
/**
* Async task class to get json by making HTTP call
*/
+ @SuppressLint("StaticFieldLeak")
private class GetData extends AsyncTask {
String TAG = getClass().getSimpleName();
private ProgressBar pBar;
@@ -178,6 +218,9 @@ public class MainNativeActivity extends AppCompatActivity {
private final Condition tryAgain = lock.newCondition();
private volatile boolean finished = false;
+ ArrayList dataListString1 = new ArrayList<>();
+ ArrayList dataListString2 = new ArrayList<>();
+
GetData(int v1, int v2) {
this.v1 = v1;
this.v2 = v2;
@@ -202,6 +245,12 @@ public class MainNativeActivity extends AppCompatActivity {
this.endless = endless;
pBarLockCount -= 1; // do not count for endless tasks, pBarSmall is used
}
+ GetData(int v1, int v2, int min1, int max1, int delta1, int min2, int max2, int delta2, boolean endless,
+ ArrayList dataListString1, ArrayList dataListString2) {
+ this(v1, v2, min1, max1, delta1, min2, max2, delta2, endless);
+ this.dataListString1 = dataListString1;
+ this.dataListString2 = dataListString2;
+ }
@Override
protected void onPreExecute() {
@@ -228,7 +277,7 @@ public class MainNativeActivity extends AppCompatActivity {
lock.lock();
do {
getJsonOverHttpData(params);
- publishProgress();
+ publishProgress(); // goes to onProgressUpdate
tryAgain.awaitUninterruptibly();
//terminateTask();
} while(!finished);
@@ -250,7 +299,7 @@ public class MainNativeActivity extends AppCompatActivity {
}
void getJsonOverHttpData(String... params) {
- // URL to get contacts JSON
+ // URL to get data JSON
String url = params[0];
HttpHandler sh = new HttpHandler();
@@ -268,6 +317,7 @@ public class MainNativeActivity extends AppCompatActivity {
//JSONArray data = jsonObj.getJSONArray("contacts");
JSONArray data = new JSONArray(jsonStr);
+ dataList.clear();
// looping through all data
for (int i = 0; i < data.length(); i++) {
@@ -324,43 +374,51 @@ public class MainNativeActivity extends AppCompatActivity {
if (n > 0) {
HashMap hm = dataList.get(n - 1);
- float temp = Float.parseFloat(hm.get("temperature"));
- String temperature = String.format(Locale.getDefault(), "%.1f", temp);
+ String temp, humidity;
+ float tempF = Float.NaN, humidityF = Float.NaN;
- float humi = Float.parseFloat(hm.get("humidity"));
- String humidity = String.format(Locale.getDefault(), "%.1f", humi);
+ temp = hm.get("temperature");
+ if (temp != null) tempF = Float.parseFloat(temp);
+ temp = String.format(Locale.getDefault(), "%.1f", tempF);
- Color green = Color.valueOf(0x71ff5f);
- Color red = Color.valueOf(0xffd1655d);
- Color blue = Color.valueOf(Color.rgb(113, 157, 195));
+ humidity = hm.get("humidity");
+ if (humidity != null) humidityF = Float.parseFloat(humidity);
+ humidity = String.format(Locale.getDefault(), "%.1f", humidityF);
Color textColor;
- textColor = valueToColor(temp, 17, 19, 23, 25, blue, green, red);
+ textColor = valueToColor(tempF, 17, 19, 23, 25, blue, green, red);
TextView tv1 = findViewById(v1);
- tv1.setText(temperature);
+ tv1.setText(temp);
tv1.setTextColor(textColor.toArgb());
- textColor = valueToColor(humi, 25, 40, 60, 75, red, green, blue);
+ textColor = valueToColor(humidityF, 25, 40, 60, 75, red, green, blue);
TextView tv2 = findViewById(v2);
tv2.setText(humidity);
tv2.setTextColor(textColor.toArgb());
if (idMin1 != 0) {
- float min1 = 100, max1 = 0, val1;
- float min2 = 100, max2 = 0, val2;
+ float min1 = 100, max1 = 0, val1 = Float.NaN;
+ float min2 = 100, max2 = 0, val2 = Float.NaN;
+ dataListString1.clear();
+ dataListString2.clear();
+ String valString1, valString2;
for (int i = 0; i < n; i++) {
hm = dataList.get(i);
- val1 = Float.parseFloat(hm.get("temperature"));
- if (val1 < min1)
- min1 = val1;
- if (val1 > max1)
- max1 = val1;
- val2 = Float.parseFloat(hm.get("humidity"));
- if (val2 < min2)
- min2 = val2;
- if (val2 > max2)
- max2 = val2;
+ valString1 = hm.get("temperature");
+ if (valString1 != null)
+ val1 = Float.parseFloat(valString1);
+ valString2 = hm.get("humidity");
+ if (valString2 != null)
+ val2 = Float.parseFloat(valString2);
+ if (val2 <= 100 && val2 >= 0) {
+ dataListString1.add(valString1);
+ dataListString2.add(valString2);
+ if (val1 < min1) min1 = val1;
+ if (val1 > max1) max1 = val1;
+ if (val2 < min2) min2 = val2;
+ if (val2 > max2) max2 = val2;
+ }
}
TextView tv;
tv = findViewById(idMin1);
@@ -418,6 +476,369 @@ public class MainNativeActivity extends AppCompatActivity {
}
+ /**
+ * Async task class to get json by making HTTP call
+ */
+ @SuppressLint("StaticFieldLeak")
+ private class OpenWeatherMap extends AsyncTask {
+ String TAG = getClass().getSimpleName();
+ private ProgressBar pBar;
+
+ // OpenWeatherMap API
+ String url = "http://api.openweathermap.org/data/2.5/weather";
+
+ // APPID (API key)
+ String APPID = "f66f296d1ced9a6121998493b84fbaeb";
+
+ // Location id
+ int id = 2850872;
+
+ // Units
+ String units = "metric";
+
+ // Language
+ String lang = "de";
+
+ ArrayList> dataList = new ArrayList<>();
+
+ int v;
+
+ /*
+ * Properties to run endless
+ */
+ boolean endless = false;
+ private ProgressBar pBarSmall;
+ private final ReentrantLock lock = new ReentrantLock();
+ private final Condition tryAgain = lock.newCondition();
+ private volatile boolean finished = false;
+
+ OpenWeatherMap(int v) {
+ this.v = v;
+ pBarLockCount += 1;
+ }
+
+ /*
+ * Constructor to run endless
+ */
+// OpenWeatherMap(int v, boolean endless) {
+// this(v);
+// this.endless = endless;
+// pBarLockCount -= 1; // do not count for endless tasks, pBarSmall is used
+// }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ // Showing progress bar
+ pBar = findViewById(R.id.progressBar);
+ if (pBar.getVisibility() == View.INVISIBLE) {
+ pBar.setVisibility(View.VISIBLE);
+ }
+ if (endless) {
+ // Showing progress bar
+ pBarSmall = findViewById(R.id.progressBarSmall);
+ if (pBarSmall.getVisibility() == View.INVISIBLE) {
+ pBarSmall.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ @Override
+ protected Void doInBackground(String... params) {
+ if(!endless)
+ getJsonOverHttpData(params);
+ else{
+ lock.lock();
+ do {
+ getJsonOverHttpData(params);
+ publishProgress(); // goes to onProgressUpdate
+ tryAgain.awaitUninterruptibly();
+ //terminateTask();
+ } while(!finished);
+ lock.unlock();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ pBarLockCount -= 1;
+ // Dismiss the progress bar
+ if (pBar.getVisibility() == View.VISIBLE && pBarLockCount == 0) {
+ pBar.setVisibility(View.GONE);
+ }
+ updateViews();
+ }
+
+ void getJsonOverHttpData(String... params) {
+ // URL to get data JSON
+ if (params.length > 0)
+ url = params[0]; // overwrite openweathermap url with given url
+ else {
+ Map query = new LinkedHashMap<>();
+ query.put("APPID", APPID);
+ query.put("id", id);
+ query.put("units", units);
+ query.put("lang", lang);
+ Log.d(TAG, url + "?" + URLBuilder.httpBuildQuery(query, "UTF-8"));
+ }
+
+ HttpHandler sh = new HttpHandler();
+
+ // Making a request to url and getting response
+ String jsonStr = sh.makeServiceCall(url);
+
+ Log.d(TAG, "Response from url: " + jsonStr);
+
+ if (jsonStr != null) {
+ try {
+ dataList.clear();
+
+ JSONObject jsonObj = new JSONObject(jsonStr);
+ String name = jsonObj.getString("name"); // City name
+ String dt = jsonObj.getString("dt"); // Time of data calculation, unix, UTC
+
+ JSONObject weather = jsonObj.getJSONArray("weather").getJSONObject(0);
+ String weatherId = weather.getString("id"); // Weather condition id
+ String weatherDescription = weather.getString("description"); // Weather condition within the group
+ String weatherIcon = weather.getString("icon"); // Weather icon id
+
+ JSONObject main = jsonObj.getJSONObject("main");
+ String mainTemp = main.getString("temp"); // Temperature. Unit Default: Kelvin, Metric: Celsius, Imperial: Fahrenheit.
+ String mainTempMin = main.getString("temp_min"); // Minimum temperature at the moment. This is deviation from current temp that is possible for large cities and megalopolises geographically expanded (use these parameter optionally). Unit Default: Kelvin, Metric: Celsius, Imperial: Fahrenheit.
+ String mainTempMax = main.getString("temp_max"); // Maximum temperature at the moment. This is deviation from current temp that is possible for large cities and megalopolises geographically expanded (use these parameter optionally). Unit Default: Kelvin, Metric: Celsius, Imperial: Fahrenheit.
+ String mainHumidity = main.getString("humidity"); // Humidity, %
+ String mainPressure = main.getString("pressure"); // Atmospheric pressure (on the sea level, if there is no sea_level or grnd_level data), hPa
+
+ JSONObject sys = jsonObj.getJSONObject("sys");
+ String sunrise = sys.getString("sunrise"); // Sunrise time, unix, UTC
+ String sunset = sys.getString("sunset"); // Sunset time, unix, UTC
+
+ JSONObject wind = jsonObj.getJSONObject("wind");
+ String windSpeed = wind.getString("speed"); // Wind speed. Unit Default: meter/sec, Metric: meter/sec, Imperial: miles/hour.
+ String windDeg = wind.getString("deg"); // Wind direction, degrees (meteorological)
+ String windGust = wind.optString("gust");
+
+ JSONObject clouds = jsonObj.getJSONObject("clouds");
+ String cloudsAll = clouds.getString("all"); // Cloudiness, %
+
+ // tmp hash map for single datum
+ HashMap datum = new HashMap<>();
+
+ // adding each child node to HashMap key => value
+ datum.put("name", name);
+ datum.put("dt", dt);
+ datum.put("weather.id", weatherId);
+ datum.put("description", weatherDescription);
+ datum.put("icon", weatherIcon);
+ datum.put("temp", mainTemp);
+ datum.put("temp_min", mainTempMin);
+ datum.put("temp_max", mainTempMax);
+ datum.put("humidity", mainHumidity);
+ datum.put("pressure", mainPressure);
+ datum.put("speed", windSpeed);
+ datum.put("deg", windDeg);
+ datum.put("gust", windGust);
+ datum.put("clouds", cloudsAll);
+ datum.put("sunrise", sunrise);
+ datum.put("sunset", sunset);
+
+ // adding datum to data list
+ dataList.add(datum);
+ } catch (final JSONException e) {
+ Log.e(TAG, "Json parsing error: " + e.getMessage());
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(getApplicationContext(),
+ "Json parsing error: " + e.getMessage(),
+ Toast.LENGTH_LONG)
+ .show();
+ }
+ });
+ }
+ } else {
+ Log.e(TAG, "Couldn't get json from server.");
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(getApplicationContext(),
+ "Couldn't get json from server. Check LogCat for possible errors!",
+ Toast.LENGTH_LONG)
+ .show();
+ }
+ });
+ }
+ }
+
+ String degToCompass(int deg) {
+ int val = (int) ((deg / 22.5) + .5);
+ String[] arr = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W",
+ "WNW", "NW", "NNW"};
+ return arr[(val % 16)];
+ }
+
+ float mps2kmph(float speed) {
+ return speed*3.6f;
+ }
+
+ // temp in °C
+ // speed (wind) in m/s
+ float apparentTemperature(float temp, float speed, float humidity) {
+ speed = mps2kmph(speed);
+ float temp_a, temp2, humidity2;
+ // Windchill
+ // http://en.wikipedia.org/wiki/Wind_chill
+ if (temp <= 10 && speed >= 4) {
+ temp_a = 13.12f + 0.6215f * temp - 11.37f * (float) pow(speed, 0.16f) +
+ 0.3965f * temp * (float) pow(speed, 0.16f);
+ }
+ // Heat index
+ // http://de.wikipedia.org/wiki/Hitzeindex
+ else if(temp >= 26.7 && humidity >= 40) {
+ temp2 = (float) pow(temp, 2);
+ humidity2 = (float) pow(humidity, 2);
+ temp_a = -8.784695f + 1.61139411f * temp + 2.338549f * humidity +
+ -0.14611605f * temp * humidity + -1.2308094e-2f * temp2 +
+ -1.6424828e-2f * humidity2 + 2.211732e-3f * temp2 * humidity +
+ 7.2546e-4f * temp * humidity2 + -3.582e-6f * temp2 * humidity2;
+ } else{
+ temp_a = temp;
+ }
+ return temp_a;
+ }
+
+
+ /**
+ * Updating parsed JSON data into Views
+ */
+ void updateViews() {
+ int n = dataList.size();
+ if (n > 0) {
+ HashMap hm = dataList.get(n - 1);
+ DateTimeFormatter formatter;
+ float tempF = Float.NaN, humidityF = Float.NaN;
+ float speedF = Float.NaN;
+
+ String name = hm.get("name");
+ formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ String dt = hm.get("dt");
+ if (dt != null) dt = Instant.ofEpochSecond(Long.parseLong(dt)).atZone(ZoneId.systemDefault()).format(formatter);
+ String weatherId = hm.get("weather.id");
+ String description = hm.get("description");
+ String icon = hm.get("icon");
+
+ String temp = hm.get("temp");
+ if (temp != null) tempF = Float.parseFloat(temp);
+ temp = String.format(Locale.getDefault(), "%.1f", tempF);
+ String tempMin = hm.get("temp_min");
+ if (tempMin != null) tempMin = String.format(Locale.getDefault(), "%.1f", Float.parseFloat(tempMin));
+ String tempMax = hm.get("temp_max");
+ if (tempMax != null) tempMax = String.format(Locale.getDefault(), "%.1f", Float.parseFloat(tempMax));
+ String humidity = hm.get("humidity");
+ if (humidity != null) humidityF = Float.parseFloat(humidity);
+ humidity = String.format(Locale.getDefault(), "%.0f", humidityF);
+
+ String pressure = hm.get("pressure");
+ String speed = hm.get("speed");
+ if (speed != null) speedF = Float.parseFloat(speed);
+ speed = String.format(Locale.getDefault(), "%.1f", mps2kmph(speedF));
+ String deg = hm.get("deg");
+ int degI = Integer.MIN_VALUE;
+ if (deg != null) degI = Integer.parseInt(deg);
+ deg = degToCompass(degI);
+ String gust = hm.get("gust");
+ assert gust != null;
+ if (!gust.equals("")) {
+ float gustF = Float.parseFloat(gust);
+ gust = String.format(Locale.getDefault(), "%.1f", mps2kmph(gustF));
+ }
+ String clouds = hm.get("clouds");
+ formatter = DateTimeFormatter.ofPattern("HH:mm");
+ String sunrise = hm.get("sunrise");
+ if (sunrise != null) sunrise = Instant.ofEpochSecond(Long.parseLong(sunrise)).atZone(ZoneId.systemDefault()).format(formatter);
+ String sunset = hm.get("sunset");
+ if (sunset != null) sunset = Instant.ofEpochSecond(Long.parseLong(sunset)).atZone(ZoneId.systemDefault()).format(formatter);
+
+ String apparent = String.format(Locale.getDefault(), "%.1f", apparentTemperature(tempF, speedF, humidityF));
+
+ TextView tv = findViewById(v);
+ if (weatherId != null && weatherId.startsWith("5")){ // 5xx rain
+ if (!gust.equals(""))
+ tv.setText(Html.fromHtml(String.format(getString(R.string.weather_outdoor_rain_and_gust),
+ name, dt, description, clouds,
+ temp, tempMin, tempMax, apparent,
+ humidity, pressure, speed, deg, gust,
+ sunrise, sunset), Html.FROM_HTML_MODE_COMPACT));
+ else
+ tv.setText(Html.fromHtml(String.format(getString(R.string.weather_outdoor_rain),
+ name, dt, description, clouds,
+ temp, tempMin, tempMax, apparent,
+ humidity, pressure, speed, deg,
+ sunrise, sunset), Html.FROM_HTML_MODE_COMPACT));
+ } else if (!gust.equals(""))
+ tv.setText(Html.fromHtml(String.format(getString(R.string.weather_outdoor_gust),
+ name, dt, description, clouds,
+ temp, tempMin, tempMax, apparent,
+ humidity, pressure, speed, deg, gust,
+ sunrise, sunset), Html.FROM_HTML_MODE_COMPACT));
+ else tv.setText(Html.fromHtml(String.format(getString(R.string.weather_outdoor),
+ name, dt, description, clouds,
+ temp, tempMin, tempMax, apparent,
+ humidity, pressure, speed, deg,
+ sunrise, sunset), Html.FROM_HTML_MODE_COMPACT));
+
+ // insert image
+ SpannableStringBuilder ssb = new SpannableStringBuilder(tv.getText());
+ Context context = getApplicationContext();
+ int id = context.getResources().getIdentifier("openweathermap_" + icon, "drawable", context.getPackageName());
+ String weather = tv.getText().toString();
+ ssb.setSpan(new CenteredImageSpan(context, id), weather.indexOf("{icon}"), weather.indexOf("{icon}")+6, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ tv.setText(ssb, TextView.BufferType.SPANNABLE);
+ }
+ }
+
+ /*
+ * Functions to run endless
+ */
+
+ @Override
+ protected void onProgressUpdate(Void... result) {
+ //super.onProgressUpdate(result);
+ // Treat this like onPostExecute(), do something with result
+ if(endless) {
+ updateViews();
+ runAgain();
+ }
+ }
+
+ @Override
+ protected void onCancelled() {
+ // Make sure we clean up if the task is killed
+ if(endless)
+ terminateTask();
+ }
+
+ void runAgain() {
+ // Call this to request data from the server again
+ lock.lock();
+ try {
+ tryAgain.signal();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ void terminateTask() {
+ // The task will only finish when we call this method
+ finished = true;
+ //lock.unlock();
+ }
+
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
diff --git a/app/src/main/java/de/weseng/wifiweatherstation/URLBuilder.java b/app/src/main/java/de/weseng/wifiweatherstation/URLBuilder.java
new file mode 100644
index 0000000..ded41c8
--- /dev/null
+++ b/app/src/main/java/de/weseng/wifiweatherstation/URLBuilder.java
@@ -0,0 +1,168 @@
+package de.weseng.wifiweatherstation;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.*;
+
+/**
+ * Class: URLBuilder
+ *
+ * Utility that helps to build URL String
+ *
+ * Test functions in URLBuilderTest.java
+ *
+ * Source
+ * - User: Gilad Tiram
+ * - Date: 6/12/13
+ * - Time: 4:02 PM
+ */
+class URLBuilder {
+
+ /**
+ * Build URL string from Map of params. Nested Map and Collection is also supported
+ *
+ * @param params Map of params for constructing the URL Query String
+ * @param encoding encoding type. If not set the "UTF-8" is selected by default
+ * @return String of type key=value&...key=value
+ */
+ static String httpBuildQuery(Map params, String encoding) {
+ if (isEmpty(encoding)) {
+ encoding = "UTF-8";
+ }
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry entry : params.entrySet()) {
+ if (sb.length() > 0) {
+ sb.append('&');
+ }
+
+ String name = entry.getKey();
+ Object value = entry.getValue();
+
+
+ if (value instanceof Map) {
+ List baseParam = new ArrayList<>();
+ baseParam.add(name);
+ String str = buildUrlFromMap(baseParam, (Map) value, encoding);
+ sb.append(str);
+
+ } else if (value instanceof Collection) {
+ List baseParam = new ArrayList<>();
+ baseParam.add(name);
+ String str = buildUrlFromCollection(baseParam, (Collection) value, encoding);
+ sb.append(str);
+
+ } else {
+ sb.append(encodeParam(name));
+ sb.append("=");
+ sb.append(encodeParam(value));
+ }
+
+
+ }
+ return sb.toString();
+ }
+
+ private static String buildUrlFromMap(List baseParam, Map