From 37b43053a8e2a5fac60aa7d300ac12bfc0160197 Mon Sep 17 00:00:00 2001 From: Daniel Weschke Date: Sun, 17 Mar 2019 15:14:01 +0100 Subject: [PATCH] add local weather, chart activity and fix orientation bug float parsing with null check and nan as default --- .idea/dictionaries/weatherstation.xml | 12 + .idea/misc.xml | 35 ++ app/build.gradle | 9 +- app/src/main/AndroidManifest.xml | 15 +- .../wifiweatherstation/CenteredImageSpan.java | 79 +++ .../wifiweatherstation/DetailActivity.java | 287 ++++++++++ .../MainNativeActivity.java | 521 ++++++++++++++++-- .../weseng/wifiweatherstation/URLBuilder.java | 168 ++++++ .../main/res/drawable/openweathermap_01d.png | Bin 0 -> 2859 bytes .../main/res/drawable/openweathermap_01n.png | Bin 0 -> 2817 bytes .../main/res/drawable/openweathermap_02d.png | Bin 0 -> 2969 bytes .../main/res/drawable/openweathermap_02n.png | Bin 0 -> 2922 bytes .../main/res/drawable/openweathermap_03d.png | Bin 0 -> 2565 bytes .../main/res/drawable/openweathermap_03n.png | Bin 0 -> 2565 bytes .../main/res/drawable/openweathermap_04d.png | Bin 0 -> 2773 bytes .../main/res/drawable/openweathermap_04n.png | Bin 0 -> 2773 bytes .../main/res/drawable/openweathermap_09d.png | Bin 0 -> 3818 bytes .../main/res/drawable/openweathermap_09n.png | Bin 0 -> 3818 bytes .../main/res/drawable/openweathermap_10d.png | Bin 0 -> 3793 bytes .../main/res/drawable/openweathermap_10n.png | Bin 0 -> 3801 bytes .../main/res/drawable/openweathermap_11d.png | Bin 0 -> 3777 bytes .../main/res/drawable/openweathermap_11n.png | Bin 0 -> 3777 bytes .../main/res/drawable/openweathermap_13d.png | Bin 0 -> 3901 bytes .../main/res/drawable/openweathermap_13n.png | Bin 0 -> 3901 bytes .../main/res/drawable/openweathermap_50d.png | Bin 0 -> 3328 bytes .../main/res/drawable/openweathermap_50n.png | Bin 0 -> 3328 bytes app/src/main/res/layout/activity_detail.xml | 17 + .../main/res/layout/activity_main_native.xml | 35 +- app/src/main/res/values-de/strings.xml | 35 ++ app/src/main/res/values/strings.xml | 39 ++ .../wifiweatherstation/URLBuilderTest.java | 137 +++++ 31 files changed, 1327 insertions(+), 62 deletions(-) create mode 100644 .idea/dictionaries/weatherstation.xml create mode 100644 app/src/main/java/de/weseng/wifiweatherstation/CenteredImageSpan.java create mode 100644 app/src/main/java/de/weseng/wifiweatherstation/DetailActivity.java create mode 100644 app/src/main/java/de/weseng/wifiweatherstation/URLBuilder.java create mode 100644 app/src/main/res/drawable/openweathermap_01d.png create mode 100644 app/src/main/res/drawable/openweathermap_01n.png create mode 100644 app/src/main/res/drawable/openweathermap_02d.png create mode 100644 app/src/main/res/drawable/openweathermap_02n.png create mode 100644 app/src/main/res/drawable/openweathermap_03d.png create mode 100644 app/src/main/res/drawable/openweathermap_03n.png create mode 100644 app/src/main/res/drawable/openweathermap_04d.png create mode 100644 app/src/main/res/drawable/openweathermap_04n.png create mode 100644 app/src/main/res/drawable/openweathermap_09d.png create mode 100644 app/src/main/res/drawable/openweathermap_09n.png create mode 100644 app/src/main/res/drawable/openweathermap_10d.png create mode 100644 app/src/main/res/drawable/openweathermap_10n.png create mode 100644 app/src/main/res/drawable/openweathermap_11d.png create mode 100644 app/src/main/res/drawable/openweathermap_11n.png create mode 100644 app/src/main/res/drawable/openweathermap_13d.png create mode 100644 app/src/main/res/drawable/openweathermap_13n.png create mode 100644 app/src/main/res/drawable/openweathermap_50d.png create mode 100644 app/src/main/res/drawable/openweathermap_50n.png create mode 100644 app/src/main/res/layout/activity_detail.xml create mode 100644 app/src/test/java/de/weseng/wifiweatherstation/URLBuilderTest.java 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 map, String encoding) { + StringBuilder sb = new StringBuilder(); + String token; + + //Build string of first level - related with params of provided Map + for (Map.Entry entry : map.entrySet()) { + + if (sb.length() > 0) { + sb.append('&'); + } + + String name = String.valueOf(entry.getKey()); + Object value = entry.getValue(); + if (value instanceof Map) { + List baseParam2 = new ArrayList<>(baseParam); + baseParam2.add(name); + String str = buildUrlFromMap(baseParam2, (Map) value, encoding); + sb.append(str); + + } else if (value instanceof List) { + List baseParam2 = new ArrayList<>(baseParam); + baseParam2.add(name); + String str = buildUrlFromCollection(baseParam2, (List) value, encoding); + sb.append(str); + } else { + token = getBaseParamString(baseParam) + "[" + name + "]=" + encodeParam(value); + sb.append(token); + } + } + + return sb.toString(); + } + + private static String buildUrlFromCollection(List baseParam, Collection coll, String encoding) { + StringBuilder sb = new StringBuilder(); + String token; + if (!(coll instanceof List)) { + coll = new ArrayList(coll); + } + List arrColl = (List) coll; + + //Build string of first level - related with params of provided Map + for (int i = 0; i < arrColl.size(); i++) { + + if (sb.length() > 0) { + sb.append('&'); + } + + Object value = arrColl.get(i); + if (value instanceof Map) { + List baseParam2 = new ArrayList<>(baseParam); + baseParam2.add(String.valueOf(i)); + String str = buildUrlFromMap(baseParam2, (Map) value, encoding); + sb.append(str); + + } else if (value instanceof List) { + List baseParam2 = new ArrayList<>(baseParam); + baseParam2.add(String.valueOf(i)); + String str = buildUrlFromCollection(baseParam2, (List) value, encoding); + sb.append(str); + } else { + token = getBaseParamString(baseParam) + "[" + i + "]=" + encodeParam(value); + sb.append(token); + } + } + + return sb.toString(); + } + + + private static String getBaseParamString(List baseParam) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < baseParam.size(); i++) { + String s = baseParam.get(i); + if (i == 0) { + sb.append(s); + } else { + sb.append("[" + s + "]"); + } + } + return sb.toString(); + } + + /** + * Check if String is either empty or null + * + * @param str string to check + * @return true if string is empty. Else return false + */ + private static boolean isEmpty(String str) { + return str == null || str.length() == 0; + } + + + private static String encodeParam(Object param) { + try { + // @throws java.io.UnsupportedEncodingException if encoding is not supported + return URLEncoder.encode(String.valueOf(param), "UTF-8"); + } catch (UnsupportedEncodingException e) { + return URLEncoder.encode(String.valueOf(param)); + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/openweathermap_01d.png b/app/src/main/res/drawable/openweathermap_01d.png new file mode 100644 index 0000000000000000000000000000000000000000..7d2f792c324408eaccb8a13e0cd16fd502dd51cf GIT binary patch literal 2859 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_02n.png b/app/src/main/res/drawable/openweathermap_02n.png new file mode 100644 index 0000000000000000000000000000000000000000..780c35c17ffa41ce796a3f0070b47664b1ea18b8 GIT binary patch literal 2922 zcmb_edpMMN8=jH#AeAje8bd|CImcvToW_K!aTrDpC1d8rv~y|>j14Q*CbC;P%CVF0 z`z&fmii9ZTuoE&BsV&)5CQU0#x%g&=B(>M~&%W37p6=)LyYKh+JlA``-*=Ot{tA5% z2xRE(MWiV27}cq(t-M;eq)C)FJ)T#n00e?8QJrAWsa#VKNNY1QC`1@S-i)Vn*$5hg z8wDW5Y@X5^1acDNm5*#dNP~*mERFy#c7~}v@XBWujD$heF2e7fVIgFHs0Wu1KCrl8V`v^ zppdhfm4Qx5j(9Sc!HiBI(u4p3gF<8AC>$Jv3qoV@C>$QW-U&I+=W|VUvDaj*Gr>+s6^l8n)RL(dG!OrOY=z9|&$@m=S7rH{uL{Nc z@tJ^9XO)@+v=j0xYz{-UEtCQXxGa&{ABULNp>sAE7Q4`gf(~82)Kt81&heSqlY9 zWzb)?9fOYNGhfbG6Bv=oS>KCEpUxGNGDIN^`UGTH|^VeG%?gwQS&*)5&P6t0cmL^`Wri zhs=dLGW8Batq++V4YRkgEL?LPN=nu6TXB}!unz7e@k^<+q}W*-+IGw0YHEP^7$kNw zM&8NJ?pt|$C7R(^k=N?@VrptQaf+Q*_L_$^coPk3Ks`^Hcyh80okedtv*h;G zk_YBi%U9)jnk8gBFIv_sd6?`SCUNh&vjDSfTf4)UXmW36#-u;KqfdvtYvSd;%@?3k z2^U1gWJlcAiEBP`=_0rPE;8Al=?{j(Kw+A&25)L$Qf*XI-Gy?SE34l0n4WaNjrWPb z%PlN|bA4lWqD-w1dokJu_~Ti>YHSd^%?l4&SLY|+w-E2s;?^QaE%UTA*LSxz*QnP% zZfrCXYdVo%=Ty>(iSIXwY{0gZgu5+uOaOhCf8Vbxw*pU0|V?E9|vDnc)UK7 z>%aZ#6>UV`l%a3IU>dsK^_=-oxSm_O1MOJM4H@OWK}}XEu05?+9$|H$^RIFk)}zaa zYPg&9dw{1&UGKd_Y4*lf!zE!?O0;`iTy)*+4N^0;HW)2=A~FF7{yc*B8LO!p8f!rr zwPW0EHr>zBY8YxfxjWK7Y2e>nLS;g8v)ztYvO>G@)13E7ywMYZsP*eL&7~DvfgQR> zgEJz|8!cOf?TJi3tUM?qDE%shEaUF&*|~=?Gob7k!CB` zgz&J#0d0Ii#(C=N#-5t2>f2dANt7 z(f_zLui{B_!4dG&i5}n3|pTx2b^^UBWJ)4%;mC1Z%o! zVY)Z)X*azP;ME^vXF%0+NqtfZ2J)p50X9vVnz%|@6OAL-)MEov|bTI^qvk@eDe20&! zFti?QDIZF?x*9NkyNi%qBuO@V)O)HEZm>Ht{PBULV>wLZ?%esQz_I+J~(A# z`@091EUh-2rH8-MUfQ_B&7v16j34;H@>Xp-SVern!EYIJemeXk$ZD~RxSI*BR{afod-@W~+#^%}2Vehwp8x;= literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_03d.png b/app/src/main/res/drawable/openweathermap_03d.png new file mode 100644 index 0000000000000000000000000000000000000000..8a4a8e9fa75b5b860ff33f84cd6bf65a9ad04e54 GIT binary patch literal 2565 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-&H|6fVg?3oVGw3ym^DX&fq~U0 zGbExU!q>+tIX_n~F(p4KRj(qq0H~UQ!KT6r$jnVGNmQuF&B-gas<2f8tFQvHLBje< z3ScEA*|tg%z5xo(`9-M;rh0~YMoM;E3JMA~MJZ`kK`w4k?LeNbQbtKhft9{~d3m{B zxv^e;QM$gNrKP35fswwEkuFe$ZgFK^Nn(X=Ua>O75STeGsl~}fnFS@8`FRQ;a}$&D zOG|8(lt3220o0wj1#pYv;5=hU{aFdS+1b8CnV*`x_BM_jXmG!D8x zRw`Az%UZayTWQM$*YHIwE*yEH7h^R&7Oqp7-Ozo>EAy?B$R$2Eb=9LsQWl;~ne#pE z-u~w{dp*S-SLeSwb6DtJ>&)_<_V;b?|2${;y{PZ;gufSl{B>ZmbaDBY@8#Cz($(^$ zQDci!k&bcB5{}KUoo?;O7ZKe3Kq0TR^lNa%=D8bnt8bNch;(=EmuxGZE%3R2;zYr< z-044@3g+5K&A0jaJVWVNyqL?wB`ThUr?+)JzR~iWHEGter#H^kCRvJmA6|ODR`+EA zqw&P4Q>VV1A+mq&xi4ZD+4>&eo5j?hDSaYhnrlU$`l?lV=3AeIA3l88zBFU?^D;Fh z0WbN^JwLkVO!Yp{(6Q)&kfYHGgMT>z#|?PO_bfE~XRQ1A$b$zDlKr!-W~7{*nE!N6 zjMHJoB^?THgdC$Mu>32KQR;c}X=lvdxc}=mMOdoKiP^Jl^>PSmSo-?$ZN_j%WrUkDLhHDTTB^`*Z5xjAZ-x{_pyIyAbHBDa;M zW@o>?_T)Q+wFX7{8-Q8`odn%YJUTv83SPWmAQ!`Hx;$?AkEN z>C=shMs=nyroQU6wLg>BpIBXNP;+%|$`9@(CoWwIGP^nXkK?&i|F2(EVnj3Iwp6RB zwuJIN;yP{OV6kLj=K0rKZ;6C#fBrV<(JzOt6PZ7Q_UYGiy}IbqD(YtzLmaT&YHJ}=F!w>LHWw(IJ@y~+DrR$aYi{&7bMufBN> zn@Z;PjsWKg9C3>#H0LP>NNWkS7;^02wJ0QO>)V^1ot^F_wX4^^Uo3df*WG|;!<9#g zb)5HJ3G6rQdma;WMw};JrDeL#wh%4TB87=N0wZI3svrJ%#-Z{wnB_qCd4m#j$Gr^J za~D5sJIe6eYD%`I*#Df}{~vfB*!SD6CSK`k)>pZfAltcq%Qu7^QhiqSzP4d*-({so z`}j7jVpM$NJ?X-iL*G=5OiV)J;^Ou(Z<_C_P{Fcm-Y2HF$7F;K&1cZhJZy43^lId% zLtj;yX20FKbt|_+0#C#84~iappRwNQwLIf~Mx5hywz0AC>$n;FLc8yu4fI#zZBZIPPNqB_1>Gr5@}PN&FZ!qgu)J&=)=KZ~FqD@Jdej{z~6x Rf*Poy=IQF^vd$@?2>@Ou)Or8_ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_03n.png b/app/src/main/res/drawable/openweathermap_03n.png new file mode 100644 index 0000000000000000000000000000000000000000..8a4a8e9fa75b5b860ff33f84cd6bf65a9ad04e54 GIT binary patch literal 2565 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-&H|6fVg?3oVGw3ym^DX&fq~U0 zGbExU!q>+tIX_n~F(p4KRj(qq0H~UQ!KT6r$jnVGNmQuF&B-gas<2f8tFQvHLBje< z3ScEA*|tg%z5xo(`9-M;rh0~YMoM;E3JMA~MJZ`kK`w4k?LeNbQbtKhft9{~d3m{B zxv^e;QM$gNrKP35fswwEkuFe$ZgFK^Nn(X=Ua>O75STeGsl~}fnFS@8`FRQ;a}$&D zOG|8(lt3220o0wj1#pYv;5=hU{aFdS+1b8CnV*`x_BM_jXmG!D8x zRw`Az%UZayTWQM$*YHIwE*yEH7h^R&7Oqp7-Ozo>EAy?B$R$2Eb=9LsQWl;~ne#pE z-u~w{dp*S-SLeSwb6DtJ>&)_<_V;b?|2${;y{PZ;gufSl{B>ZmbaDBY@8#Cz($(^$ zQDci!k&bcB5{}KUoo?;O7ZKe3Kq0TR^lNa%=D8bnt8bNch;(=EmuxGZE%3R2;zYr< z-044@3g+5K&A0jaJVWVNyqL?wB`ThUr?+)JzR~iWHEGter#H^kCRvJmA6|ODR`+EA zqw&P4Q>VV1A+mq&xi4ZD+4>&eo5j?hDSaYhnrlU$`l?lV=3AeIA3l88zBFU?^D;Fh z0WbN^JwLkVO!Yp{(6Q)&kfYHGgMT>z#|?PO_bfE~XRQ1A$b$zDlKr!-W~7{*nE!N6 zjMHJoB^?THgdC$Mu>32KQR;c}X=lvdxc}=mMOdoKiP^Jl^>PSmSo-?$ZN_j%WrUkDLhHDTTB^`*Z5xjAZ-x{_pyIyAbHBDa;M zW@o>?_T)Q+wFX7{8-Q8`odn%YJUTv83SPWmAQ!`Hx;$?AkEN z>C=shMs=nyroQU6wLg>BpIBXNP;+%|$`9@(CoWwIGP^nXkK?&i|F2(EVnj3Iwp6RB zwuJIN;yP{OV6kLj=K0rKZ;6C#fBrV<(JzOt6PZ7Q_UYGiy}IbqD(YtzLmaT&YHJ}=F!w>LHWw(IJ@y~+DrR$aYi{&7bMufBN> zn@Z;PjsWKg9C3>#H0LP>NNWkS7;^02wJ0QO>)V^1ot^F_wX4^^Uo3df*WG|;!<9#g zb)5HJ3G6rQdma;WMw};JrDeL#wh%4TB87=N0wZI3svrJ%#-Z{wnB_qCd4m#j$Gr^J za~D5sJIe6eYD%`I*#Df}{~vfB*!SD6CSK`k)>pZfAltcq%Qu7^QhiqSzP4d*-({so z`}j7jVpM$NJ?X-iL*G=5OiV)J;^Ou(Z<_C_P{Fcm-Y2HF$7F;K&1cZhJZy43^lId% zLtj;yX20FKbt|_+0#C#84~iappRwNQwLIf~Mx5hywz0AC>$n;FLc8yu4fI#zZBZIPPNqB_1>Gr5@}PN&FZ!qgu)J&=)=KZ~FqD@Jdej{z~6x Rf*Poy=IQF^vd$@?2>@Ou)Or8_ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_04d.png b/app/src/main/res/drawable/openweathermap_04d.png new file mode 100644 index 0000000000000000000000000000000000000000..4677b8596acb4bed4fc8c62633e673567e8af6db GIT binary patch literal 2773 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_09n.png b/app/src/main/res/drawable/openweathermap_09n.png new file mode 100644 index 0000000000000000000000000000000000000000..b7cb53b448d924ca4aa4a542e1e248344a13ebdd GIT binary patch literal 3818 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_10d.png b/app/src/main/res/drawable/openweathermap_10d.png new file mode 100644 index 0000000000000000000000000000000000000000..b1e1f8305ef7558bad0ab12e5298db59229d69a3 GIT binary patch literal 3793 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_11n.png b/app/src/main/res/drawable/openweathermap_11n.png new file mode 100644 index 0000000000000000000000000000000000000000..f3ce6541e2377910bee2e6c447835e3d686cee89 GIT binary patch literal 3777 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_13d.png b/app/src/main/res/drawable/openweathermap_13d.png new file mode 100644 index 0000000000000000000000000000000000000000..7b64a6cfd080465634c92f56e1eba8a5a3166c4f GIT binary patch literal 3901 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 V(K#=GDl30*9c^83SFQ2K{sUPJTaN$$ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_13n.png b/app/src/main/res/drawable/openweathermap_13n.png new file mode 100644 index 0000000000000000000000000000000000000000..7b64a6cfd080465634c92f56e1eba8a5a3166c4f GIT binary patch literal 3901 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 V(K#=GDl30*9c^83SFQ2K{sUPJTaN$$ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_50d.png b/app/src/main/res/drawable/openweathermap_50d.png new file mode 100644 index 0000000000000000000000000000000000000000..18afb341e1f8f91119d249434cf170be5e686715 GIT binary patch literal 3328 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 AO#lD@ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/openweathermap_50n.png b/app/src/main/res/drawable/openweathermap_50n.png new file mode 100644 index 0000000000000000000000000000000000000000..18afb341e1f8f91119d249434cf170be5e686715 GIT binary patch literal 3328 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 AO#lD@ literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_detail.xml b/app/src/main/res/layout/activity_detail.xml new file mode 100644 index 0000000..25b2c83 --- /dev/null +++ b/app/src/main/res/layout/activity_detail.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main_native.xml b/app/src/main/res/layout/activity_main_native.xml index 4a3d9d7..fc87044 100644 --- a/app/src/main/res/layout/activity_main_native.xml +++ b/app/src/main/res/layout/activity_main_native.xml @@ -10,8 +10,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_vertical_margin" - android:layout_marginStart="@dimen/activity_horizontal_margin" - android:layout_marginEnd="@dimen/activity_horizontal_margin" + android:layout_marginStart="@dimen/widget_horizontal_margin" + android:layout_marginEnd="@dimen/widget_horizontal_margin" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintStart_toStartOf="parent" @@ -21,6 +21,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="-8dp" + android:layout_marginStart="@dimen/widget_horizontal_margin" android:weightSum="6"> + android:layout_height="match_parent" + android:layout_marginStart="@dimen/widget_horizontal_margin" + android:layout_marginEnd="@dimen/widget_horizontal_margin"> + android:layout_height="match_parent" + android:layout_marginStart="@dimen/widget_horizontal_margin" + android:layout_marginEnd="@dimen/widget_horizontal_margin"> + android:layout_height="match_parent" + android:layout_marginStart="@dimen/widget_horizontal_margin" + android:layout_marginEnd="@dimen/widget_horizontal_margin"> + + + + Temperatur Feuchtigkeit + Wetterdaten für %s %s +
    {icon} %s (%s %%) +
    Temperatur: %s °C — %s° / %s° gefühlt: %s° +
    Feuchtigkeit: %s %% +
    Druck: %s hPa +
    Wind: %s km/h %s +
    Sonnenaufgang / -untergang: %s / %s]]>
    + Wetterdaten für %s %s +
    {icon} %s, Bewölkung: %s %% +
    Temperatur: %s °C — %s° / %s° gefühlt: %s° +
    Feuchtigkeit: %s %% +
    Druck: %s hPa +
    Wind: %s km/h %s +
    Sonnenaufgang / -untergang: %s / %s]]>
    + Wetterdaten für %s %s +
    {icon} %s, Bewölkung: %s %% +
    Temperatur: %s °C — %s° / %s° gefühlt: %s° +
    Feuchtigkeit: %s %% +
    Druck: %s hPa +
    Wind: %s km/h %s Windböe %s km/h +
    Sonnenaufgang / -untergang: %s / %s]]>
    + Wetterdaten für %s %s +
    {icon} %s (%s %%) +
    Temperatur: %s °C — %s° / %s° gefühlt: %s° +
    Feuchtigkeit: %s %% +
    Druck: %s hPa +
    Wind: %s km/h %s Windböe %s km/h +
    Sonnenaufgang / -untergang: %s / %s]]>
    + Wetterdaten für %s %s +
    {icon} %s, Niederschlagsmenge: %s mm, Bewölkung: %s %% +
    Temperatur: %s °C — %s° / %s° gefühlt: %s° +
    Feuchtigkeit: %s %% +
    Druck: %s hPa +
    Wind: %s km/h %s +
    Sonnenaufgang / -untergang: %s / %s]]>
    Einstellungen Verbindung: intern diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b4b2373..968f13b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,6 +8,45 @@ % __._ o + + Weather data for %s %s +
    {icon} %s (%s %%) +
    Temperature: %s °C — %s° / %s° feels: %s° +
    Humidity: %s %% +
    Pressure: %s hPa +
    Wind: %s km/h %s +
    Sunrise / sunset: %s / %s]]>
    + + + Weather data for %s %s +
    {icon} %s, clouds: %s %% +
    Temperature: %s °C — %s° / %s° feels: %s° +
    Humidity: %s %% +
    Pressure: %s hPa +
    Wind: %s km/h %s +
    Sunrise / sunset: %s / %s]]>
    + Weather data for %s %s +
    {icon} %s, clouds: %s %% +
    Temperature: %s °C — %s° / %s° feels: %s° +
    Humidity: %s %% +
    Pressure: %s hPa +
    Wind: %s km/h %s gust: %s km/h +
    Sunrise / sunset: %s / %s]]>
    + Weather data for %s %s +
    {icon} %s (%s %%) +
    Temperature: %s °C — %s° / %s° feels: %s° +
    Humidity: %s %% +
    Pressure: %s hPa +
    Wind: %s km/h %s gust: %s km/h +
    Sunrise / sunset: %s / %s]]>
    + + Weather data for %s %s +
    {icon} %s, rainfall: %s mm, clouds: %s %% +
    Temperature: %s °C — %s° / %s° feels: %s° +
    Humidity: %s %% +
    Pressure: %s hPa +
    Wind: %s km/h %s +
    Sunrise / sunset: %s / %s]]>
    Settings de.weseng.wifiweatherstation.MESSAGE diff --git a/app/src/test/java/de/weseng/wifiweatherstation/URLBuilderTest.java b/app/src/test/java/de/weseng/wifiweatherstation/URLBuilderTest.java new file mode 100644 index 0000000..d8b62cb --- /dev/null +++ b/app/src/test/java/de/weseng/wifiweatherstation/URLBuilderTest.java @@ -0,0 +1,137 @@ +package de.weseng.wifiweatherstation; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static de.weseng.wifiweatherstation.URLBuilder.httpBuildQuery; +import static org.junit.Assert.assertEquals; + +public class URLBuilderTest { + + @Test + public void basicTest() { + Map params = new LinkedHashMap<>(); + params.put("a", "1"); + params.put("b", "2"); + params.put("c", "3"); + + assertEquals("a=1&b=2&c=3", + httpBuildQuery(params, "UTF-8")); + } + + @Test + public void testWithMap() { + Map params = new LinkedHashMap<>(); + params.put("a", "1"); + params.put("b", "2"); + + Map cParams = new LinkedHashMap<>(); + cParams.put("c1", "c1val"); + cParams.put("c2", "c2val"); + params.put("c", cParams); + + assertEquals("a=1&b=2&c[c1]=c1val&c[c2]=c2val", + httpBuildQuery(params, "UTF-8")); + } + + @Test + public void testWithNestedMap() { + Map params = new LinkedHashMap<>(); + params.put("a", "1"); + params.put("b", "2"); + + Map cParamsLevel1 = new LinkedHashMap<>(); + cParamsLevel1.put("cL1-1", "cLevel1-1val"); + cParamsLevel1.put("cL1-2", "cLevel1-2val"); + + Map cParamsLevel2 = new LinkedHashMap<>(); + cParamsLevel2.put("cL2-1", "cLevel2-1val"); + cParamsLevel2.put("cL2-2", "cLevel2-2val"); + cParamsLevel1.put("cL1-3", cParamsLevel2); + + params.put("c", cParamsLevel1); + + assertEquals("a=1&b=2&c[cL1-1]=cLevel1-1val&c[cL1-2]=cLevel1-2val&c[cL1-3][cL2-1]=cLevel2-1val&c[cL1-3][cL2-2]=cLevel2-2val", + httpBuildQuery(params, "UTF-8")); + } + + @Test + public void testWithList() { + Map params = new LinkedHashMap<>(); + params.put("a", "1"); + params.put("b", "2"); + + List cParams = new ArrayList<>(); + cParams.add("c1val"); + cParams.add("c2val"); + params.put("c", cParams); + + assertEquals("a=1&b=2&c[0]=c1val&c[1]=c2val", + httpBuildQuery(params, "UTF-8")); + } + + @Test + public void testWithNestedList() { + Map params = new LinkedHashMap<>(); + params.put("a", "1"); + params.put("b", "2"); + + List cParamsLevel1 = new ArrayList<>(); + cParamsLevel1.add("cL1-val1"); + cParamsLevel1.add("cL12-val2"); + + List cParamsLevel2 = new ArrayList<>(); + cParamsLevel2.add("cL2-val1"); + cParamsLevel2.add("cL2-val2"); + cParamsLevel1.add(cParamsLevel2); + + params.put("c", cParamsLevel1); + + assertEquals("a=1&b=2&c[0]=cL1-val1&c[1]=cL12-val2&c[2][0]=cL2-val1&c[2][1]=cL2-val2", + httpBuildQuery(params, "UTF-8")); + } + + @Test + public void testCompound() { + Map params = new LinkedHashMap<>(); + + //flat + params.put("a", "1"); + params.put("b", "2"); + + //Map level 1 + Map cParamsLevel1 = new LinkedHashMap<>(); + cParamsLevel1.put("cL1-1", "cLevel1-1val"); + cParamsLevel1.put("cL1-2", "cLevel1-2val"); + + //Map level 2 + Map cParamsLevel2 = new LinkedHashMap<>(); + cParamsLevel2.put("cL2-1", "cLevel2-1val"); + cParamsLevel2.put("cL2-2", "cLevel2-2val"); + cParamsLevel1.put("cL1-3", cParamsLevel2); + + params.put("c", cParamsLevel1); + + //List level 1 + List dParamsLevel1 = new ArrayList<>(); + dParamsLevel1.add("dL1-val1"); + dParamsLevel1.add("dL12-val2"); + + //List level 2 + List dParamsLevel2 = new ArrayList<>(); + dParamsLevel2.add("dL2-val1"); + dParamsLevel2.add("dL2-val2"); + dParamsLevel1.add(dParamsLevel2); + + params.put("d", dParamsLevel1); + + assertEquals("a=1&b=2&c[cL1-1]=cLevel1-1val&c[cL1-2]=cLevel1-2val&c[cL1-3][cL2-1]=cLevel2-1val&c[cL1-3][cL2-2]=cLevel2-2val&d[0]=dL1-val1&d[1]=dL12-val2&d[2][0]=dL2-val1&d[2][1]=dL2-val2", + httpBuildQuery(params, "UTF-8")); + + } + +} \ No newline at end of file