First Commit

master
Nigreon 2020-07-08 23:56:29 +02:00
commit 2c47239f6e
36 changed files with 2578 additions and 0 deletions

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

34
app/build.gradle Normal file
View File

@ -0,0 +1,34 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
buildToolsVersion "29.0.1"
defaultConfig {
applicationId "net.nigreon.blegps"
minSdkVersion 25
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

BIN
app/libs/FastBLE-2.3.4.jar Normal file

Binary file not shown.

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# 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

View File

@ -0,0 +1,24 @@
package net.nigreon.blegps
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals("net.nigreon.blegps", appContext.packageName)
}
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.nigreon.blegps">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".BLEMockLocationService" ></service>
<service android:name=".BLEHRService" ></service>
<receiver android:name=".TaskerReceiver">
<intent-filter>
<action android:name="net.nigreon.blegps.TASKER_COMMAND" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@ -0,0 +1,224 @@
package net.nigreon.blegps
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.bluetooth.BluetoothGatt
import android.content.Intent
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.support.v4.app.NotificationCompat
import android.support.v4.content.LocalBroadcastManager
import android.util.Log
import com.clj.fastble.BleManager
import com.clj.fastble.callback.BleGattCallback
import com.clj.fastble.callback.BleNotifyCallback
import com.clj.fastble.callback.BleWriteCallback
import com.clj.fastble.data.BleDevice
import com.clj.fastble.exception.BleException
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.*
class BLEHRService : Service() {
private val LOG_TAG = "BLEHRService"
private val CHANNEL_ID = "NotifID2"
private val binder = sBinder()
val timerActivity = Timer()
private lateinit var bleDevice: BleDevice
private val serviceHeartRateUuid: String = "0000180d-0000-1000-8000-00805f9b34fb"
private val characteristicHRControlPointUuid: String = "00002a39-0000-1000-8000-00805f9b34fb"
private val characteristicHRMeasurementUuid: String = "00002a37-0000-1000-8000-00805f9b34fb"
private fun initNotificationHR() {
BleManager.getInstance().notify(
bleDevice,
serviceHeartRateUuid,
characteristicHRMeasurementUuid,
object : BleNotifyCallback() {
override fun onNotifySuccess() {
Log.v(LOG_TAG, "Notify HR Success")
//initNotificationPressure()
}
override fun onNotifyFailure(exception: BleException) {
Log.v(LOG_TAG, "BLE Notify HR Failure $exception")
//initNotificationPressure()
}
override fun onCharacteristicChanged(data: ByteArray) {
//Log.v(LOG_TAG, "Notify Temperature Characteristic changed")
val buffer = ByteBuffer.wrap(data)
buffer.order(ByteOrder.LITTLE_ENDIAN)
//texttemp.text = buffer.float.toString()
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERBTHRM).putExtra(ServicesConstants.BROADCAST_KEY.KEYBTHRM, buffer.get(1))
)
}
})
}
private fun startHRBLE() {
BleManager.getInstance().connect("CC:2C:24:71:9C:BC", object : BleGattCallback() {
override fun onStartConnect() {
}
override fun onConnectFail(bleDevice: BleDevice, exception: BleException) {
Log.v(LOG_TAG, "BLE HR Connection Failed $exception")
}
override fun onConnectSuccess(
bleDeviceConnect: BleDevice,
gattConnect: BluetoothGatt,
status: Int
) {
Log.v(LOG_TAG, "BLE HR Connection OK")
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERBTHR).putExtra(
ServicesConstants.BROADCAST_KEY.KEYBTSTATUSHR,
true
)
)
bleDevice = bleDeviceConnect
initNotificationHR()
val t = object : TimerTask() {
override fun run() {
startHRBLEActivity()
}
}
timerActivity.scheduleAtFixedRate(t, 0, 9500)
}
override fun onDisConnected(
isActiveDisConnected: Boolean,
bleDevice: BleDevice,
gatt: BluetoothGatt,
status: Int
) {
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERBTHR).putExtra(
ServicesConstants.BROADCAST_KEY.KEYBTSTATUSHR,
false
)
)
timerActivity.cancel()
Log.v(LOG_TAG, "BLE HR Disconnected")
}
})
}
private fun stopHRBLE() {
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERBTHR).putExtra(ServicesConstants.BROADCAST_KEY.KEYBTSTATUSHR, false)
)
BleManager.getInstance().disconnect(bleDevice)
}
private fun startHRBLEActivity() {
val data: ByteArray = byteArrayOf(0x15,0x01,0x01)
BleManager.getInstance().write(
bleDevice,
serviceHeartRateUuid,
characteristicHRControlPointUuid,
data,
object : BleWriteCallback() {
override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray) {
//Log.v(LOG_TAG, "Start Activity sended")
}
override fun onWriteFailure(exception: BleException) {
Log.v(LOG_TAG, "Start Activity Failure $exception")
}
})
}
private fun stopHRBLEActivity() {
val data: ByteArray = byteArrayOf(0x15,0x01,0x00)
BleManager.getInstance().write(
bleDevice,
serviceHeartRateUuid,
characteristicHRControlPointUuid,
data,
object : BleWriteCallback() {
override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray) {
Log.v(LOG_TAG, "Stop Activity sended")
}
override fun onWriteFailure(exception: BleException) {
Log.v(LOG_TAG, "Stop Activity send Failure $exception")
}
})
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
if(intent.action == ServicesConstants.HRSERVICEACTION.STARTFOREGROUND_ACTION) {
Log.i(LOG_TAG, "Received Start Foreground Intent ")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel
val name = "ChannelName"
val descriptionText = "ChannelDescription"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val mChannel = NotificationChannel(CHANNEL_ID, name, importance)
mChannel.description = descriptionText
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
}
var notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("BLE HR")
.setContentText("BLE HR active")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
//.setContentIntent(pendingIntent)
.build()
//pBLE = BLEProvider(application, baseContext)
startHRBLE()
startForeground(ServicesConstants.NOTIFICATION_ID.FOREGROUND_HR_SERVICE,notification)
} else if(intent.action == ServicesConstants.HRSERVICEACTION.STOPFOREGROUND_ACTION) {
Log.i(LOG_TAG, "Received Stop Foreground Intent ")
//pBLE.disconnectBLE()
stopHRBLE()
stopForeground(true)
stopSelf()
}
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
Log.i(LOG_TAG, "In onDestroy")
}
override fun onBind(intent: Intent): IBinder? {
Log.v(LOG_TAG, "in onBind")
return binder
}
override fun onRebind(intent: Intent) {
Log.v(LOG_TAG, "in onRebind")
super.onRebind(intent)
}
override fun onUnbind(intent: Intent): Boolean {
Log.v(LOG_TAG, "in onUnbind")
return true
}
inner class sBinder : Binder() {
// Return this instance of LocalService so clients can call public methods
fun getService(): BLEHRService = this@BLEHRService
}
}

View File

