From d9ff2b50624af56d9fe51e180ee326a5c13bf345 Mon Sep 17 00:00:00 2001 From: CrazyDude1994 Date: Mon, 3 Jun 2019 00:21:48 +0300 Subject: [PATCH 1/3] Added data send to UAV radar server --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 1 + .../com/telemetry/api/AddLogRequest.kt | 12 +++ .../com/telemetry/api/AddLogResponse.kt | 5 ++ .../crazydude/com/telemetry/api/ApiManager.kt | 18 ++++ .../crazydude/com/telemetry/api/ApiService.kt | 14 +++ .../com/telemetry/api/SessionCreateRequest.kt | 8 ++ .../telemetry/api/SessionCreateResponse.kt | 8 ++ .../telemetry/manager/PreferenceManager.kt | 14 ++- .../com/telemetry/service/DataService.kt | 86 +++++++++++++++++-- .../com/telemetry/ui/MapsActivity.kt | 6 ++ .../com/telemetry/ui/PrefsFragment.kt | 22 +++++ app/src/main/res/xml/preferences.xml | 25 ++++++ 13 files changed, 214 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/crazydude/com/telemetry/api/AddLogRequest.kt create mode 100644 app/src/main/java/crazydude/com/telemetry/api/AddLogResponse.kt create mode 100644 app/src/main/java/crazydude/com/telemetry/api/ApiManager.kt create mode 100644 app/src/main/java/crazydude/com/telemetry/api/ApiService.kt create mode 100644 app/src/main/java/crazydude/com/telemetry/api/SessionCreateRequest.kt create mode 100644 app/src/main/java/crazydude/com/telemetry/api/SessionCreateResponse.kt diff --git a/app/build.gradle b/app/build.gradle index 85ad3f20..52c55fa7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,6 +47,8 @@ dependencies { implementation 'androidx.preference:preference:1.0.0' implementation 'com.google.firebase:firebase-core:16.0.9' implementation 'com.jaredrummler:colorpicker:1.1.0' + implementation 'com.squareup.retrofit2:retrofit:2.5.0' + implementation 'com.squareup.retrofit2:converter-gson:2.5.0' implementation 'com.google.maps.android:android-maps-utils:0.5+' releaseImplementation 'com.crashlytics.sdk.android:crashlytics:2.10.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d027686f..ea00bb9c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,7 @@ + + @POST("api/data/add") + fun sendData(@Body addLogRequest: AddLogRequest): Call +} \ No newline at end of file diff --git a/app/src/main/java/crazydude/com/telemetry/api/SessionCreateRequest.kt b/app/src/main/java/crazydude/com/telemetry/api/SessionCreateRequest.kt new file mode 100644 index 00000000..aeab088d --- /dev/null +++ b/app/src/main/java/crazydude/com/telemetry/api/SessionCreateRequest.kt @@ -0,0 +1,8 @@ +package crazydude.com.telemetry.api + +import com.google.gson.annotations.SerializedName + +data class SessionCreateRequest( + @SerializedName("callsign") val callsign: String, + @SerializedName("model") val model: String +) \ No newline at end of file diff --git a/app/src/main/java/crazydude/com/telemetry/api/SessionCreateResponse.kt b/app/src/main/java/crazydude/com/telemetry/api/SessionCreateResponse.kt new file mode 100644 index 00000000..ee82db87 --- /dev/null +++ b/app/src/main/java/crazydude/com/telemetry/api/SessionCreateResponse.kt @@ -0,0 +1,8 @@ +package crazydude.com.telemetry.api + +import com.google.gson.annotations.SerializedName + +data class SessionCreateResponse( + @SerializedName("status") val status: String, + @SerializedName("session_id") val sessionId: String +) \ No newline at end of file diff --git a/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt b/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt index 84d35ad3..03378d82 100644 --- a/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt +++ b/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt @@ -13,7 +13,7 @@ class PreferenceManager(context: Context) { fun isLoggingEnabled(): Boolean { return sharedPreferences.getBoolean("logging_enabled", true) } - + fun isLoggingSet(): Boolean { return sharedPreferences.contains("logging_enabled") } @@ -62,4 +62,16 @@ class PreferenceManager(context: Context) { fun setMapType(mapType: Int) { sharedPreferences.edit().putInt("map_type", mapType).apply() } + + fun isSendDataEnabled(): Boolean { + return sharedPreferences.getBoolean("send_telemetry_data", false) + } + + fun getModel(): String { + return sharedPreferences.getString("model", "") ?: "" + } + + fun getCallsign(): String { + return sharedPreferences.getString("callsign", "") ?: "" + } } \ No newline at end of file diff --git a/app/src/main/java/crazydude/com/telemetry/service/DataService.kt b/app/src/main/java/crazydude/com/telemetry/service/DataService.kt index ba9d8a0e..66d48edd 100644 --- a/app/src/main/java/crazydude/com/telemetry/service/DataService.kt +++ b/app/src/main/java/crazydude/com/telemetry/service/DataService.kt @@ -1,24 +1,30 @@ package crazydude.com.telemetry.service -import android.annotation.SuppressLint import android.annotation.TargetApi import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.app.Service -import android.bluetooth.* +import android.bluetooth.BluetoothDevice import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.* +import android.widget.Toast import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat -import android.widget.Toast import com.google.android.gms.maps.model.LatLng import crazydude.com.telemetry.R +import crazydude.com.telemetry.api.* import crazydude.com.telemetry.manager.PreferenceManager -import crazydude.com.telemetry.protocol.* +import crazydude.com.telemetry.protocol.BluetoothDataPoller +import crazydude.com.telemetry.protocol.BluetoothLeDataPoller +import crazydude.com.telemetry.protocol.DataDecoder +import crazydude.com.telemetry.protocol.DataPoller import crazydude.com.telemetry.ui.MapsActivity +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response import java.io.File import java.io.FileOutputStream import java.io.IOException @@ -32,7 +38,14 @@ class DataService : Service(), DataDecoder.Listener { private var dataListener: DataDecoder.Listener? = null private val dataBinder = DataBinder() private var hasGPSFix = false + private var isArmed = false private var satellites = 0 + private var lastLatitude: Double = 0.0 + private var lastLongitude: Double = 0.0 + private var lastAltitude: Float = 0.0f + private var lastSpeed: Float = 0.0f + private var lastHeading: Float = 0.0f + private val apiHandler = Handler() private lateinit var preferenceManager: PreferenceManager val points: ArrayList = ArrayList() @@ -98,7 +111,8 @@ class DataService : Service(), DataDecoder.Listener { } if (!isBle) { - val socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")) + val socket = + device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")) dataPoller = BluetoothDataPoller(socket, this, fileOutputStream, csvFileOutputStream) } else { dataPoller = BluetoothLeDataPoller(this, device, this, fileOutputStream, csvFileOutputStream) @@ -152,12 +166,64 @@ class DataService : Service(), DataDecoder.Listener { runOnMainThread(Runnable { dataListener?.onConnected() }) + + if (preferenceManager.isSendDataEnabled()) { + createSession() + } + } + + fun createSession() { + if (!isConnected()) { + return + } + ApiManager.apiService.createSession( + SessionCreateRequest(preferenceManager.getCallsign(), preferenceManager.getModel()) + ).enqueue(object : Callback { + override fun onFailure(call: Call, t: Throwable) { + Handler() + .postDelayed({ createSession() }, 5000) + } + + override fun onResponse( + call: Call, + response: Response + ) { + response.body()?.let { + sendTelemetryData(it.sessionId) + } + } + }) + } + + fun sendTelemetryData(sessionId: String) { + if (!isConnected()) { + return + } + apiHandler.postDelayed({ + if (hasGPSFix && isArmed) { + ApiManager.apiService.sendData( + AddLogRequest(sessionId, lastLatitude, lastLongitude, lastAltitude, lastHeading, lastSpeed) + ).enqueue(object : Callback { + override fun onFailure(call: Call, t: Throwable) { + sendTelemetryData(sessionId) + } + + override fun onResponse(call: Call, response: Response) { + sendTelemetryData(sessionId) + } + }) + } else { + sendTelemetryData(sessionId) + } + }, 5000) } override fun onGPSData(latitude: Double, longitude: Double) { if (hasGPSFix) { points.add(LatLng(latitude, longitude)) } + lastLatitude = latitude + lastLongitude = longitude runOnMainThread(Runnable { dataListener?.onGPSData(latitude, longitude) }) @@ -186,12 +252,16 @@ class DataService : Service(), DataDecoder.Listener { } override fun onHeadingData(heading: Float) { + lastHeading = heading runOnMainThread(Runnable { dataListener?.onHeadingData(heading) }) } override fun onAirSpeed(speed: Float) { + if (preferenceManager.usePitotTube()) { + lastSpeed = speed + } runOnMainThread(Runnable { dataListener?.onAirSpeed(speed) }) @@ -227,6 +297,7 @@ class DataService : Service(), DataDecoder.Listener { } override fun onAltitudeData(altitude: Float) { + lastAltitude = altitude runOnMainThread(Runnable { dataListener?.onAltitudeData(altitude) }) @@ -257,6 +328,9 @@ class DataService : Service(), DataDecoder.Listener { } override fun onGSpeedData(speed: Float) { + if (!preferenceManager.usePitotTube()) { + lastSpeed = speed + } runOnMainThread(Runnable { dataListener?.onGSpeedData(speed) }) } @@ -266,10 +340,10 @@ class DataService : Service(), DataDecoder.Listener { firstFlightMode: DataDecoder.Companion.FlyMode, secondFlightMode: DataDecoder.Companion.FlyMode? ) { + isArmed = armed runOnMainThread(Runnable { dataListener?.onFlyModeData(armed, heading, firstFlightMode, secondFlightMode) }) - } private fun runOnMainThread(runnable: Runnable) { diff --git a/app/src/main/java/crazydude/com/telemetry/ui/MapsActivity.kt b/app/src/main/java/crazydude/com/telemetry/ui/MapsActivity.kt index 8fd8084a..ab610b10 100644 --- a/app/src/main/java/crazydude/com/telemetry/ui/MapsActivity.kt +++ b/app/src/main/java/crazydude/com/telemetry/ui/MapsActivity.kt @@ -32,10 +32,16 @@ import com.google.android.gms.maps.model.* import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.maps.android.SphericalUtil import crazydude.com.telemetry.R +import crazydude.com.telemetry.api.ApiManager +import crazydude.com.telemetry.api.SessionCreateRequest +import crazydude.com.telemetry.api.SessionCreateResponse import crazydude.com.telemetry.manager.PreferenceManager import crazydude.com.telemetry.protocol.DataDecoder import crazydude.com.telemetry.protocol.LogPlayer import crazydude.com.telemetry.service.DataService +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response import java.io.File import kotlin.math.roundToInt diff --git a/app/src/main/java/crazydude/com/telemetry/ui/PrefsFragment.kt b/app/src/main/java/crazydude/com/telemetry/ui/PrefsFragment.kt index a5c2a105..55f13147 100644 --- a/app/src/main/java/crazydude/com/telemetry/ui/PrefsFragment.kt +++ b/app/src/main/java/crazydude/com/telemetry/ui/PrefsFragment.kt @@ -1,13 +1,35 @@ package crazydude.com.telemetry.ui +import android.content.SharedPreferences import android.os.Bundle import androidx.preference.PreferenceFragmentCompat import crazydude.com.telemetry.R +import crazydude.com.telemetry.manager.PreferenceManager class PrefsFragment : PreferenceFragmentCompat() { + private lateinit var prefManager: PreferenceManager + private val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, _ -> + updateSummary() + } + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { preferenceManager.sharedPreferencesName = "settings" setPreferencesFromResource(R.xml.preferences, rootKey) + + prefManager = PreferenceManager(context!!) + + preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(listener) + updateSummary() + } + + override fun onDestroy() { + super.onDestroy() + preferenceManager.sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener) + } + + private fun updateSummary() { + preferenceScreen.findPreference("callsign").summary = prefManager.getCallsign() + preferenceScreen.findPreference("model").summary = prefManager.getModel() } } \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index cf75846b..a75e4dde 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -62,5 +62,30 @@ + + + + + + + + + + + + + \ No newline at end of file From 41fdd9b46b918711dfa8d56d220afbbf54b5d720 Mon Sep 17 00:00:00 2001 From: CrazyDude1994 Date: Mon, 3 Jun 2019 00:22:59 +0300 Subject: [PATCH 2/3] UAV Radar pre release --- app/build.gradle | 4 ++-- .../java/crazydude/com/telemetry/manager/PreferenceManager.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 52c55fa7..77db49dd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -22,8 +22,8 @@ android { applicationId "crazydude.com.telemetry" minSdkVersion 16 targetSdkVersion 28 - versionCode 13 - versionName "1.4" + versionCode 14 + versionName "1.5" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt b/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt index 03378d82..9d6bb4fe 100644 --- a/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt +++ b/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt @@ -13,7 +13,7 @@ class PreferenceManager(context: Context) { fun isLoggingEnabled(): Boolean { return sharedPreferences.getBoolean("logging_enabled", true) } - + fun isLoggingSet(): Boolean { return sharedPreferences.contains("logging_enabled") } From 56b6bec5152650b89e7cb7825927db0c634cb71e Mon Sep 17 00:00:00 2001 From: CrazyDude1994 Date: Wed, 5 Jun 2019 23:14:03 +0300 Subject: [PATCH 3/3] Added dialog. Changed server address --- app/build.gradle | 2 +- .../crazydude/com/telemetry/api/ApiManager.kt | 5 +++- .../telemetry/manager/PreferenceManager.kt | 8 +++++++ .../com/telemetry/ui/MapsActivity.kt | 24 +++++++++++++++++++ app/src/main/res/xml/preferences.xml | 6 ++--- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 77db49dd..a8d731c7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -22,7 +22,7 @@ android { applicationId "crazydude.com.telemetry" minSdkVersion 16 targetSdkVersion 28 - versionCode 14 + versionCode 15 versionName "1.5" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/crazydude/com/telemetry/api/ApiManager.kt b/app/src/main/java/crazydude/com/telemetry/api/ApiManager.kt index 6893e74a..b1cb5765 100644 --- a/app/src/main/java/crazydude/com/telemetry/api/ApiManager.kt +++ b/app/src/main/java/crazydude/com/telemetry/api/ApiManager.kt @@ -8,8 +8,11 @@ import retrofit2.Retrofit class ApiManager { companion object { + + const val API_URL = "https://uavradar.org/" + private val retrofit = Retrofit.Builder() - .baseUrl("http://149.154.71.100/") + .baseUrl(API_URL) .addConverterFactory(GsonConverterFactory.create()) .build() diff --git a/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt b/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt index 9d6bb4fe..f186293a 100644 --- a/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt +++ b/app/src/main/java/crazydude/com/telemetry/manager/PreferenceManager.kt @@ -67,6 +67,10 @@ class PreferenceManager(context: Context) { return sharedPreferences.getBoolean("send_telemetry_data", false) } + fun isSendDataDialogShown(): Boolean { + return sharedPreferences.contains("send_telemetry_data") + } + fun getModel(): String { return sharedPreferences.getString("model", "") ?: "" } @@ -74,4 +78,8 @@ class PreferenceManager(context: Context) { fun getCallsign(): String { return sharedPreferences.getString("callsign", "") ?: "" } + + fun setTelemetrySendingEnabled(enabled: Boolean) { + sharedPreferences.edit().putBoolean("send_telemetry_data", enabled).apply() + } } \ No newline at end of file diff --git a/app/src/main/java/crazydude/com/telemetry/ui/MapsActivity.kt b/app/src/main/java/crazydude/com/telemetry/ui/MapsActivity.kt index ab610b10..9734583d 100644 --- a/app/src/main/java/crazydude/com/telemetry/ui/MapsActivity.kt +++ b/app/src/main/java/crazydude/com/telemetry/ui/MapsActivity.kt @@ -16,6 +16,8 @@ import android.os.Build import android.os.Bundle import android.os.Environment import android.os.IBinder +import android.text.Html +import android.text.method.LinkMovementMethod import android.view.View import android.widget.* import androidx.annotation.DrawableRes @@ -479,9 +481,11 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, DataDecoder.Listen if (requestCode == REQUEST_LOCATION_PERMISSION) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { map?.isMyLocationEnabled = true + checkSendDataDialogShown() } else { AlertDialog.Builder(this) .setMessage("Location permission is needed in order to discover BLE devices and show your location on map") + .setOnDismissListener { checkSendDataDialogShown() } .setPositiveButton("OK", null) .show() } @@ -583,6 +587,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, DataDecoder.Listen map?.mapType = mapType if (checkCallingOrSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { map?.isMyLocationEnabled = true + checkSendDataDialogShown() } else { ActivityCompat.requestPermissions( this, @@ -603,6 +608,25 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, DataDecoder.Listen } } + private fun checkSendDataDialogShown() { + if (!preferenceManager.isSendDataDialogShown()) { + val dialog = AlertDialog.Builder(this) + .setMessage( + Html.fromHtml( + "You can enable telemetry data sharing. Telemetry data sharing sends data to https://uavradar.org at which" + + "you can watch for other aicraft flights (just like flightradar24, but for UAV). You can assign" + + " your callsign and your UAV model in the settings which will be used as your aircraft info. " + + "Data sent when you arm your UAV and have valid 3D GPS Fix" + ) + ) + .setPositiveButton("Enable") { _, i -> preferenceManager.setTelemetrySendingEnabled(true) } + .setNegativeButton("Disable") { _, i -> preferenceManager.setTelemetrySendingEnabled(false) } + .setCancelable(false) + .show() + dialog.findViewById(android.R.id.message)?.movementMethod = LinkMovementMethod.getInstance() + } + } + private fun showMapTypeSelectorDialog() { val fDialogTitle = "Select Map Type" val builder = AlertDialog.Builder(this) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index a75e4dde..fc9ee8d7 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -64,8 +64,8 @@ @@ -82,7 +82,7 @@ + android:data="https://uavradar.org"/>