Bluetooth added

pull/87/head
Andrew 2022-03-18 14:35:14 -04:00
rodzic de2978138a
commit dd76dcfc98
11 zmienionych plików z 359 dodań i 3 usunięć
base/src/main/java/com/rtbishop/look4sat/domain

Wyświetl plik

@ -7,6 +7,10 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<application
android:name=".presentation.MainApplication"
android:icon="@mipmap/ic_launcher"

Wyświetl plik

@ -38,6 +38,10 @@ class SettingsManager @Inject constructor(private val prefs: SharedPreferences)
const val keyRotator = "isRotatorEnabled"
const val keyRotatorAddress = "rotatorAddress"
const val keyRotatorPort = "rotatorPort"
const val keyBTEnabled = "isBTEnabled"
const val keyBTDeviceName = "BTDevice"
const val keyBTDeviceAddr = "BTDevice"
const val keyBTFormat = "BTFormat"
const val keyLatitude = "stationLat"
const val keyLongitude = "stationLon"
const val keyLocator = "stationQTH"
@ -148,6 +152,37 @@ class SettingsManager @Inject constructor(private val prefs: SharedPreferences)
prefs.edit { putString(keyRotatorPort, value) }
}
override fun getBTEnabled(): Boolean {
return prefs.getBoolean(keyBTEnabled, true)
}
override fun setBTEnabled(value: Boolean) {
prefs.edit { putBoolean(keyBTEnabled, value) }
}
override fun getBTDeviceAddr(): String {
return prefs.getString(keyBTDeviceAddr, null) ?: "00:0C:BF:13:80:5D"
}
override fun setBTDeviceAddr(value: String) {
prefs.edit { putString(keyBTDeviceAddr, value) }
}
override fun getBTDeviceName(): String {
return prefs.getString(keyBTDeviceName, null) ?: "Default"
}
override fun setBTDeviceName(value: String) {
prefs.edit { putString(keyBTDeviceName, value) }
}
override fun getBTFormat(): String {
return prefs.getString(keyBTFormat, null) ?: "W\$AZ \$EL"
}
override fun setBTFormat(value: String) {
prefs.edit { putString(keyBTFormat, value) }
}
override fun loadDataSources(): List<String> {
val sourcesList = prefs.getStringSet(keyDataSources, null)?.toList()
return if (sourcesList.isNullOrEmpty()) defaultSources else sourcesList.sortedDescending()

Wyświetl plik

@ -28,6 +28,7 @@ import com.rtbishop.look4sat.domain.predict.SatelliteManager
import com.rtbishop.look4sat.framework.LocationManager
import com.rtbishop.look4sat.framework.SettingsManager
import com.rtbishop.look4sat.framework.data.*
import com.rtbishop.look4sat.presentation.radarScreen.BTReporter
import com.rtbishop.look4sat.utility.DataParser
import com.rtbishop.look4sat.utility.DataReporter
import dagger.Module
@ -62,6 +63,10 @@ object BaseModule {
@Singleton
fun provideDataReporter(): DataReporter = DataReporter(CoroutineScope(Dispatchers.IO))
@Provides
@Singleton
fun provideBTReporter(): BTReporter = BTReporter(CoroutineScope(Dispatchers.IO))
@Provides
@Singleton
fun provideLocationManager(manager: LocationManager): ILocationManager = manager

Wyświetl plik

@ -0,0 +1,134 @@
package com.rtbishop.look4sat.presentation.radarScreen
import android.Manifest
import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import java.net.InetSocketAddress
import java.nio.ByteBuffer
import java.nio.channels.SocketChannel
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothSocket
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import java.io.OutputStream
import java.util.*
class BTReporter(private val reporterScope: CoroutineScope) {
private var rotationConnectBTJob: Job? = null
private var rotationReportingBT: Job? = null
private var satVisible=false
private var CRchar:Char = '\r'
private var NLchar:Char = '\n'
private var TBchar:Char = '\t'
private val bluetoothAdapter: BluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
private lateinit var mmOutStream: OutputStream
private val SPPID: UUID=UUID.fromString("00001101-0000-1000-8000-00805f9b34fb")
private var connected = false
private var connectInProgress = false
fun connectBTDevice(dev: String) {
if (!connected) {
rotationConnectBTJob = reporterScope.launch {
runCatching {
connectInProgress = true
val rotationBTDevice = bluetoothAdapter.getRemoteDevice(dev)
val sock = rotationBTDevice.createInsecureRfcommSocketToServiceRecord(SPPID)
sock.connect()
mmOutStream = sock.outputStream
connected = true
connectInProgress = false
Log.i("look4satBT", "Connected!")
}.onFailure { error: Throwable ->
Log.e("BT Error", "${error.message}")
}
}
}
}
fun isBTConnected():Boolean
{
return connected
}
fun connectInProg():Boolean
{
return connectInProgress
}
fun reportRotationBT(dev: String, fmt: String, AZ: Int, EL: Int) {
runCatching {
var azStr:String
var elStr:String
satVisible=(EL>1)
//Need to add leading zeros to string to ensure always 3 digits.
//Ideally this could be done via the format string but this will do for now.
if(AZ<100){
if(AZ<10){
azStr="00"
}
else
{
azStr="0"
}
azStr=azStr.plus(AZ.toString())
}
else
{
azStr=AZ.toString()
}
if(EL<100){
if(EL<10){
elStr="00"
}
else
{
elStr="0"
}
if(satVisible) {
elStr = elStr.plus(EL.toString())
}
else {
elStr="000"
}
}
else
{
elStr=EL.toString()
}
var buffer = fmt.replace("\$AZ",azStr)
buffer = buffer.replace("\$EL",elStr)
buffer = buffer.replace("\\r",CRchar.toString())
buffer = buffer.replace("\\n",NLchar.toString())
buffer = buffer.replace("\\t",TBchar.toString())
Log.i("Output is", buffer)
if(connected) {
Log.i("Sending BT", buffer)
this.mmOutStream.write(buffer.toByteArray())
Log.i("Sent", buffer)
}
}.onFailure { error: Throwable ->
Log.e("BT Error","${error.message}")
connected=false
}
}
}

Wyświetl plik

@ -18,6 +18,7 @@
package com.rtbishop.look4sat.presentation.radarScreen
import android.hardware.GeomagneticField
import android.util.Log
import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteManager
@ -41,6 +42,7 @@ import javax.inject.Inject
class RadarViewModel @Inject constructor(
private val orientationManager: OrientationManager,
private val reporter: DataReporter,
private val BTreporter: BTReporter,
private val satelliteManager: ISatelliteManager,
private val repository: IDataRepository,
private val settings: ISettingsManager
@ -60,6 +62,7 @@ class RadarViewModel @Inject constructor(
pass?.let { satPass ->
emit(satPass)
sendPassData(satPass)
sendPassDataBT(satPass)
processTransmitters(satPass)
}
}
@ -110,6 +113,28 @@ class RadarViewModel @Inject constructor(
}
}
private fun sendPassDataBT(satPass: SatPass) {
viewModelScope.launch {
while (isActive) {
val satPos = satelliteManager.getPosition(satPass.satellite, stationPos, Date().time)
if (settings.getBTEnabled()) {
val server = settings.getBTDeviceAddr()
if(BTreporter.isBTConnected()) {
val port = settings.getBTFormat()
val azimuth = satPos.azimuth.toDegrees().round(0).toInt()
val elevation = satPos.elevation.toDegrees().round(0).toInt()
BTreporter.reportRotationBT(server, port, azimuth, elevation)
}
else if(!BTreporter.connectInProg()) {
Log.i("look4satBT", "Attempting to connect...")
BTreporter.connectBTDevice(server)
}
}
delay(2000)
}
}
}
private fun processTransmitters(pass: SatPass) {
viewModelScope.launch {
delay(125)

Wyświetl plik

@ -17,12 +17,15 @@
*/
package com.rtbishop.look4sat.presentation.settingsScreen
//import com.rtbishop.look4sat.BuildConfig
import android.Manifest
import android.bluetooth.BluetoothAdapter
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.method.LinkMovementMethod
import android.view.View
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.widget.NestedScrollView
@ -31,7 +34,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.asLiveData
import androidx.navigation.fragment.findNavController
import com.rtbishop.look4sat.BuildConfig
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentSettingsBinding
import com.rtbishop.look4sat.domain.model.DataState
@ -42,6 +44,7 @@ import com.rtbishop.look4sat.utility.isValidIPv4
import com.rtbishop.look4sat.utility.isValidPort
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class SettingsFragment : Fragment(R.layout.fragment_settings) {
@ -70,7 +73,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
if (y > newY) settingsFab.hide() else settingsFab.show()
})
settingsAbout.aboutVersion.text =
String.format(getString(R.string.app_version), BuildConfig.VERSION_NAME)
String.format(getString(R.string.app_version), 0)
settingsBtnGithub.clickWithDebounce {
gotoUrl("https://github.com/rt-bishop/Look4Sat/")
}
@ -81,9 +84,12 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
gotoUrl("https://f-droid.org/en/packages/com.rtbishop.look4sat/")
}
}
setupLocationCard()
setupDataCard()
setupRemoteCard()
setupBTCard()
setupOtherCard()
setupOutroCard()
viewModel.stationPosition.asLiveData().observe(viewLifecycleOwner) { stationPos ->
@ -162,6 +168,32 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
}
}
private fun setupBTCard() {
binding.run {
settingsBtremote.BTremoteSwitch.apply {
isChecked = viewModel.getBTEnabled()
settingsBtremote.BTremoteAddress.isEnabled = isChecked
settingsBtremote.BTremoteFormat.isEnabled = isChecked
settingsBtremote.BTAddressEdit.setText(viewModel.getBTDeviceAddr())
settingsBtremote.BTFormatEdit.setText(viewModel.getBTFormat())
setOnCheckedChangeListener { _, isChecked ->
viewModel.setBTEnabled(isChecked)
settingsBtremote.BTremoteAddress.isEnabled = isChecked
settingsBtremote.BTremoteFormat.isEnabled = isChecked
}
}
settingsBtremote.BTAddressEdit.doOnTextChanged { text, _, _, _ ->
viewModel.setBTDeviceAddr(text.toString())
}
settingsBtremote.BTFormatEdit.doOnTextChanged { text, _, _, _ ->
viewModel.setBTFormat(text.toString())
}
}
}
private fun setupOtherCard() {
binding.run {
settingsOther.otherSwitchUtc.apply {

Wyświetl plik

@ -75,6 +75,22 @@ class SettingsViewModel @Inject constructor(
fun setRotatorPort(value: String) = settings.setRotatorPort(value)
fun getBTEnabled(): Boolean = settings.getBTEnabled()
fun setBTEnabled(value: Boolean) = settings.setBTEnabled(value)
fun getBTFormat(): String = settings.getBTFormat()
fun setBTFormat(value: String) = settings.setBTFormat(value)
fun getBTDeviceName(): String = settings.getBTDeviceName()
fun setBTDeviceName(value: String) = settings.setBTDeviceName(value)
fun getBTDeviceAddr(): String = settings.getBTDeviceAddr()
fun setBTDeviceAddr(value: String) = settings.setBTDeviceAddr(value)
fun getUpdateState() = repository.updateState
fun setUpdateHandled() = repository.setUpdateStateHandled()

Wyświetl plik

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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"
style="@style/SurfaceCard">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/BTremote_title"
style="@style/SettingsTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="12dp"
android:text="@string/BTremote_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/BTremote_switch"
style="@style/SettingsText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:minHeight="42dp"
android:text="@string/BTremote_switch"
app:layout_constraintEnd_toEndOf="@+id/BTremote_title"
app:layout_constraintStart_toStartOf="@+id/BTremote_title"
app:layout_constraintTop_toBottomOf="@+id/BTremote_title"
app:trackTint="@color/textDisabled" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/BTremote_address"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/BTremote_switch"
app:layout_constraintEnd_toEndOf="@+id/BTremote_title"
app:layout_constraintStart_toStartOf="@+id/BTremote_title"
tools:layout_editor_absoluteY="70dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/BT_address_edit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/BTremote_device_hint"
android:textColorHint="@color/textMain" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/BTremote_format"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/BTremote_address"
app:layout_constraintEnd_toEndOf="@+id/BTremote_title"
app:layout_constraintStart_toStartOf="@+id/BTremote_title"
tools:layout_editor_absoluteY="70dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/BT_format_edit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/BTremote_output_hint"
android:textColorHint="@color/textMain" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

Wyświetl plik

@ -82,6 +82,16 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/settings_data" />
<include
android:id="@+id/settings_btremote"
layout="@layout/card_btremote"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/view_default_margin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/settings_remote" />
<include
android:id="@+id/settings_other"
layout="@layout/card_other"
@ -90,7 +100,7 @@
android:layout_marginTop="@dimen/view_default_margin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/settings_remote" />
app:layout_constraintTop_toBottomOf="@+id/settings_btremote" />
<include
android:id="@+id/settings_outro"

Wyświetl plik

@ -139,6 +139,11 @@
<string name="remote_ip_hint">IP address</string>
<string name="remote_port_hint">Port</string>
<string name="BTremote_title">Bluetooth Output</string>
<string name="BTremote_switch">Enable Bluetooth</string>
<string name="BTremote_device_hint">Device</string>
<string name="BTremote_output_hint">Output format</string>
<string name="other_title">Other preferences</string>
<string name="other_switch_utc">Show pass time in UTC</string>
<string name="other_switch_sweep">Enable radar sweep animation</string>

Wyświetl plik

@ -77,6 +77,22 @@ interface ISettingsManager {
fun setRotatorPort(value: String)
fun getBTEnabled(): Boolean
fun setBTEnabled(value: Boolean)
fun getBTDeviceAddr(): String
fun setBTDeviceAddr(value: String)
fun getBTDeviceName(): String
fun setBTDeviceName(value: String)
fun getBTFormat(): String
fun setBTFormat(value: String)
fun loadDataSources(): List<String>
fun saveDataSources(sources: List<String>)