tracker service is now a so-called bound service

master
y20k 2018-04-06 18:14:34 +02:00
parent 39a4cbbe41
commit 8264a2b2af
5 changed files with 357 additions and 78 deletions

View File

@ -20,15 +20,18 @@ import android.Manifest;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.location.Location; import android.location.Location;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.IBinder;
import android.os.Vibrator; import android.os.Vibrator;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -52,6 +55,7 @@ import android.widget.Button;
import android.widget.Toast; import android.widget.Toast;
import org.osmdroid.config.Configuration; import org.osmdroid.config.Configuration;
import org.y20k.trackbook.core.Track;
import org.y20k.trackbook.helpers.DialogHelper; import org.y20k.trackbook.helpers.DialogHelper;
import org.y20k.trackbook.helpers.LogHelper; import org.y20k.trackbook.helpers.LogHelper;
import org.y20k.trackbook.helpers.NightModeHelper; import org.y20k.trackbook.helpers.NightModeHelper;
@ -75,6 +79,7 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
/* Main class variables */ /* Main class variables */
private TrackerService mTrackerService;
private BottomNavigationView mBottomNavigationView; private BottomNavigationView mBottomNavigationView;
private NonSwipeableViewPager mViewPager; private NonSwipeableViewPager mViewPager;
private SectionsPagerAdapter mSectionsPagerAdapter; private SectionsPagerAdapter mSectionsPagerAdapter;
@ -94,6 +99,8 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
private int mFloatingActionButtonState; private int mFloatingActionButtonState;
private int mSelectedTab; private int mSelectedTab;
boolean mBound = false;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -140,6 +147,10 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
// bind to TrackerService
Intent intent = new Intent(this, TrackerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
// register broadcast receiver for changed tracking state // register broadcast receiver for changed tracking state
mTrackingChangedReceiver = createTrackingChangedReceiver(); mTrackingChangedReceiver = createTrackingChangedReceiver();
IntentFilter trackingStoppedIntentFilter = new IntentFilter(ACTION_TRACKING_STATE_CHANGED); IntentFilter trackingStoppedIntentFilter = new IntentFilter(ACTION_TRACKING_STATE_CHANGED);
@ -170,6 +181,14 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
} }
@Override
protected void onStop() {
super.onStop();
unbindService(mConnection);
mBound = false;
}
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
@ -250,13 +269,21 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
} }
/* Request the current Track from TrackerService */
public void requestTrack() {
if (mBound) {
mTrackerService.sendTrackUpdate();
}
}
/* Handles the visual state after a save action */ /* Handles the visual state after a save action */
private void handleStateAfterSave() { private void handleStateAfterSave() {
// display and update tracks tab // display and update tracks tab
mBottomNavigationView.setSelectedItemId(R.id.navigation_last_tracks); mBottomNavigationView.setSelectedItemId(R.id.navigation_last_tracks);
// dismiss notification // dismiss notification
startTrackerService(ACTION_DISMISS, null); dismissNotification();
// hide Floating Action Button sub menu // hide Floating Action Button sub menu
showFloatingActionButtonMenu(false); showFloatingActionButtonMenu(false);
@ -267,17 +294,11 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
} }
/* Start/stop tracker service */ /* Start tracker service */
private void startTrackerService(String intentAction, @Nullable Location lastLocation) { private void startTrackerService() {
// build intent // start service so that it keeps running after unbind
Intent intent = new Intent(this, TrackerService.class); Intent intent = new Intent(this, TrackerService.class);
intent.setAction(intentAction); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (lastLocation != null && intentAction.equals(ACTION_START)) {
intent.putExtra(EXTRA_LAST_LOCATION, lastLocation);
}
// communicate with service
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && intentAction.equals(ACTION_START)) {
// ... start service in foreground to prevent it being killed on Oreo // ... start service in foreground to prevent it being killed on Oreo
startForegroundService(intent); startForegroundService(intent);
} else { } else {
@ -286,6 +307,39 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
} }
/* Start recording movements */
private void startRecording(Location lastLocation) {
startTrackerService();
if (mBound) {
mTrackerService.startTracking(lastLocation);
}
}
/* Resume recording movements */
private void resumeRecording() {
startTrackerService();
if (mBound) {
mTrackerService.resumeTracking();
}
}
/* Stop recording movements */
private void stopRecording() {
if (mBound) {
mTrackerService.stopTracking();
}
}
/* Dismiss notification */
private void dismissNotification() {
if (mBound) {
mTrackerService.dismissNotification();
}
}
/* Handles the visual state after a save action */ /* Handles the visual state after a save action */
private void handleStateAfterClear() { private void handleStateAfterClear() {
@ -293,7 +347,7 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
Toast.makeText(this, getString(R.string.toast_message_track_clear), Toast.LENGTH_LONG).show(); Toast.makeText(this, getString(R.string.toast_message_track_clear), Toast.LENGTH_LONG).show();
// dismiss notification // dismiss notification
startTrackerService(ACTION_DISMISS, null); dismissNotification();
// hide Floating Action Button sub menu // hide Floating Action Button sub menu
showFloatingActionButtonMenu(false); showFloatingActionButtonMenu(false);
@ -335,7 +389,7 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
Snackbar.make(view, R.string.snackbar_message_tracking_resumed, Snackbar.LENGTH_SHORT).setAction("Action", null).show(); Snackbar.make(view, R.string.snackbar_message_tracking_resumed, Snackbar.LENGTH_SHORT).setAction("Action", null).show();
// resume tracking // resume tracking
startTrackerService(ACTION_RESUME, null); resumeRecording();
// hide sub menu // hide sub menu
showFloatingActionButtonMenu(false); showFloatingActionButtonMenu(false);
@ -500,7 +554,7 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
Snackbar.make(view, R.string.snackbar_message_tracking_started, Snackbar.LENGTH_SHORT).setAction("Action", null).show(); Snackbar.make(view, R.string.snackbar_message_tracking_started, Snackbar.LENGTH_SHORT).setAction("Action", null).show();
// start tracker service // start tracker service
startTrackerService(ACTION_START, lastLocation); startRecording(lastLocation);
} else { } else {
Toast.makeText(this, getString(R.string.toast_message_location_services_not_ready), Toast.LENGTH_LONG).show(); Toast.makeText(this, getString(R.string.toast_message_location_services_not_ready), Toast.LENGTH_LONG).show();
@ -518,7 +572,7 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
// --> is handled by broadcast receiver // --> is handled by broadcast receiver
// stop tracker service // stop tracker service
startTrackerService(ACTION_STOP, null); stopRecording();
break; break;
@ -756,6 +810,25 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
// } // }
/**
* Defines callbacks for service binding, passed to bindService()
*/
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
TrackerService.LocalBinder binder = (TrackerService.LocalBinder) service;
mTrackerService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
/** /**
* Inner class: SectionsPagerAdapter that returns a fragment corresponding to one of the tabs. * Inner class: SectionsPagerAdapter that returns a fragment corresponding to one of the tabs.

View File

@ -228,9 +228,7 @@ public class MainActivityMapFragment extends Fragment implements TrackbookKeys {
// CASE 1: recording active // CASE 1: recording active
if (mTrackerServiceRunning) { if (mTrackerServiceRunning) {
// request an updated track recording from service // request an updated track recording from service
Intent intent = new Intent(mActivity, TrackerService.class); ((MainActivity)mActivity).requestTrack();
intent.setAction(ACTION_TRACK_REQUEST);
mActivity.startService(intent);
} }
// CASE 2: recording stopped - temp file exists // CASE 2: recording stopped - temp file exists
@ -580,6 +578,8 @@ public class MainActivityMapFragment extends Fragment implements TrackbookKeys {
/* Loads state tracker service from preferences */ /* Loads state tracker service from preferences */
private void loadTrackerServiceState(Context context) { private void loadTrackerServiceState(Context context) {
// TODO: get state directly from service, create a ServiceConnection.
// see: https://github.com/ena1106/FragmentBoundServiceExample/blob/master/app/src/main/java/it/ena1106/fragmentboundservice/BoundFragment.java
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
mTrackerServiceRunning = settings.getBoolean(PREFS_TRACKER_SERVICE_RUNNING, false); mTrackerServiceRunning = settings.getBoolean(PREFS_TRACKER_SERVICE_RUNNING, false);
} }

View File

@ -31,6 +31,7 @@ import android.location.Location;
import android.location.LocationListener; import android.location.LocationListener;
import android.location.LocationManager; import android.location.LocationManager;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle; import android.os.Bundle;
import android.os.CountDownTimer; import android.os.CountDownTimer;
import android.os.Handler; import android.os.Handler;
@ -50,6 +51,7 @@ import org.y20k.trackbook.helpers.StorageHelper;
import org.y20k.trackbook.helpers.TrackbookKeys; import org.y20k.trackbook.helpers.TrackbookKeys;
import java.util.List; import java.util.List;
import java.util.Random;
import static android.hardware.Sensor.TYPE_STEP_COUNTER; import static android.hardware.Sensor.TYPE_STEP_COUNTER;
@ -79,6 +81,8 @@ public class TrackerService extends Service implements TrackbookKeys, SensorEven
private boolean mTrackerServiceRunning; private boolean mTrackerServiceRunning;
private boolean mLocationSystemSetting; private boolean mLocationSystemSetting;
private final IBinder mBinder = new LocalBinder(); // todo move to onCreate
@Override @Override
public void onCreate() { public void onCreate() {
@ -102,69 +106,101 @@ public class TrackerService extends Service implements TrackbookKeys, SensorEven
} }
@Override
public IBinder onBind(Intent intent) {
// a client is binding to the service with bindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
// return mAllowRebind; // todo change
return true;
}
@Override
public void onRebind(Intent intent) {
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
// check if user did turn off location in device settings
if (!mLocationSystemSetting) {
LogHelper.i(LOG_TAG, "Location Setting is turned off.");
Toast.makeText(getApplicationContext(), R.string.toast_message_location_offline, Toast.LENGTH_LONG).show();
stopTracking();
return START_STICKY;
}
// RESTART CHECK: checking for empty intent - try to get saved track
if (intent == null || intent.getAction() == null) {
LogHelper.w(LOG_TAG, "Null-Intent received. Trying to restart tracking.");
startTracking(intent, false);
}
// ACTION START
else if (intent.getAction().equals(ACTION_START) && mLocationSystemSetting) {
startTracking(intent, true);
}
// ACTION RESUME
else if (intent.getAction().equals(ACTION_RESUME) && mLocationSystemSetting) {
startTracking(intent, false);
}
// ACTION STOP // ACTION STOP
else if (intent.getAction().equals(ACTION_STOP) || !mLocationSystemSetting) { if (ACTION_STOP.equals(intent.getAction())) {
mTrackerServiceRunning = false; stopTracking();
if (mTrack != null && mTimer != null) { }
stopTracking(); // ACTION RESUME
} else { else if (ACTION_RESUME.equals(intent.getAction())) {
// handle error - save state resumeTracking();
saveTrackerServiceState(mTrackerServiceRunning, FAB_STATE_DEFAULT);
}
} }
// ACTION DISMISS // // check if user did turn off location in device settings
else if (intent.getAction().equals(ACTION_DISMISS)) { // if (!mLocationSystemSetting) {
// save state // LogHelper.i(LOG_TAG, "Location Setting is turned off.");
saveTrackerServiceState(mTrackerServiceRunning, FAB_STATE_DEFAULT); // Toast.makeText(getApplicationContext(), R.string.toast_message_location_offline, Toast.LENGTH_LONG).show();
// dismiss notification // stopTracking();
mNotificationManager.cancel(TRACKER_SERVICE_NOTIFICATION_ID); // todo check if necessary? // return START_STICKY;
stopForeground(true); // }
} //
// // RESTART CHECK: checking for empty intent - try to get saved track
// ACTION TRACK REQUEST // if (intent == null || intent.getAction() == null) {
else if (intent.getAction().equals(ACTION_TRACK_REQUEST)) { // LogHelper.w(LOG_TAG, "Null-Intent received. Trying to restart tracking.");
// send track via broadcast // startTracking(intent, false);
sendTrackUpdate(); // }
} //
// // ACTION START
// else if (intent.getAction().equals(ACTION_START) && mLocationSystemSetting) {
// startTracking(intent, true);
// }
//
// // ACTION RESUME
// else if (intent.getAction().equals(ACTION_RESUME) && mLocationSystemSetting) {
// startTracking(intent, false);
// }
//
// // ACTION STOP
// else if (intent.getAction().equals(ACTION_STOP) || !mLocationSystemSetting) {
// mTrackerServiceRunning = false;
// if (mTrack != null && mTimer != null) {
// stopTracking();
// } else {
// // handle error - save state
// saveTrackerServiceState(mTrackerServiceRunning, FAB_STATE_DEFAULT);
// }
// }
//
// // ACTION DISMISS
// else if (intent.getAction().equals(ACTION_DISMISS)) {
// // save state
// saveTrackerServiceState(mTrackerServiceRunning, FAB_STATE_DEFAULT);
// // dismiss notification
// mNotificationManager.cancel(TRACKER_SERVICE_NOTIFICATION_ID); // todo check if necessary?
// stopForeground(true);
// }
//
// // ACTION TRACK REQUEST
// else if (intent.getAction().equals(ACTION_TRACK_REQUEST)) {
// // send track via broadcast
// sendTrackUpdate();
// }
// START_STICKY is used for services that are explicitly started and stopped as needed // START_STICKY is used for services that are explicitly started and stopped as needed
return START_STICKY; return START_STICKY;
} }
@Nullable // @Nullable
@Override // @Override
public IBinder onBind(Intent intent) { // public IBinder onBind(Intent intent) {
return null; // return null;
} // }
@Override @Override
@ -204,6 +240,153 @@ public class TrackerService extends Service implements TrackbookKeys, SensorEven
/* Start tracking location */ /* Start tracking location */
public void startTracking(Location lastLocation) {
if (mLocationSystemSetting) {
LogHelper.v(LOG_TAG, "Start tracking");
// create a new track - if requested
mTrack = new Track();
// get last location
if (lastLocation != null) {
mCurrentBestLocation = lastLocation;
} else {
mCurrentBestLocation = LocationHelper.determineLastKnownLocation(mLocationManager);
}
// initialize step counter
mStepCountOffset = 0;
// begin recording
recordMovements();
} else {
LogHelper.i(LOG_TAG, "Location Setting is turned off.");
Toast.makeText(getApplicationContext(), R.string.toast_message_location_offline, Toast.LENGTH_LONG).show();
}
}
/* Resume tracking after stop/pause */
public void resumeTracking() {
if (mLocationSystemSetting) {
LogHelper.v(LOG_TAG, "Recording resumed");
// create a new track - if requested
StorageHelper storageHelper = new StorageHelper(this);
if (storageHelper.tempFileExists()) {
// load temp track file
mTrack = storageHelper.loadTrack(FILE_TEMP_TRACK);
// try to mark last waypoint as stopover
int lastWayPoint = mTrack.getWayPoints().size() - 1;
if (lastWayPoint >= 0) {
mTrack.getWayPoints().get(lastWayPoint).setIsStopOver(true);
}
} else {
// fallback, if tempfile did not exist
LogHelper.e(LOG_TAG, "Unable to find previously saved track temp file.");
mTrack = new Track();
}
// get last location
if (mTrack.getSize() > 0) {
mCurrentBestLocation = mTrack.getWayPointLocation(mTrack.getSize() -1);
} else {
mCurrentBestLocation = LocationHelper.determineLastKnownLocation(mLocationManager);
}
// initialize step counter
mStepCountOffset = mTrack.getStepCount();
// begin recording
recordMovements();
} else {
LogHelper.i(LOG_TAG, "Location Setting is turned off.");
Toast.makeText(getApplicationContext(), R.string.toast_message_location_offline, Toast.LENGTH_LONG).show();
}
}
/* Record movements */
private void recordMovements() {
// add last location as WayPoint to track
addWayPointToTrack();
// put up notification
displayNotification(true);
// create gps and network location listeners
startFindingLocation();
// start timer that periodically request a location update
startIntervalTimer();
// start counting steps
startStepCounter();
// register content observer for changes in System Settings
this.getContentResolver().registerContentObserver(android.provider.Settings.Secure.CONTENT_URI, true, mSettingsContentObserver);
// start service in foreground
startForeground(TRACKER_SERVICE_NOTIFICATION_ID, mNotification);
}
private void startStepCounter() {
boolean stepCounterAvailable;
stepCounterAvailable = mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(TYPE_STEP_COUNTER), SensorManager.SENSOR_DELAY_UI);
if (stepCounterAvailable) {
LogHelper.v(LOG_TAG, "Pedometer sensor available: Registering listener.");
} else {
LogHelper.i(LOG_TAG, "Pedometer sensor not available.");
mTrack.setStepCount(-1);
}
}
/* Set timer to retrieve new locations and to prevent endless tracking */
private void startIntervalTimer() {
mTimer = new CountDownTimer(EIGHT_HOURS_IN_MILLISECONDS, FIFTEEN_SECONDS_IN_MILLISECONDS) {
@Override
public void onTick(long millisUntilFinished) {
// update track duration - and add duration from previously interrupted / paused session
long previouslyRecordedDuration = mTrack.getTrackDuration();
long duration = EIGHT_HOURS_IN_MILLISECONDS - millisUntilFinished + previouslyRecordedDuration;
mTrack.setDuration(duration);
// try to add WayPoint to Track
addWayPointToTrack();
// update notification
mNotification = NotificationHelper.getUpdatedNotification(TrackerService.this, mNotificationBuilder, mTrack);
mNotificationManager.notify(TRACKER_SERVICE_NOTIFICATION_ID, mNotification);
// save a temp file in case the service has been killed by the system
SaveTempTrackAsyncHelper saveTempTrackAsyncHelper = new SaveTempTrackAsyncHelper();
saveTempTrackAsyncHelper.execute();
}
@Override
public void onFinish() {
// stop tracking after eight hours
stopTracking();
}
};
mTimer.start();
}
/* Display notification */
private void displayNotification(boolean trackingState) {
mNotificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANEL_ID_RECORDING_CHANNEL);
mNotification = NotificationHelper.getNotification(this, mNotificationBuilder, mTrack, trackingState);
mNotificationManager.notify(TRACKER_SERVICE_NOTIFICATION_ID, mNotification); // todo check if necessary in pre Android O
}
/* Start tracking location */ // todo remove
private void startTracking(@Nullable Intent intent, boolean createNewTrack) { private void startTracking(@Nullable Intent intent, boolean createNewTrack) {
LogHelper.v(LOG_TAG, "Service received command: START"); LogHelper.v(LOG_TAG, "Service received command: START");
@ -301,8 +484,8 @@ public class TrackerService extends Service implements TrackbookKeys, SensorEven
/* Stop tracking location */ /* Stop tracking location */
private void stopTracking() { public void stopTracking() {
LogHelper.v(LOG_TAG, "Service received command: STOP"); LogHelper.v(LOG_TAG, "Recording stopped");
// store current date and time // store current date and time
mTrack.setRecordingEnd(); mTrack.setRecordingEnd();
@ -318,9 +501,7 @@ public class TrackerService extends Service implements TrackbookKeys, SensorEven
saveTempTrackAsyncHelper.execute(); saveTempTrackAsyncHelper.execute();
// change notification // change notification
mNotificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANEL_ID_RECORDING_CHANNEL); displayNotification(false);
mNotification = NotificationHelper.getNotification(this, mNotificationBuilder, mTrack, false);
mNotificationManager.notify(TRACKER_SERVICE_NOTIFICATION_ID, mNotification);
// remove listeners // remove listeners
stopFindingLocation(); stopFindingLocation();
@ -334,6 +515,17 @@ public class TrackerService extends Service implements TrackbookKeys, SensorEven
} }
/* Dismiss notification */
public void dismissNotification() {
// save state
saveTrackerServiceState(mTrackerServiceRunning, FAB_STATE_DEFAULT);
// cancel notification
mNotificationManager.cancel(TRACKER_SERVICE_NOTIFICATION_ID); // todo check if necessary?
stopForeground(true);
}
/* Adds a new WayPoint to current track */ /* Adds a new WayPoint to current track */
private void addWayPointToTrack() { private void addWayPointToTrack() {
@ -376,7 +568,7 @@ public class TrackerService extends Service implements TrackbookKeys, SensorEven
/* Broadcasts a track update */ /* Broadcasts a track update */
private void sendTrackUpdate() { public void sendTrackUpdate() {
if (mTrack != null) { if (mTrack != null) {
Intent i = new Intent(); Intent i = new Intent();
i.setAction(ACTION_TRACK_UPDATED); i.setAction(ACTION_TRACK_UPDATED);
@ -467,6 +659,20 @@ public class TrackerService extends Service implements TrackbookKeys, SensorEven
} }
/**
* Inner class: Local Binder that returns this service
*/
public class LocalBinder extends Binder {
TrackerService getService() {
// return this instance of TrackerService so clients can call public methods
return TrackerService.this;
}
}
/**
* End of inner class
*/
/** /**
* Inner class: SettingsContentObserver is a custom ContentObserver for changes in Android Settings * Inner class: SettingsContentObserver is a custom ContentObserver for changes in Android Settings
*/ */

View File

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

View File

@ -1,6 +1,6 @@
#Thu Nov 02 15:43:54 CET 2017 #Wed Apr 04 15:52:45 CEST 2018
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip