online lab

This commit is contained in:
Philipp Wo 2019-05-18 08:57:06 +02:00
parent e845195e9b
commit 4ff5b18998
14 changed files with 280 additions and 50 deletions

View File

@ -17,7 +17,8 @@
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res;file://$MODULE_DIR$/build/generated/res/resValues/debug" />
<option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
@ -92,6 +93,7 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundle_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check_manifest_result" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/duplicate_classes_check" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
@ -102,24 +104,24 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_split_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint_jar" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_jni_libs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/metadata_feature_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/signing_config" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/tmp" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/validate_signing_config" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
@ -141,18 +143,22 @@
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-compat:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:cardview-v7:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-location:16.0.0@aar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-maps:16.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.persistence:db-framework:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-utils:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:recyclerview-v7:28.0.0@aar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:monitor:1.0.2@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-places-placereport:16.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:28.0.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:interpolator:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.persistence.room:runtime:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:transition:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:drawerlayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-v4:28.0.0@aar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: net.sf.kxml:kxml2:2.3.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:documentfile:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:slidingpanelayout:28.0.0@aar" level="project" />
@ -165,15 +171,19 @@
<orderEntry type="library" scope="TEST" name="Gradle: com.google.code.findbugs:jsr305:2.0.1@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:versionedparcelable:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-base:16.0.1@aar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:viewpager:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.persistence.room:common:1.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-basement:16.0.1@aar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-integration:1.3@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-library:1.3@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:coordinatorlayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:customview:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:swiperefreshlayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-media-compat:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-tasks:16.0.1@aar" level="project" />
</component>
</module>

View File

@ -22,13 +22,18 @@ android {
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-media-compat:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.google.android.gms:play-services-maps:16.1.0'
implementation 'android.arch.persistence.room:runtime:1.1.1'
implementation 'com.google.android.gms:play-services-location:16.0.0'
annotationProcessor 'android.arch.persistence.room:compiler:1.1.1'
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'
implementation 'android.arch.persistence.room:runtime:1.1.1'
annotationProcessor 'android.arch.persistence.room:compiler:1.1.1'
}

View File

@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@ -24,6 +25,8 @@
</activity>
<meta-data android:name="com.google.android.geo.API_KEY" android:value="@string/google_maps_key"/>
<service android:name=".service.LocationService"/>
<activity android:name=".ui.activity.SplashActivity"

View File

@ -7,21 +7,26 @@ import android.support.annotation.NonNull;
@Entity(tableName = "location")
public class Location {
@PrimaryKey
@NonNull
@ColumnInfo(name= "location")
@ColumnInfo(name = "location")
private String location;
@ColumnInfo(name= "city")
@ColumnInfo(name = "city")
private String city;
@ColumnInfo(name= "country")
@ColumnInfo(name = "country")
private String country;
@ColumnInfo(name = "latitude")
private double latitude;
@ColumnInfo(name = "longitude")
private double longitude;
public Location(String location, String city, String country) {
public Location(@NonNull String location, String city, String country, double latitude, double longitude) {
this.location = location;
this.city = city;
this.country = country;
this.latitude = latitude;
this.longitude = longitude;
}
public String getLocation() {
@ -44,16 +49,29 @@ public class Location {
return country;
}
public void setCountry(String county) {
this.country = county;
public void setCountry(String country) {
this.country = country;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
@Override
public String toString() {
return "Location{" +
"location='" + location + '\'' +
", city='" + city + '\'' +
", county='" + country + '\'' +
'}';
return location + ", " + city + ", " + country;
}
}

View File

@ -1,15 +1,24 @@
package at.fhj.airkoality.service;
import android.Manifest;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import at.fhj.airkoality.AirKoalityApplication;
import at.fhj.airkoality.R;
import at.fhj.airkoality.ui.activity.MainActivity;
@ -20,6 +29,9 @@ public class LocationService extends Service {
private String TAG = "LocationService";
private Notification notification;
private FusedLocationProviderClient fsdpc;
private LocationCallback locationCallback;
@Override
public IBinder onBind(Intent intent) {
@ -31,27 +43,64 @@ public class LocationService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service started");
startForeground(1337,notification);
startForeground(1337, notification);
startLocationUpdates();
return START_STICKY;
}
@Override
public void onCreate(){
Log.d(TAG, "Service created");
public void onCreate() {
super.onCreate();
setupNotification();
Log.d(TAG, "Service created");
fsdpc = LocationServices.getFusedLocationProviderClient(this);
locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult){
if(locationResult.getLocations().size()!=0){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(LocationService.this);
preferences.edit()
.putFloat("latitude", (float) locationResult.getLocations().get(0).getLatitude())
.putFloat("longitude", (float) locationResult.getLocations().get(0).getLongitude()).apply();
}
}
};
}
}
@Override
public void onDestroy(){
public void onDestroy() {
Log.d(TAG, "Service destroyed");
stopLocationUpdates();
super.onDestroy();
}
private void startLocationUpdates() {
LocationRequest request = new LocationRequest();
request.setInterval(5000);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
fsdpc.requestLocationUpdates(request, locationCallback, null);
}
}
private void stopLocationUpdates(){
fsdpc.removeLocationUpdates(locationCallback);
}
private void setupNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);

View File

@ -1,21 +1,22 @@
package at.fhj.airkoality.ui.activity;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -23,6 +24,7 @@ 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;
@ -81,14 +83,14 @@ public class MainActivity extends AppCompatActivity implements RequestCallback {
setSelectedFragment(LOCATION_LIST);
sharedPreferences.edit()
.putString(FRAGMENT_PREF_KEY, LOCATION_LIST)
.commit();
.apply();
break;
case R.id.action_map:
setSelectedFragment(MAP);
sharedPreferences.edit()
.putString(FRAGMENT_PREF_KEY, MAP)
.commit();
.apply();
break;
}
return true;
@ -100,9 +102,18 @@ public class MainActivity extends AppCompatActivity implements RequestCallback {
}
private void fetchLocations(){
private void fetchLocations() {
HttpsGetTask httpsGetTask = new HttpsGetTask(this);
httpsGetTask.execute("https://api.openaq.org/v1/locations?country=AT&limit=200");
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
float latitude = preferences.getFloat("latitude", -1000);
float longitude = preferences.getFloat("longitude", -1000);
if (latitude == -1000 || longitude == -1000) {
httpsGetTask.execute("https://api.openaq.org/v1/locations?country=AT&limit=200");
} else {
httpsGetTask.execute("https://api.openaq.org/v1/locations?coordinates=" + latitude + "," + longitude + "&radius=" + 200000 + "&limit=10000");
}
}
@ -168,6 +179,8 @@ public class MainActivity extends AppCompatActivity implements RequestCallback {
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
@ -178,6 +191,9 @@ public class MainActivity extends AppCompatActivity implements RequestCallback {
case R.id.action_service_off:
stopLocationService();
return true;
case R.id.action_refresh:
fetchLocations();
return true;
default:
return false;
}
@ -185,11 +201,25 @@ public class MainActivity extends AppCompatActivity implements RequestCallback {
}
private void startLocationService(){
Intent intent = new Intent(this, LocationService.class);
startService(intent);
private void startLocationService() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(this, LocationService.class);
startService(intent);
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 4711);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == 4711) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startLocationService();
}
}
}
}
private void stopLocationService(){
@ -220,9 +250,20 @@ public class MainActivity extends AppCompatActivity implements RequestCallback {
String city = locationResult.getString("city");
String country = locationResult.getString("country");
Location location = new Location(locationName, city, country);
JSONObject coordinates = locationResult.getJSONObject("coordinates");
double latitude = coordinates.getDouble("latitude");
double longitude = coordinates.getDouble("longitude");
Location location = new Location(locationName, city, country, latitude, longitude);
locations.add(location);
}
return locations;
}
}

