From f44f77a493c051740789cf2dd762a28e11f80182 Mon Sep 17 00:00:00 2001 From: Daniel Weschke Date: Sat, 9 Mar 2019 21:16:49 +0100 Subject: [PATCH] change to min sdk 26, move local check to settings and add native activity --- app/build.gradle | 7 +- app/src/main/AndroidManifest.xml | 32 +- .../wifiweatherstation/HttpHandler.java | 63 +++ .../wifiweatherstation/LoginActivity.java | 363 ++++++++++++++++++ .../wifiweatherstation/MainActivity.java | 69 +--- .../MainNativeActivity.java | 310 +++++++++++++++ .../wifiweatherstation/SettingsActivity.java | 86 ++++- app/src/main/res/layout/activity_login.xml | 66 ++++ .../main/res/layout/activity_main_native.xml | 198 ++++++++++ app/src/main/res/layout/activity_settings.xml | 36 +- app/src/main/res/values-de/strings.xml | 9 +- app/src/main/res/values/dimens.xml | 7 + app/src/main/res/values/strings.xml | 28 +- build.gradle | 2 +- 14 files changed, 1188 insertions(+), 88 deletions(-) create mode 100644 app/src/main/java/de/weseng/wifiweatherstation/HttpHandler.java create mode 100644 app/src/main/java/de/weseng/wifiweatherstation/LoginActivity.java create mode 100644 app/src/main/java/de/weseng/wifiweatherstation/MainNativeActivity.java create mode 100644 app/src/main/res/layout/activity_login.xml create mode 100644 app/src/main/res/layout/activity_main_native.xml create mode 100644 app/src/main/res/values/dimens.xml diff --git a/app/build.gradle b/app/build.gradle index 9895c17..b14403f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,10 +4,10 @@ android { compileSdkVersion 28 defaultConfig { applicationId "de.weseng.wifiweatherstation" - minSdkVersion 16 + minSdkVersion 26 targetSdkVersion 28 - versionCode 20190302 - versionName "2019.3.2" + versionCode 20190309 + versionName "2019.3.9" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { @@ -22,6 +22,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.android.support:support-annotations:28.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b7b63b8..2241dd7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,9 +3,13 @@ xmlns:tools="http://schemas.android.com/tools" package="de.weseng.wifiweatherstation"> + + + + - + - + + - - - + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/de/weseng/wifiweatherstation/HttpHandler.java b/app/src/main/java/de/weseng/wifiweatherstation/HttpHandler.java new file mode 100644 index 0000000..2c91780 --- /dev/null +++ b/app/src/main/java/de/weseng/wifiweatherstation/HttpHandler.java @@ -0,0 +1,63 @@ +package de.weseng.wifiweatherstation; + +import android.util.Log; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.URL; + +public class HttpHandler { + + private static final String TAG = HttpHandler.class.getSimpleName(); + + public HttpHandler() { + } + + public String makeServiceCall(String reqUrl) { + String response = null; + try { + URL url = new URL(reqUrl); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + // read the response + InputStream in = new BufferedInputStream(conn.getInputStream()); + response = convertStreamToString(in); + } catch (MalformedURLException e) { + Log.e(TAG, "MalformedURLException: " + e.getMessage()); + } catch (ProtocolException e) { + Log.e(TAG, "ProtocolException: " + e.getMessage()); + } catch (IOException e) { + Log.e(TAG, "IOException: " + e.getMessage()); + } catch (Exception e) { + Log.e(TAG, "Exception: " + e.getMessage()); + } + return response; + } + + private String convertStreamToString(InputStream is) { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + StringBuilder sb = new StringBuilder(); + + String line; + try { + while ((line = reader.readLine()) != null) { + sb.append(line).append('\n'); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/weseng/wifiweatherstation/LoginActivity.java b/app/src/main/java/de/weseng/wifiweatherstation/LoginActivity.java new file mode 100644 index 0000000..a73f42b --- /dev/null +++ b/app/src/main/java/de/weseng/wifiweatherstation/LoginActivity.java @@ -0,0 +1,363 @@ +package de.weseng.wifiweatherstation; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.annotation.TargetApi; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.support.annotation.NonNull; + +import android.app.Activity; +import android.app.LoaderManager.LoaderCallbacks; + +import android.content.CursorLoader; +import android.content.Loader; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; + +import android.os.Build; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.text.TextUtils; +import android.view.KeyEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.inputmethod.EditorInfo; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +import static android.Manifest.permission.READ_CONTACTS; +import static de.weseng.wifiweatherstation.SettingsActivity.PREFS_NAME; + +/** + * A login screen that offers login via email/password. + */ +public class LoginActivity extends Activity implements LoaderCallbacks { + + /** + * Id to identity READ_CONTACTS permission request. + */ + private static final int REQUEST_READ_CONTACTS = 0; + + /** + * A dummy authentication store containing known user names and passwords. + * TODO: remove after connecting to a real authentication system. + */ + private static final String[] DUMMY_CREDENTIALS = new String[]{ + "foo@example.com:hello", "bar@example.com:world" + }; + /** + * Keep track of the login task to ensure we can cancel it if requested. + */ + private UserLoginTask mAuthTask = null; + + // UI references. + private AutoCompleteTextView mEmailView; + private EditText mPasswordView; + private View mProgressView; + private View mLoginFormView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); + boolean nativeView = settings.getBoolean("nativeView", true); + + if(nativeView){ + Intent intent = new Intent(this, MainNativeActivity.class); + startActivity(intent); + } else { + Intent intent = new Intent(this, MainActivity.class); + startActivity(intent); + } + + // + // BEGIN Template Login Activity + // +// setContentView(R.layout.activity_login); +// // Set up the login form. +// mEmailView = (AutoCompleteTextView) findViewById(R.id.email); +// populateAutoComplete(); +// +// mPasswordView = (EditText) findViewById(R.id.password); +// mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { +// @Override +// public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { +// if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) { +// attemptLogin(); +// return true; +// } +// return false; +// } +// }); +// +// Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button); +// mEmailSignInButton.setOnClickListener(new OnClickListener() { +// @Override +// public void onClick(View view) { +// attemptLogin(); +// } +// }); +// +// mLoginFormView = findViewById(R.id.login_form); +// mProgressView = findViewById(R.id.login_progress); + } + + private void populateAutoComplete() { + if (!mayRequestContacts()) { + return; + } + + getLoaderManager().initLoader(0, null, this); + } + + private boolean mayRequestContacts() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return true; + } + if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { + return true; + } + if (shouldShowRequestPermissionRationale(READ_CONTACTS)) { + // TODO: alert the user with a Snackbar/AlertDialog giving them the permission rationale + // To use the Snackbar from the design support library, ensure that the activity extends + // AppCompatActivity and uses the Theme.AppCompat theme. + } else { + requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); + } + return false; + } + + /** + * Callback received when a permissions request has been completed. + */ + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + if (requestCode == REQUEST_READ_CONTACTS) { + if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + populateAutoComplete(); + } + } + } + + + /** + * Attempts to sign in or register the account specified by the login form. + * If there are form errors (invalid email, missing fields, etc.), the + * errors are presented and no actual login attempt is made. + */ + private void attemptLogin() { + if (mAuthTask != null) { + return; + } + + // Reset errors. + mEmailView.setError(null); + mPasswordView.setError(null); + + // Store values at the time of the login attempt. + String email = mEmailView.getText().toString(); + String password = mPasswordView.getText().toString(); + + boolean cancel = false; + View focusView = null; + + // Check for a valid password, if the user entered one. + if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { + mPasswordView.setError(getString(R.string.error_invalid_password)); + focusView = mPasswordView; + cancel = true; + } + + // Check for a valid email address. + if (TextUtils.isEmpty(email)) { + mEmailView.setError(getString(R.string.error_field_required)); + focusView = mEmailView; + cancel = true; + } else if (!isEmailValid(email)) { + mEmailView.setError(getString(R.string.error_invalid_email)); + focusView = mEmailView; + cancel = true; + } + + if (cancel) { + // There was an error; don't attempt login and focus the first + // form field with an error. + focusView.requestFocus(); + } else { + // Show a progress spinner, and kick off a background task to + // perform the user login attempt. + showProgress(true); + mAuthTask = new UserLoginTask(email, password); + mAuthTask.execute((Void) null); + } + } + + private boolean isEmailValid(String email) { + //TODO: Replace this with your own logic + return email.contains("@"); + } + + private boolean isPasswordValid(String password) { + //TODO: Replace this with your own logic + return password.length() > 4; + } + + /** + * Shows the progress UI and hides the login form. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) + private void showProgress(final boolean show) { + // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow + // for very easy animations. If available, use these APIs to fade-in + // the progress spinner. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { + int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); + + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + mLoginFormView.animate().setDuration(shortAnimTime).alpha( + show ? 0 : 1).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + } + }); + + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + mProgressView.animate().setDuration(shortAnimTime).alpha( + show ? 1 : 0).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + } + }); + } else { + // The ViewPropertyAnimator APIs are not available, so simply show + // and hide the relevant UI components. + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + } + } + + @Override + public Loader onCreateLoader(int i, Bundle bundle) { + return new CursorLoader(this, + // Retrieve data rows for the device user's 'profile' contact. + Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI, + ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION, + + // Select only email addresses. + ContactsContract.Contacts.Data.MIMETYPE + + " = ?", new String[]{ContactsContract.CommonDataKinds.Email + .CONTENT_ITEM_TYPE}, + + // Show primary email addresses first. Note that there won't be + // a primary email address if the user hasn't specified one. + ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"); + } + + @Override + public void onLoadFinished(Loader cursorLoader, Cursor cursor) { + List emails = new ArrayList<>(); + cursor.moveToFirst(); + while (!cursor.isAfterLast()) { + emails.add(cursor.getString(ProfileQuery.ADDRESS)); + cursor.moveToNext(); + } + + addEmailsToAutoComplete(emails); + } + + @Override + public void onLoaderReset(Loader cursorLoader) { + + } + + private void addEmailsToAutoComplete(List emailAddressCollection) { + //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list. + ArrayAdapter adapter = + new ArrayAdapter<>(LoginActivity.this, + android.R.layout.simple_dropdown_item_1line, emailAddressCollection); + + mEmailView.setAdapter(adapter); + } + + + private interface ProfileQuery { + String[] PROJECTION = { + ContactsContract.CommonDataKinds.Email.ADDRESS, + ContactsContract.CommonDataKinds.Email.IS_PRIMARY, + }; + + int ADDRESS = 0; + int IS_PRIMARY = 1; + } + + /** + * Represents an asynchronous login/registration task used to authenticate + * the user. + */ + public class UserLoginTask extends AsyncTask { + + private final String mEmail; + private final String mPassword; + + UserLoginTask(String email, String password) { + mEmail = email; + mPassword = password; + } + + @Override + protected Boolean doInBackground(Void... params) { + // TODO: attempt authentication against a network service. + + try { + // Simulate network access. + Thread.sleep(2000); + } catch (InterruptedException e) { + return false; + } + + for (String credential : DUMMY_CREDENTIALS) { + String[] pieces = credential.split(":"); + if (pieces[0].equals(mEmail)) { + // Account exists, return true if the password matches. + return pieces[1].equals(mPassword); + } + } + + // TODO: register the new account here. + return true; + } + + @Override + protected void onPostExecute(final Boolean success) { + mAuthTask = null; + showProgress(false); + + if (success) { + finish(); + } else { + mPasswordView.setError(getString(R.string.error_incorrect_password)); + mPasswordView.requestFocus(); + } + } + + @Override + protected void onCancelled() { + mAuthTask = null; + showProgress(false); + } + } +} + diff --git a/app/src/main/java/de/weseng/wifiweatherstation/MainActivity.java b/app/src/main/java/de/weseng/wifiweatherstation/MainActivity.java index df1eb46..afbc223 100644 --- a/app/src/main/java/de/weseng/wifiweatherstation/MainActivity.java +++ b/app/src/main/java/de/weseng/wifiweatherstation/MainActivity.java @@ -2,17 +2,9 @@ package de.weseng.wifiweatherstation; import android.annotation.SuppressLint; import android.app.ProgressDialog; -import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.Color; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.wifi.SupplicantState; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Base64; @@ -25,13 +17,9 @@ import android.webkit.WebViewClient; import android.widget.Toast; import java.io.InputStream; -import java.util.List; - public class MainActivity extends AppCompatActivity { - public static final String PREFS_NAME = "Settings"; WebView myWebView; - public static String MESSAGE = "de.weseng.wifiweatherstation.MESSAGE"; @SuppressLint("SetJavaScriptEnabled") @Override @@ -62,11 +50,6 @@ public class MainActivity extends AppCompatActivity { myWebView.setBackgroundColor(Color.TRANSPARENT); myWebView.setPadding(0, 0, 0, 0); - SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); - String ssidLocal = "\"" + settings.getString("ssidLocal", getString(R.string.ssid)) + "\""; - - MESSAGE = ""; - myWebView.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { @@ -75,16 +58,19 @@ public class MainActivity extends AppCompatActivity { @Override public void onPageFinished(WebView view, String url) { + // Dismiss the progress dialog + pd.dismiss(); + // Inject CSS when page is done loading injectCSS(); - pd.dismiss(); //String webUrl = myWebView.getUrl(); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { /* will open a new web page in webview*/ + // loadUrl needs: AndroidManifest.xml listOfConfigurations = wifiManager.getConfiguredNetworks(); - for (int index = 0; index < listOfConfigurations.size(); index++) { - WifiConfiguration configuration = listOfConfigurations.get(index); - if (configuration.networkId == wifiInfo.getNetworkId()) { - return configuration.SSID; - } - } - return null; - } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // Check if the key event was the Back button and if there's history @@ -181,7 +138,6 @@ public class MainActivity extends AppCompatActivity { myWebView.restoreState(savedInstanceState); } - // Inject CSS method: read style.css from assets folder // Append stylesheet to document head private void injectCSS() { @@ -208,9 +164,8 @@ public class MainActivity extends AppCompatActivity { /** Called when the user taps the Send button */ public void settings() { Intent intentSettings = new Intent(this, SettingsActivity.class); - intentSettings.putExtra(MESSAGE, MESSAGE); + //intentSettings.putExtra(MESSAGE, MESSAGE); startActivity(intentSettings); } - } diff --git a/app/src/main/java/de/weseng/wifiweatherstation/MainNativeActivity.java b/app/src/main/java/de/weseng/wifiweatherstation/MainNativeActivity.java new file mode 100644 index 0000000..b9d7519 --- /dev/null +++ b/app/src/main/java/de/weseng/wifiweatherstation/MainNativeActivity.java @@ -0,0 +1,310 @@ +package de.weseng.wifiweatherstation; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.os.AsyncTask; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; + +import static de.weseng.wifiweatherstation.SettingsActivity.PREFS_NAME; + +public class MainNativeActivity extends AppCompatActivity { + private int pDialogLockCount = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main_native); + + SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); + String urlLocal = settings.getString("urlLocal", getString(R.string.url_local)); + String urlGlobal = settings.getString("urlGlobal", getString(R.string.url_global)); + + 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(); + if (urlGlobal != null && urlGlobal.length() > 0) + urlPre = urlGlobal.substring(urlGlobal.length() - 1).equals("/") ? urlGlobal : urlGlobal + "/"; + } + //Toast.makeText(getApplicationContext(), urlPre, Toast.LENGTH_LONG).show(); + new GetData(R.id.textViewTemperatureValue1, R.id.textViewHumidityValue1).execute( + urlPre + "api.php?host=192.168.1.71&last"); + new GetData(R.id.textViewTemperatureValue2, R.id.textViewHumidityValue2).execute( + urlPre + "api.php?host=192.168.1.72&last"); + new GetData(R.id.textViewTemperatureValue3, R.id.textViewHumidityValue3).execute( + urlPre + "api.php?host=192.168.1.73&last"); + + } + } + + /** + * Async task class to get json by making HTTP call + */ + private class GetData extends AsyncTask { + String TAG = getClass().getSimpleName(); + private ProgressBar pBar; + + ArrayList> dataList = new ArrayList<>(); + + int v1, v2; + + GetData(int v1, int v2) { + this.v1 = v1; + this.v2 = v2; + pDialogLockCount += 1; + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + // Showing progress bar +// if (pBar == null) { +// pBar= new ProgressBar(MainNativeActivity.this); +// } + pBar = findViewById(R.id.progressBar); + if (pBar.getVisibility() == View.INVISIBLE) { + pBar.setVisibility(View.VISIBLE); + } + } + + @Override + protected Void doInBackground(String... params) { + // URL to get contacts JSON + String url = params[0]; + + 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 { + //JSONObject jsonObj = new JSONObject(jsonStr); + + // Getting JSON Array node + //JSONArray data = jsonObj.getJSONArray("contacts"); + + JSONArray data = new JSONArray(jsonStr); + + // looping through all data + for (int i = 0; i < data.length(); i++) { + JSONObject c = data.getJSONObject(i); + + String dateandtime = c.getString("dateandtime"); + String sensor = c.getString("sensor"); + String temperature = c.getString("temperature"); + String humidity = c.getString("humidity"); + + // tmp hash map for single datum + HashMap datum = new HashMap<>(); + + // adding each child node to HashMap key => value + datum.put("dateandtime", dateandtime); + datum.put("sensor", sensor); + datum.put("temperature", temperature); + datum.put("humidity", humidity); + + // 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(); + } + }); + + } + + return null; + } + + int interpolate(int pBegin, int pEnd, int pStep, int pMax) { + if (pBegin < pEnd) { + return ((pEnd - pBegin) * (pStep / pMax)) + pBegin; + } else { + return ((pBegin - pEnd) * (1 - (pStep / pMax))) + pEnd; + } + } + + int interpolateColor(int theColorBegin, int theColorEnd, + int theNumStep, int theNumSteps) { + int theR0 = (theColorBegin & 0xff0000) >> 16; + int theG0 = (theColorBegin & 0x00ff00) >> 8; + int theB0 = (theColorBegin & 0x0000ff); + //int theB0 = (theColorBegin & 0x0000ff) >> 0; + + int theR1 = (theColorEnd & 0xff0000) >> 16; + int theG1 = (theColorEnd & 0x00ff00) >> 8; + int theB1 = (theColorEnd & 0x0000ff); + //int theB1 = (theColorEnd & 0x0000ff) >> 0; + + int theR = interpolate(theR0, theR1, theNumStep, theNumSteps); + int theG = interpolate(theG0, theG1, theNumStep, theNumSteps); + int theB = interpolate(theB0, theB1, theNumStep, theNumSteps); + + return (((theR << 8) | theG) << 8) | theB; + } + + Color valueToColor(float value, float lowMin, float lowMax, float highMin, float highMax, + Color below, Color ideal, Color above) { + return Color.valueOf(Color.parseColor(String.format("#%06X", + valueToColorRGB(value, lowMin, lowMax, highMin, highMax, below, ideal, above)))); + } + + int valueToColorRGB(float value, float lowMin, float lowMax, float highMin, float highMax, + Color below, Color ideal, Color above) { + int bel = Integer.parseInt(String.format("%06X", (0xFFFFFF & below.toArgb())), 16); + int ide = Integer.parseInt(String.format("%06X", (0xFFFFFF & ideal.toArgb())), 16); + int abo = Integer.parseInt(String.format("%06X", (0xFFFFFF & above.toArgb())), 16); + + return valueToColorRGB(value, lowMin, lowMax, highMin, highMax, bel, ide, abo); + } + + int valueToColorRGB(float value, float lowMin, float lowMax, float highMin, float highMax, + int below, int ideal, int above) { + /* + * lowMin < lowMax < highMin < highMax + * + * Example use for temperatures with color blue for too cold (below), green for ideal + * and red for too warm (above) temperatures: + * - Below lowMin the color is blue + * - Between lowMin and lowMax is color is linear interpolated between blue and green + * - Between lowMax and highMin the color is green + * - Between highMin and highMax the color is linear interpolated between green and red + * - Above highMax the color is red + */ + int result = 0xFFFFFF; // white + int theNumSteps = 100; + int theColorBegin, theColorEnd, ratio; + + if (value <= highMin && value >= lowMax) + result = ideal; + else if (value > highMax) + result = above; + else if (value > highMin) { + theColorBegin = ideal; + theColorEnd = above; + ratio = (int) ((value - highMin) / (highMax - highMin) * 100); + result = interpolateColor(theColorBegin, theColorEnd, ratio, theNumSteps); + } else if (value < lowMin) + result = below; + else if (value < lowMax) { + theColorBegin = ideal; + theColorEnd = below; + ratio = (int) ((lowMax - value) / (lowMax - lowMin) * 100); + result = interpolateColor(theColorBegin, theColorEnd, ratio, theNumSteps); + } + return result; + } + + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + pDialogLockCount -= 1; + // Dismiss the progress bar + if (pBar.getVisibility() == View.VISIBLE && pDialogLockCount == 0) { + pBar.setVisibility(View.GONE); + } + /* + * Updating parsed JSON data into Views + * */ + + int n = dataList.size(); + if (n > 0) { + HashMap hm = dataList.get(n - 1); + + float temp = Float.parseFloat(hm.get("temperature")); + String temperature = String.format("%.1f", temp); + + float humi = Float.parseFloat(hm.get("humidity")); + String humidity = String.format("%.1f", humi); + + Color green = Color.valueOf(0xff00ff00); + Color red = Color.valueOf(0xffd1655d); + Color blue = Color.valueOf(Color.rgb(113, 157, 195)); + + Color textColor; + + textColor = valueToColor(temp, 17, 19, 23, 25, blue, green, red); + TextView tv1 = findViewById(v1); + tv1.setText(temperature); + tv1.setTextColor(textColor.toArgb()); + + textColor = valueToColor(humi, 25, 40, 60, 75, red, green, blue); + TextView tv2 = findViewById(v2); + tv2.setText(humidity); + tv2.setTextColor(textColor.toArgb()); + } + } + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle item selection + switch (item.getItemId()) { + case R.id.settings: + settings(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + /** + * Called when the user taps the Send button + */ + public void settings() { + Intent intentSettings = new Intent(this, SettingsActivity.class); + //intentSettings.putExtra(MESSAGE, MESSAGE); + startActivity(intentSettings); + } +} diff --git a/app/src/main/java/de/weseng/wifiweatherstation/SettingsActivity.java b/app/src/main/java/de/weseng/wifiweatherstation/SettingsActivity.java index 9d49519..fa31a30 100644 --- a/app/src/main/java/de/weseng/wifiweatherstation/SettingsActivity.java +++ b/app/src/main/java/de/weseng/wifiweatherstation/SettingsActivity.java @@ -1,16 +1,29 @@ package de.weseng.wifiweatherstation; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.SupplicantState; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; +import android.widget.CompoundButton; import android.widget.EditText; +import android.widget.Switch; import android.widget.TextView; +import java.util.List; + public class SettingsActivity extends AppCompatActivity { public static final String PREFS_NAME = "Settings"; + public static String MESSAGE; @Override protected void onCreate(Bundle savedInstanceState) { @@ -19,18 +32,36 @@ public class SettingsActivity extends AppCompatActivity { // Get the Intent that started this activity and extract the string Intent intent = getIntent(); - String message = intent.getStringExtra(MainActivity.MESSAGE); - + String message = intent.getStringExtra(MESSAGE); + if (message == null) { + message = ""; + } + message = getString(R.string.message) + message; + if (isLocal(getApplicationContext())) { + message += "\n" + getString(R.string.message_connection_internal); + } else { + message += "\n" + getString(R.string.message_connection_external); + } SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); String ssidLocal = settings.getString("ssidLocal", getString(R.string.ssid)); String urlLocal = settings.getString("urlLocal", getString(R.string.url_local)); String urlGlobal = settings.getString("urlGlobal", getString(R.string.url_global)); + boolean nativeView = settings.getBoolean("nativeView", true); //message = message.concat("\nSSIDLocal: " + ssidLocal); // Capture the layout's TextView and set the string as its text TextView textView = findViewById(R.id.textView); textView.setText(message); + final Switch switchView = findViewById(R.id.switchView); + switchView.setChecked(nativeView); + switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + save("nativeView", isChecked); + } + }); + final EditText editTextSsid = findViewById(R.id.editTextSsidLocal); editTextSsid.setText(ssidLocal); editTextSsid.setOnKeyListener(new View.OnKeyListener() { @@ -94,15 +125,16 @@ public class SettingsActivity extends AppCompatActivity { } @Override - protected void onStop(){ + protected void onStop() { super.onStop(); save_all(); } public void save_all() { - save("ssidLocal", ((EditText)findViewById(R.id.editTextSsidLocal)).getText().toString()); - save("urlLocal", ((EditText)findViewById(R.id.editTextUrlLocal)).getText().toString()); - save("urlGlobal", ((EditText)findViewById(R.id.editTextUrlGlobal)).getText().toString()); + save("ssidLocal", ((EditText) findViewById(R.id.editTextSsidLocal)).getText().toString()); + save("urlLocal", ((EditText) findViewById(R.id.editTextUrlLocal)).getText().toString()); + save("urlGlobal", ((EditText) findViewById(R.id.editTextUrlGlobal)).getText().toString()); + save("nativeView", ((Switch) findViewById(R.id.switchView)).isChecked()); } public void save(String key, String value) { @@ -115,4 +147,46 @@ public class SettingsActivity extends AppCompatActivity { // Commit the edits! editor.apply(); } + + public void save(String key, Boolean value) { + // We need an Editor object to make preference changes. + // All objects are from android.context.Context + SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); + SharedPreferences.Editor editor = settings.edit(); + editor.putBoolean(key, value); + + // Commit the edits! + editor.apply(); + } + + public static String findSSIDForWifiInfo(WifiManager wifiManager, WifiInfo wifiInfo) { + List listOfConfigurations = wifiManager.getConfiguredNetworks(); + for (int index = 0; index < listOfConfigurations.size(); index++) { + WifiConfiguration configuration = listOfConfigurations.get(index); + if (configuration.networkId == wifiInfo.getNetworkId()) { + return configuration.SSID; + } + } + return null; + } + + public static boolean isLocal(@NonNull Context context) { + + SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, 0); + String ssidLocal = "\"" + settings.getString("ssidLocal", context.getString(R.string.ssid)) + "\""; + + ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + //NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + NetworkInfo mWifi = connManager.getNetworkInfo(connManager.getActiveNetwork()); + if (mWifi.isConnected()) { + WifiManager wifiMgr = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + WifiInfo wifiInfo = wifiMgr.getConnectionInfo(); + if (wifiInfo.getSupplicantState() == SupplicantState.COMPLETED) { + String ssid = findSSIDForWifiInfo(wifiMgr, wifiInfo); + if (ssid != null) + return ssid.equals(ssidLocal); + } + } + return false; + } } diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..de31d62 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + +