diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1501da5..a2929f3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,6 +26,7 @@ + + \ No newline at end of file diff --git a/app/src/main/java/de/tadris/fitness/Instance.java b/app/src/main/java/de/tadris/fitness/Instance.java index 517a295..4257edc 100644 --- a/app/src/main/java/de/tadris/fitness/Instance.java +++ b/app/src/main/java/de/tadris/fitness/Instance.java @@ -23,6 +23,9 @@ import android.content.Context; import androidx.room.Room; +import java.util.ArrayList; +import java.util.List; + import de.tadris.fitness.data.AppDatabase; import de.tadris.fitness.location.LocationListener; @@ -40,14 +43,13 @@ public class Instance { } public AppDatabase db; - public LocationListener locationListener; public UserPreferences userPreferences; + public List locationChangeListeners= new ArrayList<>(); private Instance(Context context) { db = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME) .allowMainThreadQueries() .build(); - locationListener= new LocationListener(context); userPreferences= new UserPreferences(); } } diff --git a/app/src/main/java/de/tadris/fitness/activity/RecordWorkoutActivity.java b/app/src/main/java/de/tadris/fitness/activity/RecordWorkoutActivity.java index 84a160c..09e3728 100644 --- a/app/src/main/java/de/tadris/fitness/activity/RecordWorkoutActivity.java +++ b/app/src/main/java/de/tadris/fitness/activity/RecordWorkoutActivity.java @@ -20,8 +20,10 @@ package de.tadris.fitness.activity; import android.Manifest; +import android.content.Intent; import android.content.pm.PackageManager; import android.location.Location; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; @@ -66,6 +68,7 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location boolean isResumed= false; private Handler mHandler= new Handler(); PowerManager.WakeLock wakeLock; + Intent locationListener; @Override protected void onCreate(Bundle savedInstanceState) { @@ -92,11 +95,16 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location startUpdater(); acquireWakelock(); + + Instance.getInstance(this).locationChangeListeners.add(this); + + startListener(); + } private void acquireWakelock(){ PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); - PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "de.tadris.fitotrack:workout_recorder"); + wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "de.tadris.fitotrack:workout_recorder"); wakeLock.acquire(1000*60*120); } @@ -163,7 +171,24 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (hasPermission()) { - Instance.getInstance(this).locationListener.enableMyLocation(); + startListener(); + } + } + + public void stopListener(){ + stopService(locationListener); + } + + public void startListener(){ + if(locationListener == null){ + locationListener= new Intent(this, LocationListener.class); + }else{ + stopListener(); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(locationListener); + }else{ + startService(locationListener); } } @@ -184,20 +209,20 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location if(wakeLock.isHeld()){ wakeLock.release(); } + Instance.getInstance(this).locationChangeListeners.remove(this); + stopListener(); } @Override public void onPause(){ super.onPause(); downloadLayer.onPause(); - Instance.getInstance(this).locationListener.unregisterLocationChangeListeners(this); isResumed= false; } public void onResume(){ super.onResume(); downloadLayer.onResume(); - Instance.getInstance(this).locationListener.registerLocationChangeListeners(this); isResumed= true; } diff --git a/app/src/main/java/de/tadris/fitness/data/WorkoutManager.java b/app/src/main/java/de/tadris/fitness/data/WorkoutManager.java index 810bdc2..1988af9 100644 --- a/app/src/main/java/de/tadris/fitness/data/WorkoutManager.java +++ b/app/src/main/java/de/tadris/fitness/data/WorkoutManager.java @@ -20,6 +20,7 @@ package de.tadris.fitness.data; import android.content.Context; +import android.util.Log; import java.util.List; @@ -34,6 +35,16 @@ public class WorkoutManager { workout.id= System.currentTimeMillis(); + // Delete Samples with same time + for(int i= samples.size()-2; i >= 0; i--){ + WorkoutSample sample= samples.get(i); + WorkoutSample lastSample= samples.get(i+1); + if(sample.absoluteTime == lastSample.absoluteTime){ + samples.remove(lastSample); + Log.i("WorkoutManager", "Removed samples at " + sample.absoluteTime + " rel: " + sample.relativeTime + "; " + lastSample.relativeTime); + } + } + // Calculating values double length= 0; for(int i= 1; i < samples.size(); i++){ diff --git a/app/src/main/java/de/tadris/fitness/location/LocationListener.java b/app/src/main/java/de/tadris/fitness/location/LocationListener.java index e7ad0cb..ffd3516 100644 --- a/app/src/main/java/de/tadris/fitness/location/LocationListener.java +++ b/app/src/main/java/de/tadris/fitness/location/LocationListener.java @@ -1,3 +1,4 @@ + /* * Copyright (c) 2019 Jannis Scheibe * @@ -19,23 +20,23 @@ package de.tadris.fitness.location; -import android.Manifest; +import android.app.Notification; +import android.app.Service; import android.content.Context; -import android.content.pm.PackageManager; +import android.content.Intent; import android.location.Location; import android.location.LocationManager; import android.os.Bundle; - -import androidx.core.app.ActivityCompat; +import android.os.IBinder; +import android.util.Log; import org.mapsforge.core.model.LatLong; -import java.util.ArrayList; -import java.util.List; +import de.tadris.fitness.Instance; +import de.tadris.fitness.R; +import de.tadris.fitness.util.NotificationHelper; -public class LocationListener implements android.location.LocationListener { - - public static LatLong static_lastLocation; +public class LocationListener extends Service { /** * @param location the location whose geographical coordinates should be converted. @@ -45,113 +46,95 @@ public class LocationListener implements android.location.LocationListener { return new LatLong(location.getLatitude(), location.getLongitude()); } - private Context activity; - private Location lastLocation; - private final LocationManager locationManager; - private boolean myLocationEnabled; + private static final String TAG = "LocationListener"; + private LocationManager mLocationManager = null; + private static final int LOCATION_INTERVAL = 1000; - public LocationListener(Context context) { - super(); - this.activity= context; - this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); - } + private class LocationChangedListener implements android.location.LocationListener { + Location mLastLocation; - - /** - * Stops the receiving of location updates. Has no effect if location updates are already disabled. - */ - public synchronized void disableMyLocation() { - if (this.myLocationEnabled) { - this.myLocationEnabled = false; - try { - this.locationManager.removeUpdates(this); - } catch (RuntimeException runtimeException) { - // do we need to catch security exceptions for this call on Android 6? - } + public LocationChangedListener(String provider) { + Log.i(TAG, "LocationListener " + provider); + mLastLocation = new Location(provider); } - } - public synchronized void enableMyLocation() { - enableBestAvailableProvider(); - } - - /** - * @return the most-recently received location fix (might be null). - */ - public synchronized Location getLastLocation() { - return this.lastLocation; - } - - /** - * @return true if the receiving of location updates is currently enabled, false otherwise. - */ - public synchronized boolean isMyLocationEnabled() { - return this.myLocationEnabled; - } - - @Override - public void onLocationChanged(Location location) { - - synchronized (this) { - this.lastLocation = location; - - LatLong latLong = locationToLatLong(location); - static_lastLocation= latLong; - - for(LocationChangeListener listener : this.locationChangeListeners){ + @Override + public void onLocationChanged(Location location) { + Log.i(TAG, "onLocationChanged: " + location); + mLastLocation.set(location); + for(LocationChangeListener listener : Instance.getInstance(getBaseContext()).locationChangeListeners){ listener.onLocationChange(location); } } - } - @Override - public void onProviderDisabled(String provider) { - enableBestAvailableProvider(); - } - - @Override - public void onProviderEnabled(String provider) { - enableBestAvailableProvider(); - } - - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - // do nothing - } - - private void enableBestAvailableProvider() { - disableMyLocation(); - - boolean result = false; - for (String provider : this.locationManager.getProviders(true)) { - if (LocationManager.GPS_PROVIDER.equals(provider)) { - result = true; - if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - return; - } - this.locationManager.requestLocationUpdates(provider, 0, 0, this); - Location location= this.locationManager.getLastKnownLocation(provider); - if(location != null){ - onLocationChanged(location); - } - } + @Override + public void onProviderDisabled(String provider) { + Log.i(TAG, "onProviderDisabled: " + provider); } - this.myLocationEnabled = result; - } - private List locationChangeListeners= new ArrayList<>(); - - public void registerLocationChangeListeners(LocationChangeListener listener){ - if(locationChangeListeners.size() == 0){ - enableMyLocation(); + @Override + public void onProviderEnabled(String provider) { + Log.i(TAG, "onProviderEnabled: " + provider); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + Log.i(TAG, "onStatusChanged: " + provider); } - locationChangeListeners.add(listener); } - public void unregisterLocationChangeListeners(LocationChangeListener listener){ - locationChangeListeners.remove(listener); - if(locationChangeListeners.size() == 0){ - disableMyLocation(); + LocationChangedListener gpsListener= new LocationChangedListener(LocationManager.GPS_PROVIDER); + + @Override + public IBinder onBind(Intent arg0) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i(TAG, "onStartCommand"); + super.onStartCommand(intent, flags, startId); + + Notification.Builder builder = new Notification.Builder(this) + .setContentTitle(getText(R.string.trackerRunning)) + .setContentText(getText(R.string.trackerRunningMessage)); + //.setSmallIcon(R.drawable.icon) + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + NotificationHelper.createChannels(this); + builder.setChannelId(NotificationHelper.CHANNEL_WORKOUT); + } + + startForeground(10, builder.build()); + + return START_STICKY; + } + + @Override + public void onCreate() { + Log.i(TAG, "onCreate"); + initializeLocationManager(); + try { + mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, LOCATION_INTERVAL, 0, gpsListener); + } catch (java.lang.SecurityException ex) { + Log.i(TAG, "fail to request location update, ignore", ex); + } catch (IllegalArgumentException ex) { + Log.d(TAG, "gps provider does not exist " + ex.getMessage()); + } + } + + @Override + public void onDestroy() { + Log.i(TAG, "onDestroy"); + super.onDestroy(); + if (mLocationManager != null) { + mLocationManager.removeUpdates(gpsListener); + } + } + + private void initializeLocationManager() { + Log.i(TAG, "initializeLocationManager"); + if (mLocationManager == null) { + mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE); } } diff --git a/app/src/main/java/de/tadris/fitness/location/WorkoutRecorder.java b/app/src/main/java/de/tadris/fitness/location/WorkoutRecorder.java index d0c0d29..f535e33 100644 --- a/app/src/main/java/de/tadris/fitness/location/WorkoutRecorder.java +++ b/app/src/main/java/de/tadris/fitness/location/WorkoutRecorder.java @@ -73,7 +73,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener Log.i("Recorder", "Start"); workout.start= System.currentTimeMillis(); resume(); - Instance.getInstance(context).locationListener.registerLocationChangeListeners(this); + Instance.getInstance(context).locationChangeListeners.add(this); startWatchdog(); }else if(state == RecordingState.PAUSED){ resume(); @@ -143,7 +143,7 @@ public class WorkoutRecorder implements LocationListener.LocationChangeListener workout.duration= time; workout.pauseDuration= pauseTime; state= RecordingState.STOPPED; - Instance.getInstance(context).locationListener.unregisterLocationChangeListeners(this); + Instance.getInstance(context).locationChangeListeners.remove(this); } public void save(){ diff --git a/app/src/main/java/de/tadris/fitness/util/NotificationHelper.java b/app/src/main/java/de/tadris/fitness/util/NotificationHelper.java new file mode 100644 index 0000000..3900700 --- /dev/null +++ b/app/src/main/java/de/tadris/fitness/util/NotificationHelper.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 Jannis Scheibe + * + * This file is part of FitoTrack + * + * FitoTrack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FitoTrack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.tadris.fitness.util; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; + +import de.tadris.fitness.R; + +public class NotificationHelper { + + public static String CHANNEL_WORKOUT= "workout"; + + public static void createChannels(Context context){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(context, CHANNEL_WORKOUT, R.string.trackingInfo, R.string.trackingInfoDescription, NotificationManager.IMPORTANCE_LOW); + } + } + + private static void createNotificationChannel(Context context, String id, int nameId, int descriptionId, int importance) { + // Create the NotificationChannel, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + CharSequence name = context.getString(nameId); + String description = context.getString(descriptionId); + NotificationChannel channel = new NotificationChannel(id, name, importance); + channel.setDescription(description); + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + NotificationManager notificationManager = context.getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(channel); + } + } + +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ad9eef..2734a6f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,5 +42,11 @@ Delete Workout Do you really want to delete the workout? + Tracker is running + Your workout is being recorded + + Tracking Info + Info about the tracker running + Cancel