diff --git a/AirKoality/app/src/main/AndroidManifest.xml b/AirKoality/app/src/main/AndroidManifest.xml index 7d3a5c2..2510f05 100644 --- a/AirKoality/app/src/main/AndroidManifest.xml +++ b/AirKoality/app/src/main/AndroidManifest.xml @@ -2,6 +2,9 @@ xmlns:tools="http://schemas.android.com/tools" package="at.fhj.airkoality"> + + + - + + @@ -19,7 +23,7 @@ diff --git a/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/AirKoalityDB.java b/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/AirKoalityDB.java index ab52a9b..c40c23e 100644 --- a/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/AirKoalityDB.java +++ b/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/AirKoalityDB.java @@ -3,11 +3,14 @@ package at.fhj.airkoality.db.room; import android.arch.persistence.room.Database; import android.arch.persistence.room.Room; import android.arch.persistence.room.RoomDatabase; +import android.arch.persistence.room.TypeConverters; import android.content.Context; +import at.fhj.airkoality.model.LatestMeasurements; import at.fhj.airkoality.model.Location; -@Database(entities = {Location.class}, version = 1,exportSchema = false) +@Database(entities = {Location.class, LatestMeasurements.class}, version = 1,exportSchema = false) +@TypeConverters({Converters.class}) public abstract class AirKoalityDB extends RoomDatabase { private static AirKoalityDB instance; @@ -15,6 +18,8 @@ public abstract class AirKoalityDB extends RoomDatabase { public abstract LocationDAO locationDAO(); + + public static AirKoalityDB getDatabase(Context context){ if (instance == null) { instance = Room.databaseBuilder(context, AirKoalityDB.class, "airkoality").allowMainThreadQueries().build(); @@ -23,4 +28,5 @@ public abstract class AirKoalityDB extends RoomDatabase { } + public abstract LatestMeasurementsDAO latestMeasurementsDAO(); } diff --git a/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/Converters.java b/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/Converters.java new file mode 100644 index 0000000..5df046b --- /dev/null +++ b/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/Converters.java @@ -0,0 +1,38 @@ +package at.fhj.airkoality.db.room; + +import android.arch.persistence.room.TypeConverter; + +import java.util.ArrayList; + +import at.fhj.airkoality.model.Measurement; + +public class Converters { + + @TypeConverter + public static ArrayList fromStringToMeasurements(String value) { + ArrayList measurementsList = new ArrayList<>(); + String[] measurements = value.split("\\|"); + + for (String s : measurements) { + String[] parts = s.split(";"); + measurementsList.add(new Measurement(parts[0], Double.valueOf(parts[1]), parts[2])); + } + + return measurementsList; + } + + @TypeConverter + public static String fromMeasurementsToString(ArrayList measurements) { + String measurementsString = ""; + + if(measurements != null) { + for (int i = 0; i < measurements.size(); i++) { + measurementsString += measurements.get(i).getParameter() + ";" + measurements.get(i).getValue() + ";" + measurements.get(i).getUnit(); + if (i != measurements.size() - 1) { + measurementsString += "|"; + } + } + } + return measurementsString; + } +} \ No newline at end of file diff --git a/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/LatestMeasurementsDAO.java b/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/LatestMeasurementsDAO.java new file mode 100644 index 0000000..4dd366b --- /dev/null +++ b/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/LatestMeasurementsDAO.java @@ -0,0 +1,17 @@ +package at.fhj.airkoality.db.room; + +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.OnConflictStrategy; +import android.arch.persistence.room.Query; + +import at.fhj.airkoality.model.LatestMeasurements; + +@Dao +public interface LatestMeasurementsDAO { + @Query("SELECT * FROM latest_measurements WHERE locationName = :locationName") + LatestMeasurements getForLocation(String locationName); + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void add(LatestMeasurements latestMeasurements); +} diff --git a/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/LocationDAO.java b/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/LocationDAO.java index 16eab3a..70f4d8b 100644 --- a/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/LocationDAO.java +++ b/AirKoality/app/src/main/java/at/fhj/airkoality/db/room/LocationDAO.java @@ -16,7 +16,8 @@ public interface LocationDAO { List getAll(); @Query("SELECT * FROM location WHERE location = :locationName") - Location getLocationWithName(String locationName); + Location getLocationWithName(String locationName); + @Insert(onConflict = OnConflictStrategy.REPLACE) void addLocation(Location location); @@ -24,5 +25,6 @@ public interface LocationDAO { @Insert(onConflict = OnConflictStrategy.REPLACE) void addAll(List locations); - + @Query("DELETE FROM location") + void clear(); } diff --git a/AirKoality/app/src/main/java/at/fhj/airkoality/model/LatestMeasurements.java b/AirKoality/app/src/main/java/at/fhj/airkoality/model/LatestMeasurements.java new file mode 100644 index 0000000..0d56aef --- /dev/null +++ b/AirKoality/app/src/main/java/at/fhj/airkoality/model/LatestMeasurements.java @@ -0,0 +1,34 @@ +package at.fhj.airkoality.model; + +import android.arch.persistence.room.ColumnInfo; +import android.arch.persistence.room.Entity; +import android.arch.persistence.room.PrimaryKey; +import android.support.annotation.NonNull; + +import java.util.ArrayList; + +@Entity(tableName = "latest_measurements") +public class LatestMeasurements { + @PrimaryKey + @NonNull + private String locationName; + @ColumnInfo(name = "measurements") + private ArrayList measurements; + + @NonNull + public String getLocationName() { + return locationName; + } + + public void setLocationName(@NonNull String locationName) { + this.locationName = locationName; + } + + public ArrayList getMeasurements() { + return measurements; + } + + public void setMeasurements(ArrayList measurements) { + this.measurements = measurements; + } +} diff --git a/AirKoality/app/src/main/java/at/fhj/airkoality/model/Measurement.java b/AirKoality/app/src/main/java/at/fhj/airkoality/model/Measurement.java new file mode 100644 index 0000000..92109d8 --- /dev/null +++ b/AirKoality/app/src/main/java/at/fhj/airkoality/model/Measurement.java @@ -0,0 +1,37 @@ +package at.fhj.airkoality.model; + +public class Measurement { + private String parameter; + private double value; + private String unit; + + public Measurement(String parameter, double value, String unit) { + this.parameter = parameter; + this.value = value; + this.unit = unit; + } + + public String getParameter() { + return parameter; + } + + public void setParameter(String parameter) { + this.parameter = parameter; + } + + public double getValue() { + return value; + } + + public void setValue(double value) { + this.value = value; + } + + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; + } +} \ No newline at end of file diff --git a/AirKoality/app/src/main/java/at/fhj/airkoality/ui/activity/MainActivity.java b/AirKoality/app/src/main/java/at/fhj/airkoality/ui/activity/MainActivity.java index 66cd196..6505bc2 100644 --- a/AirKoality/app/src/main/java/at/fhj/airkoality/ui/activity/MainActivity.java +++ b/AirKoality/app/src/main/java/at/fhj/airkoality/ui/activity/MainActivity.java @@ -1,5 +1,7 @@ package at.fhj.airkoality.ui.activity; +import android.app.ProgressDialog; +import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; @@ -12,19 +14,35 @@ import android.support.v7.app.AppCompatActivity; import android.view.MenuItem; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + import at.fhj.airkoality.R; +import at.fhj.airkoality.db.room.AirKoalityDB; +import at.fhj.airkoality.model.Location; +import at.fhj.airkoality.network.HttpsGetTask; +import at.fhj.airkoality.network.RequestCallback; import at.fhj.airkoality.ui.fragment.LocationListFragment; import at.fhj.airkoality.ui.fragment.MapFragment; -public class MainActivity extends AppCompatActivity { +public class MainActivity extends AppCompatActivity implements RequestCallback { - private Fragment locationListFragment; - private Fragment mapFragment; + private LocationListFragment locationListFragment; + private MapFragment mapFragment; private static final String LOCATION_LIST = "location_list"; private static final String MAP = "map"; private static final String FRAGMENT_PREF_KEY = "last_fragment"; + private ProgressDialog progressDialog; + + private AirKoalityDB database; + + private BottomNavigationView bottomNavigationView; @@ -41,6 +59,9 @@ public class MainActivity extends AppCompatActivity { bottomNavigationView = findViewById(R.id.bnvMain); + progressDialog = new ProgressDialog(this); + + database = AirKoalityDB.getDatabase(this); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); @@ -50,11 +71,9 @@ public class MainActivity extends AppCompatActivity { transaction.commit(); - bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { - @Override - public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { + bottomNavigationView.setOnNavigationItemSelectedListener(menuItem -> { - switch (menuItem.getItemId()){ + switch (menuItem.getItemId()){ case R.id.action_location: setSelectedFragment(LOCATION_LIST); sharedPreferences.edit() @@ -69,14 +88,22 @@ public class MainActivity extends AppCompatActivity { .commit(); break; } - return false; - } + return true; + }); setSelectedFragment(sharedPreferences.getString("last_fragment", LOCATION_LIST)); + fetchLocations(); } + private void fetchLocations(){ + HttpsGetTask httpsGetTask = new HttpsGetTask(this); + httpsGetTask.execute("https://api.openaq.org/v1/locations?country=AT&limit=200"); + } + + + private void setSelectedFragment(String fragmentName){ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); switch (fragmentName) { @@ -92,4 +119,67 @@ public class MainActivity extends AppCompatActivity { transaction.commit(); } + @Override + public void onRequestStart() { + progressDialog.setMessage("Fetching..."); + progressDialog.show(); + } + + @Override + public void onResult(String result) { + new Thread(()->{ + + List locations; + //parse result + if(result == null){ + System.out.println("no locations"); + }else { +//write to db + try { + locations = parseLocations(result); + database.locationDAO().clear(); + database.locationDAO().addAll(locations); + }catch (JSONException e){ + e.printStackTrace(); + } + + + } + + //notify fragments + dismissProgressDialog(); + updateFragments(); + + }).start(); + + + } + + private void updateFragments() { + runOnUiThread(() -> { + locationListFragment.refresh(); + mapFragment.refresh(); + }); + } + + private void dismissProgressDialog() { + runOnUiThread(()-> progressDialog.dismiss()); + } + + private List parseLocations(String json) throws JSONException { + List locations = new ArrayList<>(); + JSONObject jsonObject = new JSONObject(json); + JSONArray results = jsonObject.getJSONArray("results"); + + for (int i = 0; i < results.length(); i++){ + JSONObject locationResult = results.getJSONObject(i); + String locationName = locationResult.getString("location"); + String city = locationResult.getString("city"); + String country = locationResult.getString("country"); + + Location location = new Location(locationName, city, country); + locations.add(location); + } + return locations; + } } diff --git a/AirKoality/app/src/main/java/at/fhj/airkoality/ui/activity/MeasurementActivity.java b/AirKoality/app/src/main/java/at/fhj/airkoality/ui/activity/MeasurementActivity.java new file mode 100644 index 0000000..07f673b --- /dev/null +++ b/AirKoality/app/src/main/java/at/fhj/airkoality/ui/activity/MeasurementActivity.java @@ -0,0 +1,208 @@ +package at.fhj.airkoality.ui.activity; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.widget.TextView; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + +import at.fhj.airkoality.R; +import at.fhj.airkoality.db.room.AirKoalityDB; + +import at.fhj.airkoality.model.LatestMeasurements; +import at.fhj.airkoality.model.Measurement; +import at.fhj.airkoality.network.HttpsGetTask; +import at.fhj.airkoality.network.RequestCallback; + +public class MeasurementActivity extends AppCompatActivity implements RequestCallback { + + private AirKoalityDB database; + + private String locationName; + + private TextView tvLocation; + private TextView tvBCvalue; + private TextView tvCOvalue; + private TextView tvO3value; + private TextView tvPM10value; + private TextView tvPM25value; + private TextView tvSO2value; + private TextView tvNO2value; + + private NetworkStateReceiver networkStateReceiver; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_measurement); + + tvLocation = findViewById(R.id.tvLocation); + tvBCvalue = findViewById(R.id.tvBCvalue); + tvCOvalue = findViewById(R.id.tvCOvalue); + tvO3value = findViewById(R.id.tvO3value); + tvPM10value = findViewById(R.id.tvPM10value); + tvPM25value = findViewById(R.id.tvPM25value); + tvSO2value = findViewById(R.id.tvSO2value); + tvNO2value = findViewById(R.id.tvNO2value); + + + + //TODO: get locationname from intent + locationName = getIntent().getStringExtra("location_name"); + + tvLocation.setText(locationName); + database = AirKoalityDB.getDatabase(this); + + networkStateReceiver = new NetworkStateReceiver(); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayShowHomeEnabled(true); + + setTitle(R.string.latest_measurements); + + fetchLatestMeasurements(locationName); + } + + @Override + protected void onResume() { + super.onResume(); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); + + registerReceiver(networkStateReceiver, intentFilter); + } + + @Override + protected void onPause() { + super.onPause(); + unregisterReceiver(networkStateReceiver); + } + + + @Override + public boolean onSupportNavigateUp() { + onBackPressed(); + return true; + } + + private void fetchLatestMeasurements(String locationName) { + HttpsGetTask httpsGetTask = new HttpsGetTask(this); + httpsGetTask.execute("https://api.openaq.org/v1/latest?location=" + locationName); + } + + + @Override + public void onRequestStart() { + } + + public void updateUI(LatestMeasurements latestMeasurements) { + if (latestMeasurements != null) { + runOnUiThread(() -> { + for (Measurement measurement : latestMeasurements.getMeasurements()) { + String valueString = "" + measurement.getValue() + " " + measurement.getUnit(); + switch (measurement.getParameter().toLowerCase()) { + case "bc": + tvBCvalue.setText(valueString); + break; + case "co": + tvCOvalue.setText(valueString); + break; + case "no2": + tvNO2value.setText(valueString); + break; + case "o3": + tvO3value.setText(valueString); + break; + case "pm10": + tvPM10value.setText(valueString); + break; + case "pm25": + tvPM25value.setText(valueString); + break; + case "so2": + tvSO2value.setText(valueString); + break; + } + } + }); + } + } + + @Override + public void onResult(String result) { + new Thread(() -> { + LatestMeasurements latestMeasurements; + if (result == null) { + latestMeasurements = database.latestMeasurementsDAO().getForLocation(locationName); + } else { + latestMeasurements = parseLatestMeasurements(result); + if (latestMeasurements != null) { + database.latestMeasurementsDAO().add(latestMeasurements); + } + } + + updateUI(latestMeasurements); + }).start(); + } + + private LatestMeasurements parseLatestMeasurements(String json) { + LatestMeasurements latestMeasurements = null; + try { + JSONObject jsonObject = new JSONObject(json); + JSONArray results = jsonObject.getJSONArray("results"); + JSONObject result = results.getJSONObject(0); + JSONArray measurementsArray = result.getJSONArray("measurements"); + + + latestMeasurements = new LatestMeasurements(); + latestMeasurements.setLocationName(result.getString("location")); + + ArrayList measurements = new ArrayList<>(); + for (int i = 0; i < measurementsArray.length(); i++) { + JSONObject measurementResult = measurementsArray.getJSONObject(i); + String parameter = measurementResult.getString("parameter"); + double value = measurementResult.getDouble("value"); + String unit = measurementResult.getString("unit"); + measurements.add(new Measurement(parameter, value, unit)); + } + latestMeasurements.setMeasurements(measurements); + } catch (JSONException e) { + e.printStackTrace(); + } + return latestMeasurements; + } + + + class NetworkStateReceiver extends BroadcastReceiver{ + + + @Override + public void onReceive(Context context, Intent intent) { + if(isConnected(context)) { + fetchLatestMeasurements(locationName); + } + } + + + private boolean isConnected(Context context){ + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = cm.getActiveNetworkInfo(); + + return (networkInfo != null && networkInfo.isConnected()); + } + + } + + +} diff --git a/AirKoality/app/src/main/java/at/fhj/airkoality/ui/fragment/LocationListFragment.java b/AirKoality/app/src/main/java/at/fhj/airkoality/ui/fragment/LocationListFragment.java index 85f28c4..0d34b78 100644 --- a/AirKoality/app/src/main/java/at/fhj/airkoality/ui/fragment/LocationListFragment.java +++ b/AirKoality/app/src/main/java/at/fhj/airkoality/ui/fragment/LocationListFragment.java @@ -1,5 +1,6 @@ package at.fhj.airkoality.ui.fragment; +import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -17,6 +18,8 @@ import java.util.List; import at.fhj.airkoality.R; import at.fhj.airkoality.db.room.AirKoalityDB; import at.fhj.airkoality.model.Location; +import at.fhj.airkoality.model.Measurement; +import at.fhj.airkoality.ui.activity.MeasurementActivity; import at.fhj.airkoality.ui.adapter.LocationListAdapter; public class LocationListFragment extends Fragment implements LocationListAdapter.ItemClickListener { @@ -31,32 +34,27 @@ public class LocationListFragment extends Fragment implements LocationListAdapte locationList = view.findViewById(R.id.rvLocationList); List locations = new ArrayList<>(); - - locations.add(new Location("Daheim", "Graz" , "Österreich")); - locations.add(new Location("Daheim2", "Graz" , "Österreich")); - locations.add(new Location("Daheim3", "Graz" , "Österreich")); - locations.add(new Location("Daheim4", "Graz" , "Österreich")); - locations.add(new Location("Daheim5", "Graz" , "Österreich")); - locations.add(new Location("Daheim6", "Graz" , "Österreich")); - locations.add(new Location("Daheim7", "Graz" , "Österreich")); - locations.add(new Location("Daheim8", "Graz" , "Österreich")); - - - AirKoalityDB.getDatabase(getContext()).locationDAO().addAll(locations); - - LocationListAdapter adapter = new LocationListAdapter(AirKoalityDB.getDatabase(getContext()).locationDAO().getAll(),this); - - - - locationList.setAdapter(adapter); locationList.setLayoutManager(new LinearLayoutManager(getContext())); return view; } + private void fetchLocations() { + LocationListAdapter adapter = new LocationListAdapter(AirKoalityDB.getDatabase(getContext()).locationDAO().getAll(),this); + locationList.setAdapter(adapter); + + } + + + public void refresh(){ + fetchLocations(); + } @Override public void onItemClicked(Location location, int position) { Toast.makeText(getContext(), "Clicked " + location, Toast.LENGTH_SHORT).show(); + Intent intent = new Intent(getContext(), MeasurementActivity.class); + intent.putExtra("location_name", location.getLocation()); + startActivity(intent); } } diff --git a/AirKoality/app/src/main/java/at/fhj/airkoality/ui/fragment/MapFragment.java b/AirKoality/app/src/main/java/at/fhj/airkoality/ui/fragment/MapFragment.java index 00cbfb7..300b01f 100644 --- a/AirKoality/app/src/main/java/at/fhj/airkoality/ui/fragment/MapFragment.java +++ b/AirKoality/app/src/main/java/at/fhj/airkoality/ui/fragment/MapFragment.java @@ -21,4 +21,8 @@ public class MapFragment extends Fragment { return view; } + + public void refresh(){ + + } } diff --git a/AirKoality/app/src/main/res/layout/activity_measurement.xml b/AirKoality/app/src/main/res/layout/activity_measurement.xml new file mode 100644 index 0000000..6c11cbb --- /dev/null +++ b/AirKoality/app/src/main/res/layout/activity_measurement.xml @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AirKoality/app/src/main/res/values/dimens.xml b/AirKoality/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..7463368 --- /dev/null +++ b/AirKoality/app/src/main/res/values/dimens.xml @@ -0,0 +1,4 @@ + + + 50dp + \ No newline at end of file diff --git a/AirKoality/app/src/main/res/values/strings.xml b/AirKoality/app/src/main/res/values/strings.xml index cfb1d3c..1085fde 100644 --- a/AirKoality/app/src/main/res/values/strings.xml +++ b/AirKoality/app/src/main/res/values/strings.xml @@ -6,4 +6,5 @@ Location: City: Country: + Latest measurements: diff --git a/AirKoality/app/src/main/res/values/styles.xml b/AirKoality/app/src/main/res/values/styles.xml index 347214d..f53de03 100644 --- a/AirKoality/app/src/main/res/values/styles.xml +++ b/AirKoality/app/src/main/res/values/styles.xml @@ -1,20 +1,28 @@ - - - - + + + +