@ -0,0 +1,378 @@
package net.nigreon.blegps
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.location.LocationManager
import android.os.*
import android.support.v4.app.NotificationCompat
import android.support.v4.content.LocalBroadcastManager
import android.util.Log
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.text.SimpleDateFormat
import java.util.*
import kotlin.experimental.or
class BLEMockLocationService : Service() {
private val LOG_TAG = "BLEMockLocationService"
private val CHANNEL_ID = "NotifID"
public val ACTIVATION_CHANGE = 1
private lateinit var pBLE: BLEProvider
private lateinit var mockGPS: MockLocationProvider
private var locationReceived = false
private var qualityReceived = false
private var gpsverbose = false
private var gpxopened = false
private var hrenable = false
private var tempenable = false
private lateinit var gpxoutputstream: FileOutputStream
var gpsConf: GPSConfiguration = GPSConfiguration()
// Location
private var currentLatitude: Double = -1.0
private var currentLongitude: Double = -1.0
private var currentElevation: Double = -1.0
private var currentSpeed: Float = -1.0f
private var currentHeading: Float = -1.0f
private var currentEHPE: Float = -1.0f
// Quality
private var currentEVPE: Float = -1.0f
private var currentHDOP: Float = -1.0f
private var currentVDOP: Float = -1.0f
private var currentSatview: Byte = -1
private var currentSatused: Byte = -1
private var currentElevationGPS: Double = -1.0
private var currentHR: Byte = 0
private var currentTemperature: Float = 0.0f
private val binder = sBinder()
fun locationChanged(intent: Intent)
{
Log.v(LOG_TAG, "Latitude Service Broadcast received")
currentLatitude = intent.getDoubleExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONLAT,-1.0)
currentLongitude = intent.getDoubleExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONLON,-1.0)
currentEHPE = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONEHPE,-1.0f)
currentElevation = intent.getDoubleExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONELE,-1.0)
currentSpeed = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONSPEED,-1.0f)
currentHeading = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONHEADING,-1.0f)
mockGPS.pushLocation(currentLatitude,currentLongitude,currentElevation, currentSpeed, currentHeading, currentEHPE)
locationReceived = true
storeGPX()
}
fun qualityChanged(intent: Intent)
{
//Log.v(LOG_TAG, "Latitude Service Broadcast received")
currentEHPE = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYEHPE,-1.0f)
currentEVPE = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYEVPE,-1.0f)
currentHDOP = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYHDOP,-1.0f)
currentVDOP = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYVDOP,-1.0f)
currentSatview = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYSATVIEW,-1)
currentSatused = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYSATUSED,-1)
currentElevationGPS = intent.getDoubleExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYELEGPS,-1.0)
qualityReceived = true
storeGPX()
}
fun HRChanged(intent: Intent)
{
currentHR = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYBTHRM,-1)
}
fun bmpStatusChanged(intent: Intent)
{
tempenable = intent.getBooleanExtra(ServicesConstants.BROADCAST_KEY.KEYBMPSTATUS, false)
}
fun tempChanged(intent: Intent)
{
currentTemperature = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYTEMP, -1.0f)
}
fun taskerChanged(bundle: Bundle) {
Log.v(LOG_TAG, "taskerChanged")
if(bundle.containsKey(ServicesConstants.BROADCAST_KEY.KEYBOOST)) {
/*if (bundle.getBoolean(ServicesConstants.BROADCAST_KEY.KEYBOOST)) {
//gpsConf.gpsBoost = intent.getBooleanExtra(ServicesConstants.BROADCAST_KEY.KEYBOOST, false)
gpsConf.gpsBoost = true
} else {
gpsConf.gpsBoost = false
}*/
gpsConf.gpsBoost = bundle.getBoolean(ServicesConstants.BROADCAST_KEY.KEYBOOST)
sendConfBLE(gpsConf)
Log.v(LOG_TAG, "Received Tasker Intent : Boost ${gpsConf.gpsBoost}")
}
}
fun storeGPX()
{
if(gpxopened) {
if(!gpsverbose and locationReceived)
{
//writeGPX("${SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(Date())} $currentLatitude $currentLongitude $currentElevation m\n")
writeGPX("<trkpt lat=\"$currentLatitude\" lon=\"$currentLongitude\"><ele>$currentElevation</ele><time>${SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(Date())}</time></trkpt>\n")
locationReceived = false
qualityReceived = false
} else if(gpsverbose and locationReceived and qualityReceived) {
//writeGPX("${SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(Date())} $currentLatitude $currentLongitude $currentElevation m\n")
//writeGPX("$currentEHPE m $currentEVPE m $currentHDOP $currentVDOP $currentSatused/$currentSatview $currentElevationGPS m\n")
//if(hrenable) { writeGPX("$currentHR bpm\n")}
//if(tempenable) { writeGPX("${currentTemperature}°C\n")}
writeGPX("<trkpt lat=\"$currentLatitude\" lon=\"$currentLongitude\"><ele>$currentElevation</ele><time>${SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(Date())}</time>\n" +
"<extensions>\n")
if(hrenable or tempenable)
{
writeGPX("<gpxtpx:TrackPointExtension>")
if(hrenable)
{
writeGPX("<gpxtpx:atemp>$currentHR</gpxtpx:atemp>")
}
if(tempenable)
{
writeGPX("<gpxtpx:atemp>$currentTemperature</gpxtpx:atemp>")
}
writeGPX("</gpxtpx:TrackPointExtension>\n")
}
writeGPX("<blegps:BLEGPSExtension><blegps:ehpe>$currentEHPE</blegps:ehpe><blegps:evpe>$currentEVPE</blegps:evpe><blegps:hdop>$currentHDOP</blegps:hdop><blegps:vdop>$currentVDOP</blegps:vdop><blegps:satused>$currentSatused</blegps:satused><blegps:satview>$currentSatview</blegps:satview><blegps:gpselevation>$currentElevationGPS</blegps:gpselevation></blegps:BLEGPSExtension>\n")
writeGPX("</extensions>\n</trkpt>\n")
locationReceived = false
qualityReceived = false
}
}
}
fun writeGPX(str: String)
{
try {
gpxoutputstream.write(str.toByteArray())
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
override fun onCreate() {
super.onCreate()
mockGPS = MockLocationProvider(LocationManager.GPS_PROVIDER, baseContext)
val brReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
ServicesConstants.BROADCAST_FILTER.FILTERLOCATION -> locationChanged(intent)
ServicesConstants.BROADCAST_FILTER.FILTERQUALITY-> qualityChanged(intent)
ServicesConstants.BROADCAST_FILTER.FILTERBTHRM-> HRChanged(intent)
ServicesConstants.BROADCAST_FILTER.FILTERTEMP-> tempChanged(intent)
ServicesConstants.BROADCAST_FILTER.FILTERBMP-> bmpStatusChanged(intent)
//BROADCAST_CHANGE_TYPE_CHANGED -> handleChangeTypeChanged()
}
}
}
val manager = LocalBroadcastManager.getInstance(this)
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERLOCATION))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERQUALITY))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERBTHRM))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERTEMP))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERBMP))
//pBLE.registerBMP()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
if(intent.action == ServicesConstants.MOCKSERVICEACTION.STARTFOREGROUND_ACTION) {
Log.i(LOG_TAG, "Received Start Foreground Intent ")
// val intentNotif = Intent(this, MainActivity::class.java)
// intentNotif.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
// intentNotif.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACT
//val pendingIntent = PendingIntent.getActivity(this, 0, intentNotif, 0)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel
val name = "ChannelName"
val descriptionText = "ChannelDescription"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val mChannel = NotificationChannel(CHANNEL_ID, name, importance)
mChannel.description = descriptionText
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
}
var notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("BLE GPS")
.setContentText("BLE & Mock active")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
//.setContentIntent(pendingIntent)
.build()
pBLE = BLEProvider(application, baseContext)
mockGPS.start()
startForeground(ServicesConstants.NOTIFICATION_ID.FOREGROUND_MOCK_SERVICE,notification)
} else if(intent.action == ServicesConstants.MOCKSERVICEACTION.STOPFOREGROUND_ACTION) {
Log.i(LOG_TAG, "Received Stop Foreground Intent ")
pBLE.disconnectBLE()
mockGPS.shutdown()
stopForeground(true)
stopSelf()
} else if(intent.action == ServicesConstants.MOCKSERVICEACTION.TASKER_ACTION){
Log.i(LOG_TAG, "intent action : ${intent.action}")
taskerChanged(intent.extras)
}
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
Log.i(LOG_TAG, "In onDestroy")
}
override fun onBind(intent: Intent): IBinder? {
Log.v(LOG_TAG, "in onBind")
return binder
}
override fun onRebind(intent: Intent) {
Log.v(LOG_TAG, "in onRebind")
super.onRebind(intent)
}
override fun onUnbind(intent: Intent): Boolean {
Log.v(LOG_TAG, "in onUnbind")
return true
}
fun sendCalibrateBLE(calibrateval: Int)
{
val buffer = ByteBuffer.allocate(4)
buffer.order(ByteOrder.LITTLE_ENDIAN)
buffer.putInt(calibrateval)
pBLE.sendCalibrate(buffer.array())
}
fun HRStatusChanged(enable: Boolean)
{
hrenable = enable
}
fun openGPX(datefilename: String) {
//val datefilename: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
var gpxfilename = File(
Environment.getExternalStorageDirectory().path + File.separator + "BTGPS",
datefilename + ".gpx"
)
var gpxdir = File(Environment.getExternalStorageDirectory().path + File.separator + "BTGPS")
var success = true;
if (!gpxdir.exists()) {
success = gpxdir.mkdir();
}
if (success) {
if(!gpxfilename.exists()) {
try {
gpxoutputstream = FileOutputStream(gpxfilename, true)
//val gpxheader = "GPX Header\n"
val gpxheader =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" +
"<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" version=\"1.1\" " +
"xmlns:gpxtpx=\"http://www.garmin.com/xmlschemas/TrackPointExtension/v1\" " +
"xmlns:blegps=\"https://www.nigreon.net\"" +
">\n" +
"<metadata>" +
"<name>${datefilename}</name>" +
"<desc>${datefilename}</desc>\n" +
"<time>${SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(Date())}</time>\n" +
"</metadata>\n" +
"<trk>\n" +
"<name>${datefilename}</name>\n" +
"<trkseg>\n"
writeGPX(gpxheader)
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
} else {
try {
gpxoutputstream = FileOutputStream(gpxfilename, true)
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
gpxopened = true
}
}
fun closeGPX()
{
//val gpxfooter = "GPX Footer\n"
val gpxfooter = "</trkseg></trk></gpx>\n"
writeGPX(gpxfooter)
gpxoutputstream.close()
gpxopened = false
}
//fun sendConfBLE(bmpnrf: Boolean, bmpble: Boolean, bmp2gps: Boolean, exttemp: Boolean, gpsble: Boolean, gpsbleverb: Boolean, gpsserial: Boolean, gpsmode: Int, gpspoller: Short, gpssendbt: Short, bmppoller: Short) {
fun sendConfBLE(gpsConfNew: GPSConfiguration) {
gpsConf = gpsConfNew
val buffer = ByteBuffer.allocate(8)
buffer.order(ByteOrder.LITTLE_ENDIAN)
var activateByte1: Byte = 0
var activateByte2: Byte = 0
if(gpsConf.bmpNrf) { activateByte1 = activateByte1.or(0x01) }
if(gpsConf.bmpBT) { activateByte1 = activateByte1.or(0x02) }
if(gpsConf.bmp2gps) { activateByte1 = activateByte1.or(0x04) }
if(gpsConf.extTemp) { activateByte1 = activateByte1.or(0x08) }
if(gpsConf.gpsBLE) { activateByte2 = activateByte2.or(0x01) }
if(gpsConf.gpsVerbose) { activateByte2 = activateByte2.or(0x02) }
if(gpsConf.gpsSerial) { activateByte2 = activateByte2.or(0x04) }
if(gpsConf.gpsMode == 1 || gpsConf.gpsBoost) {
activateByte2 = activateByte2.or(0x08)
} else if(gpsConf.gpsMode == 2) {
activateByte2 = activateByte2.or(0x10)
}
buffer.put(activateByte1)
buffer.put(activateByte2)
if(gpsConf.gpsBoost) {
buffer.putShort(4)
buffer.putShort(gpsConf.bmpPollerSend)
buffer.putShort(4)
} else {
buffer.putShort(gpsConf.gpsPollerSend)
buffer.putShort(gpsConf.bmpPollerSend)
buffer.putShort(gpsConf.gpsSendBTSend)
}
pBLE.sendConf(buffer.array())
}
inner class sBinder : Binder() {
// Return this instance of LocalService so clients can call public methods
fun getService(): BLEMockLocationService = this@BLEMockLocationService
}
}

View File

@ -0,0 +1,389 @@
package net.nigreon.blegps
import android.app.Application
import android.bluetooth.BluetoothGatt
import android.content.Context
import android.content.Intent
import android.support.v4.content.LocalBroadcastManager
import android.util.Log
import com.clj.fastble.BleManager
import com.clj.fastble.data.BleDevice
import com.clj.fastble.exception.BleException
import com.clj.fastble.callback.BleGattCallback
import com.clj.fastble.callback.BleNotifyCallback
import com.clj.fastble.callback.BleReadCallback
import com.clj.fastble.callback.BleWriteCallback
import java.nio.ByteBuffer
import java.nio.ByteOrder
import android.os.CountDownTimer
import android.os.Handler
class BLEProvider(val application: Application, val baseContext: Context)
{
private lateinit var bleDevice: BleDevice
private val serviceEnvironmentalSensingUuid: String = "0000181a-0000-1000-8000-00805f9b34fb"
private val serviceLNUuid: String = "00001819-0000-1000-8000-00805f9b34fb"
private val characteristicPressureUuid: String = "00002a6d-0000-1000-8000-00805f9b34fb"
private val characteristicTemperatureUuid: String = "00002a6e-0000-1000-8000-00805f9b34fb"
private val characteristicLSUuid: String = "00002a67-0000-1000-8000-00805f9b34fb"
private val characteristicPQUuid: String = "00002a69-0000-1000-8000-00805f9b34fb"
private val serviceWriteUuid: String = "00002000-0000-1000-8000-00805f9b34fb"
private val characteristicSatUuid: String = "00002002-0000-1000-8000-00805f9b34fb"
private val characteristicWriteUuid: String = "00002001-0000-1000-8000-00805f9b34fb"
private val characteristicCalibrateUuid: String = "00002003-0000-1000-8000-00805f9b34fb"
private var connected = false
private var reconnectcount = 0
private val LOG_TAG = "BLEProvider"
//private lateinit var baseContext: Context
init
{
connectBLE()
}
fun delayFunction(function: ()-> Unit, delay: Long) {
Handler().postDelayed(function, delay)
}
private fun connectBLE()
{
//BleManager.getInstance().init(application)
/*BleManager.getInstance()
.enableLog(true)
.setReConnectCount(10, 60000)*/
//BleManager.getInstance().operateTimeout = 5000
//BleManager.getInstance().connect("24:6F:28:16:C1:F2", object : BleGattCallback() {
BleManager.getInstance().connect("E9:8E:8A:12:6F:3F", object : BleGattCallback() {
override fun onStartConnect() {
}
override fun onConnectFail(bleDevice: BleDevice, exception: BleException) {
Log.v(LOG_TAG, "BLE Connection Failed $exception")
if(reconnectcount <= 15) {
reconnectcount++
delayFunction( { connectBLE() }, 60000)
} else {
reconnectcount=0
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERBT).putExtra(ServicesConstants.BROADCAST_KEY.KEYBTSTATUS, false)
)
connected=false
}
}
override fun onConnectSuccess(bleDeviceConnect: BleDevice, gattConnect: BluetoothGatt, status: Int) {
Log.v(LOG_TAG, "BLE Connection OK")
connected = true
reconnectcount = 0
bleDevice = bleDeviceConnect
//initNotificationTemperature()
object : CountDownTimer(3000, 500) {
var tickCount = 0
override fun onFinish() {
// When timer is finished
// Execute your code here
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERBT).putExtra(ServicesConstants.BROADCAST_KEY.KEYBTSTATUS, true)
)
}
override fun onTick(millisUntilFinished: Long) {
//Log.v(LOG_TAG, "Tick: $millisUntilFinished")
when (tickCount) {
0 -> initNotificationTemperature()
1 -> initNotificationPQ()
2 -> initNotificationPressure()
3 -> initNotificationSat()
4 -> initNotificationLS()
}
tickCount++
/*if(millisUntilFinished == 5000L)
{
initNotificationTemperature()
} else if(millisUntilFinished == 4000L) {
initNotificationPQ()
} else if(millisUntilFinished == 3000L) {
initNotificationPressure()
} else if(millisUntilFinished == 2000L) {
initNotificationSat()
} else if(millisUntilFinished == 1000L) {
initNotificationLS()
}*/
// millisUntilFinished The amount of time until finished.
}
}.start()
//initNotificationPressure()
//initNotificationLS()
}
override fun onDisConnected(
isActiveDisConnected: Boolean,
bleDevice: BleDevice,
gatt: BluetoothGatt,
status: Int
) {
//connected = false
//LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
// Intent(ServicesConstants.BROADCAST_FILTER.FILTERBT).putExtra(ServicesConstants.BROADCAST_KEY.KEYBTSTATUS, false)
//)
Log.v(LOG_TAG, "BLE Disconnected")
if(connected) {
connectBLE()
}
}
})
}
private fun initNotificationPressure() {
BleManager.getInstance().notify(
bleDevice,
serviceEnvironmentalSensingUuid,
characteristicPressureUuid,
object : BleNotifyCallback() {
override fun onNotifySuccess() {
Log.v(LOG_TAG, "BLE Notify Pressure Success")
//initNotificationLS()
}
override fun onNotifyFailure(exception: BleException) {
Log.v(LOG_TAG, "BLE Notify Pressure Failure $exception")
//initNotificationLS()
}
override fun onCharacteristicChanged(data: ByteArray) {
val buffer = ByteBuffer.wrap(data)
buffer.order(ByteOrder.LITTLE_ENDIAN)
Log.v(LOG_TAG, "BLE Notify Pressure Characteristic changed")
//textpressure.text = buffer.getInt().toString()
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERPRESSURE).putExtra(ServicesConstants.BROADCAST_KEY.KEYPRESSURE, buffer.int)
)
}
})
}
private fun initNotificationTemperature() {
BleManager.getInstance().notify(
bleDevice,
serviceEnvironmentalSensingUuid,
characteristicTemperatureUuid,
object : BleNotifyCallback() {
override fun onNotifySuccess() {
Log.v(LOG_TAG, "Notify Temperature Success")
//initNotificationPressure()
}
override fun onNotifyFailure(exception: BleException) {
Log.v(LOG_TAG, "BLE Notify Temperature Failure $exception")
//initNotificationPressure()
}
override fun onCharacteristicChanged(data: ByteArray) {
Log.v(LOG_TAG, "Notify Temperature Characteristic changed")
val buffer = ByteBuffer.wrap(data)
buffer.order(ByteOrder.LITTLE_ENDIAN)
//texttemp.text = buffer.float.toString()
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERTEMP).putExtra(ServicesConstants.BROADCAST_KEY.KEYTEMP, buffer.short/100f)
)
}
})
}
private fun initNotificationLS() {
BleManager.getInstance().notify(
bleDevice,
serviceLNUuid,
characteristicLSUuid,
object : BleNotifyCallback() {
override fun onNotifySuccess() {
Log.v(LOG_TAG, "Notify LS Success")
//initNotificationPQ()
}
override fun onNotifyFailure(exception: BleException) {
Log.v(LOG_TAG, "BLE Notify LS Failure $exception")
//initNotificationPQ()
}
@UseExperimental(ExperimentalUnsignedTypes::class)
override fun onCharacteristicChanged(data: ByteArray) {
Log.v(LOG_TAG, "Notify LS Characteristic changed ${data.size}")
val buffer = ByteBuffer.wrap(data)
buffer.order(ByteOrder.LITTLE_ENDIAN)
//val flags: UShort = buffer.short.toUShort()
val speed: Float = buffer.short.toUShort().toFloat()/100.0f
val latitude: Double = buffer.int/10000000.0
val longitude: Double = buffer.int/10000000.0
val ehpe: Float = buffer.int.toUInt().toFloat()/100.0f
//val ehpe: Float = 5.0f
val elevation: Double = buffer.int/100.0
val heading: Float = buffer.short.toUShort().toFloat()/100.0f
Log.v(LOG_TAG, "LS $latitude $longitude $speed $heading")
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERLOCATION).putExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONLAT, latitude)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONLON, longitude)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONEHPE, ehpe)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONELE, elevation)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONSPEED, speed)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONHEADING, heading)
)
}
})
}
private fun initNotificationPQ() {
BleManager.getInstance().notify(
bleDevice,
serviceLNUuid,
characteristicPQUuid,
object : BleNotifyCallback() {
override fun onNotifySuccess() {
Log.v(LOG_TAG, "Notify PQ Success")
//initNotificationSat()
}
override fun onNotifyFailure(exception: BleException) {
Log.v(LOG_TAG, "BLE Notify PQ Failure $exception")
//initNotificationSat()
}
@UseExperimental(ExperimentalUnsignedTypes::class)
override fun onCharacteristicChanged(data: ByteArray) {
Log.v(LOG_TAG, "Notify PQ Characteristic changed")
val buffer = ByteBuffer.wrap(data)
buffer.order(ByteOrder.LITTLE_ENDIAN)
//val flags = buffer.short
val sat_view: Byte = buffer.get().toUByte().toByte()
val sat_used: Byte = buffer.get().toUByte().toByte()
val ehpe: Float = buffer.int.toUInt().toFloat()/100.0f
val evpe: Float = buffer.int.toUInt().toFloat()/100.0f
val hdop: Float = buffer.get().toUByte().toByte()*2/10.0f
val vdop: Float = buffer.get().toUByte().toByte()*2/10.0f
val elevation_gps: Double = buffer.int/100.0
Log.v(LOG_TAG, "PQ $sat_view $sat_used $ehpe $evpe $hdop $vdop $elevation_gps")
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERQUALITY).putExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYSATVIEW, sat_view)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYSATUSED, sat_used)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYEHPE, ehpe)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYEVPE, evpe)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYHDOP, hdop)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYVDOP, vdop)
.putExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYELEGPS, elevation_gps)
)
}
})
}
private fun initNotificationSat() {
BleManager.getInstance().notify(
bleDevice,
serviceWriteUuid,
characteristicSatUuid,
object : BleNotifyCallback() {
override fun onNotifySuccess() {
Log.v(LOG_TAG, "Notify Sat Success")
//initNotificationSat()
}
override fun onNotifyFailure(exception: BleException) {
Log.v(LOG_TAG, "BLE Notify Sat Failure $exception")
//initNotificationSat()
}
override fun onCharacteristicChanged(data: ByteArray) {
Log.v(LOG_TAG, "Notify Sat Characteristic changed")
val buffer = ByteBuffer.wrap(data)
buffer.order(ByteOrder.LITTLE_ENDIAN)
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERSAT).putExtra(ServicesConstants.BROADCAST_KEY.KEYSATGPSALL, buffer.get())
.putExtra(ServicesConstants.BROADCAST_KEY.KEYSATGPSUSED, buffer.get())
.putExtra(ServicesConstants.BROADCAST_KEY.KEYSATSBASALL, buffer.get())
.putExtra(ServicesConstants.BROADCAST_KEY.KEYSATSBASUSED, buffer.get())
.putExtra(ServicesConstants.BROADCAST_KEY.KEYSATGLONASSALL, buffer.get())
.putExtra(ServicesConstants.BROADCAST_KEY.KEYSATGLONASSUSED, buffer.get())
.putExtra(ServicesConstants.BROADCAST_KEY.KEYSATGALILEOALL, buffer.get())
.putExtra(ServicesConstants.BROADCAST_KEY.KEYSATGALILEOUSED, buffer.get())
)
}
})
}
fun disconnectBLE()
{
if(connected) {
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERBT).putExtra(ServicesConstants.BROADCAST_KEY.KEYBTSTATUS, false)
)
connected = false
BleManager.getInstance().stopNotify(
bleDevice,
serviceEnvironmentalSensingUuid,
characteristicTemperatureUuid
)
BleManager.getInstance()
.stopNotify(bleDevice, serviceEnvironmentalSensingUuid, characteristicPressureUuid)
BleManager.getInstance().stopNotify(bleDevice, serviceLNUuid, characteristicLSUuid)
BleManager.getInstance().stopNotify(bleDevice, serviceLNUuid, characteristicPQUuid)
BleManager.getInstance().stopNotify(bleDevice, serviceWriteUuid, characteristicSatUuid)
BleManager.getInstance().disconnect(bleDevice)
//BleManager.getInstance().destroy()
}
}
fun sendCalibrate(data: ByteArray) {
if(connected) {
BleManager.getInstance().write(
bleDevice,
serviceWriteUuid,
characteristicCalibrateUuid,
data,
object : BleWriteCallback() {
override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray) {
Log.v(LOG_TAG, "Conf sended")
}
override fun onWriteFailure(exception: BleException) {
Log.v(LOG_TAG, "Conf send Failure $exception")
}
})
}
}
fun sendConf(data: ByteArray) {
if (connected) {
BleManager.getInstance().write(
bleDevice,
serviceWriteUuid,
characteristicWriteUuid,
data,
object : BleWriteCallback() {
override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray) {
Log.v(LOG_TAG, "Conf sended")
}
override fun onWriteFailure(exception: BleException) {
Log.v(LOG_TAG, "Conf send Failure $exception")
}
})
}
}
/*fun registerBMP()
{
initNotificationPressure()
initNotificationTemperature()
}*/
}

