diff --git a/GPSTracker/.gitignore b/GPSTracker/.gitignore
new file mode 100644
index 0000000..d794100
--- /dev/null
+++ b/GPSTracker/.gitignore
@@ -0,0 +1,29 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Android template
+
+# Generated files
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+# Log Files
+# Android Studio Navigation editor temp files
+# Android Studio captures folder
+# Intellij
+*.iml
+.idea/workspace.xml
+.idea/gradle.xml
+.idea/dictionaries
+.idea/libraries
+
+# Keystore files
+# Uncomment the following line if you do not want to check your keystore files in.
+#*.jks
+
+# External native build folder generated in Android Studio 2.2 and later
+# Google Services (e.g. APIs or Firebase)
+# Freeline
diff --git a/GPSTracker/.idea/compiler.xml b/GPSTracker/.idea/compiler.xml
new file mode 100644
index 0000000..290e589
--- /dev/null
+++ b/GPSTracker/.idea/compiler.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GPSTracker/.idea/copyright/profiles_settings.xml b/GPSTracker/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/GPSTracker/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/GPSTracker/.idea/encodings.xml b/GPSTracker/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/GPSTracker/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GPSTracker/.idea/inspectionProfiles/Project_Default.xml b/GPSTracker/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..f3f7314
--- /dev/null
+++ b/GPSTracker/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GPSTracker/.idea/inspectionProfiles/profiles_settings.xml b/GPSTracker/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..3b31283
--- /dev/null
+++ b/GPSTracker/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GPSTracker/.idea/misc.xml b/GPSTracker/.idea/misc.xml
new file mode 100644
index 0000000..c1129a9
--- /dev/null
+++ b/GPSTracker/.idea/misc.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Android
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ C:\Users\Vadim\AppData\Roaming\Subversion
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GPSTracker/.idea/modules.xml b/GPSTracker/.idea/modules.xml
new file mode 100644
index 0000000..11241f4
--- /dev/null
+++ b/GPSTracker/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GPSTracker/.idea/runConfigurations.xml b/GPSTracker/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/GPSTracker/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GPSTracker/app/.gitignore b/GPSTracker/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/GPSTracker/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/GPSTracker/app/build.gradle b/GPSTracker/app/build.gradle
new file mode 100644
index 0000000..35e5667
--- /dev/null
+++ b/GPSTracker/app/build.gradle
@@ -0,0 +1,46 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 26
+ buildToolsVersion "26.0.2"
+ defaultConfig {
+ applicationId "ru.spbau.farutin_solikov.gpstracker"
+ minSdkVersion 16
+ //noinspection OldTargetApi
+ targetSdkVersion 26
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ vectorDrawables.useSupportLibrary = true
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ targetCompatibility JavaVersion.VERSION_1_7
+ sourceCompatibility JavaVersion.VERSION_1_7
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+
+ compile 'com.android.support:appcompat-v7:26.0.0'
+ compile 'com.android.support:design:26.0.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+ compile 'com.android.support:support-compat:26.0.0'
+ compile 'com.android.support:support-v4:26.0.0'
+ compile 'com.android.support:support-vector-drawable:26.0.0'
+
+ compile 'com.google.android.gms:play-services-maps:11.0.4'
+ compile 'com.getbase:floatingactionbutton:1.10.1'
+ compile 'com.google.maps.android:android-maps-utils:0.4'
+
+ testCompile 'junit:junit:4.12'
+}
diff --git a/GPSTracker/app/libs/mysql-connector-java-5.0.8-bin.jar b/GPSTracker/app/libs/mysql-connector-java-5.0.8-bin.jar
new file mode 100644
index 0000000..0170c3e
Binary files /dev/null and b/GPSTracker/app/libs/mysql-connector-java-5.0.8-bin.jar differ
diff --git a/GPSTracker/app/proguard-rules.pro b/GPSTracker/app/proguard-rules.pro
new file mode 100644
index 0000000..f68ab60
--- /dev/null
+++ b/GPSTracker/app/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\Users\Vadim\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/GPSTracker/app/src/androidTest/java/ru/spbau/farutin_solikov/gpstracker/ExampleInstrumentedTest.java b/GPSTracker/app/src/androidTest/java/ru/spbau/farutin_solikov/gpstracker/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..634da12
--- /dev/null
+++ b/GPSTracker/app/src/androidTest/java/ru/spbau/farutin_solikov/gpstracker/ExampleInstrumentedTest.java
@@ -0,0 +1,31 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("ru.spbau.farutin_solikov.gpstracker", appContext.getPackageName());
+ }
+
+ @Test
+ public void testCheckID() {
+ assertEquals(DBManager.isValidDeviceId("11111111"), true);
+ }
+}
diff --git a/GPSTracker/app/src/main/AndroidManifest.xml b/GPSTracker/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6c20df7
--- /dev/null
+++ b/GPSTracker/app/src/main/AndroidManifest.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/AlarmActivity.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/AlarmActivity.java
new file mode 100644
index 0000000..8945875
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/AlarmActivity.java
@@ -0,0 +1,128 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.support.v4.app.NotificationCompat;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * Alarm mode - notifying user once vehicle has changed position.
+ */
+public class AlarmActivity extends DrawerActivity {
+ private static final int ALARM_NOTIFICATION_ID = 1;
+
+ private Controller.AlarmCoordinatesReceiver receiver;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.content_alarm);
+ setUpButtons();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ try {
+ unregisterReceiver(receiver);
+ } catch (IllegalArgumentException ignored) {
+ // no API methods to tell if it is registered at the moment
+ }
+ }
+
+ /**
+ * Notifies user and changes layout content.
+ *
+ * @param position position where vehicle was moved to
+ */
+ public void positionChanged(final Coordinate position) {
+ notifyUser();
+
+ Button track = findViewById(R.id.alarm_button);
+ track.setText(R.string.title_button_track_position);
+ track.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ closeNotification();
+
+ Intent intent = new Intent(AlarmActivity.this, TrackerActivity.class);
+ intent.putExtra(getString(R.string.extra_position), position);
+ startActivity(intent);
+ }
+ });
+
+ TextView message = findViewById(R.id.alarm_message);
+ message.setVisibility(View.VISIBLE);
+ }
+
+ private void notifyUser() {
+ if (Controller.notificationsOn(this)) {
+ NotificationCompat.Builder builder =
+ new NotificationCompat.Builder(AlarmActivity.this, getString(R.string.alarm_channel_id))
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentTitle(getString(R.string.title_alarm_notification));
+
+ Intent resultIntent = new Intent(AlarmActivity.this, AlarmActivity.class);
+ PendingIntent resultPendingIntent =
+ PendingIntent.getActivity(
+ AlarmActivity.this,
+ 0,
+ resultIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ );
+ builder.setContentIntent(resultPendingIntent);
+ builder.setAutoCancel(true);
+
+ NotificationManager notifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ assert notifyManager != null;
+ // IDE тут "жаловался" потому что теоретически `notifyManager` может содержать `null`,
+ // один из способов его успокоить это написать `assert`.
+ // Лучше, конечно, как-то обрабобтать эту ситуацию, но это не всегда возможно.
+ notifyManager.notify(ALARM_NOTIFICATION_ID, builder.build());
+ }
+ }
+
+ private void closeNotification() {
+ NotificationManager notifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ assert notifyManager != null;
+ notifyManager.cancel(ALARM_NOTIFICATION_ID);
+ }
+
+ private void setUpButtons() {
+ final Button alarmButton = findViewById(R.id.alarm_button);
+ alarmButton.setText(R.string.title_button_turn_on);
+ alarmButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Controller.startCoordinatesService(AlarmActivity.this);
+
+ alarmButton.setText(R.string.title_button_turn_off);
+ alarmButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ closeNotification();
+ setUpButtons();
+
+ Controller.stopCoordinatesService();
+
+ try {
+ unregisterReceiver(receiver);
+ } catch (IllegalArgumentException ignored) {
+ // no API methods to tell if it is registered at the moment
+ }
+ }
+ });
+
+ receiver = new Controller.AlarmCoordinatesReceiver(AlarmActivity.this);
+ IntentFilter intentSFilter = new IntentFilter(getString(R.string.broadcast_content_coordinates));
+ registerReceiver(receiver, intentSFilter);
+ }
+ });
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/AppCompatPreferenceActivity.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/AppCompatPreferenceActivity.java
new file mode 100644
index 0000000..ec9f4c7
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/AppCompatPreferenceActivity.java
@@ -0,0 +1,105 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatDelegate;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ */
+public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
+
+ private AppCompatDelegate mDelegate;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getDelegate().installViewFactory();
+ getDelegate().onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ getDelegate().onPostCreate(savedInstanceState);
+ }
+
+ public ActionBar getSupportActionBar() {
+ return getDelegate().getSupportActionBar();
+ }
+
+ @NonNull
+ @Override
+ public MenuInflater getMenuInflater() {
+ return getDelegate().getMenuInflater();
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ getDelegate().setContentView(layoutResID);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getDelegate().setContentView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().setContentView(view, params);
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().addContentView(view, params);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getDelegate().onPostResume();
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ super.onTitleChanged(title, color);
+ getDelegate().setTitle(title);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getDelegate().onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ getDelegate().onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ getDelegate().onDestroy();
+ }
+
+ public void invalidateOptionsMenu() {
+ getDelegate().invalidateOptionsMenu();
+ }
+
+ private AppCompatDelegate getDelegate() {
+ if (mDelegate == null) {
+ mDelegate = AppCompatDelegate.create(this, null);
+ }
+ return mDelegate;
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Controller.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Controller.java
new file mode 100644
index 0000000..966ff77
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Controller.java
@@ -0,0 +1,278 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.widget.Toast;
+
+import com.google.android.gms.maps.GoogleMap;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.content.Context.MODE_PRIVATE;
+
+/**
+ * Class with supporting methods.
+ */
+public class Controller {
+ /**
+ * SharedPreferences filename.
+ */
+ public static final String PREF_FILE = "prefs";
+
+ /**
+ * Starts CoordinateService.
+ *
+ * @param context context to enqueue with
+ */
+ public static void startCoordinatesService(Context context) {
+ CoordinatesService.enqueueWork(context, new Intent());
+ }
+
+ /**
+ * Stops CoordinateService.
+ */
+ public static void stopCoordinatesService() {
+ CoordinatesService.stop();
+ }
+
+ /**
+ * Returns stored user id
+ *
+ * @param context context with SharedPreferences
+ * @return stored deviceId
+ */
+ public static String getUserID(Context context) {
+ SharedPreferences sharedPreferences = context.getSharedPreferences(PREF_FILE, MODE_PRIVATE);
+ return sharedPreferences.getString(context.getString(R.string.preference_user_id), "");
+ }
+
+ /**
+ * Fetches new coordinates from the database.
+ *
+ * @param id id of the coordinate after which should take new coordinates
+ * @return new coordinates
+ */
+ public static List fetchCoordinates(int id) {
+ return DBManager.fetchCoordinates(id);
+ }
+
+ /**
+ * Fetches all displacements from the database.
+ *
+ * @return all displacements
+ */
+ public static List fetchDisplacements() {
+ return DBManager.fetchDisplacements();
+ }
+
+ /**
+ * Deletes all rows from table.
+ */
+ public static void clearTable() {
+ DBManager.clearTable();
+ }
+
+ /**
+ * Saves route to the database.
+ *
+ * @param route route to save
+ * @param name route name
+ */
+ // (?) Почему данные получаете через сервис, а отправляете напрямую?
+ //
+ // Сервис нужен, чтобы данные считывались постоянно после запуска и до его отключения
+ // (причем не в главном потоке), а отправить нужно единожды.
+ public static void sendCoordinates(List route, String name) {
+ DBManager.sendCoordinates(route, name);
+ }
+
+ /**
+ * Checks whether id is correct or not.
+ *
+ * @param deviceId input id
+ * @return true if id has correct format and exists, false otherwise
+ */
+ public static boolean checkDeviceId(String deviceId) {
+ //For testing
+ //return deviceId.length() % 5 == 4 && DBManager.isValidDeviceId(deviceId);
+ return DBManager.isValidDeviceId(deviceId);
+ }
+
+ /**
+ * Checks whether user has already logged in or not.
+ *
+ * @param context context with SharedPreferences
+ * @return true if user has already logged in, false otherwise
+ */
+ public static boolean userLoggedIn(Context context) {
+ SharedPreferences sharedPreferences = context.getSharedPreferences(PREF_FILE, MODE_PRIVATE);
+ return sharedPreferences.getString(context.getString(R.string.preference_user_id), "").length() > 0;
+ }
+
+ /**
+ * Checks whether notifications are turn on or not.
+ *
+ * @param context context with SharedPreferences
+ * @return true if notifications are turn on, false otherwise
+ */
+ public static boolean notificationsOn(Context context) {
+ SharedPreferences sharedPreferences = context.getSharedPreferences(PREF_FILE, MODE_PRIVATE);
+ return sharedPreferences.getString(context.getString(R.string.preference_notifications_new_message), "true").equals("true");
+ }
+
+ /**
+ * Saves device id.
+ *
+ * @param context context with SharedPreferences
+ * @param deviceId id to save
+ */
+ public static void saveUserDeviceId(Context context, String deviceId) {
+ DBManager.setDeviceId(deviceId);
+
+ SharedPreferences sharedPreferences = context.getSharedPreferences(PREF_FILE, MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.putString(context.getString(R.string.preference_user_id), deviceId);
+ editor.apply();
+ }
+
+ /**
+ * Makes snapshot of map with route and builds intent with it to send.
+ *
+ * @param instance activity from which snapshot is sending
+ * @param map map to send
+ * @param routeName route name
+ */
+ public static void sendSnapshot(final RouteActivity instance, GoogleMap map, final String routeName) {
+ GoogleMap.SnapshotReadyCallback callback = new GoogleMap.SnapshotReadyCallback() {
+ // зачем заводить поле для этого?
+ //Bitmap bitmap;
+ // fixed
+
+ @Override
+ public void onSnapshotReady(Bitmap snapshot) {
+ try {
+ File outputDir = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
+ instance.getString(R.string.app_name));
+
+ if (!outputDir.exists()) {
+ outputDir.mkdir();
+ }
+
+ File outputFile = new File(outputDir, routeName + ".png");
+ FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
+
+ snapshot.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
+ MediaScannerConnection.scanFile(instance,
+ new String[]{outputFile.getPath()},
+ new String[]{"image/png"},
+ null);
+
+ Uri attachment = Uri.fromFile(outputFile);
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_SEND);
+ intent.setType("image/*");
+ intent.putExtra(Intent.EXTRA_STREAM, attachment);
+
+ instance.startActivity(Intent.createChooser(intent, instance.getString(R.string.title_chooser)));
+ } catch (IOException e) {
+ Toast.makeText(instance, instance.getString(R.string.toast_route) + e.getMessage(), Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+
+ map.snapshot(callback);
+ }
+
+ /**
+ * Fetches piece of a route.
+ *
+ * @param start start position
+ * @param stop stop position
+ * @return coordinates
+ */
+ public static List fetchCoordinates(int start, int stop) {
+ return DBManager.fetchCoordinates(start, stop);
+ }
+
+ /**
+ * Deletes corresponding route in the database
+ *
+ * @param name of route to be deleted
+ */
+ public static void deleteDisplacement(String name){
+ DBManager.deleteDisplacement(name);
+ }
+
+ /**
+ * Class to receive coordinates broadcast by CoordinatorService.
+ */
+ private static class CoordinatesReceiver extends BroadcastReceiver {
+ ArrayList coordinates = null;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Bundle notificationData = intent.getExtras();
+ coordinates = notificationData.getParcelableArrayList(context.getString(R.string.extra_coordinates));
+ }
+ }
+
+ /**
+ * Once coordinates are received, notifies AlarmActivity.
+ */
+ public static class AlarmCoordinatesReceiver extends CoordinatesReceiver {
+ // можно сделать final
+ // (*) но зачем?
+ //
+ // AlarmCoordinatesReceiver создается для конкретного экземпляра AlarmActivity,
+ // к которому он и должен быть привязан. Ключевым словом final показываем, что
+ // менять этот экземпляр на другой нельзя.
+ private final AlarmActivity alarmActivityInstance;
+
+ public AlarmCoordinatesReceiver(AlarmActivity instance) {
+ alarmActivityInstance = instance;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ stopCoordinatesService();
+
+ try {
+ alarmActivityInstance.unregisterReceiver(this);
+ } catch (IllegalArgumentException ignored) {
+ // no API methods to tell if it is registered at the moment
+ }
+
+ super.onReceive(context, intent);
+ alarmActivityInstance.positionChanged(coordinates.get(coordinates.size() - 1));
+ }
+ }
+
+ /**
+ * Once coordinates are received, draws route in TrackerActivity.
+ */
+ public static class TrackerCoordinatesReceiver extends CoordinatesReceiver {
+ private final TrackerActivity trackerActivityInstance;
+
+ public TrackerCoordinatesReceiver(TrackerActivity instance) {
+ trackerActivityInstance = instance;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ super.onReceive(context, intent);
+ trackerActivityInstance.drawRoute(coordinates);
+ }
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Coordinate.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Coordinate.java
new file mode 100644
index 0000000..76e3c7e
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Coordinate.java
@@ -0,0 +1,63 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Class for storing map coordinates.
+ */
+public class Coordinate implements Parcelable {
+ private final double lat;
+ private final double lng;
+ private final int id;
+
+ Coordinate(double lat, double lng, int id) {
+ // [minor] неконсистентно: `this.foo`; `foo`
+ // почему то `x` то `lat`?
+ // fixed
+ this.lat = lat;
+ this.lng = lng;
+ this.id = id;
+ }
+
+ public double getLat() {
+ return lat;
+ }
+
+ public double getLng() {
+ return lng;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeDouble(lat);
+ parcel.writeDouble(lng);
+ parcel.writeInt(id);
+ }
+
+ public static final Creator CREATOR
+ = new Creator() {
+ public Coordinate createFromParcel(Parcel in) {
+ return new Coordinate(in);
+ }
+
+ public Coordinate[] newArray(int size) {
+ return new Coordinate[size];
+ }
+ };
+
+ private Coordinate(Parcel in) {
+ lat = in.readDouble();
+ lng = in.readDouble();
+ id = in.readInt();
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/CoordinatesService.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/CoordinatesService.java
new file mode 100644
index 0000000..ea659e9
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/CoordinatesService.java
@@ -0,0 +1,89 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import android.support.v4.app.JobIntentService;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.lang.Math.abs;
+
+/**
+ * This class checks periodically whether position of the vehicle has been changed or not.
+ */
+public class CoordinatesService extends JobIntentService {
+ private static final String TAG = "CoordinatesService";
+ public static final int JOB_ID = 1000;
+ private static final double EPS = 1.0E-06;
+ private static final int SLEEP = 1000;
+ private static boolean isActive;
+
+ // coordinates можно сделать локальной переменной и передовать в broadcastCoordinates как параметр
+ //private ArrayList coordinates;
+ // fixed
+
+ public static void enqueueWork(Context context, Intent work) {
+ isActive = true;
+ enqueueWork(context, CoordinatesService.class, JOB_ID, work);
+ }
+
+ public static void stop() {
+ isActive = false;
+ }
+
+ @Override
+ protected void onHandleWork(@NonNull Intent intent) {
+ List coordinates;
+ boolean positionChanged;
+
+ double lat = 0;
+ double lng = 0;
+ int id = -1;
+
+ // (?) Почему надо чистить таблицу?
+ //
+ // Там лежат координаты, оставшиеся от предыдущей поездки, сейчас они уже не нужны.
+ // Более того, иначе они будут считаться первыми координатами нового запуска.
+ Controller.clearTable();
+
+ while (isActive) {
+ coordinates = Controller.fetchCoordinates(id);
+ positionChanged = false;
+
+ for (Coordinate pos : coordinates) {
+ if (abs(pos.getLat() - lat) > EPS || abs(pos.getLng() - lng) > EPS) {
+ positionChanged = true;
+ break;
+ }
+ }
+
+ if (positionChanged) {
+ if (coordinates.size() != 0) {
+ Coordinate pos = coordinates.get(coordinates.size() - 1);
+ lat = pos.getLat();
+ lng = pos.getLng();
+ id = pos.getId();
+ }
+
+ broadcastCoordinates(coordinates);
+ }
+
+ try {
+ Thread.sleep(SLEEP);
+ } catch (InterruptedException e) {
+ Log.w(TAG, e.getMessage());
+ }
+ }
+ }
+
+ private void broadcastCoordinates(List coordinates){
+ Intent broadcastIntent = new Intent();
+ broadcastIntent.setAction(getString(R.string.broadcast_content_coordinates));
+ broadcastIntent.putParcelableArrayListExtra(getString(R.string.extra_coordinates), (ArrayList extends Parcelable>) coordinates);
+ sendBroadcast(broadcastIntent);
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/DBManager.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/DBManager.java
new file mode 100644
index 0000000..44c5453
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/DBManager.java
@@ -0,0 +1,389 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.util.Log;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class for interaction with the database.
+ */
+public class DBManager {
+ private static final String TAG = "DBManager";
+ private static String deviceId = "";
+
+ private static final String driver = "com.mysql.jdbc.Driver";
+ private static final String url = "jdbc:mysql://146.185.144.144:3306/gps?autoReconnect=true&useSSL=false";
+ // На всякий случай напишу, что так делать не надо, но для прототипа не критично.
+ private static final String user = "android";
+ private static final String password = "GPSTracker-MySQL123";
+
+ public static void setDeviceId(String deviceId) {
+ DBManager.deviceId = deviceId;
+ }
+
+ /**
+ * Fetches new coordinates from the database.
+ *
+ * @param id id of the coordinate after which should take new coordinates
+ * @return new coordinates
+ */
+ // Лучше возвращать List, а ингда даже Collection
+ // (*) почему?
+ //
+ // Более общий тип, чтобы методы, использующие возвращаемое значение,
+ // меньше зависели от реализации данного метода.
+ public static List fetchCoordinates(int id) {
+ Connection con = null;
+ PreparedStatement preparedStatement = null;
+ ResultSet rs = null;
+
+ ArrayList coordinates = new ArrayList<>();
+
+ try {
+ Class.forName(driver);
+
+ con = DriverManager.getConnection(url, user, password);
+ String sql = "SELECT * FROM " + deviceId + "_coordinates " + "where id > ?";
+
+ preparedStatement = con.prepareStatement(sql);
+ preparedStatement.setInt(1, id);
+ rs = preparedStatement.executeQuery();
+
+ double lat;
+ double lng;
+ int coordinate_id;
+
+ while (rs.next()) {
+ lat = rs.getDouble("lat");
+ lng = rs.getDouble("lng");
+ coordinate_id = rs.getInt("id");
+ coordinates.add(new Coordinate(lat, lng, coordinate_id));
+
+ // TODO: remove, demo only
+ break;
+ }
+ } catch (SQLException | ClassNotFoundException e) {
+ // лучше логировать https://developer.android.com/reference/android/util/Log.html
+ // fixed
+ Log.w(TAG, e.getMessage());
+ } finally {
+ // было бы логичнее/правильнее закрывать ресурсы в обратном порядке
+ // (*) почему?
+ //
+ // Ресурсы, созданные позже, могут зависеть от ресурсов, созданных раньше.
+ try {
+ if (rs != null) {
+ rs.close();
+ }
+ } catch (SQLException ignored) {
+ }
+ try {
+ if (preparedStatement != null) {
+ preparedStatement.close();
+ }
+ } catch (SQLException ignored) {
+ }
+ try {
+ if (con != null) {
+ con.close();
+ }
+ } catch (SQLException ignored) {
+ }
+ }
+
+ return coordinates;
+ }
+
+ public static List fetchCoordinates(int start, int stop) {
+ Connection con = null;
+ PreparedStatement preparedStatement = null;
+ ResultSet rs = null;
+
+ ArrayList coordinates = new ArrayList<>();
+
+ try {
+ Class.forName(driver);
+
+ con = DriverManager.getConnection(url, user, password);
+ String sql = "SELECT * FROM " + deviceId + "_all " + "where id > ? and id < ?";
+
+ preparedStatement = con.prepareStatement(sql);
+ preparedStatement.setInt(1, start - 1);
+ preparedStatement.setInt(2, stop + 1);
+ rs = preparedStatement.executeQuery();
+
+ double lat;
+ double lng;
+ int coordinate_id;
+
+ System.err.println(start + " " + stop);
+ while (rs.next()) {
+ lat = rs.getDouble("lat");
+ lng = rs.getDouble("lng");
+ coordinate_id = rs.getInt("id");
+ coordinates.add(new Coordinate(lat, lng, coordinate_id));
+ }
+ } catch (SQLException | ClassNotFoundException e) {
+ Log.w(TAG, e.getMessage());
+ } finally {
+ try {
+ if (rs != null) {
+ rs.close();
+ }
+ } catch (SQLException ignored) {
+ }
+ try {
+ if (preparedStatement != null) {
+ preparedStatement.close();
+ }
+ } catch (SQLException ignored) {
+ }
+ try {
+ if (con != null) {
+ con.close();
+ }
+ } catch (SQLException ignored) {
+ }
+ }
+
+ return coordinates;
+ }
+
+ /**
+ * Fetches all displacements from the database.
+ *
+ * @return all displacement
+ */
+ public static List fetchDisplacements() {
+ Connection con = null;
+ PreparedStatement preparedStatement = null;
+ ResultSet rs = null;
+
+ ArrayList displacements = new ArrayList<>();
+
+ try {
+ Class.forName(driver);
+
+ con = DriverManager.getConnection(url, user, password);
+ String sql = "SELECT * FROM " + deviceId + "_routes ";
+
+ preparedStatement = con.prepareStatement(sql);
+ rs = preparedStatement.executeQuery();
+
+ int start, stop;
+ String name;
+
+ while (rs.next()) {
+ name = rs.getString("name");
+ start = rs.getInt("start");
+ stop = rs.getInt("stop");
+ displacements.add(new Displacement(start, stop, name));
+ }
+ } catch (SQLException | ClassNotFoundException e) {
+ Log.w(TAG, e.getMessage());
+ } finally {
+ try {
+ if (rs != null) {
+ rs.close();
+ }
+ } catch (SQLException ignored) {
+ }
+ try {
+ if (preparedStatement != null) {
+ preparedStatement.close();
+ }
+ } catch (SQLException ignored) {
+ }
+ try {
+ if (con != null) {
+ con.close();
+ }
+ } catch (SQLException ignored) {
+ }
+ }
+
+ return displacements;
+ }
+
+ /**
+ * Deletes all rows from table.
+ */
+ public static void clearTable() {
+ try {
+ Class.forName(driver);
+
+ Connection con = DriverManager.getConnection(url, user, password);
+
+ try {
+ String table = deviceId + "_coordinates";
+ String query = "DELETE FROM " + table;
+ PreparedStatement preparedStatement = con.prepareStatement(query);
+ preparedStatement.executeUpdate(query);
+ } catch (SQLException e) {
+ Log.w(TAG, e.getMessage());
+ }
+
+ con.close();
+ } catch (ClassNotFoundException | SQLException e) {
+ Log.w(TAG, e.getMessage());
+ }
+ }
+
+ /**
+ * Delete corresponding route in database
+ *
+ * @param name of route
+ */
+ public static void deleteDisplacement(String name) {
+ try {
+ Class.forName(driver);
+
+ Connection con = DriverManager.getConnection(url, user, password);
+
+ try {
+ String table = deviceId + "_routes";
+ String query = "delete from " + table + " where name = '" + name + "' limit 1";
+ PreparedStatement preparedStatement = con.prepareStatement(query);
+ preparedStatement.executeUpdate(query);
+ } catch (SQLException e) {
+ Log.w(TAG, e.getMessage());
+ }
+
+ con.close();
+ } catch (ClassNotFoundException | SQLException e) {
+ Log.w(TAG, e.getMessage());
+ }
+ }
+
+ /**
+ * Saves route to the database.
+ *
+ * @param route route to save
+ * @param name route name
+ */
+ public static void sendCoordinates(List route, String name) {
+ Connection con = null;
+ Statement stmt = null;
+
+ try {
+ Class.forName(driver);
+
+ con = DriverManager.getConnection(url, user, password);
+
+ stmt = con.createStatement();
+
+ int start = -1, stop = -1;
+
+ String get_maxID = "select MAX(id) from " + deviceId + "_all";
+
+ ResultSet maxID = stmt.executeQuery(get_maxID);
+
+ if (maxID.next()) {
+ start = maxID.getInt(1);
+ }
+
+ // странное решение с созданием отдельной таблицы для каждого route
+ //fixed
+
+ if (route != null) {
+ for (Coordinate coordinate : route) {
+ String sql = "INSERT INTO "
+ + deviceId
+ + "_all"
+ + " (lat, lng) VALUES ("
+ + String.valueOf(coordinate.getLat()) + ", "
+ + String.valueOf(coordinate.getLng()) + ")";
+ stmt.executeUpdate(sql);
+ }
+ }
+
+ maxID = stmt.executeQuery(get_maxID);
+
+ if (maxID.next()) {
+ stop = maxID.getInt(1);
+ }
+
+ String sql = "INSERT INTO "
+ + deviceId
+ + "_routes"
+ + " (name, start, stop) VALUES ('"
+ + name + "', "
+ + start + ", "
+ + stop + ")";
+ stmt.executeUpdate(sql);
+ } catch (Exception e) {
+ Log.w(TAG, e.getMessage());
+ } finally {
+ try {
+ if (stmt != null)
+ stmt.close();
+ } catch (SQLException ignored) {
+ }
+ try {
+ if (con != null)
+ con.close();
+ } catch (SQLException e) {
+ Log.w(TAG, e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Checks whether id is correct or not.
+ *
+ * @param deviceId input id
+ * @return true if id exists, false otherwise
+ */
+ // возможно стоит переименовать, например в isExistingDeviceId или isValidDeviceId
+ // fixed
+ public static boolean isValidDeviceId(String deviceId) {
+ Connection con = null;
+ PreparedStatement preparedStatement;
+ ResultSet rs;
+ Statement stmt = null;
+
+ int answer = 0;
+
+ try {
+ Class.forName(driver);
+
+ con = DriverManager.getConnection(url, user, password);
+
+ stmt = con.createStatement();
+
+ String sql = "select count(*) from ids where id = ?;";
+
+ preparedStatement = con.prepareStatement(sql);
+ preparedStatement.setString(1, deviceId);
+ rs = preparedStatement.executeQuery();
+
+ if (rs.next()) {
+ answer = rs.getInt("count(*)");
+ }
+
+ } catch (Exception e) {
+ Log.w(TAG, e.getMessage());
+ } finally {
+ try {
+ if (stmt != null)
+ con.close();
+ } catch (SQLException ignored) {
+ }
+ try {
+ if (con != null)
+ con.close();
+ } catch (SQLException e) {
+ Log.w(TAG, e.getMessage());
+ }
+ }
+
+ return answer == 1;
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Displacement.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Displacement.java
new file mode 100644
index 0000000..9025a9c
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Displacement.java
@@ -0,0 +1,29 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+/**
+ * Start and stop of a route with name.
+ */
+public class Displacement {
+
+ public int getStart() {
+ return start;
+ }
+
+ public int getStop() {
+ return stop;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ private int start;
+ private int stop;
+ private String name;
+
+ Displacement(int start, int stop, String name) {
+ this.start = start;
+ this.stop = stop;
+ this.name = name;
+ }
+}
\ No newline at end of file
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/DrawerActivity.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/DrawerActivity.java
new file mode 100644
index 0000000..126c2f5
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/DrawerActivity.java
@@ -0,0 +1,87 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+/**
+ * Basic DrawerActivity with navigation.
+ */
+// @SuppressLint("Registered")
+// Все же, видимо, правильнее сделать так как предлагает IDE (в сплывающем сообщении можно нажать "more" и получить больше информации)
+public abstract class DrawerActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
+
+ @Override
+ @SuppressLint("InflateParams")
+ public void setContentView(final int layoutResID) {
+ DrawerLayout drawerLayout = (DrawerLayout) getLayoutInflater().inflate(R.layout.activity_main, null);
+ FrameLayout contentFrame = drawerLayout.findViewById(R.id.content_frame);
+ getLayoutInflater().inflate(layoutResID, contentFrame, true);
+ super.setContentView(drawerLayout);
+
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ NavigationView navigationView = findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+ ImageView settings = findViewById(R.id.settings);
+ settings.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(DrawerActivity.this, SettingsActivity.class);
+ startActivity(intent);
+ }
+ });
+ }
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ Intent exitApp = new Intent(Intent.ACTION_MAIN);
+ exitApp.addCategory(Intent.CATEGORY_HOME);
+ exitApp.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(exitApp);
+ }
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ int id = item.getItemId();
+ Class classToStart = TrackerActivity.class;
+
+ if (id == R.id.nav_tracker) {
+ classToStart = TrackerActivity.class;
+ } else if (id == R.id.nav_alarm) {
+ classToStart = AlarmActivity.class;
+ } else if (id == R.id.nav_history) {
+ classToStart = HistoryActivity.class;
+ }
+
+ Intent intent = new Intent(DrawerActivity.this, classToStart);
+ startActivity(intent);
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/HistoryActivity.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/HistoryActivity.java
new file mode 100644
index 0000000..c85b9c9
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/HistoryActivity.java
@@ -0,0 +1,152 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Activity with saved routes.
+ */
+public class HistoryActivity extends DrawerActivity {
+
+ private static final String TAG = "HistoryActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.content_history);
+
+ final ListView historyList = findViewById(R.id.history_list);
+ final List displacements;
+ final ArrayList routesNames = new ArrayList<>();
+
+ try {
+ displacements = new FetchDisplacements().execute().get();
+ for (Displacement displacement : displacements) {
+ routesNames.add(displacement.getName());
+ }
+
+ final ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, routesNames);
+ historyList.setAdapter(adapter);
+ historyList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ Route route = null;
+ try {
+ route = new FetchRoutes().execute(displacements.get(position)).get();
+ } catch (InterruptedException e) {
+ Log.w(TAG, e.getMessage());
+ } catch (ExecutionException e) {
+ Log.w(TAG, e.getMessage());
+ }
+ ArrayList coordinates = new ArrayList<>();
+ coordinates.addAll(route.getRoute());
+ System.err.println("ZHIII +" + coordinates.size());
+
+ Intent intent = new Intent(HistoryActivity.this, RouteActivity.class);
+ intent.putExtra(getString(R.string.extra_route_name), routesNames.get(position));
+ intent.putParcelableArrayListExtra(getString(R.string.extra_coordinates), coordinates);
+ startActivity(intent);
+ }
+ });
+
+ historyList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView> adapterView, View view, final int i, long l) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(HistoryActivity.this);
+
+ builder.setPositiveButton(getString(R.string.title_button_delete), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ String deleted = routesNames.remove(i);
+ adapter.notifyDataSetChanged();
+
+ if (routesNames.size() == 0) {
+ TextView emptyList = findViewById(R.id.empty_list);
+ emptyList.setVisibility(View.VISIBLE);
+ }
+
+ new DeleteRoute().execute(deleted);
+ }
+ });
+
+ builder.setNegativeButton(getString(R.string.title_button_cancel), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+
+ }
+ });
+
+ builder.setTitle(getString(R.string.title_dialog_delete));
+ builder.create().show();
+
+ return true;
+ }
+ });
+
+ if (routesNames.size() == 0) {
+ TextView emptyList = findViewById(R.id.empty_list);
+ emptyList.setVisibility(View.VISIBLE);
+ }
+ } catch (InterruptedException e) {
+ Log.w(TAG, e.getMessage());
+ } catch (ExecutionException e) {
+ Log.w(TAG, e.getMessage());
+ }
+
+ }
+
+ /**
+ * Fetches all routes from the database in separate thread.
+ */
+ private static class FetchDisplacements extends AsyncTask> {
+
+ @Override
+ protected List doInBackground(Void... voids) {
+
+ return Controller.fetchDisplacements();
+ }
+ }
+
+ /**
+ * Fetches all coordinates for given displacement in separate thread.
+ */
+ private static class FetchRoutes extends AsyncTask {
+
+ @Override
+ protected Route doInBackground(Displacement... displacements) {
+ for (Displacement displacement : displacements) {
+ return new Route(Controller.fetchCoordinates(displacement.getStart(), displacement.getStop()), displacement.getName());
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Fetches all coordinates for given displacement in separate thread.
+ */
+ private static class DeleteRoute extends AsyncTask {
+
+ @Override
+ protected Void doInBackground(String... strings) {
+ for (String string : strings) {
+ Controller.deleteDisplacement(string);
+ return null;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/LoginActivity.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/LoginActivity.java
new file mode 100644
index 0000000..ba9dd92
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/LoginActivity.java
@@ -0,0 +1,120 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Activity to log in.
+ */
+public class LoginActivity extends AppCompatActivity {
+ private static final String TAG = "LoginActivity";
+ private EditText deviceId;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ if (Controller.userLoggedIn(LoginActivity.this)) {
+ DBManager.setDeviceId(Controller.getUserID(LoginActivity.this).replaceAll("\\s+", ""));
+ loginSuccess();
+ }
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+
+ deviceId = findViewById(R.id.device_id);
+ deviceId.addTextChangedListener(new FourLetterFormatWatcher());
+
+ Button login = findViewById(R.id.login_button);
+ login.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ String sDeviceId = deviceId.getText().toString();
+ try {
+ if (new CheckID().execute(sDeviceId.replaceAll("\\s+","")).get()){
+ Controller.saveUserDeviceId(LoginActivity.this, sDeviceId);
+ loginSuccess();
+ } else {
+ Toast.makeText(LoginActivity.this, getString(R.string.toast_login), Toast.LENGTH_SHORT).show();
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ Log.w(TAG, e.getMessage());
+ }
+ }
+ });
+ }
+
+ private void loginSuccess() {
+ Intent intent = new Intent(LoginActivity.this, TrackerActivity.class);
+ startActivity(intent);
+ }
+
+ /**
+ * Changes input id so that it corresponds following format:
+ * XXXX XXXX XXXX ...
+ */
+ private class FourLetterFormatWatcher implements TextWatcher {
+ private static final char SPACE = ' ';
+ private boolean lock;
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (lock) {
+ return;
+ }
+
+ lock = true;
+
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) == SPACE) {
+ s.delete(i, i + 1);
+ }
+ }
+
+ for (int i = 0; i < s.length(); i++) {
+ if (Character.isLowerCase(s.charAt(i))) {
+ s.replace(i, i + 1, String.valueOf(Character.toUpperCase(s.charAt(i))));
+ }
+ }
+
+ for (int i = 4; i < s.length(); i += 5) {
+ s.insert(i, String.valueOf(SPACE));
+ }
+
+ lock = false;
+ }
+ }
+
+ /**
+ * Checks if given ID is in database in separate thread.
+ */
+ private static class CheckID extends AsyncTask {
+
+ @Override
+ protected Boolean doInBackground(String... strings) {
+
+ for (String id : strings) {
+ return Controller.checkDeviceId(id);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Route.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Route.java
new file mode 100644
index 0000000..7fee3be
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/Route.java
@@ -0,0 +1,24 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import java.util.List;
+
+/**
+ * A route with name.
+ */
+public class Route {
+ public List getRoute() {
+ return route;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ private List route;
+ private String name;
+
+ Route(List route, String name) {
+ this.route = route;
+ this.name = name;
+ }
+}
\ No newline at end of file
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/RouteActivity.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/RouteActivity.java
new file mode 100644
index 0000000..e4997c9
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/RouteActivity.java
@@ -0,0 +1,194 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.Manifest;
+import android.animation.ObjectAnimator;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.getbase.floatingactionbutton.FloatingActionButton;
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.SupportMapFragment;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.PolylineOptions;
+import com.google.maps.android.SphericalUtil;
+
+import java.util.ArrayList;
+import static java.lang.Thread.sleep;
+
+/**
+ * Activity to display saved route on the map.
+ */
+public class RouteActivity extends DrawerActivity implements OnMapReadyCallback {
+ private static final String TAG = "RouteActivity";
+ private static final int ZOOM = 15;
+ private static final int WRITE_EXTERNAL_STORAGE_REQUEST_CODE = 1;
+
+ private GoogleMap map;
+ private String routeName;
+ private final ArrayList route = new ArrayList<>();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.content_route);
+
+ SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.map);
+ mapFragment.getMapAsync(this);
+
+ Intent intent = getIntent();
+ routeName = intent.getStringExtra(getString(R.string.extra_route_name));
+ ArrayList coordinates = intent.getParcelableArrayListExtra(getString(R.string.extra_coordinates));
+
+ for (Coordinate coordinate : coordinates) {
+ route.add(new LatLng(coordinate.getLat(), coordinate.getLng()));
+ }
+
+ setUpUIElements();
+ }
+
+ // не должно ли это поведение общим для всех активити?
+ //
+ // Для основных трех активити (к которым есть доступ через меню слева в NavigationView)
+ // в этом месте хочется выходить из приложения (как и написано в соответствующем методе
+ // в DrawerActivity). Пользователь может какое-то количество раз между ними переключаться
+ // из меню, но по смыслу они не являются предками друг друга в плане иерархии активити.
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ finish();
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+ switch (requestCode) {
+ case WRITE_EXTERNAL_STORAGE_REQUEST_CODE: {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ Controller.sendSnapshot(this, map, routeName);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onMapReady(GoogleMap googleMap) {
+ map = googleMap;
+ drawRoute();
+ }
+
+ private void setUpUIElements() {
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ ImageView share = new ImageView(this);
+ share.setImageResource(R.drawable.ic_share_white_24dp);
+ Toolbar.LayoutParams params = new Toolbar.LayoutParams(Toolbar.LayoutParams.WRAP_CONTENT, Toolbar.LayoutParams.WRAP_CONTENT);
+ params.gravity = Gravity.END;
+ share.setLayoutParams(params);
+ toolbar.addView(share);
+
+ share.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (ContextCompat.checkSelfPermission(RouteActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ ActivityCompat.requestPermissions(RouteActivity.this,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ WRITE_EXTERNAL_STORAGE_REQUEST_CODE);
+ } else {
+ Controller.sendSnapshot(RouteActivity.this, map, routeName);
+ }
+ }
+ });
+
+ final RelativeLayout infoLayout = findViewById(R.id.route_info);
+ infoLayout.post(new Runnable() {
+ @Override
+ public void run() {
+ RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) infoLayout.getLayoutParams();
+ params.setMargins(0, 0, 0, -1 * infoLayout.getHeight());
+ infoLayout.setLayoutParams(params); }
+ });
+
+ final FloatingActionButton find = findViewById(R.id.route_info_button);
+ find.setIcon(R.drawable.ic_info_outline_white_24dp);
+ find.setColorNormalResId(R.color.primaryColor);
+ find.setColorPressedResId(R.color.primaryLightColor);
+ find.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ ObjectAnimator animX = ObjectAnimator.ofFloat(find, View.TRANSLATION_X, 0, find.getWidth());
+ animX.setDuration(700);
+ animX.start();
+
+ try {
+ sleep(500);
+ } catch (InterruptedException e) {
+ Log.w(TAG, e.getMessage());
+ }
+
+ ObjectAnimator animY = ObjectAnimator.ofFloat(infoLayout, View.TRANSLATION_Y, 0, -1 * infoLayout.getHeight());
+ animY.setDuration(1000);
+ animY.start();
+ }
+ });
+
+ ImageView hide = findViewById(R.id.hide_info);
+ hide.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ ObjectAnimator animY = ObjectAnimator.ofFloat(infoLayout, View.TRANSLATION_Y, -1 * infoLayout.getHeight(), 0);
+ animY.setDuration(700);
+ animY.start();
+
+ try {
+ sleep(500);
+ } catch (InterruptedException e) {
+ Log.w(TAG, e.getMessage());
+ }
+
+ ObjectAnimator animX = ObjectAnimator.ofFloat(find, View.TRANSLATION_X, find.getWidth(), 0);
+ animX.setDuration(1000);
+ animX.start();
+ }
+ });
+
+ double length = SphericalUtil.computeLength(route);
+ TextView routeLength = findViewById(R.id.route_length);
+ routeLength.setText(getString(R.string.route_length_format, length));
+ }
+
+ private void drawRoute() {
+ PolylineOptions polylineOptions = new PolylineOptions().geodesic(true);
+
+ for (LatLng position : route) {
+ polylineOptions.add(position);
+ }
+
+ map.addPolyline(polylineOptions);
+
+ if (route.size() != 0) {
+ LatLng position = route.get(route.size() - 1);
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(position, ZOOM));
+ }
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/SettingsActivity.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/SettingsActivity.java
new file mode 100644
index 0000000..39de059
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/SettingsActivity.java
@@ -0,0 +1,170 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.support.v7.app.ActionBar;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+import android.support.v7.app.AlertDialog;
+import android.view.MenuItem;
+
+import java.util.List;
+
+public class SettingsActivity extends AppCompatPreferenceActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setupActionBar();
+ }
+
+ private void setupActionBar() {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+
+ if (id == android.R.id.home) {
+ finish();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ private static final Preference.OnPreferenceChangeListener onPreferenceChangeListener = new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ preference.setSummary(value.toString());
+
+ SharedPreferences sharedPreferences = preference.getContext().getSharedPreferences(Controller.PREF_FILE, MODE_PRIVATE);
+ SharedPreferences.Editor e = sharedPreferences.edit();
+ e.putString(preference.getKey(), value.toString());
+ e.apply();
+
+ return true;
+ }
+ };
+
+ private static boolean isXLargeTablet(Context context) {
+ return (context.getResources().getConfiguration().screenLayout
+ & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ }
+
+ private static void setBooleanPreferenceChangeListener(Preference preference) {
+ preference.setOnPreferenceChangeListener(onPreferenceChangeListener);
+
+ onPreferenceChangeListener.onPreferenceChange(preference,
+ PreferenceManager
+ .getDefaultSharedPreferences(preference.getContext())
+ .getBoolean(preference.getKey(), true));
+ }
+
+ @Override
+ public boolean onIsMultiPane() {
+ return isXLargeTablet(this);
+ }
+
+ @Override
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void onBuildHeaders(List target) {
+ loadHeadersFromResource(R.xml.pref_headers, target);
+ }
+
+ /**
+ * This method stops fragment injection in malicious applications.
+ * Make sure to deny any unknown fragments here.
+ */
+ protected boolean isValidFragment(String fragmentName) {
+ return PreferenceFragment.class.getName().equals(fragmentName)
+ || UserIDPreferenceFragment.class.getName().equals(fragmentName)
+ || NotificationPreferenceFragment.class.getName().equals(fragmentName);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public static class NotificationPreferenceFragment extends PreferenceFragment {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.pref_notification);
+ setHasOptionsMenu(true);
+
+ setBooleanPreferenceChangeListener(findPreference(getString(R.string.preference_notifications_new_message)));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+ if (id == android.R.id.home) {
+ startActivity(new Intent(getActivity(), SettingsActivity.class));
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public static class UserIDPreferenceFragment extends PreferenceFragment {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.pref_user_id);
+ setHasOptionsMenu(true);
+
+ SharedPreferences sharedPreferences = getActivity().getSharedPreferences(Controller.PREF_FILE, MODE_PRIVATE);
+ findPreference(getString(R.string.preference_user_id)).setSummary(sharedPreferences.getString(getString(R.string.preference_user_id), ""));
+
+ Preference quit = findPreference(getString(R.string.preference_quit));
+ quit.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ builder.setPositiveButton(R.string.title_button_change, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ SharedPreferences sharedPreferences = getActivity().getSharedPreferences(Controller.PREF_FILE, MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.remove(getString(R.string.preference_user_id));
+ editor.apply();
+
+ startActivity(new Intent(getActivity(), LoginActivity.class));
+ }
+ });
+
+ builder.setNegativeButton(R.string.title_button_cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+
+ }
+ });
+
+ builder.setTitle(R.string.title_dialog_change);
+ builder.create().show();
+
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+ if (id == android.R.id.home) {
+ startActivity(new Intent(getActivity(), SettingsActivity.class));
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/TrackerActivity.java b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/TrackerActivity.java
new file mode 100644
index 0000000..e8621bf
--- /dev/null
+++ b/GPSTracker/app/src/main/java/ru/spbau/farutin_solikov/gpstracker/TrackerActivity.java
@@ -0,0 +1,249 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+
+import com.getbase.floatingactionbutton.FloatingActionButton;
+import com.getbase.floatingactionbutton.FloatingActionsMenu;
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.SupportMapFragment;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.MarkerOptions;
+import com.google.android.gms.maps.model.PolylineOptions;
+
+import java.util.ArrayList;
+
+/**
+ * Activity to track current position and display route on the map.
+ */
+public class TrackerActivity extends DrawerActivity implements OnMapReadyCallback {
+ private static final int ZOOM = 20;
+
+ private Controller.TrackerCoordinatesReceiver receiver;
+ private GoogleMap map;
+ private LatLng lastPosition;
+ private Coordinate startPosition;
+ private boolean startImmediately = false;
+ private static ArrayList route;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.content_tracker);
+
+ SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.map);
+ mapFragment.getMapAsync(this);
+
+ createMenu();
+
+ Intent intent = getIntent();
+ if (intent.hasExtra(getString(R.string.extra_position))) {
+ startPosition = intent.getParcelableExtra(getString(R.string.extra_position));
+ startImmediately = true;
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ try {
+ unregisterReceiver(receiver);
+ } catch (IllegalArgumentException ignored) {
+ // no API methods to tell if it is registered at the moment
+ }
+ }
+
+ @Override
+ public void onMapReady(GoogleMap googleMap) {
+ map = googleMap;
+
+ if (startImmediately) {
+ startTracking(startPosition);
+ }
+ }
+
+ /**
+ * Draws route on Google Map.
+ *
+ * @param coordinates route to draw
+ */
+ public void drawRoute(ArrayList coordinates) {
+ PolylineOptions polylineOptions = new PolylineOptions().geodesic(true);
+
+ if (lastPosition == null) {
+ Coordinate position = coordinates.get(0);
+ lastPosition = new LatLng(position.getLat(), position.getLng());
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(lastPosition, ZOOM));
+ route.add(position);
+ }
+
+ polylineOptions.add(lastPosition);
+ for (Coordinate position : coordinates) {
+ polylineOptions.add(new LatLng(position.getLat(), position.getLng()));
+ route.add(position);
+ }
+
+ Coordinate position = coordinates.get(coordinates.size() - 1);
+ lastPosition = new LatLng(position.getLat(), position.getLng());
+
+ map.addPolyline(polylineOptions);
+ }
+
+ private void createMenu() {
+ final FloatingActionsMenu menu = findViewById(R.id.menu_tracker);
+
+ FloatingActionButton start = new FloatingActionButton(this);
+ start.setIcon(R.drawable.ic_play_arrow_white_24dp);
+ start.setColorNormalResId(R.color.primaryColor);
+ start.setColorPressedResId(R.color.primaryLightColor);
+ start.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ menu.collapse();
+
+ if (map != null) {
+ startTracking(null);
+ }
+ }
+ });
+
+ FloatingActionButton stop = new FloatingActionButton(this);
+ stop.setIcon(R.drawable.ic_stop_white_24dp);
+ stop.setColorNormalResId(R.color.primaryColor);
+ stop.setColorPressedResId(R.color.primaryLightColor);
+ stop.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ menu.collapse();
+ Controller.stopCoordinatesService();
+
+ try {
+ unregisterReceiver(receiver);
+ } catch (IllegalArgumentException ignored) {
+ // no API methods to tell if it is registered at the moment
+ }
+ }
+ });
+
+ FloatingActionButton save = new FloatingActionButton(this);
+ save.setIcon(R.drawable.ic_save_white_24dp);
+ save.setColorNormalResId(R.color.primaryColor);
+ save.setColorPressedResId(R.color.primaryLightColor);
+ save.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ menu.collapse();
+ saveRoute();
+ }
+ });
+
+ menu.addButton(start);
+ menu.addButton(stop);
+ menu.addButton(save);
+
+ FloatingActionButton find = findViewById(R.id.find_tracker);
+ find.setIcon(R.drawable.ic_my_location_white_24dp);
+ find.setColorNormalResId(R.color.primaryColor);
+ find.setColorPressedResId(R.color.primaryLightColor);
+ find.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (lastPosition != null) {
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(lastPosition, ZOOM));
+ }
+ }
+ });
+ }
+
+ private void startTracking(Coordinate startPosition) {
+ map.clear();
+ route = new ArrayList<>();
+
+ if (startPosition == null) {
+ lastPosition = null;
+ } else {
+ // кажется запись в lastPosition можно заменить на запись в локальную переменную
+ //
+ // Лишней была строчка в конце этого блока.
+ lastPosition = new LatLng(startPosition.getLat(), startPosition.getLng());
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(lastPosition, ZOOM));
+ map.addMarker(new MarkerOptions().position(lastPosition));
+ route.add(startPosition);
+
+ //lastPosition = null;
+ }
+
+ Controller.startCoordinatesService(TrackerActivity.this);
+
+ receiver = new Controller.TrackerCoordinatesReceiver(this);
+ IntentFilter intentSFilter = new IntentFilter(getString(R.string.broadcast_content_coordinates));
+ registerReceiver(receiver, intentSFilter);
+ }
+
+ private void saveRoute() {
+ DialogFragment saveDialogFragment = new SaveDialogFragment();
+ saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.title_dialog_save));
+ }
+
+ public static class SaveDialogFragment extends DialogFragment {
+ @NonNull
+ @Override
+ @SuppressLint("InflateParams")
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ final LayoutInflater inflater = getActivity().getLayoutInflater();
+ final View view = inflater.inflate(R.layout.dialog_save, null);
+
+ builder.setView(view)
+ .setTitle(R.string.title_dialog_save)
+ .setPositiveButton(R.string.title_button_save, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ EditText nameInput = view.findViewById(R.id.route_name);
+ String name = nameInput.getText().toString();
+
+ if (name.length() != 0) {
+ new SendRoute().execute(new Route(route, name));
+// Controller.sendCoordinates(route, name);
+ }
+ }
+ })
+ .setNegativeButton(R.string.title_button_cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ SaveDialogFragment.this.getDialog().cancel();
+ }
+ });
+
+ return builder.create();
+ }
+ }
+
+ /**
+ * Sends given route to the database in separate thread.
+ */
+ private static class SendRoute extends AsyncTask {
+ @Override
+ protected Void doInBackground(Route... routes) {
+ for (Route route : routes) {
+ Controller.sendCoordinates(route.getRoute(), route.getName());
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_info_outline_white_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_info_outline_white_24dp.png
new file mode 100644
index 0000000..c7b1113
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_info_outline_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_down_white_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_down_white_24dp.png
new file mode 100644
index 0000000..bbb4fb4
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_down_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_map_black_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_map_black_24dp.png
new file mode 100644
index 0000000..c81d0e0
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_map_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_more_vert_white_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_more_vert_white_24dp.png
new file mode 100644
index 0000000..67f07e4
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_more_vert_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_my_location_white_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_my_location_white_24dp.png
new file mode 100644
index 0000000..745db48
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_my_location_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_near_me_black_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_near_me_black_24dp.png
new file mode 100644
index 0000000..de879d3
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_near_me_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_notifications_black_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_notifications_black_24dp.png
new file mode 100644
index 0000000..12969c5
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_notifications_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_perm_identity_black_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_perm_identity_black_24dp.png
new file mode 100644
index 0000000..dc78670
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_perm_identity_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..57c9fa5
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_save_white_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_save_white_24dp.png
new file mode 100644
index 0000000..dd3f106
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_save_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..b09a692
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-hdpi/ic_stop_white_24dp.png b/GPSTracker/app/src/main/res/drawable-hdpi/ic_stop_white_24dp.png
new file mode 100644
index 0000000..dfff26c
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-hdpi/ic_stop_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_info_outline_white_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_info_outline_white_24dp.png
new file mode 100644
index 0000000..353e064
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_info_outline_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_keyboard_arrow_down_white_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_keyboard_arrow_down_white_24dp.png
new file mode 100644
index 0000000..ef8a4b6
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_keyboard_arrow_down_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_map_black_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_map_black_24dp.png
new file mode 100644
index 0000000..bcc500e
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_map_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_more_vert_white_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_more_vert_white_24dp.png
new file mode 100644
index 0000000..017e45e
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_more_vert_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_my_location_white_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_my_location_white_24dp.png
new file mode 100644
index 0000000..d1c563c
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_my_location_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_near_me_black_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_near_me_black_24dp.png
new file mode 100644
index 0000000..523222e
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_near_me_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_notifications_black_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_notifications_black_24dp.png
new file mode 100644
index 0000000..32562b0
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_notifications_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_perm_identity_black_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_perm_identity_black_24dp.png
new file mode 100644
index 0000000..3c019ca
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_perm_identity_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..c61e948
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_save_white_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_save_white_24dp.png
new file mode 100644
index 0000000..015062e
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_save_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..e944fd7
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-mdpi/ic_stop_white_24dp.png b/GPSTracker/app/src/main/res/drawable-mdpi/ic_stop_white_24dp.png
new file mode 100644
index 0000000..b84d2f6
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-mdpi/ic_stop_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_info_outline_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_info_outline_white_24dp.png
new file mode 100644
index 0000000..c571b2e
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_info_outline_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_keyboard_arrow_down_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_keyboard_arrow_down_white_24dp.png
new file mode 100644
index 0000000..058cebb
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_keyboard_arrow_down_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_map_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_map_black_24dp.png
new file mode 100644
index 0000000..a3ecc71
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_map_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_more_vert_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_more_vert_white_24dp.png
new file mode 100644
index 0000000..efab8a7
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_more_vert_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_my_location_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_my_location_white_24dp.png
new file mode 100644
index 0000000..ffab865
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_my_location_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_near_me_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_near_me_black_24dp.png
new file mode 100644
index 0000000..df62429
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_near_me_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_notifications_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_notifications_black_24dp.png
new file mode 100644
index 0000000..98cbec6
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_notifications_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_perm_identity_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_perm_identity_black_24dp.png
new file mode 100644
index 0000000..7a95c11
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_perm_identity_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..a3c80e7
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_save_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_save_white_24dp.png
new file mode 100644
index 0000000..adda095
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_save_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..22a8783
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xhdpi/ic_stop_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_stop_white_24dp.png
new file mode 100644
index 0000000..3ad2c9c
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xhdpi/ic_stop_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_info_outline_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_info_outline_white_24dp.png
new file mode 100644
index 0000000..c41a5fc
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_info_outline_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_down_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_down_white_24dp.png
new file mode 100644
index 0000000..f9622b7
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_down_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_map_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_map_black_24dp.png
new file mode 100644
index 0000000..827c9fc
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_map_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_more_vert_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_more_vert_white_24dp.png
new file mode 100644
index 0000000..d322813
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_more_vert_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_my_location_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_my_location_white_24dp.png
new file mode 100644
index 0000000..387ecdf
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_my_location_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_near_me_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_near_me_black_24dp.png
new file mode 100644
index 0000000..d5c3702
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_near_me_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_notifications_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_notifications_black_24dp.png
new file mode 100644
index 0000000..74c46cf
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_notifications_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_perm_identity_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_perm_identity_black_24dp.png
new file mode 100644
index 0000000..d54de22
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_perm_identity_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..547ef30
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_save_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_save_white_24dp.png
new file mode 100644
index 0000000..3e0ce1a
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_save_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..a35b3cd
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_stop_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_stop_white_24dp.png
new file mode 100644
index 0000000..801d341
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxhdpi/ic_stop_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_info_outline_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_info_outline_white_24dp.png
new file mode 100644
index 0000000..3a82cab
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_info_outline_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_down_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_down_white_24dp.png
new file mode 100644
index 0000000..30948d9
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_down_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_map_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_map_black_24dp.png
new file mode 100644
index 0000000..da75c65
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_map_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png
new file mode 100644
index 0000000..2f2cb3d
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_my_location_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_my_location_white_24dp.png
new file mode 100644
index 0000000..c55220a
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_my_location_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_near_me_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_near_me_black_24dp.png
new file mode 100644
index 0000000..1e70e21
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_near_me_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_notifications_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_notifications_black_24dp.png
new file mode 100644
index 0000000..b06e6bc
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_notifications_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_perm_identity_black_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_perm_identity_black_24dp.png
new file mode 100644
index 0000000..46ce7ef
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_perm_identity_black_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..be5c062
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_save_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_save_white_24dp.png
new file mode 100644
index 0000000..bd80bf1
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_save_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..e351c7b
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_stop_white_24dp.png b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_stop_white_24dp.png
new file mode 100644
index 0000000..5239336
Binary files /dev/null and b/GPSTracker/app/src/main/res/drawable-xxxhdpi/ic_stop_white_24dp.png differ
diff --git a/GPSTracker/app/src/main/res/layout/activity_login.xml b/GPSTracker/app/src/main/res/layout/activity_login.xml
new file mode 100644
index 0000000..4bf520a
--- /dev/null
+++ b/GPSTracker/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GPSTracker/app/src/main/res/layout/activity_main.xml b/GPSTracker/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..2650753
--- /dev/null
+++ b/GPSTracker/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/GPSTracker/app/src/main/res/layout/app_bar_main.xml b/GPSTracker/app/src/main/res/layout/app_bar_main.xml
new file mode 100644
index 0000000..c98e34d
--- /dev/null
+++ b/GPSTracker/app/src/main/res/layout/app_bar_main.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GPSTracker/app/src/main/res/layout/content_alarm.xml b/GPSTracker/app/src/main/res/layout/content_alarm.xml
new file mode 100644
index 0000000..4d07a48
--- /dev/null
+++ b/GPSTracker/app/src/main/res/layout/content_alarm.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
diff --git a/GPSTracker/app/src/main/res/layout/content_history.xml b/GPSTracker/app/src/main/res/layout/content_history.xml
new file mode 100644
index 0000000..6db7485
--- /dev/null
+++ b/GPSTracker/app/src/main/res/layout/content_history.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GPSTracker/app/src/main/res/layout/content_route.xml b/GPSTracker/app/src/main/res/layout/content_route.xml
new file mode 100644
index 0000000..6bc8ba5
--- /dev/null
+++ b/GPSTracker/app/src/main/res/layout/content_route.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GPSTracker/app/src/main/res/layout/content_tracker.xml b/GPSTracker/app/src/main/res/layout/content_tracker.xml
new file mode 100644
index 0000000..e673152
--- /dev/null
+++ b/GPSTracker/app/src/main/res/layout/content_tracker.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
diff --git a/GPSTracker/app/src/main/res/layout/dialog_save.xml b/GPSTracker/app/src/main/res/layout/dialog_save.xml
new file mode 100644
index 0000000..106cda1
--- /dev/null
+++ b/GPSTracker/app/src/main/res/layout/dialog_save.xml
@@ -0,0 +1,15 @@
+
+
+
\ No newline at end of file
diff --git a/GPSTracker/app/src/main/res/layout/nav_header_main.xml b/GPSTracker/app/src/main/res/layout/nav_header_main.xml
new file mode 100644
index 0000000..4aae60b
--- /dev/null
+++ b/GPSTracker/app/src/main/res/layout/nav_header_main.xml
@@ -0,0 +1,17 @@
+
+
+
+
diff --git a/GPSTracker/app/src/main/res/menu/activity_main_drawer.xml b/GPSTracker/app/src/main/res/menu/activity_main_drawer.xml
new file mode 100644
index 0000000..5a24907
--- /dev/null
+++ b/GPSTracker/app/src/main/res/menu/activity_main_drawer.xml
@@ -0,0 +1,18 @@
+
+
diff --git a/GPSTracker/app/src/main/res/mipmap-hdpi/ic_launcher.png b/GPSTracker/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..91f88de
Binary files /dev/null and b/GPSTracker/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/GPSTracker/app/src/main/res/mipmap-mdpi/ic_launcher.png b/GPSTracker/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..99858e2
Binary files /dev/null and b/GPSTracker/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/GPSTracker/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/GPSTracker/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..5a7becb
Binary files /dev/null and b/GPSTracker/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/GPSTracker/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/GPSTracker/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..fd16c8b
Binary files /dev/null and b/GPSTracker/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/GPSTracker/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/GPSTracker/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15bf49f
Binary files /dev/null and b/GPSTracker/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/GPSTracker/app/src/main/res/values/colors.xml b/GPSTracker/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..a5f70d4
--- /dev/null
+++ b/GPSTracker/app/src/main/res/values/colors.xml
@@ -0,0 +1,15 @@
+
+
+ #009688
+ #52c7b8
+ #00675b
+
+ #00bfa5
+
+
+ #000000
+ #000000
+
+ #F5F5F5
+
diff --git a/GPSTracker/app/src/main/res/values/dimens.xml b/GPSTracker/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..5a42599
--- /dev/null
+++ b/GPSTracker/app/src/main/res/values/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 16dp
+ 16dp
+ 160dp
+
diff --git a/GPSTracker/app/src/main/res/values/strings.xml b/GPSTracker/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..0490526
--- /dev/null
+++ b/GPSTracker/app/src/main/res/values/strings.xml
@@ -0,0 +1,49 @@
+
+ GPSTracker
+
+ Open navigation drawer
+ Close navigation drawer
+
+ Position has changed!
+ Settings
+ Delete this route?
+ Change device ID?
+ Save route
+ Delete
+ Cancel
+ Change
+ Save
+ Login
+ Turn On
+ Turn Off
+ Track position
+ Choose app:
+
+ ru.spbau.farutin_solikov.gpstracker.position
+ ru.spbau.farutin_solikov.gpstracker.route_name
+ ru.spbau.farutin_solikov.gpstracker.coordinates
+
+ ru.spbau.farutin_solikov.gpstracker.BroadcastCoordinatesAction
+ ru.spbau.farutin_solikov.gpstracker.ALARM
+
+ Wrong Device Id
+ Failed making screenshot:
+ Route length: %1$,.2fm
+ Device Id
+ Position has changed!
+ You don\'t have saved routes
+ Route name
+
+ Tracker
+ Alarm
+ History
+
+ notifications_new_message
+ user_id
+ quit
+
+ Notifications
+ User ID
+ Hide
+
+
diff --git a/GPSTracker/app/src/main/res/values/styles.xml b/GPSTracker/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..779896b
--- /dev/null
+++ b/GPSTracker/app/src/main/res/values/styles.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/GPSTracker/app/src/main/res/xml/pref_headers.xml b/GPSTracker/app/src/main/res/xml/pref_headers.xml
new file mode 100644
index 0000000..a7128ef
--- /dev/null
+++ b/GPSTracker/app/src/main/res/xml/pref_headers.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/GPSTracker/app/src/main/res/xml/pref_notification.xml b/GPSTracker/app/src/main/res/xml/pref_notification.xml
new file mode 100644
index 0000000..5779024
--- /dev/null
+++ b/GPSTracker/app/src/main/res/xml/pref_notification.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/GPSTracker/app/src/main/res/xml/pref_user_id.xml b/GPSTracker/app/src/main/res/xml/pref_user_id.xml
new file mode 100644
index 0000000..7a0cc64
--- /dev/null
+++ b/GPSTracker/app/src/main/res/xml/pref_user_id.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/GPSTracker/app/src/test/java/ru/spbau/farutin_solikov/gpstracker/ExampleUnitTest.java b/GPSTracker/app/src/test/java/ru/spbau/farutin_solikov/gpstracker/ExampleUnitTest.java
new file mode 100644
index 0000000..9c65211
--- /dev/null
+++ b/GPSTracker/app/src/test/java/ru/spbau/farutin_solikov/gpstracker/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package ru.spbau.farutin_solikov.gpstracker;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/GPSTracker/build.gradle b/GPSTracker/build.gradle
new file mode 100644
index 0000000..f6da21a
--- /dev/null
+++ b/GPSTracker/build.gradle
@@ -0,0 +1,31 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+
+ apply plugin: 'java'
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.3'
+ sourceCompatibility = 1.7
+ targetCompatibility = 1.7
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ maven {
+ url "https://maven.google.com"
+ }
+ }
+}
+
+/*task clean(type: Delete) {
+ delete rootProject.buildDir
+}*/
diff --git a/GPSTracker/gradle.properties b/GPSTracker/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/GPSTracker/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/GPSTracker/gradle/wrapper/gradle-wrapper.jar b/GPSTracker/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/GPSTracker/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/GPSTracker/gradle/wrapper/gradle-wrapper.properties b/GPSTracker/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..1d67e3f
--- /dev/null
+++ b/GPSTracker/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sat Oct 14 19:03:02 MSK 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/GPSTracker/gradlew b/GPSTracker/gradlew
new file mode 100644
index 0000000..9d82f78
--- /dev/null
+++ b/GPSTracker/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/GPSTracker/gradlew.bat b/GPSTracker/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/GPSTracker/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/GPSTracker/settings.gradle b/GPSTracker/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/GPSTracker/settings.gradle
@@ -0,0 +1 @@
+include ':app'