View File

@ -1,28 +1,123 @@
package at.fhj.airkoality.ui.fragment;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import at.fhj.airkoality.R;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
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.ui.activity.MeasurementActivity;
public class MapFragment extends Fragment implements OnMapReadyCallback, GoogleMap.OnInfoWindowClickListener {
private MapView mapView;
private GoogleMap map;
private AirKoalityDB database;
public class MapFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_map, container, false);
mapView = view.findViewById(R.id.map);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
database = AirKoalityDB.getDatabase(getContext());
return view;
}
private void enableMyLocation() {
if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if(map != null) {
map.setMyLocationEnabled(true);
}
}
}
public void refresh(){
addMapMarkers();
}
private void addMapMarkers() {
if(map != null) {
new Thread(() -> {
List<Location> locations = database.locationDAO().getAll();
getActivity().runOnUiThread(() -> {
map.clear();
for (Location location : locations) {
MarkerOptions markerOptions = new MarkerOptions()
.title(location.getLocation())
.snippet(location.getCity() + ", " + location.getCountry())
.position(new LatLng(location.getLatitude(), location.getLongitude()));
map.addMarker(markerOptions);
}
});
}).start();
}
}
@Override
public void onMapReady(GoogleMap googleMap) {
map = googleMap;
map.setOnInfoWindowClickListener(this);
enableMyLocation();
}
@Override
public void onInfoWindowClick(Marker marker) {
Intent intent = new Intent(getContext(), MeasurementActivity.class);
intent.putExtra("location_name", marker.getTitle());
startActivity(intent);
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
public void onDestroy() {
mapView.onDestroy();
super.onDestroy();
}
}

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#0019FD"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
</vector>

View File

@ -3,13 +3,10 @@
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Map Fragment"
android:textSize="25dp"/>
<com.google.android.gms.maps.MapView
android:layout_width="match_parent"
android:id="@+id/map"
android:layout_height="match_parent"></com.google.android.gms.maps.MapView>

View File

@ -6,6 +6,7 @@
<item android:id="@+id/action_service_on" app:showAsAction="always" android:title="Service on" android:icon="@drawable/ic_location_on"/>
<item android:id="@+id/action_service_off" app:showAsAction="always" android:title="Service off" android:icon="@drawable/ic_location_off"/>
<item android:id="@+id/action_refresh" app:showAsAction="always" android:title="refresh" android:icon="@drawable/ic_refresh_black_24dp"/>
</menu>

View File

@ -7,4 +7,5 @@
<string name="city_label">City:</string>
<string name="country_label">Country:</string>
<string name="latest_measurements">Latest measurements:</string>
<string name="google_maps_key">AIzaSyBQhcmDlyP01yNwTd4RTlatQIAJH3wJHw0</string>
</resources>

View File

@ -7,7 +7,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
classpath 'com.android.tools.build:gradle:3.4.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -1,6 +1,6 @@
#Fri Mar 01 16:44:03 CET 2019
#Fri May 17 19:19:06 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

View File

@ -87,4 +87,9 @@ Service wird im Manifest deklariert
Um einen Foregroundservice zu starten muss das innerhalb von 5 Sekunden nach "start Service" (Intent)
Notifications benötigen immer einen Channel (ab Android O verfügbar)
Notifications benötigen immer einen Channel und Channel ID (ab Android O verfügbar)
## 17.05.2019 - Online
## 18.05.2019 - Online