View File

@ -0,0 +1,16 @@
package net.nigreon.blegps
class GPSConfiguration {
var bmpNrf: Boolean = false
var bmpBT: Boolean = false
var bmp2gps: Boolean = false
var extTemp: Boolean = false
var gpsBLE: Boolean = false
var gpsVerbose: Boolean = false
var gpsSerial: Boolean = false
var gpsMode: Int = 0
var gpsBoost: Boolean = false
var gpsPollerSend: Short = 0
var gpsSendBTSend: Short = 0
var bmpPollerSend: Short = 0
}

View File

@ -0,0 +1,614 @@
package net.nigreon.blegps
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import android.content.Intent
import android.os.IBinder
import android.content.ComponentName
import android.content.Context
import android.content.ServiceConnection
import android.support.v4.content.LocalBroadcastManager
import android.content.BroadcastReceiver
import android.content.IntentFilter
import android.os.Handler
import android.util.Log
import android.view.View
import android.widget.AdapterView
import android.location.Location
import com.clj.fastble.BleManager
import java.text.SimpleDateFormat
import java.util.*
import android.content.SharedPreferences
import android.os.Environment
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
//https://stackoverflow.com/questions/1625249/android-how-to-bind-spinner-to-custom-object-list
class MainActivity : AppCompatActivity() {
private val LOG_TAG = "MainActivity"
private lateinit var mBoundService: BLEMockLocationService
var mServiceBound = false
private lateinit var mBoundServiceHR: BLEHRService
var mServiceBoundHR = false
var currentPressure: Int = -1
var currentTemperature: Float = -1.0f
var BTConnected = false
var BTHRConnected = false
var lastLocation: Location = Location("LastLocation")
private lateinit var gpxfilename: String
private var gpxopened = false
private lateinit var sharedPreferences: SharedPreferences
/*override fun onStart() {
super.onStart()
// Bind to LocalService
}*/
fun delayFunction(function: ()-> Unit, delay: Long) {
Handler().postDelayed(function, delay)
}
fun disconnectBT()
{
Intent(this, BLEMockLocationService::class.java).also { intent ->
intent.action = ServicesConstants.MOCKSERVICEACTION.STOPFOREGROUND_ACTION
startService(intent)
}
unbindService(mServiceConnection)
mServiceBound = false
}
fun disconnectBTHR()
{
Intent(this, BLEHRService::class.java).also { intent ->
intent.action = ServicesConstants.HRSERVICEACTION.STOPFOREGROUND_ACTION
startService(intent)
}
unbindService(mServiceConnectionHR)
mServiceBoundHR = false
}
fun filter01changed(intent: Intent)
{
Log.v(LOG_TAG, "Filter01 Broadcast received")
text01.text=intent.getIntExtra(ServicesConstants.BROADCAST_KEY.KEY01,-1).toString()
}
fun temperatureChanged(intent: Intent)
{
Log.v(LOG_TAG, "Temperature Broadcast received")
currentTemperature = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYTEMP, -1.0f)
writeBMPText()
}
fun pressureChanged(intent: Intent)
{
Log.v(LOG_TAG, "Pressure Broadcast received")
currentPressure = intent.getIntExtra(ServicesConstants.BROADCAST_KEY.KEYPRESSURE,-1)
writeBMPText()
}
fun BTChanged(intent: Intent)
{
val connected = intent.getBooleanExtra(ServicesConstants.BROADCAST_KEY.KEYBTSTATUS,false)
if(connected) {
BTConnected=true
text01.text = "BT Connected"
Log.v(LOG_TAG, "BT Connected Broadcast received")
mBoundService.HRStatusChanged(BTHRConnected)
sendConfToBLEService()
if(swlog2file.isChecked())
{
mBoundService.openGPX(gpxfilename)
gpxopened = true
}
} else {
text01.text = "BT Disconnected"
Log.v(LOG_TAG, "BT Disconnected Broadcast received")
BTConnected=false
swblepower.setChecked(false)
}
writeBMPText()
}
fun BTChangedHR(intent: Intent)
{
val connected = intent.getBooleanExtra(ServicesConstants.BROADCAST_KEY.KEYBTSTATUSHR,false)
if(connected) {
BTHRConnected=true
text01.text = "BT HR Connected"
Log.v(LOG_TAG, "BT HR Connected Broadcast received")
if(BTConnected) { mBoundService.HRStatusChanged(true) }
} else {
text01.text = "BT HR Disconnected"
Log.v(LOG_TAG, "BT HR Disconnected Broadcast received")
BTHRConnected=false
if(BTConnected) { mBoundService.HRStatusChanged(false) }
}
writeBMPText()
}
fun BTChangedHRM(intent: Intent)
{
texthr.text = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYBTHRM,0).toString() + " bpm"
}
private fun convertLatLon(latitude: Double, longitude: Double): String {
val builder = StringBuilder()
val latitudeDegrees = Location.convert(Math.abs(latitude), Location.FORMAT_MINUTES)
//val latitudeSplit1 = latitudeDegrees.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (latitude < 0) {
builder.append("S ")
} else {
builder.append("N ")
}
val latitudeSplit1 = latitudeDegrees.split(':')
builder.append(latitudeSplit1[0])
builder.append("°")
val latitudeSplit2 = latitudeSplit1[1].split(',')
builder.append(latitudeSplit2[0])
builder.append(".")
builder.append(latitudeSplit2[1])
builder.append(" ")
val longitudeDegrees = Location.convert(Math.abs(longitude), Location.FORMAT_MINUTES)
if (longitude < 0) {
builder.append("W ")
} else {
builder.append("E ")
}
val longitudeSplit1 = longitudeDegrees.split(':')
builder.append(longitudeSplit1[0])
builder.append("°")
val longitudeSplit2 = longitudeSplit1[1].split(',')
builder.append(longitudeSplit2[0])
builder.append(".")
builder.append(longitudeSplit2[1])
return builder.toString()
}
fun writeBMPText()
{
textbmp.text = "${currentPressure/100.0f} hPa $currentTemperature°C"
}
fun writeLocationText(intent: Intent)
{
val currentLatitude = intent.getDoubleExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONLAT,-1.0)
val currentLongitude = intent.getDoubleExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONLON,-1.0)
val currentEHPE = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONEHPE,-1.0f)
val currentElevation = intent.getDoubleExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONELE,-1.0)
val currentSpeed = (intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONSPEED,-1.0f))/1000.0f*3600.0f
val currentHeading = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYLOCATIONHEADING,-1.0f)
var currentLocation: Location = Location("CurrentLocation")
currentLocation.latitude = currentLatitude
currentLocation.longitude = currentLongitude
textlastfix.text = "${SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS").format(Date())}"
textlocation.text = "${convertLatLon(currentLatitude, currentLongitude)} $currentElevation m \n $currentLatitude $currentLongitude $currentElevation m"
textspeedheading.text = "$currentSpeed km/h $currentHeading° ${currentLocation.distanceTo(lastLocation)} m"
lastLocation = currentLocation
if(swblegpsverbose.isChecked == false) {
textquality.text = "$currentEHPE m"
textsatellites.text = "Satellites in verbose"
}
}
fun writeQualityText(intent: Intent)
{
val currentEHPE = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYEHPE,-1.0f)
val currentEVPE = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYEVPE,-1.0f)
val currentHDOP = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYHDOP,-1.0f)
val currentVDOP = intent.getFloatExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYVDOP,-1.0f)
val sat_view = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYSATVIEW,-1)
val sat_used = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYSATUSED,-1)
val elevation_gps = intent.getDoubleExtra(ServicesConstants.BROADCAST_KEY.KEYQUALITYELEGPS,-1.0)
textquality.text = "$currentEHPE m $currentEVPE m $currentHDOP $currentVDOP $sat_used/$sat_view $elevation_gps m"
}
fun writeSatText(intent: Intent)
{
val gpsall = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYSATGPSALL,-1)
val gpsused = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYSATGPSUSED,-1)
val sbasall = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYSATSBASALL,-1)
val sbasused = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYSATSBASUSED,-1)
val glonassall = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYSATGLONASSALL,-1)
val glonassused = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYSATGLONASSUSED,-1)
val galileoall = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYSATGALILEOALL,-1)
val galileoused = intent.getByteExtra(ServicesConstants.BROADCAST_KEY.KEYSATGALILEOUSED,-1)
textsatellites.text = "GPS: $gpsused/$gpsall SBAS: $sbasused/$sbasall GLONASS: $glonassused/$glonassall GALILEO: $galileoused/$galileoall"
}
private fun sendCalibrateToBLEService()
{
if(BTConnected) {
val calibrateval: Int = editcalibratebmp2gps.text.toString().toInt()*100
mBoundService.sendCalibrateBLE(calibrateval)
}
}
private fun sendConfToBLEService()
{
if(BTConnected) {
val bmppollersend: Float = resources.getStringArray(R.array.pollerval)[spinbmp.selectedItemPosition].toFloat() * 4
var gpspollersend: Float
var gpssendbtsend: Float
var gpsmode: Int
/*if(swblegpsboost.isChecked)
{
gpspollersend = 4f
gpssendbtsend = 4f
gpsmode = 1
} else {*/
gpspollersend = resources.getStringArray(R.array.pollerval)[spingps.selectedItemPosition].toFloat() * 4
gpssendbtsend = resources.getStringArray(R.array.pollerval)[spingpssendbt.selectedItemPosition].toFloat() * 4
gpsmode = spingpsmode.selectedItemPosition
//}
var gpsConf: GPSConfiguration = GPSConfiguration()
gpsConf.bmpNrf = swbmpnrf.isChecked
gpsConf.bmpBT = swbmpbt.isChecked
gpsConf.bmp2gps = swbmp2gps.isChecked
gpsConf.extTemp = swexttemp.isChecked
gpsConf.gpsBLE = swgpsble.isChecked
gpsConf.gpsVerbose = swblegpsverbose.isChecked
//gpsConf.gpsBoost = swblegpsboost.isChecked
gpsConf.gpsSerial = swgpsserial.isChecked
gpsConf.gpsMode = gpsmode
gpsConf.gpsPollerSend = gpspollersend.toShort()
gpsConf.gpsSendBTSend = gpssendbtsend.toShort()
gpsConf.bmpPollerSend = bmppollersend.toShort()
mBoundService.sendConfBLE(gpsConf)
}
}
private fun sendStopConfToBLEService()
{
if(BTConnected) {
val gpspollersend: Float =
resources.getStringArray(R.array.pollerval)[spingps.selectedItemPosition].toFloat() * 4
val bmppollersend: Float =
resources.getStringArray(R.array.pollerval)[spinbmp.selectedItemPosition].toFloat() * 4
val gpssendbtsend: Float =
resources.getStringArray(R.array.pollerval)[spingpssendbt.selectedItemPosition].toFloat() * 4
var gpsConf: GPSConfiguration = GPSConfiguration()
gpsConf.bmpNrf = false
gpsConf.bmpBT = false
gpsConf.bmp2gps = false
gpsConf.extTemp = false
gpsConf.gpsBLE = false
gpsConf.gpsVerbose = false
gpsConf.gpsSerial = false
gpsConf.gpsMode = spingpsmode.selectedItemPosition
gpsConf.gpsPollerSend = gpspollersend.toShort()
gpsConf.gpsSendBTSend = gpssendbtsend.toShort()
gpsConf.bmpPollerSend = bmppollersend.toShort()
mBoundService.sendConfBLE(gpsConf)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
BleManager.getInstance().init(application)
BleManager.getInstance()
.enableLog(true)
.setReConnectCount(1, 5000)
BleManager.getInstance().operateTimeout = 5000
val brReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
ServicesConstants.BROADCAST_FILTER.FILTER01 -> filter01changed(intent)
ServicesConstants.BROADCAST_FILTER.FILTERBT -> BTChanged(intent)
ServicesConstants.BROADCAST_FILTER.FILTERBTHR -> BTChangedHR(intent)
ServicesConstants.BROADCAST_FILTER.FILTERBTHRM -> BTChangedHRM(intent)
ServicesConstants.BROADCAST_FILTER.FILTERTEMP -> temperatureChanged(intent)
ServicesConstants.BROADCAST_FILTER.FILTERPRESSURE -> pressureChanged(intent)
ServicesConstants.BROADCAST_FILTER.FILTERLOCATION -> writeLocationText(intent)
ServicesConstants.BROADCAST_FILTER.FILTERQUALITY -> writeQualityText(intent)
ServicesConstants.BROADCAST_FILTER.FILTERSAT -> writeSatText(intent)
//BROADCAST_CHANGE_TYPE_CHANGED -> handleChangeTypeChanged()
}
}
}
spinbmp.setSelection(11)
spingps.setSelection(3)
spingpsmode.setSelection(1)
spingpssendbt.setSelection(10)
lastLocation.latitude = 0.0
lastLocation.longitude = 0.0
sharedPreferences = baseContext.getSharedPreferences("blegps_config", MODE_PRIVATE)
/*sharedPreferences
.edit()
.putInt("inttest", 64)
.putString("stringtest", "24:6F:28:16:C1:F2")
.apply()*/
//text01.text = "MAC ${sharedPreferences.getString("stringtest", null)}"
//mButton.setEnabled(false)
/*buttonread.setOnClickListener {
//text01.text=edit01.text.toString()
BleManager.getInstance().read(
bleDevice,
serviceWriteUuid,
characteristicWriteUuid,
object : BleReadCallback() {
override fun onReadSuccess(data: ByteArray) {
text01.text = "Read OK"
textread.text = data.toString()
}
override fun onReadFailure(exception: BleException) {
text01.text = "Read Failure $exception"
}
}
)
}
buttonwrite.setOnClickListener {
val writeba: ByteArray = edit01.text.toString().toByteArray()
BleManager.getInstance().write(
bleDevice,
serviceWriteUuid,
characteristicWriteUuid,
writeba,
object : BleWriteCallback() {
override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray) {
text01.text = "Write OK"
}
override fun onWriteFailure(exception: BleException) {
text01.text = "Write Failure $exception"
}
}
)
}*/
swblepower.setOnCheckedChangeListener { _, isChecked ->
if(isChecked) {
Intent(this, BLEMockLocationService::class.java).also { intent ->
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
intent.action = ServicesConstants.MOCKSERVICEACTION.STARTFOREGROUND_ACTION
startService(intent)
}
} else {
sendStopConfToBLEService()
delayFunction({ disconnectBT() }, 1500)
}
}
swblehrpower.setOnCheckedChangeListener { _, isChecked ->
if(isChecked) {
Intent(this, BLEHRService::class.java).also { intent ->
bindService(intent, mServiceConnectionHR, Context.BIND_AUTO_CREATE)
intent.action = ServicesConstants.HRSERVICEACTION.STARTFOREGROUND_ACTION
startService(intent)
}
} else {
//sendStopConfToBLEService()
delayFunction({ disconnectBTHR() }, 1500)
}
}
swbmpnrf.setOnCheckedChangeListener { _,_ ->
sendConfToBLEService()
}
swbmpbt.setOnCheckedChangeListener { _, checked ->
if(checked) {
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERBMP).putExtra(ServicesConstants.BROADCAST_KEY.KEYBMPSTATUS, true)
)
} else {
LocalBroadcastManager.getInstance(baseContext).sendBroadcast(
Intent(ServicesConstants.BROADCAST_FILTER.FILTERBMP).putExtra(ServicesConstants.BROADCAST_KEY.KEYBMPSTATUS, false)
)
}
sendConfToBLEService()
}
swbmp2gps.setOnCheckedChangeListener { _,_ ->
sendConfToBLEService()
}
swexttemp.setOnCheckedChangeListener { _,_ ->
sendConfToBLEService()
}
swgpsble.setOnCheckedChangeListener { _,_ ->
sendConfToBLEService()
}
swblegpsverbose.setOnCheckedChangeListener { _, _ ->
sendConfToBLEService()
}
swgpsserial.setOnCheckedChangeListener { _,_ ->
sendConfToBLEService()
}
/*swblegpsboost.setOnCheckedChangeListener { _,_ ->
sendConfToBLEService()
}*/
swlog2file.setOnCheckedChangeListener { _, checked ->
if(checked) {
gpxfilename = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
if (BTConnected) {
mBoundService.openGPX(gpxfilename)
gpxopened = true
}
} else {
if(BTConnected) {
mBoundService.closeGPX()
} else if(gpxopened) {
var gpxhandle = File(
Environment.getExternalStorageDirectory().path + File.separator + "BTGPS",
gpxfilename + ".gpx"
)
var gpxoutputstream = FileOutputStream(gpxhandle, true)
try {
val gpxfooter = "</trkseg></trk></gpx>\n"
gpxoutputstream.write(gpxfooter.toByteArray())
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
gpxoutputstream.close()
}
gpxopened = false
}
//sendConfToBLEService()
}
spingpsmode.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(p0: AdapterView<*>?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
sendConfToBLEService()
}
}
spinbmp.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(p0: AdapterView<*>?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
sendConfToBLEService()
}
}
spingps.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(p0: AdapterView<*>?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
sendConfToBLEService()
}
}
spingpssendbt.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(p0: AdapterView<*>?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
sendConfToBLEService()
}
}
buttoncalibratebmp2gps.setOnClickListener {
//text01.text = editcalibratebmp2gps.text
sendCalibrateToBLEService()
}
/*startmock.setOnClickListener {
//val startIntent = Intent(this,BLEMockLocationService::class.java)
//startIntent.action = ServicesConstants.MOCKSERVICEACTION.STARTFOREGROUND_ACTION
//startService(startIntent)
Intent(this, BLEMockLocationService::class.java).also { intent ->
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
intent.action = ServicesConstants.MOCKSERVICEACTION.STARTFOREGROUND_ACTION
startService(intent)
}
}
stopmock.setOnClickListener {
//val stopIntent = Intent(this,BLEMockLocationService::class.java)
//stopIntent.action = ServicesConstants.MOCKSERVICEACTION.STOPFOREGROUND_ACTION
//startService(stopIntent)
//LocalBroadcastManager.getInstance(this)
// .unregisterReceiver(brReceiver)
Intent(this, BLEMockLocationService::class.java).also { intent ->
intent.action = ServicesConstants.MOCKSERVICEACTION.STOPFOREGROUND_ACTION
startService(intent)
}
unbindService(mServiceConnection)
mServiceBound = false
}*/
/*bCounter.setOnClickListener {
textread.text=mBoundService.getCounter().toString()
}*/
val manager = LocalBroadcastManager.getInstance(this)
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTER01))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERBT))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERBTHR))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERBTHRM))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERTEMP))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERPRESSURE))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERLOCATION))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERQUALITY))
manager.registerReceiver(brReceiver, IntentFilter(ServicesConstants.BROADCAST_FILTER.FILTERSAT))
//LocalBroadcastManager.getInstance(this)
// .unregisterReceiver(broadCastReceiver)
}
override fun onPause() {
super.onPause()
if(!swlog2file.isChecked) {
swblegpsverbose.setChecked(false)
}
}
private val mServiceConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName) {
mServiceBound = false
}
override fun onServiceConnected(name: ComponentName, service: IBinder) {
val binder = service as BLEMockLocationService.sBinder
mBoundService = binder.getService()
mServiceBound = true
}
}
private val mServiceConnectionHR = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName) {
mServiceBoundHR = false
}
override fun onServiceConnected(name: ComponentName, service: IBinder) {
val binderHR = service as BLEHRService.sBinder
mBoundServiceHR = binderHR.getService()
mServiceBoundHR = true
}
}
}

View File

@ -0,0 +1,40 @@
package net.nigreon.blegps
import android.content.Context
import android.location.Location
import android.location.LocationManager
import android.os.SystemClock
class MockLocationProvider(val providerName: String, val ctx: Context) {
private val lm = ctx.getSystemService(
Context.LOCATION_SERVICE
) as LocationManager
fun start() {
lm.addTestProvider(
providerName, false, false, false, false, true,
true, true, 0, 5
)
lm.setTestProviderEnabled(providerName, true)
}
fun pushLocation(lat: Double, lon: Double, alt: Double, speed: Float, heading: Float, accuracy: Float) {
val mockLocation = Location(providerName)
mockLocation.latitude = lat
mockLocation.longitude = lon
mockLocation.altitude = alt
mockLocation.speed = speed
mockLocation.bearing = heading
mockLocation.time = System.currentTimeMillis()
mockLocation.accuracy = accuracy
mockLocation.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
lm.setTestProviderLocation(providerName, mockLocation)
}
fun shutdown() {
lm.removeTestProvider(providerName)
}
}

View File

@ -0,0 +1,73 @@
package net.nigreon.blegps
class ServicesConstants {
interface MOCKSERVICEACTION {
companion object {
val MAIN_ACTION = "net.nigreon.blegps.mockservice.action.main"
val STARTFOREGROUND_ACTION = "net.nigreon.blegps.mockservice.action.startforeground"
val STOPFOREGROUND_ACTION = "net.nigreon.blegps.mockservice.action.stopforeground"
val TASKER_ACTION = "net.nigreon.blegps.mockservice.action.tasker"
}
}
interface HRSERVICEACTION {
companion object {
//val MAIN_ACTION = "net.nigreon.blegps.mockservice.action.main"
val STARTFOREGROUND_ACTION = "net.nigreon.blegps.hrservice.action.startforeground"
val STOPFOREGROUND_ACTION = "net.nigreon.blegps.hrservice.action.stopforeground"
}
}
interface NOTIFICATION_ID {
companion object {
val FOREGROUND_MOCK_SERVICE = 101
val FOREGROUND_HR_SERVICE = 102
}
}
interface BROADCAST_FILTER {
companion object {
const val FILTER01 = "just.a.filter"
const val FILTERBT = "filter.bt"
const val FILTERBMP = "filter.bmp"
const val FILTERBTHR = "filter.bthr"
const val FILTERBTHRM = "filter.bthrm"
const val FILTERTEMP = "filter.temp"
const val FILTERPRESSURE = "filter.pressure"
const val FILTERLOCATION = "filter.location"
const val FILTERQUALITY = "filter.quality"
const val FILTERSAT = "filter.sat"
const val FILTERTASKER= "filter.tasker"
}
}
interface BROADCAST_KEY {
companion object {
const val KEY01 = "key01"
const val KEYBTSTATUS = "btstatus"
const val KEYBTSTATUSHR = "btstatushr"
const val KEYBTHRM = "bthrm"
const val KEYBMPSTATUS = "bmpstatus"
const val KEYTEMP = "temperature"
const val KEYPRESSURE = "pressure"
const val KEYBOOST = "boost"
const val KEYLOCATIONLAT = "latitude"
const val KEYLOCATIONLON = "longitude"
const val KEYLOCATIONELE = "elevation"
const val KEYLOCATIONSPEED = "speed"
const val KEYLOCATIONHEADING = "heading"
const val KEYLOCATIONEHPE = "lehpe"
const val KEYQUALITYSATVIEW = "satview"
const val KEYQUALITYSATUSED = "satused"
const val KEYQUALITYEHPE = "ehpe"
const val KEYQUALITYEVPE = "evpe"
const val KEYQUALITYHDOP = "hdop"
const val KEYQUALITYVDOP = "vdop"
const val KEYQUALITYELEGPS = "elevation_gps"
const val KEYSATGPSALL = "gpsall"
const val KEYSATGPSUSED = "gpsused"
const val KEYSATSBASALL = "sbasall"
const val KEYSATSBASUSED = "sbasused"
const val KEYSATGLONASSALL = "glonassall"
const val KEYSATGLONASSUSED = "glonassused"
const val KEYSATGALILEOALL = "galileoall"
const val KEYSATGALILEOUSED = "galileoused"
}
}
}

View File

@ -0,0 +1,23 @@
package net.nigreon.blegps
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.support.v4.content.ContextCompat
import android.support.v4.content.LocalBroadcastManager
import android.util.Log
class TaskerReceiver: BroadcastReceiver() {
private val LOG_TAG = "TaskerReceiver"
override fun onReceive(context: Context, intent: Intent) {
Log.v(LOG_TAG, "Receive")
val serviceIntent = Intent(context, BLEMockLocationService::class.java)
serviceIntent.action=ServicesConstants.MOCKSERVICEACTION.TASKER_ACTION
//serviceIntent.putExtras(Intent(ServicesConstants.BROADCAST_FILTER.FILTERTASKER).putExtra(ServicesConstants.BROADCAST_KEY.KEYBOOST, false))
serviceIntent.putExtras(intent)
//LocalBroadcastManager.getInstance(context).sendBroadcast(
// Intent(ServicesConstants.BROADCAST_FILTER.FILTERTASKER).putExtra(ServicesConstants.BROADCAST_KEY.KEYBOOST, true))
ContextCompat.startForegroundService(context, serviceIntent)
}
}

View File

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,338 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:context=".MainActivity">
<Switch
android:text="BLE GPS Power"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swblepower">
</Switch>
<Switch
android:text="BLE HR Power"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swblehrpower"/>
<!--<Button
android:text="Start Mock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp"
android:id="@+id/startmock"/>
<Button
android:text="Stop Mock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp"
android:id="@+id/stopmock"/>-->
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:context=".MainActivity">
<Switch
android:text="BMP nRF"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swbmpnrf"/>
<Switch
android:text="BMP BT"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swbmpbt"/>
<Switch
android:text="Ext Temp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swexttemp"/>
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:context=".MainActivity">
<Switch
android:text="GPS BLE"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swgpsble"/>
<Switch
android:text="GPS Serial"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swgpsserial"/>
<Switch
android:text="BMP2GPS"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swbmp2gps"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/text01"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Last fix time"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textlastfix"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Notification BMP"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textbmp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Location"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textlocation"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Speed/Heading"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textspeedheading"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="HR"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/texthr"/>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:context=".MainActivity">
<Switch
android:text="BLE GPS Verbose"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swblegpsverbose"/>
<Switch
android:text="Log2File"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swlog2file"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Quality"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textquality"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Satellites"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textsatellites"/>
<!-- <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Read"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textread"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:padding="10dp"
android:hint="Value Hint"
android:id="@+id/edit01"/>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:context=".MainActivity">
<Button
android:text="Read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp"
android:id="@+id/buttonread"/>
<Button
android:text="Write"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp"
android:id="@+id/buttonwrite"/>
<Button
android:text="Get Counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp"
android:id="@+id/bCounter"/>
</LinearLayout>-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BMP Poller"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textspinbmp"/>
<Spinner
android:id="@+id/spinbmp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/poller" />
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GPS Mode"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textspingpsmode"/>
<Spinner
android:id="@+id/spingpsmode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/gpsmode"/>
<!--<Switch
android:text="BLE GPS Boost"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/swblegpsboost"/>-->
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GPS Poller"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textspingps"/>
<Spinner
android:id="@+id/spingps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/poller"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GPS BT"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textspingpssendbt"/>
<Spinner
android:id="@+id/spingpssendbt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/poller"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:context=".MainActivity"
android:focusableInTouchMode="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Calibrate BMP2GPS"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/textcalibratebmp2gps"/>
<EditText
android:id="@+id/editcalibratebmp2gps"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="0dp"
android:padding="10dp"
android:inputType="number" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Calibrate"
android:layout_margin="0dp"
android:padding="10dp"
android:layout_gravity="center_horizontal"
android:id="@+id/buttoncalibratebmp2gps"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>

View File

@ -0,0 +1,76 @@
<resources>
<string name="app_name">BLEGPS</string>
<string-array name="poller">
<!--<item>250 ms</item>
<item>500 ms</item>
<item>750 ms</item>-->
<item>1 s</item>
<item>2 s</item>
<item>3 s</item>
<item>4 s</item>
<item>5 s</item>
<item>6 s</item>
<item>7 s</item>
<item>8 s</item>
<item>9 s</item>
<item>10 s</item>
<item>20 s</item>
<item>30 s</item>
<item>40 s</item>
<item>50 s</item>
<item>1 min</item>
<item>2 min</item>
<item>3 min</item>
<item>4 min</item>
<item>5 min</item>
<item>6 min</item>
<item>7 min</item>
<item>8 min</item>
<item>9 min</item>
<item>10 min</item>
<item>20 min</item>
<item>30 min</item>
<item>40 min</item>
<item>50 min</item>
<item>60 min</item>
</string-array>
<string-array name="pollerval">
<!--<item>0.25</item>
<item>0.50</item>
<item>0.75</item>-->
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
<item>20</item>
<item>30</item>
<item>40</item>
<item>50</item>
<item>60</item>
<item>120</item>
<item>180</item>
<item>240</item>
<item>300</item>
<item>360</item>
<item>420</item>
<item>480</item>
<item>540</item>
<item>600</item>
<item>1200</item>
<item>1800</item>
<item>2400</item>
<item>3000</item>
<item>3600</item>
</string-array>
<string-array name="gpsmode">
<item>Auto</item>
<item>Normal</item>
<item>Eco</item>
</string-array>
</resources>

View File

@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@ -0,0 +1,17 @@
package net.nigreon.blegps
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

28
build.gradle Normal file
View File

@ -0,0 +1,28 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.31'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

15
gradle.properties Normal file
View File

@ -0,0 +1,15 @@
# 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=-Xmx256m
# 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
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

1
settings.gradle Normal file
View File

@ -0,0 +1 @@
include ':app'