Further classes and dependencies cleanup
No files matched your search
@@ -6,9 +6,8 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 33
|
|
||||||
buildToolsVersion '33.0.0'
|
|
||||||
namespace 'com.rtbishop.look4sat'
|
namespace 'com.rtbishop.look4sat'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.rtbishop.look4sat"
|
applicationId "com.rtbishop.look4sat"
|
||||||
@@ -21,7 +20,6 @@ android {
|
|||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
useSupportLibrary true
|
useSupportLibrary true
|
||||||
}
|
}
|
||||||
|
|
||||||
kapt {
|
kapt {
|
||||||
arguments {
|
arguments {
|
||||||
arg("room.schemaLocation", "$projectDir/schemas")
|
arg("room.schemaLocation", "$projectDir/schemas")
|
||||||
@@ -31,6 +29,9 @@ android {
|
|||||||
correctErrorTypes true
|
correctErrorTypes true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
buildFeatures {
|
||||||
|
compose true
|
||||||
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
@@ -38,29 +39,25 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buildFeatures {
|
|
||||||
compose true
|
|
||||||
viewBinding true
|
|
||||||
}
|
|
||||||
composeOptions {
|
|
||||||
kotlinCompilerExtensionVersion compose_compiler_version
|
|
||||||
}
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
}
|
}
|
||||||
|
composeOptions {
|
||||||
|
kotlinCompilerExtensionVersion compose_compiler_version
|
||||||
|
}
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||||
}
|
}
|
||||||
testOptions {
|
|
||||||
animationsDisabled true
|
|
||||||
unitTests.includeAndroidResources true
|
|
||||||
}
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
resources {
|
resources {
|
||||||
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
testOptions {
|
||||||
|
animationsDisabled true
|
||||||
|
unitTests.includeAndroidResources true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
@@ -69,43 +66,34 @@ kotlin {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(":base")
|
implementation project(":base")
|
||||||
implementation "androidx.core:core-ktx:$core_ktx_version"
|
// Android
|
||||||
implementation "androidx.core:core-splashscreen:$core_splashscreen_version"
|
implementation "androidx.core:core-splashscreen:$splashscreen_version"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:$constraint_version"
|
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
|
||||||
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
|
|
||||||
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
|
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
implementation "androidx.room:room-ktx:$room_version"
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
implementation "com.google.dagger:hilt-android:$hilt_android_version"
|
||||||
kapt "com.google.dagger:hilt-compiler:$hilt_version"
|
kapt "com.google.dagger:hilt-compiler:$hilt_android_version"
|
||||||
implementation "com.jakewharton.timber:timber:$timber_version"
|
// Compose
|
||||||
implementation "org.osmdroid:osmdroid-android:$osmdroid_version"
|
|
||||||
|
|
||||||
implementation "androidx.activity:activity-compose:$activity_compose_version"
|
implementation "androidx.activity:activity-compose:$activity_compose_version"
|
||||||
implementation "androidx.compose.animation:animation:$compose_version"
|
implementation "androidx.compose.animation:animation:$compose_version"
|
||||||
implementation "androidx.compose.compiler:compiler:$compose_compiler_version"
|
implementation "androidx.compose.compiler:compiler:$compose_compiler_version"
|
||||||
implementation "androidx.compose.material:material:$material_version"
|
|
||||||
implementation "androidx.compose.material3:material3:$material3_version"
|
implementation "androidx.compose.material3:material3:$material3_version"
|
||||||
implementation "androidx.compose.runtime:runtime:$compose_version"
|
implementation "androidx.compose.runtime:runtime:$compose_version"
|
||||||
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
|
|
||||||
implementation "androidx.compose.ui:ui:$compose_version"
|
implementation "androidx.compose.ui:ui:$compose_version"
|
||||||
implementation "androidx.compose.ui:ui-tooling:$compose_version"
|
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
|
||||||
implementation "androidx.hilt:hilt-navigation-compose:$hilt_compose_version"
|
implementation "androidx.hilt:hilt-navigation-compose:$hilt_compose_version"
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
|
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
|
||||||
implementation "androidx.navigation:navigation-compose:$navigation_version"
|
implementation "androidx.navigation:navigation-compose:$navigation_version"
|
||||||
|
// Utility
|
||||||
|
implementation "com.jakewharton.timber:timber:$timber_version"
|
||||||
|
implementation "org.osmdroid:osmdroid-android:$osmdroid_version"
|
||||||
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
|
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
|
||||||
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
|
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
|
||||||
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakcanary_version"
|
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakcanary_version"
|
||||||
|
// Test
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.mockito:mockito-core:$mockito_version"
|
testImplementation "org.mockito:mockito-core:$mockito_version"
|
||||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_test_version"
|
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_test_version"
|
||||||
|
|
||||||
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
|
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
|
||||||
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_test_version"
|
|
||||||
androidTestImplementation "org.mockito:mockito-android:$mockito_version"
|
androidTestImplementation "org.mockito:mockito-android:$mockito_version"
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
@@ -12,13 +13,16 @@
|
|||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".presentation.MainApplication"
|
android:name=".presentation.MainApplication"
|
||||||
android:allowBackup="false"
|
android:allowBackup="true"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Look4Sat.Main">
|
android:theme="@style/Theme.Look4Sat.Main"
|
||||||
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
android:name=".presentation.MainActivity"
|
android:name=".presentation.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@@ -30,4 +34,4 @@
|
|||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
Before Width: | Height: | Size: 25 KiB |
@@ -22,8 +22,6 @@ import android.content.Context
|
|||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.SystemClock
|
|
||||||
import android.view.View
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
@@ -47,15 +45,3 @@ class MainActivity : ComponentActivity() {
|
|||||||
super.attachBaseContext(newBase)
|
super.attachBaseContext(newBase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun View.clickWithDebounce(debounceTime: Long = 875L, action: () -> Unit) {
|
|
||||||
this.setOnClickListener(object : View.OnClickListener {
|
|
||||||
private var lastClickTime: Long = 0
|
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
|
||||||
if (SystemClock.elapsedRealtime() - lastClickTime < debounceTime) return
|
|
||||||
else action()
|
|
||||||
lastClickTime = SystemClock.elapsedRealtime()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ import androidx.compose.material.icons.outlined.PlayArrow
|
|||||||
import androidx.compose.material.icons.outlined.Search
|
import androidx.compose.material.icons.outlined.Search
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -40,7 +40,7 @@ fun EntriesScreen(
|
|||||||
viewModel: EntriesViewModel = hiltViewModel(),
|
viewModel: EntriesViewModel = hiltViewModel(),
|
||||||
passesViewModel: PassesViewModel = hiltViewModel()
|
passesViewModel: PassesViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
val state = viewModel.satData.observeAsState(DataState.Loading)
|
val state = viewModel.satData.collectAsState(initial = DataState.Loading)
|
||||||
|
|
||||||
val showDialog = rememberSaveable { mutableStateOf(false) }
|
val showDialog = rememberSaveable { mutableStateOf(false) }
|
||||||
val toggleDialog = { showDialog.value = showDialog.value.not() }
|
val toggleDialog = { showDialog.value = showDialog.value.not() }
|
||||||
|
|||||||
@@ -17,50 +17,59 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.presentation.entriesScreen
|
package com.rtbishop.look4sat.presentation.entriesScreen
|
||||||
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||||
import com.rtbishop.look4sat.domain.model.DataState
|
import com.rtbishop.look4sat.domain.model.DataState
|
||||||
import com.rtbishop.look4sat.domain.model.SatItem
|
import com.rtbishop.look4sat.domain.model.SatItem
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class EntriesViewModel @Inject constructor(
|
class EntriesViewModel @Inject constructor(
|
||||||
private val repository: IDataRepository,
|
private val repository: IDataRepository, private val settings: ISettingsManager
|
||||||
private val settings: ISettingsManager
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val satType = MutableLiveData("All")
|
private val satType = MutableStateFlow("All")
|
||||||
// private val transModes = MutableLiveData(settings.loadModesSelection())
|
// private val transModes = MutableLiveData(settings.loadModesSelection())
|
||||||
private val currentQuery = MutableLiveData(String())
|
private val currentQuery = MutableStateFlow(String())
|
||||||
private val itemsFromRepo = liveData {
|
private val itemsFromRepo = MutableStateFlow<List<SatItem>>(emptyList())
|
||||||
delay(250)
|
private val itemsWithType = satType.flatMapLatest { type ->
|
||||||
emit(loadEntriesWithSelection())
|
|
||||||
} as MutableLiveData
|
|
||||||
private val itemsWithType = satType.switchMap { type ->
|
|
||||||
itemsFromRepo.map { items -> filterByType(items, type) }
|
itemsFromRepo.map { items -> filterByType(items, type) }
|
||||||
}
|
}
|
||||||
// private val itemsWithModes = transModes.switchMap { modes ->
|
// private val itemsWithModes = transModes.switchMap { modes ->
|
||||||
// itemsWithType.map { items -> filterByModes(items, modes) }
|
// itemsWithType.map { items -> filterByModes(items, modes) }
|
||||||
// }
|
// }
|
||||||
private val itemsWithQuery = currentQuery.switchMap { query ->
|
private val itemsWithQuery = currentQuery.flatMapLatest { query ->
|
||||||
itemsWithType.map { items -> filterByQuery(items, query) }
|
itemsWithType.map { items -> filterByQuery(items, query) }
|
||||||
}
|
}
|
||||||
val satData = itemsWithQuery.map { items -> DataState.Success(items) }
|
val satData = itemsWithQuery.map { items -> DataState.Success(items) }
|
||||||
val satTypes: List<String> = settings.sourcesMap.keys.sorted()
|
val satTypes: List<String> = settings.sourcesMap.keys.sorted()
|
||||||
|
|
||||||
fun getSatType() = satType.value ?: "All"
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
delay(250)
|
||||||
|
itemsFromRepo.value = loadEntriesWithSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSatType() = satType.value
|
||||||
|
|
||||||
fun setSatType(type: String) {
|
fun setSatType(type: String) {
|
||||||
satType.value = type
|
satType.value = type
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectCurrentItems(selectAll: Boolean) {
|
fun selectCurrentItems(selectAll: Boolean) {
|
||||||
itemsWithQuery.value?.let { itemsWithQuery ->
|
viewModelScope.launch {
|
||||||
updateSelection(itemsWithQuery.map { item -> item.catnum }, selectAll)
|
val lastValue = itemsWithQuery.last()
|
||||||
|
updateSelection(lastValue.map { item -> item.catnum }, selectAll)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +83,13 @@ class EntriesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun saveSelection(): List<Int> {
|
fun saveSelection(): List<Int> {
|
||||||
return itemsFromRepo.value?.let { itemsAll ->
|
return itemsFromRepo.value.let { itemsAll ->
|
||||||
itemsAll.filter { item -> item.isSelected }.map { item -> item.catnum }
|
itemsAll.filter { item -> item.isSelected }.map { item -> item.catnum }
|
||||||
} ?: emptyList()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
|
fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
|
||||||
itemsFromRepo.value?.let { itemsAll ->
|
itemsFromRepo.value.let { itemsAll ->
|
||||||
val copiedList = itemsAll.map { item -> item.copy() }
|
val copiedList = itemsAll.map { item -> item.copy() }
|
||||||
catNums.forEach { catnum ->
|
catNums.forEach { catnum ->
|
||||||
copiedList.find { item -> item.catnum == catnum }?.isSelected = isSelected
|
copiedList.find { item -> item.catnum == catnum }?.isSelected = isSelected
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -63,10 +63,10 @@ fun MapScreen(viewModel: MapViewModel = hiltViewModel()) {
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
Configuration.getInstance().load(context, viewModel.getPreferences())
|
Configuration.getInstance().load(context, viewModel.getPreferences())
|
||||||
viewModel.selectDefaultSatellite(-1)
|
viewModel.selectDefaultSatellite(-1)
|
||||||
val stationPos = viewModel.stationPos.observeAsState()
|
val stationPos = viewModel.stationPos.collectAsState(initial = null)
|
||||||
val positions = viewModel.positions.observeAsState()
|
val positions = viewModel.positions.collectAsState(initial = null)
|
||||||
val satTrack = viewModel.track.observeAsState()
|
val satTrack = viewModel.track.collectAsState(initial = null)
|
||||||
val footprint = viewModel.footprint.observeAsState()
|
val footprint = viewModel.footprint.collectAsState(initial = null)
|
||||||
val positionClick = { satellite: Satellite -> viewModel.selectSatellite(satellite) }
|
val positionClick = { satellite: Satellite -> viewModel.selectSatellite(satellite) }
|
||||||
Timber.d("MapScreen recomposition")
|
Timber.d("MapScreen recomposition")
|
||||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
@@ -195,8 +195,7 @@ private fun MapView(modifier: Modifier = Modifier, update: ((map: MapView) -> Un
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun rememberMapViewWithLifecycle(context: Context = LocalContext.current): MapView {
|
private fun rememberMapViewWithLifecycle(context: Context = LocalContext.current): MapView {
|
||||||
// add clipToOutline = true if needed
|
val mapView = remember { MapView(context) }.apply {
|
||||||
val mapView = remember { MapView(context).apply { id = R.id.osm_map_view } }.apply {
|
|
||||||
setMultiTouchControls(true)
|
setMultiTouchControls(true)
|
||||||
setTileSource(TileSourceFactory.WIKIMEDIA)
|
setTileSource(TileSourceFactory.WIKIMEDIA)
|
||||||
minZoomLevel = getMinZoom(resources.displayMetrics.heightPixels) + 0.25
|
minZoomLevel = getMinZoom(resources.displayMetrics.heightPixels) + 0.25
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
package com.rtbishop.look4sat.presentation.mapScreen
|
package com.rtbishop.look4sat.presentation.mapScreen
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
import com.rtbishop.look4sat.domain.ISatelliteManager
|
||||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||||
@@ -30,8 +31,12 @@ import com.rtbishop.look4sat.utility.toDegrees
|
|||||||
import com.rtbishop.look4sat.utility.toTimerString
|
import com.rtbishop.look4sat.utility.toTimerString
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.collections.set
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@@ -50,23 +55,23 @@ class MapViewModel @Inject constructor(
|
|||||||
private var dataUpdateRate = 1000L
|
private var dataUpdateRate = 1000L
|
||||||
private var selectedSatellite: Satellite? = null
|
private var selectedSatellite: Satellite? = null
|
||||||
|
|
||||||
val stationPos = liveData {
|
val stationPos = flow {
|
||||||
val osmLat = clipLat(stationPosition.lat)
|
val osmLat = clipLat(stationPosition.lat)
|
||||||
val osmLon = clipLon(stationPosition.lon)
|
val osmLon = clipLon(stationPosition.lon)
|
||||||
emit(GeoPos(osmLat, osmLon))
|
emit(GeoPos(osmLat, osmLon))
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _track = MutableLiveData<List<List<GeoPos>>>()
|
private val _track = MutableSharedFlow<List<List<GeoPos>>>()
|
||||||
val track: LiveData<List<List<GeoPos>>> = _track
|
val track: SharedFlow<List<List<GeoPos>>> = _track
|
||||||
|
|
||||||
private val _footprint = MutableLiveData<SatPos>()
|
private val _footprint = MutableSharedFlow<SatPos>()
|
||||||
val footprint: LiveData<SatPos> = _footprint
|
val footprint: SharedFlow<SatPos> = _footprint
|
||||||
|
|
||||||
private val _mapData = MutableLiveData<MapData>()
|
private val _mapData = MutableSharedFlow<MapData>()
|
||||||
val mapData: LiveData<MapData> = this._mapData
|
val mapData: SharedFlow<MapData> = _mapData
|
||||||
|
|
||||||
private val _positions = MutableLiveData<Map<Satellite, GeoPos>>()
|
private val _positions = MutableSharedFlow<Map<Satellite, GeoPos>>()
|
||||||
val positions: LiveData<Map<Satellite, GeoPos>> = _positions
|
val positions: SharedFlow<Map<Satellite, GeoPos>> = _positions
|
||||||
|
|
||||||
fun getPreferences() = preferences
|
fun getPreferences() = preferences
|
||||||
|
|
||||||
@@ -144,7 +149,7 @@ class MapViewModel @Inject constructor(
|
|||||||
currentTrack.add(currentPosition)
|
currentTrack.add(currentPosition)
|
||||||
}
|
}
|
||||||
satTracks.add(currentTrack)
|
satTracks.add(currentTrack)
|
||||||
_track.postValue(satTracks)
|
_track.emit(satTracks)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getPositions(satellites: List<Satellite>, pos: GeoPos, date: Date) {
|
private suspend fun getPositions(satellites: List<Satellite>, pos: GeoPos, date: Date) {
|
||||||
@@ -155,12 +160,12 @@ class MapViewModel @Inject constructor(
|
|||||||
val osmLon = clipLon(satPos.longitude.toDegrees())
|
val osmLon = clipLon(satPos.longitude.toDegrees())
|
||||||
positions[satellite] = GeoPos(osmLat, osmLon)
|
positions[satellite] = GeoPos(osmLat, osmLon)
|
||||||
}
|
}
|
||||||
_positions.postValue(positions)
|
_positions.emit(positions)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
|
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
|
||||||
val satPos = satelliteManager.getPosition(satellite, pos, date.time)
|
val satPos = satelliteManager.getPosition(satellite, pos, date.time)
|
||||||
_footprint.postValue(satPos)
|
_footprint.emit(satPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getSatData(sat: Satellite, pos: GeoPos, date: Date) {
|
private suspend fun getSatData(sat: Satellite, pos: GeoPos, date: Date) {
|
||||||
@@ -202,7 +207,7 @@ class MapViewModel @Inject constructor(
|
|||||||
phase,
|
phase,
|
||||||
visibility
|
visibility
|
||||||
)
|
)
|
||||||
_mapData.postValue(satData)
|
_mapData.emit(satData)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clipLat(latitude: Double): Double {
|
private fun clipLat(latitude: Double): Double {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@file:OptIn(ExperimentalMaterialApi::class)
|
|
||||||
|
|
||||||
package com.rtbishop.look4sat.presentation.passesScreen
|
package com.rtbishop.look4sat.presentation.passesScreen
|
||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
@@ -8,14 +6,9 @@ import androidx.compose.foundation.clickable
|
|||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshState
|
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -34,16 +27,19 @@ import com.rtbishop.look4sat.domain.predict.NearEarthSat
|
|||||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||||
import com.rtbishop.look4sat.presentation.MainTheme
|
import com.rtbishop.look4sat.presentation.MainTheme
|
||||||
|
import com.rtbishop.look4sat.presentation.pullRefresh.PullRefreshIndicator
|
||||||
|
import com.rtbishop.look4sat.presentation.pullRefresh.PullRefreshState
|
||||||
|
import com.rtbishop.look4sat.presentation.pullRefresh.pullRefresh
|
||||||
|
import com.rtbishop.look4sat.presentation.pullRefresh.rememberPullRefreshState
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
private val sdf = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
|
private val sdf = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PassesScreen(navToRadar: () -> Unit, viewModel: PassesViewModel = hiltViewModel()) {
|
fun PassesScreen(navToRadar: () -> Unit, viewModel: PassesViewModel = hiltViewModel()) {
|
||||||
val state = viewModel.passes.observeAsState()
|
val state = viewModel.passes.collectAsState(initial = null)
|
||||||
val timerText = viewModel.timerText.observeAsState()
|
val timerText = viewModel.timerText.collectAsState(initial = null)
|
||||||
val isRefreshing = state.value is DataState.Loading
|
val isRefreshing = state.value is DataState.Loading
|
||||||
val refreshState = rememberPullRefreshState(refreshing = isRefreshing,
|
val refreshState = rememberPullRefreshState(refreshing = isRefreshing,
|
||||||
onRefresh = { viewModel.calculatePasses() })
|
onRefresh = { viewModel.calculatePasses() })
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.presentation.passesScreen
|
package com.rtbishop.look4sat.presentation.passesScreen
|
||||||
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
import com.rtbishop.look4sat.domain.ISatelliteManager
|
||||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||||
@@ -26,6 +27,8 @@ import com.rtbishop.look4sat.domain.predict.SatPass
|
|||||||
import com.rtbishop.look4sat.utility.toTimerString
|
import com.rtbishop.look4sat.utility.toTimerString
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@@ -35,13 +38,12 @@ class PassesViewModel @Inject constructor(
|
|||||||
private val settings: ISettingsManager
|
private val settings: ISettingsManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _passes = MutableLiveData<DataState<List<SatPass>>>()
|
private val _passes = MutableStateFlow<DataState<List<SatPass>>>(DataState.Loading)
|
||||||
private val timerData = Triple("Next - Id:Null", "Name: Null", "00:00:00")
|
private val timerData = Triple("Next - Id:Null", "Name: Null", "00:00:00")
|
||||||
private val _timerText = MutableLiveData<Triple<String, String, String>>()
|
private val _timerText = MutableStateFlow(timerData)
|
||||||
private var passesProcessing: Job? = null
|
private var passesProcessing: Job? = null
|
||||||
val entriesTotal: LiveData<Int> = repository.getEntriesTotal().asLiveData()
|
val passes: StateFlow<DataState<List<SatPass>>> = _passes
|
||||||
val passes: LiveData<DataState<List<SatPass>>> = _passes
|
val timerText: StateFlow<Triple<String, String, String>?> = _timerText
|
||||||
val timerText: LiveData<Triple<String, String, String>> = _timerText
|
|
||||||
|
|
||||||
fun getHoursAhead() = settings.getHoursAhead()
|
fun getHoursAhead() = settings.getHoursAhead()
|
||||||
|
|
||||||
@@ -63,18 +65,18 @@ class PassesViewModel @Inject constructor(
|
|||||||
val name = nextPass.name
|
val name = nextPass.name
|
||||||
val millisBeforeStart = nextPass.aosTime.minus(timeNow)
|
val millisBeforeStart = nextPass.aosTime.minus(timeNow)
|
||||||
val timerString = millisBeforeStart.toTimerString()
|
val timerString = millisBeforeStart.toTimerString()
|
||||||
_timerText.postValue(Triple("Next - Id:$catNum", name, timerString))
|
_timerText.emit(Triple("Next - Id:$catNum", name, timerString))
|
||||||
} catch (e: NoSuchElementException) {
|
} catch (e: NoSuchElementException) {
|
||||||
val lastPass = newPasses.last()
|
val lastPass = newPasses.last()
|
||||||
val catNum = lastPass.catNum
|
val catNum = lastPass.catNum
|
||||||
val name = lastPass.name
|
val name = lastPass.name
|
||||||
val millisBeforeEnd = lastPass.losTime.minus(timeNow)
|
val millisBeforeEnd = lastPass.losTime.minus(timeNow)
|
||||||
val timerString = millisBeforeEnd.toTimerString()
|
val timerString = millisBeforeEnd.toTimerString()
|
||||||
_timerText.postValue(Triple("Next - Id:$catNum", name, timerString))
|
_timerText.emit(Triple("Next - Id:$catNum", name, timerString))
|
||||||
}
|
}
|
||||||
} else _timerText.postValue(timerData)
|
} else _timerText.emit(timerData)
|
||||||
|
|
||||||
_passes.postValue(DataState.Success(newPasses))
|
_passes.emit(DataState.Success(newPasses))
|
||||||
delay(1000)
|
delay(1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +99,7 @@ class PassesViewModel @Inject constructor(
|
|||||||
selection: List<Int>? = null
|
selection: List<Int>? = null
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_passes.postValue(DataState.Loading)
|
_passes.emit(DataState.Loading)
|
||||||
// _timerText.postValue(timerDefaultText)
|
// _timerText.postValue(timerDefaultText)
|
||||||
passesProcessing?.cancelAndJoin()
|
passesProcessing?.cancelAndJoin()
|
||||||
selection?.let { items -> settings.saveEntriesSelection(items) }
|
selection?.let { items -> settings.saveEntriesSelection(items) }
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.rtbishop.look4sat.presentation.pullRefresh
|
||||||
|
|
||||||
|
import androidx.compose.animation.Crossfade
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.Canvas
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Rect
|
||||||
|
import androidx.compose.ui.geometry.center
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Path
|
||||||
|
import androidx.compose.ui.graphics.PathFillType
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||||
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
|
import androidx.compose.ui.graphics.drawscope.rotate
|
||||||
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default indicator for Compose pull-to-refresh, based on Android's SwipeRefreshLayout.
|
||||||
|
* @param refreshing A boolean representing whether a refresh is occurring.
|
||||||
|
* @param state The [PullRefreshState] which controls where and how the indicator will be drawn.
|
||||||
|
* @param modifier Modifiers for the indicator.
|
||||||
|
* @param backgroundColor The color of the indicator's background.
|
||||||
|
* @param contentColor The color of the indicator's arc and arrow.
|
||||||
|
* @param scale A boolean controlling whether the indicator's size scales with pull progress or not.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun PullRefreshIndicator(
|
||||||
|
refreshing: Boolean,
|
||||||
|
state: PullRefreshState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
backgroundColor: Color = MaterialTheme.colorScheme.surface,
|
||||||
|
contentColor: Color = contentColorFor(backgroundColor),
|
||||||
|
scale: Boolean = false
|
||||||
|
) {
|
||||||
|
val showElevation by remember(refreshing, state) {
|
||||||
|
derivedStateOf { refreshing || state.position > 0.5f }
|
||||||
|
}
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
modifier = modifier
|
||||||
|
.size(IndicatorSize)
|
||||||
|
.pullRefreshIndicatorTransform(state, scale),
|
||||||
|
shape = SpinnerShape,
|
||||||
|
color = backgroundColor,
|
||||||
|
shadowElevation = if (showElevation) Elevation else 0.dp,
|
||||||
|
) {
|
||||||
|
Crossfade(
|
||||||
|
targetState = refreshing, animationSpec = tween(durationMillis = CrossfadeDurationMs)
|
||||||
|
) { refreshing ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
val spinnerSize = (ArcRadius + StrokeWidth).times(2)
|
||||||
|
|
||||||
|
if (refreshing) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
color = contentColor,
|
||||||
|
strokeWidth = StrokeWidth,
|
||||||
|
modifier = Modifier.size(spinnerSize),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
CircularArrowIndicator(state, contentColor, Modifier.size(spinnerSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifier.size MUST be specified.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
private fun CircularArrowIndicator(
|
||||||
|
state: PullRefreshState,
|
||||||
|
color: Color,
|
||||||
|
modifier: Modifier,
|
||||||
|
) {
|
||||||
|
val path = remember { Path().apply { fillType = PathFillType.EvenOdd } }
|
||||||
|
|
||||||
|
Canvas(modifier.semantics { contentDescription = "Refreshing" }) {
|
||||||
|
val values = ArrowValues(state.progress)
|
||||||
|
|
||||||
|
rotate(degrees = values.rotation) {
|
||||||
|
val arcRadius = ArcRadius.toPx() + StrokeWidth.toPx() / 2f
|
||||||
|
val arcBounds = Rect(
|
||||||
|
size.center.x - arcRadius,
|
||||||
|
size.center.y - arcRadius,
|
||||||
|
size.center.x + arcRadius,
|
||||||
|
size.center.y + arcRadius
|
||||||
|
)
|
||||||
|
drawArc(
|
||||||
|
color = color,
|
||||||
|
alpha = values.alpha,
|
||||||
|
startAngle = values.startAngle,
|
||||||
|
sweepAngle = values.endAngle - values.startAngle,
|
||||||
|
useCenter = false,
|
||||||
|
topLeft = arcBounds.topLeft,
|
||||||
|
size = arcBounds.size,
|
||||||
|
style = Stroke(
|
||||||
|
width = StrokeWidth.toPx(), cap = StrokeCap.Square
|
||||||
|
)
|
||||||
|
)
|
||||||
|
drawArrow(path, arcBounds, color, values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
private class ArrowValues(
|
||||||
|
val alpha: Float,
|
||||||
|
val rotation: Float,
|
||||||
|
val startAngle: Float,
|
||||||
|
val endAngle: Float,
|
||||||
|
val scale: Float
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun ArrowValues(progress: Float): ArrowValues {
|
||||||
|
// Discard first 40% of progress. Scale remaining progress to full range between 0 and 100%.
|
||||||
|
val adjustedPercent = max(min(1f, progress) - 0.4f, 0f) * 5 / 3
|
||||||
|
// How far beyond the threshold pull has gone, as a percentage of the threshold.
|
||||||
|
val overshootPercent = abs(progress) - 1.0f
|
||||||
|
// Limit the overshoot to 200%. Linear between 0 and 200.
|
||||||
|
val linearTension = overshootPercent.coerceIn(0f, 2f)
|
||||||
|
// Non-linear tension. Increases with linearTension, but at a decreasing rate.
|
||||||
|
val tensionPercent = linearTension - linearTension.pow(2) / 4
|
||||||
|
|
||||||
|
// Calculations based on SwipeRefreshLayout specification.
|
||||||
|
val alpha = progress.coerceIn(0f, 1f)
|
||||||
|
val endTrim = adjustedPercent * MaxProgressArc
|
||||||
|
val rotation = (-0.25f + 0.4f * adjustedPercent + tensionPercent) * 0.5f
|
||||||
|
val startAngle = rotation * 360
|
||||||
|
val endAngle = (rotation + endTrim) * 360
|
||||||
|
val scale = min(1f, adjustedPercent)
|
||||||
|
|
||||||
|
return ArrowValues(alpha, rotation, startAngle, endAngle, scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawArrow(arrow: Path, bounds: Rect, color: Color, values: ArrowValues) {
|
||||||
|
arrow.reset()
|
||||||
|
arrow.moveTo(0f, 0f) // Move to left corner
|
||||||
|
arrow.lineTo(x = ArrowWidth.toPx() * values.scale, y = 0f) // Line to right corner
|
||||||
|
|
||||||
|
// Line to tip of arrow
|
||||||
|
arrow.lineTo(
|
||||||
|
x = ArrowWidth.toPx() * values.scale / 2, y = ArrowHeight.toPx() * values.scale
|
||||||
|
)
|
||||||
|
|
||||||
|
val radius = min(bounds.width, bounds.height) / 2f
|
||||||
|
val inset = ArrowWidth.toPx() * values.scale / 2f
|
||||||
|
arrow.translate(
|
||||||
|
Offset(
|
||||||
|
x = radius + bounds.center.x - inset, y = bounds.center.y + StrokeWidth.toPx() / 2f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
arrow.close()
|
||||||
|
rotate(degrees = values.endAngle) {
|
||||||
|
drawPath(path = arrow, color = color, alpha = values.alpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val CrossfadeDurationMs = 100
|
||||||
|
private const val MaxProgressArc = 0.8f
|
||||||
|
|
||||||
|
private val IndicatorSize = 40.dp
|
||||||
|
private val SpinnerShape = CircleShape
|
||||||
|
private val ArcRadius = 7.5.dp
|
||||||
|
private val StrokeWidth = 2.5.dp
|
||||||
|
private val ArrowWidth = 10.dp
|
||||||
|
private val ArrowHeight = 5.dp
|
||||||
|
private val Elevation = 6.dp
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.rtbishop.look4sat.presentation.pullRefresh
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.composed
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
|
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||||
|
import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.Drag
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
|
import androidx.compose.ui.platform.debugInspectorInfo
|
||||||
|
import androidx.compose.ui.platform.inspectable
|
||||||
|
import androidx.compose.ui.unit.Velocity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A modifier for translating the position and scaling the size of a pull-to-refresh indicator
|
||||||
|
* based on the given [PullRefreshState].
|
||||||
|
* @param state The [PullRefreshState] which determines the position of the indicator.
|
||||||
|
* @param scale A boolean controlling whether the indicator's size scales with pull progress or not.
|
||||||
|
*/
|
||||||
|
fun Modifier.pullRefreshIndicatorTransform(
|
||||||
|
state: PullRefreshState,
|
||||||
|
scale: Boolean = false,
|
||||||
|
) = composed(inspectorInfo = debugInspectorInfo {
|
||||||
|
name = "pullRefreshIndicatorTransform"
|
||||||
|
properties["state"] = state
|
||||||
|
properties["scale"] = scale
|
||||||
|
}) {
|
||||||
|
var height by remember { mutableStateOf(0) }
|
||||||
|
|
||||||
|
Modifier
|
||||||
|
.onSizeChanged { height = it.height }
|
||||||
|
.graphicsLayer {
|
||||||
|
translationY = state.position - height
|
||||||
|
|
||||||
|
if (scale && !state.refreshing) {
|
||||||
|
val scaleFraction = LinearOutSlowInEasing
|
||||||
|
.transform(state.position / state.threshold)
|
||||||
|
.coerceIn(0f, 1f)
|
||||||
|
scaleX = scaleFraction
|
||||||
|
scaleY = scaleFraction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PullRefresh modifier to be used in conjunction with [PullRefreshState]. Provides a connection
|
||||||
|
* to the nested scroll system. Based on Android's SwipeRefreshLayout.
|
||||||
|
* @param state The [PullRefreshState] associated with this pull-to-refresh component.
|
||||||
|
* The state will be updated by this modifier.
|
||||||
|
* @param enabled If not enabled, all scroll delta and fling velocity will be ignored.
|
||||||
|
*/
|
||||||
|
fun Modifier.pullRefresh(
|
||||||
|
state: PullRefreshState, enabled: Boolean = true
|
||||||
|
) = inspectable(inspectorInfo = debugInspectorInfo {
|
||||||
|
name = "pullRefresh"
|
||||||
|
properties["state"] = state
|
||||||
|
properties["enabled"] = enabled
|
||||||
|
}) {
|
||||||
|
Modifier.pullRefresh(state::onPull, { state.onRelease() }, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A modifier for building pull-to-refresh components. Provides a connection to the nested scroll
|
||||||
|
* system.
|
||||||
|
* @param onPull Callback for dispatching vertical scroll delta, takes float pullDelta as argument.
|
||||||
|
* Positive delta (pulling down) is dispatched only if the child does not consume it (i.e. pulling
|
||||||
|
* down despite being at the top of a scrollable component), whereas negative delta (swiping up) is
|
||||||
|
* dispatched first (in case it is needed to push the indicator back up), and then whatever is not
|
||||||
|
* consumed is passed on to the child.
|
||||||
|
* @param onRelease Callback for when drag is released, takes float flingVelocity as argument.
|
||||||
|
* @param enabled If not enabled, all scroll delta and fling velocity will be ignored and neither
|
||||||
|
* [onPull] nor [onRelease] will be invoked.
|
||||||
|
*/
|
||||||
|
fun Modifier.pullRefresh(
|
||||||
|
onPull: (pullDelta: Float) -> Float,
|
||||||
|
onRelease: suspend (flingVelocity: Float) -> Unit,
|
||||||
|
enabled: Boolean = true
|
||||||
|
) = inspectable(inspectorInfo = debugInspectorInfo {
|
||||||
|
name = "pullRefresh"
|
||||||
|
properties["onPull"] = onPull
|
||||||
|
properties["onRelease"] = onRelease
|
||||||
|
properties["enabled"] = enabled
|
||||||
|
}) {
|
||||||
|
Modifier.nestedScroll(PullRefreshNestedScrollConnection(onPull, onRelease, enabled))
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PullRefreshNestedScrollConnection(
|
||||||
|
private val onPull: (pullDelta: Float) -> Float,
|
||||||
|
private val onRelease: suspend (flingVelocity: Float) -> Unit,
|
||||||
|
private val enabled: Boolean
|
||||||
|
) : NestedScrollConnection {
|
||||||
|
|
||||||
|
override fun onPreScroll(
|
||||||
|
available: Offset, source: NestedScrollSource
|
||||||
|
): Offset = when {
|
||||||
|
!enabled -> Offset.Zero
|
||||||
|
source == Drag && available.y < 0 -> Offset(0f, onPull(available.y)) // Swiping up
|
||||||
|
else -> Offset.Zero
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostScroll(
|
||||||
|
consumed: Offset, available: Offset, source: NestedScrollSource
|
||||||
|
): Offset = when {
|
||||||
|
!enabled -> Offset.Zero
|
||||||
|
source == Drag && available.y > 0 -> Offset(0f, onPull(available.y)) // Pulling down
|
||||||
|
else -> Offset.Zero
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onPreFling(available: Velocity): Velocity {
|
||||||
|
onRelease(available.y)
|
||||||
|
return Velocity.Zero
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.rtbishop.look4sat.presentation.pullRefresh
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.animate
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a [PullRefreshState] that is remembered across compositions.
|
||||||
|
* Changes to [refreshing] will result in [PullRefreshState] being updated.
|
||||||
|
* @param refreshing A boolean representing whether a refresh is currently occurring.
|
||||||
|
* @param onRefresh The function to be called to trigger a refresh.
|
||||||
|
* @param refreshThreshold The threshold below which, if a release
|
||||||
|
* occurs, [onRefresh] will be called.
|
||||||
|
* @param refreshingOffset The offset at which the indicator will be drawn while refreshing. This
|
||||||
|
* offset corresponds to the position of the bottom of the indicator.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun rememberPullRefreshState(
|
||||||
|
refreshing: Boolean,
|
||||||
|
onRefresh: () -> Unit,
|
||||||
|
refreshThreshold: Dp = PullRefreshDefaults.RefreshThreshold,
|
||||||
|
refreshingOffset: Dp = PullRefreshDefaults.RefreshingOffset,
|
||||||
|
): PullRefreshState {
|
||||||
|
require(refreshThreshold > 0.dp) { "The refresh trigger must be greater than zero!" }
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val onRefreshState = rememberUpdatedState(onRefresh)
|
||||||
|
val thresholdPx: Float
|
||||||
|
val refreshingOffsetPx: Float
|
||||||
|
|
||||||
|
with(LocalDensity.current) {
|
||||||
|
thresholdPx = refreshThreshold.toPx()
|
||||||
|
refreshingOffsetPx = refreshingOffset.toPx()
|
||||||
|
}
|
||||||
|
|
||||||
|
// refreshThreshold and refreshingOffset should not be changed after instantiation, so any
|
||||||
|
// changes to these values are ignored.
|
||||||
|
val state = remember(scope) {
|
||||||
|
PullRefreshState(scope, onRefreshState, refreshingOffsetPx, thresholdPx)
|
||||||
|
}
|
||||||
|
|
||||||
|
SideEffect {
|
||||||
|
state.setRefreshing(refreshing)
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A state object that can be used in conjunction with [pullRefresh] to add pull-to-refresh
|
||||||
|
* behaviour to a scroll component. Based on Android's SwipeRefreshLayout.
|
||||||
|
*
|
||||||
|
* Provides [progress], a float representing how far the user has pulled as a percentage of the
|
||||||
|
* refreshThreshold. Values of one or less indicate that the user has not yet pulled past the
|
||||||
|
* threshold. Values greater than one indicate how far past the threshold the user has pulled.
|
||||||
|
*
|
||||||
|
* Can be used in conjunction with [pullRefreshIndicatorTransform] to implement Android-like
|
||||||
|
* pull-to-refresh behaviour with a custom indicator.
|
||||||
|
*
|
||||||
|
* Should be created using [rememberPullRefreshState].
|
||||||
|
*/
|
||||||
|
class PullRefreshState internal constructor(
|
||||||
|
private val animationScope: CoroutineScope,
|
||||||
|
private val onRefreshState: State<() -> Unit>,
|
||||||
|
private val refreshingOffset: Float,
|
||||||
|
internal val threshold: Float
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* A float representing how far the user has pulled as a percentage of the refreshThreshold.
|
||||||
|
*
|
||||||
|
* If the component has not been pulled at all, progress is zero. If the pull has reached
|
||||||
|
* halfway to the threshold, progress is 0.5f. A value greater than 1 indicates that pull has
|
||||||
|
* gone beyond the refreshThreshold - e.g. a value of 2f indicates that the user has pulled to
|
||||||
|
* two times the refreshThreshold.
|
||||||
|
*/
|
||||||
|
val progress get() = adjustedDistancePulled / threshold
|
||||||
|
|
||||||
|
internal val refreshing get() = _refreshing
|
||||||
|
internal val position get() = _position
|
||||||
|
|
||||||
|
private val adjustedDistancePulled by derivedStateOf { distancePulled * DragMultiplier }
|
||||||
|
|
||||||
|
private var _refreshing by mutableStateOf(false)
|
||||||
|
private var _position by mutableStateOf(0f)
|
||||||
|
private var distancePulled by mutableStateOf(0f)
|
||||||
|
|
||||||
|
internal fun onPull(pullDelta: Float): Float {
|
||||||
|
if (this._refreshing) return 0f // Already refreshing, do nothing.
|
||||||
|
|
||||||
|
val newOffset = (distancePulled + pullDelta).coerceAtLeast(0f)
|
||||||
|
val dragConsumed = newOffset - distancePulled
|
||||||
|
distancePulled = newOffset
|
||||||
|
_position = calculateIndicatorPosition()
|
||||||
|
return dragConsumed
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun onRelease() {
|
||||||
|
if (!this._refreshing) {
|
||||||
|
if (adjustedDistancePulled > threshold) {
|
||||||
|
onRefreshState.value()
|
||||||
|
} else {
|
||||||
|
animateIndicatorTo(0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
distancePulled = 0f
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun setRefreshing(refreshing: Boolean) {
|
||||||
|
if (this._refreshing != refreshing) {
|
||||||
|
this._refreshing = refreshing
|
||||||
|
this.distancePulled = 0f
|
||||||
|
animateIndicatorTo(if (refreshing) refreshingOffset else 0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun animateIndicatorTo(offset: Float) = animationScope.launch {
|
||||||
|
animate(initialValue = _position, targetValue = offset) { value, _ ->
|
||||||
|
_position = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculateIndicatorPosition(): Float = when {
|
||||||
|
// If drag hasn't gone past the threshold, the position is the adjustedDistancePulled.
|
||||||
|
adjustedDistancePulled <= threshold -> adjustedDistancePulled
|
||||||
|
else -> {
|
||||||
|
// How far beyond the threshold pull has gone, as a percentage of the threshold.
|
||||||
|
val overshootPercent = abs(progress) - 1.0f
|
||||||
|
// Limit the overshoot to 200%. Linear between 0 and 200.
|
||||||
|
val linearTension = overshootPercent.coerceIn(0f, 2f)
|
||||||
|
// Non-linear tension. Increases with linearTension, but at a decreasing rate.
|
||||||
|
val tensionPercent = linearTension - linearTension.pow(2) / 4
|
||||||
|
// The additional offset beyond the threshold.
|
||||||
|
val extraOffset = threshold * tensionPercent
|
||||||
|
threshold + extraOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default parameter values for [rememberPullRefreshState].
|
||||||
|
*/
|
||||||
|
object PullRefreshDefaults {
|
||||||
|
/**
|
||||||
|
* If the indicator is below this threshold offset when it is released, a refresh
|
||||||
|
* will be triggered.
|
||||||
|
*/
|
||||||
|
val RefreshThreshold = 80.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset at which the indicator should be rendered whilst a refresh is occurring.
|
||||||
|
*/
|
||||||
|
val RefreshingOffset = 56.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The distance pulled is multiplied by this value to give us the adjusted distance pulled, which
|
||||||
|
* is used in calculating the indicator position (when the adjusted distance pulled is less than
|
||||||
|
* the refresh threshold, it is the indicator position, otherwise the indicator position is
|
||||||
|
* derived from the progress).
|
||||||
|
*/
|
||||||
|
private const val DragMultiplier = 0.5f
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
/*
|
|
||||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
|
||||||
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.rtbishop.look4sat.presentation.radarScreen
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
|
||||||
import com.rtbishop.look4sat.R
|
|
||||||
import com.rtbishop.look4sat.databinding.FragmentRadarBinding
|
|
||||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
|
||||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
|
||||||
import com.rtbishop.look4sat.utility.toDegrees
|
|
||||||
import com.rtbishop.look4sat.utility.toTimerString
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class RadarFragment : Fragment(R.layout.fragment_radar) {
|
|
||||||
|
|
||||||
private val viewModel: RadarViewModel by viewModels()
|
|
||||||
private val radioAdapter = RadioAdapter()
|
|
||||||
private var binding: FragmentRadarBinding? = null
|
|
||||||
private var radarView: RadarView? = null
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
binding = FragmentRadarBinding.bind(view).apply {
|
|
||||||
radarRecycler.apply {
|
|
||||||
setHasFixedSize(true)
|
|
||||||
this.adapter = radioAdapter
|
|
||||||
this.layoutManager = LinearLayoutManager(requireContext())
|
|
||||||
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
|
||||||
addItemDecoration(DividerItemDecoration(requireContext(), 1))
|
|
||||||
}
|
|
||||||
setupObservers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
viewModel.enableSensor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
viewModel.disableSensor()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
binding?.radarRecycler?.adapter = null
|
|
||||||
radarView = null
|
|
||||||
binding = null
|
|
||||||
super.onDestroyView()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupObservers() {
|
|
||||||
viewModel.getPass(45000, System.currentTimeMillis()).observe(viewLifecycleOwner) { pass ->
|
|
||||||
binding?.run {
|
|
||||||
radarView = RadarView(requireContext()).apply {
|
|
||||||
setShowAim(viewModel.getUseCompass())
|
|
||||||
setScanning(viewModel.getShowSweep())
|
|
||||||
}
|
|
||||||
radarCard.addView(radarView)
|
|
||||||
viewModel.radarData.observe(viewLifecycleOwner) { passData ->
|
|
||||||
radarView?.setPosition(passData.satPos)
|
|
||||||
radarView?.setPositions(passData.satTrack)
|
|
||||||
setPassText(pass, passData.satPos)
|
|
||||||
}
|
|
||||||
viewModel.transmitters.observe(viewLifecycleOwner) { list ->
|
|
||||||
if (list.isNotEmpty()) {
|
|
||||||
radioAdapter.submitList(list)
|
|
||||||
radarProgress.visibility = View.INVISIBLE
|
|
||||||
} else {
|
|
||||||
radarProgress.visibility = View.INVISIBLE
|
|
||||||
radarEmptyLayout.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
radarView?.invalidate()
|
|
||||||
}
|
|
||||||
viewModel.orientation.observe(viewLifecycleOwner) { value ->
|
|
||||||
radarView?.setOrientation(value.first, value.second, value.third)
|
|
||||||
}
|
|
||||||
// radarBtnBack.clickWithDebounce { findNavController().navigateUp() }
|
|
||||||
// radarBtnMap.clickWithDebounce {
|
|
||||||
// val direction = RadarFragmentDirections.globalToMap(pass.catNum)
|
|
||||||
// findNavController().navigate(direction)
|
|
||||||
// }
|
|
||||||
// radarBtnNotify.isEnabled = false
|
|
||||||
// radarBtnSettings.clickWithDebounce {
|
|
||||||
// val direction = RadarFragmentDirections.globalToSettings()
|
|
||||||
// findNavController().navigate(direction)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setPassText(satPass: SatPass, satPos: SatPos) {
|
|
||||||
binding?.run {
|
|
||||||
val timeNow = System.currentTimeMillis()
|
|
||||||
val radarAzim = getString(R.string.radar_az_value)
|
|
||||||
val radarElev = getString(R.string.radar_el_value)
|
|
||||||
val radarAlt = getString(R.string.radar_alt_value)
|
|
||||||
val radarDist = getString(R.string.radar_dist_value)
|
|
||||||
radarAzValue.text = String.format(radarAzim, satPos.azimuth.toDegrees())
|
|
||||||
radarElValue.text = String.format(radarElev, satPos.elevation.toDegrees())
|
|
||||||
radarAltValue.text = String.format(radarAlt, satPos.altitude)
|
|
||||||
radarDstValue.text = String.format(radarDist, satPos.distance)
|
|
||||||
if (satPos.eclipsed) {
|
|
||||||
radarVisibility.text = getText(R.string.radar_eclipsed)
|
|
||||||
} else {
|
|
||||||
radarVisibility.text = getText(R.string.radar_visible)
|
|
||||||
}
|
|
||||||
if (!satPass.isDeepSpace) {
|
|
||||||
if (timeNow < satPass.aosTime) {
|
|
||||||
val millisBeforeStart = satPass.aosTime.minus(timeNow)
|
|
||||||
radarTimer.text = millisBeforeStart.toTimerString()
|
|
||||||
} else {
|
|
||||||
val millisBeforeEnd = satPass.losTime.minus(timeNow)
|
|
||||||
radarTimer.text = millisBeforeEnd.toTimerString()
|
|
||||||
if (timeNow > satPass.losTime) {
|
|
||||||
radarTimer.text = 0L.toTimerString()
|
|
||||||
// findNavController().navigateUp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else radarTimer.text = 0L.toTimerString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -55,3 +55,79 @@ fun RadarScreen(viewModel: RadarViewModel = hiltViewModel()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//private val divider = 1000000f
|
||||||
|
//radioDownlink.text = String.format(Locale.ENGLISH, link, downlink / divider)
|
||||||
|
//radioUplink.text = String.format(Locale.ENGLISH, link, uplink / divider)
|
||||||
|
|
||||||
|
//private fun setupObservers() {
|
||||||
|
// viewModel.getPass(45000, System.currentTimeMillis()).observe(viewLifecycleOwner) { pass ->
|
||||||
|
// binding?.run {
|
||||||
|
// radarView = RadarView(requireContext()).apply {
|
||||||
|
// setShowAim(viewModel.getUseCompass())
|
||||||
|
// setScanning(viewModel.getShowSweep())
|
||||||
|
// }
|
||||||
|
// radarCard.addView(radarView)
|
||||||
|
// viewModel.radarData.observe(viewLifecycleOwner) { passData ->
|
||||||
|
// radarView?.setPosition(passData.satPos)
|
||||||
|
// radarView?.setPositions(passData.satTrack)
|
||||||
|
// setPassText(pass, passData.satPos)
|
||||||
|
// }
|
||||||
|
// viewModel.transmitters.observe(viewLifecycleOwner) { list ->
|
||||||
|
// if (list.isNotEmpty()) {
|
||||||
|
// radioAdapter.submitList(list)
|
||||||
|
// radarProgress.visibility = View.INVISIBLE
|
||||||
|
// } else {
|
||||||
|
// radarProgress.visibility = View.INVISIBLE
|
||||||
|
// radarEmptyLayout.visibility = View.VISIBLE
|
||||||
|
// }
|
||||||
|
// radarView?.invalidate()
|
||||||
|
// }
|
||||||
|
// viewModel.orientation.observe(viewLifecycleOwner) { value ->
|
||||||
|
// radarView?.setOrientation(value.first, value.second, value.third)
|
||||||
|
// }
|
||||||
|
// radarBtnBack.clickWithDebounce { findNavController().navigateUp() }
|
||||||
|
// radarBtnMap.clickWithDebounce {
|
||||||
|
// val direction = RadarFragmentDirections.globalToMap(pass.catNum)
|
||||||
|
// findNavController().navigate(direction)
|
||||||
|
// }
|
||||||
|
// radarBtnNotify.isEnabled = false
|
||||||
|
// radarBtnSettings.clickWithDebounce {
|
||||||
|
// val direction = RadarFragmentDirections.globalToSettings()
|
||||||
|
// findNavController().navigate(direction)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun setPassText(satPass: SatPass, satPos: SatPos) {
|
||||||
|
// binding?.run {
|
||||||
|
// val timeNow = System.currentTimeMillis()
|
||||||
|
// val radarAzim = getString(R.string.radar_az_value)
|
||||||
|
// val radarElev = getString(R.string.radar_el_value)
|
||||||
|
// val radarAlt = getString(R.string.radar_alt_value)
|
||||||
|
// val radarDist = getString(R.string.radar_dist_value)
|
||||||
|
// radarAzValue.text = String.format(radarAzim, satPos.azimuth.toDegrees())
|
||||||
|
// radarElValue.text = String.format(radarElev, satPos.elevation.toDegrees())
|
||||||
|
// radarAltValue.text = String.format(radarAlt, satPos.altitude)
|
||||||
|
// radarDstValue.text = String.format(radarDist, satPos.distance)
|
||||||
|
// if (satPos.eclipsed) {
|
||||||
|
// radarVisibility.text = getText(R.string.radar_eclipsed)
|
||||||
|
// } else {
|
||||||
|
// radarVisibility.text = getText(R.string.radar_visible)
|
||||||
|
// }
|
||||||
|
// if (!satPass.isDeepSpace) {
|
||||||
|
// if (timeNow < satPass.aosTime) {
|
||||||
|
// val millisBeforeStart = satPass.aosTime.minus(timeNow)
|
||||||
|
// radarTimer.text = millisBeforeStart.toTimerString()
|
||||||
|
// } else {
|
||||||
|
// val millisBeforeEnd = satPass.losTime.minus(timeNow)
|
||||||
|
// radarTimer.text = millisBeforeEnd.toTimerString()
|
||||||
|
// if (timeNow > satPass.losTime) {
|
||||||
|
// radarTimer.text = 0L.toTimerString()
|
||||||
|
//// findNavController().navigateUp()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else radarTimer.text = 0L.toTimerString()
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@@ -22,7 +22,6 @@ import android.util.Log
|
|||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.liveData
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
import com.rtbishop.look4sat.domain.ISatelliteManager
|
||||||
@@ -37,10 +36,11 @@ import com.rtbishop.look4sat.utility.DataReporter
|
|||||||
import com.rtbishop.look4sat.utility.round
|
import com.rtbishop.look4sat.utility.round
|
||||||
import com.rtbishop.look4sat.utility.toDegrees
|
import com.rtbishop.look4sat.utility.toDegrees
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class RadarViewModel @Inject constructor(
|
class RadarViewModel @Inject constructor(
|
||||||
@@ -60,7 +60,7 @@ class RadarViewModel @Inject constructor(
|
|||||||
val transmitters: LiveData<List<SatRadio>> = _transmitters
|
val transmitters: LiveData<List<SatRadio>> = _transmitters
|
||||||
val orientation: LiveData<Triple<Float, Float, Float>> = _orientation
|
val orientation: LiveData<Triple<Float, Float, Float>> = _orientation
|
||||||
|
|
||||||
fun getPass(catNum: Int, aosTime: Long) = liveData {
|
fun getPass(catNum: Int, aosTime: Long) = flow {
|
||||||
satManager.calculatedPasses.collect { passes ->
|
satManager.calculatedPasses.collect { passes ->
|
||||||
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
|
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
|
||||||
pass?.let { satPass ->
|
pass?.let { satPass ->
|
||||||
@@ -111,8 +111,8 @@ class RadarViewModel @Inject constructor(
|
|||||||
if (settings.getRotatorEnabled()) {
|
if (settings.getRotatorEnabled()) {
|
||||||
val server = settings.getRotatorServer()
|
val server = settings.getRotatorServer()
|
||||||
val port = settings.getRotatorPort().toInt()
|
val port = settings.getRotatorPort().toInt()
|
||||||
val azimuth = satPos.azimuth.toDegrees().round(2)
|
val azimuth = satPos.azimuth.toDegrees().round(1)
|
||||||
val elevation = satPos.elevation.toDegrees().round(2)
|
val elevation = satPos.elevation.toDegrees().round(1)
|
||||||
reporter.reportRotation(server, port, azimuth, elevation)
|
reporter.reportRotation(server, port, azimuth, elevation)
|
||||||
}
|
}
|
||||||
_passData.postValue(RadarData(satPos, track))
|
_passData.postValue(RadarData(satPos, track))
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
|
||||||
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.rtbishop.look4sat.presentation.radarScreen
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.AsyncListDiffer
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.rtbishop.look4sat.R
|
|
||||||
import com.rtbishop.look4sat.databinding.ItemRadioBinding
|
|
||||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class RadioAdapter : RecyclerView.Adapter<RadioAdapter.TransHolder>() {
|
|
||||||
|
|
||||||
private val diffCallback = object : DiffUtil.ItemCallback<SatRadio>() {
|
|
||||||
override fun areItemsTheSame(oldItem: SatRadio, newItem: SatRadio): Boolean {
|
|
||||||
return oldItem.uuid == newItem.uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: SatRadio, newItem: SatRadio): Boolean {
|
|
||||||
return oldItem.downlink == newItem.downlink
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private val differ = AsyncListDiffer(this, diffCallback)
|
|
||||||
|
|
||||||
fun submitList(items: List<SatRadio>) = differ.submitList(items)
|
|
||||||
|
|
||||||
override fun getItemCount() = differ.currentList.size
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransHolder {
|
|
||||||
return TransHolder.from(parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: TransHolder, position: Int) {
|
|
||||||
holder.bind(differ.currentList[position])
|
|
||||||
}
|
|
||||||
|
|
||||||
class TransHolder private constructor(private val binding: ItemRadioBinding) :
|
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
|
|
||||||
private val divider = 1000000f
|
|
||||||
private val stringYes = itemView.context.getString(R.string.radio_string_yes)
|
|
||||||
private val stringNo = itemView.context.getString(R.string.radio_string_no)
|
|
||||||
private val link = itemView.context.getString(R.string.radio_link_low)
|
|
||||||
private val linkNull = itemView.context.getString(R.string.radio_no_link)
|
|
||||||
private val mode = itemView.context.getString(R.string.radio_mode)
|
|
||||||
private val inverted = itemView.context.getString(R.string.radio_inverted)
|
|
||||||
|
|
||||||
fun bind(radio: SatRadio) {
|
|
||||||
binding.run {
|
|
||||||
radioInfo.text = radio.info
|
|
||||||
radio.downlink.let { downlink ->
|
|
||||||
if (downlink != null) {
|
|
||||||
radioDownlink.text = String.format(Locale.ENGLISH, link, downlink / divider)
|
|
||||||
} else {
|
|
||||||
radioDownlink.text = linkNull
|
|
||||||
}
|
|
||||||
}
|
|
||||||
radio.uplink.let { uplink ->
|
|
||||||
if (uplink != null) {
|
|
||||||
radioUplink.text = String.format(Locale.ENGLISH, link, uplink / divider)
|
|
||||||
} else {
|
|
||||||
radioUplink.text = linkNull
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (radio.mode != null) {
|
|
||||||
radioMode.text = String.format(mode, radio.mode)
|
|
||||||
} else {
|
|
||||||
radioMode.text = String.format(mode, stringNo)
|
|
||||||
}
|
|
||||||
if (radio.isInverted) {
|
|
||||||
radioInverted.text = String.format(inverted, stringYes)
|
|
||||||
} else {
|
|
||||||
radioInverted.text = String.format(inverted, stringNo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun from(parent: ViewGroup): TransHolder {
|
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
|
||||||
return TransHolder(ItemRadioBinding.inflate(inflater, parent, false))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
/*
|
|
||||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
|
||||||
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.widget.NestedScrollView
|
|
||||||
import androidx.core.widget.doOnTextChanged
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.lifecycle.asLiveData
|
|
||||||
import com.rtbishop.look4sat.R
|
|
||||||
import com.rtbishop.look4sat.databinding.FragmentSettingsBinding
|
|
||||||
import com.rtbishop.look4sat.domain.model.DataState
|
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
|
||||||
import com.rtbishop.look4sat.presentation.clickWithDebounce
|
|
||||||
import com.rtbishop.look4sat.utility.isValidIPv4
|
|
||||||
import com.rtbishop.look4sat.utility.isValidPort
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
|
||||||
|
|
||||||
private val viewModel: SettingsViewModel by viewModels()
|
|
||||||
private val bluetooth = when {
|
|
||||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.S -> Manifest.permission.BLUETOOTH
|
|
||||||
else -> Manifest.permission.BLUETOOTH_CONNECT
|
|
||||||
}
|
|
||||||
private val bluetoothContract = ActivityResultContracts.RequestPermission()
|
|
||||||
private val bluetoothRequest = registerForActivityResult(bluetoothContract) { isGranted ->
|
|
||||||
if (!isGranted) {
|
|
||||||
showToast(getString(R.string.BTremote_perm_error))
|
|
||||||
toggleBTstate(isGranted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private val locationFine = Manifest.permission.ACCESS_FINE_LOCATION
|
|
||||||
private val locationCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
|
||||||
private val locationContract = ActivityResultContracts.RequestMultiplePermissions()
|
|
||||||
private val locationRequest = registerForActivityResult(locationContract) { permissions ->
|
|
||||||
when {
|
|
||||||
permissions[locationFine] == true -> viewModel.setPositionFromGps()
|
|
||||||
permissions[locationCoarse] == true -> viewModel.setPositionFromNet()
|
|
||||||
else -> showToast(getString(R.string.location_gps_error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private val contentContract = ActivityResultContracts.GetContent()
|
|
||||||
private val contentRequest = registerForActivityResult(contentContract) { uri ->
|
|
||||||
uri?.let { viewModel.updateFromFile(uri.toString()) }
|
|
||||||
}
|
|
||||||
private lateinit var binding: FragmentSettingsBinding
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
binding = FragmentSettingsBinding.bind(view).apply {
|
|
||||||
// settingsBtnBack.clickWithDebounce { findNavController().navigateUp() }
|
|
||||||
settingsScroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, y, _, newY ->
|
|
||||||
if (y > newY) settingsFab.hide() else settingsFab.show()
|
|
||||||
})
|
|
||||||
settingsBtnGithub.clickWithDebounce {
|
|
||||||
gotoUrl("https://github.com/rt-bishop/Look4Sat/")
|
|
||||||
}
|
|
||||||
settingsFab.clickWithDebounce {
|
|
||||||
gotoUrl("https://ko-fi.com/rt_bishop")
|
|
||||||
}
|
|
||||||
settingsBtnFdroid.clickWithDebounce {
|
|
||||||
gotoUrl("https://f-droid.org/en/packages/com.rtbishop.look4sat/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setupLocationCard()
|
|
||||||
setupDataCard()
|
|
||||||
setupRemoteCard()
|
|
||||||
setupBTCard()
|
|
||||||
setupOtherCard()
|
|
||||||
viewModel.stationPosition.asLiveData().observe(viewLifecycleOwner) { stationPos ->
|
|
||||||
stationPos?.let { handleStationPosition(stationPos) }
|
|
||||||
}
|
|
||||||
viewModel.getUpdateState().asLiveData().observe(viewLifecycleOwner) { updateState ->
|
|
||||||
updateState?.let { handleSatState(updateState) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupLocationCard() {
|
|
||||||
binding.run {
|
|
||||||
setPositionText(viewModel.getStationPosition())
|
|
||||||
settingsLocation.locationBtnGps.clickWithDebounce {
|
|
||||||
locationRequest.launch(arrayOf(locationFine, locationCoarse))
|
|
||||||
}
|
|
||||||
// settingsLocation.locationBtnManual.clickWithDebounce {
|
|
||||||
// val action = SettingsFragmentDirections.globalToPosition()
|
|
||||||
// findNavController().navigate(action)
|
|
||||||
// }
|
|
||||||
// settingsLocation.locationBtnQth.clickWithDebounce {
|
|
||||||
// val action = SettingsFragmentDirections.globalToLocator()
|
|
||||||
// findNavController().navigate(action)
|
|
||||||
// }
|
|
||||||
// getNavResult<Pair<Double, Double>>(R.id.nav_settings, "position") { position ->
|
|
||||||
// viewModel.setStationPosition(position.first, position.second)
|
|
||||||
// }
|
|
||||||
// getNavResult<String>(R.id.nav_settings, "locator") { locator ->
|
|
||||||
// viewModel.setPositionFromQth(locator)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupDataCard() {
|
|
||||||
binding.run {
|
|
||||||
setUpdateTime(viewModel.getLastUpdateTime())
|
|
||||||
settingsData.dataBtnWeb.clickWithDebounce { viewModel.updateFromWeb() }
|
|
||||||
settingsData.dataBtnFile.clickWithDebounce { contentRequest.launch("*/*") }
|
|
||||||
settingsData.dataBtnClear.clickWithDebounce { viewModel.clearAllData() }
|
|
||||||
viewModel.entriesTotal.observe(viewLifecycleOwner) { number ->
|
|
||||||
val entriesFormat = getString(R.string.data_entries)
|
|
||||||
settingsData.dataEntries.text = String.format(entriesFormat, number)
|
|
||||||
}
|
|
||||||
viewModel.radiosTotal.observe(viewLifecycleOwner) { number ->
|
|
||||||
val radiosFormat = getString(R.string.data_radios)
|
|
||||||
settingsData.dataRadios.text = String.format(radiosFormat, number)
|
|
||||||
}
|
|
||||||
// getNavResult<List<String>>(R.id.nav_settings, "sources") {
|
|
||||||
// viewModel.updateFromWeb()
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupRemoteCard() {
|
|
||||||
binding.run {
|
|
||||||
settingsRemote.remoteSwitch.apply {
|
|
||||||
isChecked = viewModel.getRotatorEnabled()
|
|
||||||
settingsRemote.remoteIp.isEnabled = isChecked
|
|
||||||
settingsRemote.remoteIpEdit.setText(viewModel.getRotatorServer())
|
|
||||||
settingsRemote.remotePort.isEnabled = isChecked
|
|
||||||
settingsRemote.remotePortEdit.setText(viewModel.getRotatorPort())
|
|
||||||
setOnCheckedChangeListener { _, isChecked ->
|
|
||||||
viewModel.setRotatorEnabled(isChecked)
|
|
||||||
settingsRemote.remoteIp.isEnabled = isChecked
|
|
||||||
settingsRemote.remotePort.isEnabled = isChecked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
settingsRemote.remoteIpEdit.doOnTextChanged { text, _, _, _ ->
|
|
||||||
if (text.toString().isValidIPv4()) viewModel.setRotatorServer(text.toString())
|
|
||||||
}
|
|
||||||
settingsRemote.remotePortEdit.doOnTextChanged { text, _, _, _ ->
|
|
||||||
if (text.toString().isValidPort()) viewModel.setRotatorPort(text.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupBTCard() {
|
|
||||||
binding.run {
|
|
||||||
settingsBtremote.BTremoteSwitch.apply {
|
|
||||||
isChecked = viewModel.getBTEnabled()
|
|
||||||
settingsBtremote.BTremoteAddress.isEnabled = isChecked
|
|
||||||
settingsBtremote.BTAddressEdit.setText(viewModel.getBTDeviceAddr())
|
|
||||||
settingsBtremote.BTremoteFormat.isEnabled = isChecked
|
|
||||||
settingsBtremote.BTFormatEdit.setText(viewModel.getBTFormat())
|
|
||||||
setOnCheckedChangeListener { _, isChecked ->
|
|
||||||
toggleBTstate(isChecked)
|
|
||||||
bluetoothRequest.launch(bluetooth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
settingsBtremote.BTAddressEdit.doOnTextChanged { text, _, _, _ ->
|
|
||||||
viewModel.setBTDeviceAddr(text.toString())
|
|
||||||
}
|
|
||||||
settingsBtremote.BTFormatEdit.doOnTextChanged { text, _, _, _ ->
|
|
||||||
viewModel.setBTFormat(text.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun toggleBTstate(value: Boolean) {
|
|
||||||
binding.run {
|
|
||||||
viewModel.setBTEnabled(value)
|
|
||||||
settingsBtremote.BTremoteSwitch.isChecked = value
|
|
||||||
settingsBtremote.BTremoteAddress.isEnabled = value
|
|
||||||
settingsBtremote.BTremoteFormat.isEnabled = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupOtherCard() {
|
|
||||||
binding.run {
|
|
||||||
settingsOther.otherSwitchUtc.apply {
|
|
||||||
isChecked = viewModel.getUseUTC()
|
|
||||||
setOnCheckedChangeListener { _, isChecked -> viewModel.setUseUTC(isChecked) }
|
|
||||||
}
|
|
||||||
settingsOther.otherSwitchUpdate.apply {
|
|
||||||
isChecked = viewModel.getAutoUpdateEnabled()
|
|
||||||
setOnCheckedChangeListener { _, isChecked -> viewModel.setAutoUpdateEnabled(isChecked) }
|
|
||||||
}
|
|
||||||
settingsOther.otherSwitchSweep.apply {
|
|
||||||
isChecked = viewModel.getShowSweep()
|
|
||||||
setOnCheckedChangeListener { _, isChecked -> viewModel.setShowSweep(isChecked) }
|
|
||||||
}
|
|
||||||
settingsOther.otherSwitchSensors.apply {
|
|
||||||
isChecked = viewModel.getUseCompass()
|
|
||||||
setOnCheckedChangeListener { _, isChecked -> viewModel.setUseCompass(isChecked) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleStationPosition(pos: DataState<GeoPos>) {
|
|
||||||
when (pos) {
|
|
||||||
is DataState.Success -> {
|
|
||||||
setPositionText(pos.data)
|
|
||||||
binding.settingsLocation.locationProgress.isIndeterminate = false
|
|
||||||
viewModel.setPositionHandled()
|
|
||||||
showToast(getString(R.string.location_success))
|
|
||||||
}
|
|
||||||
is DataState.Error -> {
|
|
||||||
binding.settingsLocation.locationProgress.isIndeterminate = false
|
|
||||||
viewModel.setPositionHandled()
|
|
||||||
showToast(pos.message.toString())
|
|
||||||
}
|
|
||||||
DataState.Loading -> {
|
|
||||||
binding.settingsLocation.locationProgress.isIndeterminate = true
|
|
||||||
}
|
|
||||||
DataState.Handled -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setPositionText(geoPos: GeoPos) {
|
|
||||||
binding.run {
|
|
||||||
val latFormat = getString(R.string.location_lat)
|
|
||||||
val lonFormat = getString(R.string.location_lon)
|
|
||||||
settingsLocation.locationLat.text = String.format(latFormat, geoPos.lat)
|
|
||||||
settingsLocation.locationLon.text = String.format(lonFormat, geoPos.lon)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleSatState(state: DataState<Long>) {
|
|
||||||
when (state) {
|
|
||||||
is DataState.Success -> {
|
|
||||||
binding.settingsData.dataProgress.isIndeterminate = false
|
|
||||||
setUpdateTime(state.data)
|
|
||||||
viewModel.setUpdateHandled()
|
|
||||||
if (state.data == 0L) {
|
|
||||||
showToast(getString(R.string.data_clear_success))
|
|
||||||
} else {
|
|
||||||
showToast(getString(R.string.data_success))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is DataState.Error -> {
|
|
||||||
binding.settingsData.dataProgress.isIndeterminate = false
|
|
||||||
viewModel.setUpdateHandled()
|
|
||||||
showToast(getString(R.string.data_error))
|
|
||||||
}
|
|
||||||
is DataState.Loading -> {
|
|
||||||
binding.settingsData.dataProgress.isIndeterminate = true
|
|
||||||
}
|
|
||||||
is DataState.Handled -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpdateTime(updateTime: Long) {
|
|
||||||
val updatePattern = getString(R.string.data_update)
|
|
||||||
val updateDate = if (updateTime == 0L) {
|
|
||||||
getString(R.string.pass_placeholder)
|
|
||||||
} else {
|
|
||||||
SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault()).format(Date(updateTime))
|
|
||||||
}
|
|
||||||
binding.settingsData.dataUpdate.text = String.format(updatePattern, updateDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showToast(message: String) {
|
|
||||||
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun gotoUrl(url: String) {
|
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -132,3 +132,250 @@ private fun CardCredits(modifier: Modifier = Modifier) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//private val viewModel: SettingsViewModel by viewModels()
|
||||||
|
//private val bluetooth = when {
|
||||||
|
// Build.VERSION.SDK_INT < Build.VERSION_CODES.S -> Manifest.permission.BLUETOOTH
|
||||||
|
// else -> Manifest.permission.BLUETOOTH_CONNECT
|
||||||
|
//}
|
||||||
|
//private val bluetoothContract = ActivityResultContracts.RequestPermission()
|
||||||
|
//private val bluetoothRequest = registerForActivityResult(bluetoothContract) { isGranted ->
|
||||||
|
// if (!isGranted) {
|
||||||
|
// showToast(getString(R.string.BTremote_perm_error))
|
||||||
|
// toggleBTstate(isGranted)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//private val locationFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
//private val locationCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
|
//private val locationContract = ActivityResultContracts.RequestMultiplePermissions()
|
||||||
|
//private val locationRequest = registerForActivityResult(locationContract) { permissions ->
|
||||||
|
// when {
|
||||||
|
// permissions[locationFine] == true -> viewModel.setPositionFromGps()
|
||||||
|
// permissions[locationCoarse] == true -> viewModel.setPositionFromNet()
|
||||||
|
// else -> showToast(getString(R.string.location_gps_error))
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//private val contentContract = ActivityResultContracts.GetContent()
|
||||||
|
//private val contentRequest = registerForActivityResult(contentContract) { uri ->
|
||||||
|
// uri?.let { viewModel.updateFromFile(uri.toString()) }
|
||||||
|
//}
|
||||||
|
//private lateinit var binding: FragmentSettingsBinding
|
||||||
|
//
|
||||||
|
//override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
// super.onViewCreated(view, savedInstanceState)
|
||||||
|
// binding = FragmentSettingsBinding.bind(view).apply {
|
||||||
|
// settingsBtnBack.clickWithDebounce { findNavController().navigateUp() }
|
||||||
|
// settingsScroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, y, _, newY ->
|
||||||
|
// if (y > newY) settingsFab.hide() else settingsFab.show()
|
||||||
|
// })
|
||||||
|
// settingsBtnGithub.clickWithDebounce {
|
||||||
|
// gotoUrl("https://github.com/rt-bishop/Look4Sat/")
|
||||||
|
// }
|
||||||
|
// settingsFab.clickWithDebounce {
|
||||||
|
// gotoUrl("https://ko-fi.com/rt_bishop")
|
||||||
|
// }
|
||||||
|
// settingsBtnFdroid.clickWithDebounce {
|
||||||
|
// gotoUrl("https://f-droid.org/en/packages/com.rtbishop.look4sat/")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// setupLocationCard()
|
||||||
|
// setupDataCard()
|
||||||
|
// setupRemoteCard()
|
||||||
|
// setupBTCard()
|
||||||
|
// setupOtherCard()
|
||||||
|
// viewModel.stationPosition.asLiveData().observe(viewLifecycleOwner) { stationPos ->
|
||||||
|
// stationPos?.let { handleStationPosition(stationPos) }
|
||||||
|
// }
|
||||||
|
// viewModel.getUpdateState().asLiveData().observe(viewLifecycleOwner) { updateState ->
|
||||||
|
// updateState?.let { handleSatState(updateState) }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun setupLocationCard() {
|
||||||
|
// binding.run {
|
||||||
|
// setPositionText(viewModel.getStationPosition())
|
||||||
|
// settingsLocation.locationBtnGps.clickWithDebounce {
|
||||||
|
// locationRequest.launch(arrayOf(locationFine, locationCoarse))
|
||||||
|
// }
|
||||||
|
// settingsLocation.locationBtnManual.clickWithDebounce {
|
||||||
|
// val action = SettingsFragmentDirections.globalToPosition()
|
||||||
|
// findNavController().navigate(action)
|
||||||
|
// }
|
||||||
|
// settingsLocation.locationBtnQth.clickWithDebounce {
|
||||||
|
// val action = SettingsFragmentDirections.globalToLocator()
|
||||||
|
// findNavController().navigate(action)
|
||||||
|
// }
|
||||||
|
// getNavResult<Pair<Double, Double>>(R.id.nav_settings, "position") { position ->
|
||||||
|
// viewModel.setStationPosition(position.first, position.second)
|
||||||
|
// }
|
||||||
|
// getNavResult<String>(R.id.nav_settings, "locator") { locator ->
|
||||||
|
// viewModel.setPositionFromQth(locator)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun setupDataCard() {
|
||||||
|
// binding.run {
|
||||||
|
// setUpdateTime(viewModel.getLastUpdateTime())
|
||||||
|
// settingsData.dataBtnWeb.clickWithDebounce { viewModel.updateFromWeb() }
|
||||||
|
// settingsData.dataBtnFile.clickWithDebounce { contentRequest.launch("*/*") }
|
||||||
|
// settingsData.dataBtnClear.clickWithDebounce { viewModel.clearAllData() }
|
||||||
|
// viewModel.entriesTotal.observe(viewLifecycleOwner) { number ->
|
||||||
|
// val entriesFormat = getString(R.string.data_entries)
|
||||||
|
// settingsData.dataEntries.text = String.format(entriesFormat, number)
|
||||||
|
// }
|
||||||
|
// viewModel.radiosTotal.observe(viewLifecycleOwner) { number ->
|
||||||
|
// val radiosFormat = getString(R.string.data_radios)
|
||||||
|
// settingsData.dataRadios.text = String.format(radiosFormat, number)
|
||||||
|
// }
|
||||||
|
// getNavResult<List<String>>(R.id.nav_settings, "sources") {
|
||||||
|
// viewModel.updateFromWeb()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun setupRemoteCard() {
|
||||||
|
// binding.run {
|
||||||
|
// settingsRemote.remoteSwitch.apply {
|
||||||
|
// isChecked = viewModel.getRotatorEnabled()
|
||||||
|
// settingsRemote.remoteIp.isEnabled = isChecked
|
||||||
|
// settingsRemote.remoteIpEdit.setText(viewModel.getRotatorServer())
|
||||||
|
// settingsRemote.remotePort.isEnabled = isChecked
|
||||||
|
// settingsRemote.remotePortEdit.setText(viewModel.getRotatorPort())
|
||||||
|
// setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
// viewModel.setRotatorEnabled(isChecked)
|
||||||
|
// settingsRemote.remoteIp.isEnabled = isChecked
|
||||||
|
// settingsRemote.remotePort.isEnabled = isChecked
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// settingsRemote.remoteIpEdit.doOnTextChanged { text, _, _, _ ->
|
||||||
|
// if (text.toString().isValidIPv4()) viewModel.setRotatorServer(text.toString())
|
||||||
|
// }
|
||||||
|
// settingsRemote.remotePortEdit.doOnTextChanged { text, _, _, _ ->
|
||||||
|
// if (text.toString().isValidPort()) viewModel.setRotatorPort(text.toString())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun setupBTCard() {
|
||||||
|
// binding.run {
|
||||||
|
// settingsBtremote.BTremoteSwitch.apply {
|
||||||
|
// isChecked = viewModel.getBTEnabled()
|
||||||
|
// settingsBtremote.BTremoteAddress.isEnabled = isChecked
|
||||||
|
// settingsBtremote.BTAddressEdit.setText(viewModel.getBTDeviceAddr())
|
||||||
|
// settingsBtremote.BTremoteFormat.isEnabled = isChecked
|
||||||
|
// settingsBtremote.BTFormatEdit.setText(viewModel.getBTFormat())
|
||||||
|
// setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
// toggleBTstate(isChecked)
|
||||||
|
// bluetoothRequest.launch(bluetooth)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// settingsBtremote.BTAddressEdit.doOnTextChanged { text, _, _, _ ->
|
||||||
|
// viewModel.setBTDeviceAddr(text.toString())
|
||||||
|
// }
|
||||||
|
// settingsBtremote.BTFormatEdit.doOnTextChanged { text, _, _, _ ->
|
||||||
|
// viewModel.setBTFormat(text.toString())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun toggleBTstate(value: Boolean) {
|
||||||
|
// binding.run {
|
||||||
|
// viewModel.setBTEnabled(value)
|
||||||
|
// settingsBtremote.BTremoteSwitch.isChecked = value
|
||||||
|
// settingsBtremote.BTremoteAddress.isEnabled = value
|
||||||
|
// settingsBtremote.BTremoteFormat.isEnabled = value
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun setupOtherCard() {
|
||||||
|
// binding.run {
|
||||||
|
// settingsOther.otherSwitchUtc.apply {
|
||||||
|
// isChecked = viewModel.getUseUTC()
|
||||||
|
// setOnCheckedChangeListener { _, isChecked -> viewModel.setUseUTC(isChecked) }
|
||||||
|
// }
|
||||||
|
// settingsOther.otherSwitchUpdate.apply {
|
||||||
|
// isChecked = viewModel.getAutoUpdateEnabled()
|
||||||
|
// setOnCheckedChangeListener { _, isChecked -> viewModel.setAutoUpdateEnabled(isChecked) }
|
||||||
|
// }
|
||||||
|
// settingsOther.otherSwitchSweep.apply {
|
||||||
|
// isChecked = viewModel.getShowSweep()
|
||||||
|
// setOnCheckedChangeListener { _, isChecked -> viewModel.setShowSweep(isChecked) }
|
||||||
|
// }
|
||||||
|
// settingsOther.otherSwitchSensors.apply {
|
||||||
|
// isChecked = viewModel.getUseCompass()
|
||||||
|
// setOnCheckedChangeListener { _, isChecked -> viewModel.setUseCompass(isChecked) }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun handleStationPosition(pos: DataState<GeoPos>) {
|
||||||
|
// when (pos) {
|
||||||
|
// is DataState.Success -> {
|
||||||
|
// setPositionText(pos.data)
|
||||||
|
// binding.settingsLocation.locationProgress.isIndeterminate = false
|
||||||
|
// viewModel.setPositionHandled()
|
||||||
|
// showToast(getString(R.string.location_success))
|
||||||
|
// }
|
||||||
|
// is DataState.Error -> {
|
||||||
|
// binding.settingsLocation.locationProgress.isIndeterminate = false
|
||||||
|
// viewModel.setPositionHandled()
|
||||||
|
// showToast(pos.message.toString())
|
||||||
|
// }
|
||||||
|
// DataState.Loading -> {
|
||||||
|
// binding.settingsLocation.locationProgress.isIndeterminate = true
|
||||||
|
// }
|
||||||
|
// DataState.Handled -> {}
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun setPositionText(geoPos: GeoPos) {
|
||||||
|
// binding.run {
|
||||||
|
// val latFormat = getString(R.string.location_lat)
|
||||||
|
// val lonFormat = getString(R.string.location_lon)
|
||||||
|
// settingsLocation.locationLat.text = String.format(latFormat, geoPos.lat)
|
||||||
|
// settingsLocation.locationLon.text = String.format(lonFormat, geoPos.lon)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun handleSatState(state: DataState<Long>) {
|
||||||
|
// when (state) {
|
||||||
|
// is DataState.Success -> {
|
||||||
|
// binding.settingsData.dataProgress.isIndeterminate = false
|
||||||
|
// setUpdateTime(state.data)
|
||||||
|
// viewModel.setUpdateHandled()
|
||||||
|
// if (state.data == 0L) {
|
||||||
|
// showToast(getString(R.string.data_clear_success))
|
||||||
|
// } else {
|
||||||
|
// showToast(getString(R.string.data_success))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// is DataState.Error -> {
|
||||||
|
// binding.settingsData.dataProgress.isIndeterminate = false
|
||||||
|
// viewModel.setUpdateHandled()
|
||||||
|
// showToast(getString(R.string.data_error))
|
||||||
|
// }
|
||||||
|
// is DataState.Loading -> {
|
||||||
|
// binding.settingsData.dataProgress.isIndeterminate = true
|
||||||
|
// }
|
||||||
|
// is DataState.Handled -> {}
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun setUpdateTime(updateTime: Long) {
|
||||||
|
// val updatePattern = getString(R.string.data_update)
|
||||||
|
// val updateDate = if (updateTime == 0L) {
|
||||||
|
// getString(R.string.pass_placeholder)
|
||||||
|
// } else {
|
||||||
|
// SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault()).format(Date(updateTime))
|
||||||
|
// }
|
||||||
|
// binding.settingsData.dataUpdate.text = String.format(updatePattern, updateDate)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun showToast(message: String) {
|
||||||
|
// Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//private fun gotoUrl(url: String) {
|
||||||
|
// startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
||||||
|
//}
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
package com.rtbishop.look4sat.presentation.settingsScreen
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.asLiveData
|
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
import com.rtbishop.look4sat.domain.ILocationManager
|
import com.rtbishop.look4sat.domain.ILocationManager
|
||||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||||
@@ -35,8 +34,8 @@ class SettingsViewModel @Inject constructor(
|
|||||||
private val settings: ISettingsManager
|
private val settings: ISettingsManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val entriesTotal = repository.getEntriesTotal().asLiveData()
|
val entriesTotal = repository.getEntriesTotal()
|
||||||
val radiosTotal = repository.getRadiosTotal().asLiveData()
|
val radiosTotal = repository.getRadiosTotal()
|
||||||
|
|
||||||
fun updateFromFile(uri: String) = repository.updateFromFile(uri)
|
fun updateFromFile(uri: String) = repository.updateFromFile(uri)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="512"
|
android:viewportWidth="512"
|
||||||
android:viewportHeight="512">
|
android:viewportHeight="512">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="287.97"
|
android:viewportWidth="287.97"
|
||||||
android:viewportHeight="287.97">
|
android:viewportHeight="287.97">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="128"
|
android:viewportWidth="128"
|
||||||
android:viewportHeight="128">
|
android:viewportHeight="128">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
<?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"
|
|
||||||
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_constraintEnd_toEndOf="@+id/BTremote_switch"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/BTremote_switch"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/BTremote_switch">
|
|
||||||
|
|
||||||
<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"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/BTremote_address"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/BTremote_address"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/BTremote_address">
|
|
||||||
|
|
||||||
<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>
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
<?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"
|
|
||||||
style="@style/SurfaceCard">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/data_title"
|
|
||||||
style="@style/SettingsTitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:text="@string/data_title"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/data_progress"
|
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/data_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/data_title"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/data_title" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/data_update"
|
|
||||||
style="@style/SettingsText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:text="@string/data_update"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/data_title"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/data_title" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/data_entries"
|
|
||||||
style="@style/SettingsText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:text="@string/data_entries"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/data_radios"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/data_update"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/data_update" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/data_radios"
|
|
||||||
style="@style/SettingsText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/data_radios"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/data_entries"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/data_progress"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/data_entries" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/data_btn_web"
|
|
||||||
style="@style/NormalButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:layout_marginBottom="6dp"
|
|
||||||
android:text="@string/btn_web"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/data_btn_file"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/data_entries" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/data_btn_file"
|
|
||||||
style="@style/NormalButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="2dp"
|
|
||||||
android:layout_marginEnd="2dp"
|
|
||||||
android:text="@string/btn_import"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/data_btn_web"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/data_btn_clear"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/data_btn_web" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/data_btn_clear"
|
|
||||||
style="@style/NormalButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:text="@string/btn_clear"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/data_btn_web"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/data_btn_file" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
<?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"
|
|
||||||
style="@style/SurfaceCard">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/location_title"
|
|
||||||
style="@style/SettingsTitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:text="@string/location_title"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/location_progress"
|
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/location_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/location_title"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/location_title" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/location_lat"
|
|
||||||
style="@style/SettingsText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:text="@string/location_lat"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/location_lon"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/location_title"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/location_title" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/location_lon"
|
|
||||||
style="@style/SettingsText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/location_lon"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/location_lat"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/location_progress"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/location_lat" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/location_btn_gps"
|
|
||||||
style="@style/NormalButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:layout_marginBottom="6dp"
|
|
||||||
android:text="@string/btn_gps"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/location_btn_manual"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/location_lat" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/location_btn_manual"
|
|
||||||
style="@style/NormalButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="2dp"
|
|
||||||
android:layout_marginEnd="2dp"
|
|
||||||
android:text="@string/btn_manual"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/location_btn_gps"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/location_btn_qth"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/location_btn_gps" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/location_btn_qth"
|
|
||||||
style="@style/NormalButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:text="@string/btn_qth"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/location_btn_gps"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/location_btn_manual" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
<?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"
|
|
||||||
style="@style/SurfaceCard">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/other_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/other_title"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/other_switch_utc"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/other_switch_utc"
|
|
||||||
style="@style/SettingsText"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:minHeight="42dp"
|
|
||||||
android:text="@string/other_switch_utc"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/other_switch_update"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/other_title"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/other_title"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/other_title"
|
|
||||||
app:trackTint="@color/textDisabled" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/other_switch_update"
|
|
||||||
style="@style/SettingsText"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="42dp"
|
|
||||||
android:text="@string/other_switch_update"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/other_switch_sweep"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/other_switch_utc"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/other_switch_utc"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/other_switch_utc"
|
|
||||||
app:trackTint="@color/textDisabled" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/other_switch_sweep"
|
|
||||||
style="@style/SettingsText"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="42dp"
|
|
||||||
android:text="@string/other_switch_sweep"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/other_switch_sensors"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/other_switch_update"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/other_switch_update"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/other_switch_update"
|
|
||||||
app:trackTint="@color/textDisabled" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/other_switch_sensors"
|
|
||||||
style="@style/SettingsText"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="6dp"
|
|
||||||
android:minHeight="42dp"
|
|
||||||
android:text="@string/other_switch_sensors"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/other_switch_sweep"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/other_switch_sweep"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/other_switch_sweep"
|
|
||||||
app:trackTint="@color/textDisabled" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
<?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"
|
|
||||||
style="@style/SurfaceCard">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/remote_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/remote_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/remote_switch"
|
|
||||||
style="@style/SettingsText"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="42dp"
|
|
||||||
android:text="@string/remote_switch"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/remote_title"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/remote_title"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/remote_title"
|
|
||||||
app:trackTint="@color/textDisabled" />
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/remote_ip"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/remote_port"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/remote_switch"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/remote_switch"
|
|
||||||
app:layout_constraintWidth_percent="0.64">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/remote_ip_edit"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:digits="0123456789."
|
|
||||||
android:hint="@string/remote_ip_hint"
|
|
||||||
android:inputType="number"
|
|
||||||
android:textColorHint="@color/textMain" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/remote_port"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/remote_ip"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/remote_switch"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/remote_ip"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/remote_ip">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/remote_port_edit"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="@string/remote_port_hint"
|
|
||||||
android:inputType="number"
|
|
||||||
android:textColorHint="@color/textMain" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
|
||||||
android:transitionGroup="true"
|
|
||||||
android:keepScreenOn="true">
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/radar_toolbar"
|
|
||||||
style="@style/Toolbar"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/radar_btn_back"
|
|
||||||
style="@style/ToolbarButton"
|
|
||||||
android:layout_gravity="start|center_vertical"
|
|
||||||
android:contentDescription="@string/btn_back"
|
|
||||||
android:src="@drawable/ic_prev" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_timer"
|
|
||||||
style="@style/ToolbarTimer" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/radar_btn_map"
|
|
||||||
style="@style/ToolbarButton"
|
|
||||||
android:layout_gravity="end|center_vertical"
|
|
||||||
android:contentDescription="@string/btn_map"
|
|
||||||
android:src="@drawable/ic_world" />
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/radar_card"
|
|
||||||
style="@style/SurfaceCard"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="@dimen/view_default_margin"
|
|
||||||
android:layout_marginTop="@dimen/view_default_margin"
|
|
||||||
android:layout_marginEnd="@dimen/view_default_margin"
|
|
||||||
app:layout_constraintDimensionRatio="1:1"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/radar_toolbar" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_az_value"
|
|
||||||
style="@style/RadarValue"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:text="@string/radar_az_value"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/radar_card"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/radar_card" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_az"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/radar_az_text"
|
|
||||||
android:textSize="@dimen/text_size_small"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/radar_az_value"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/radar_az_value" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_el_value"
|
|
||||||
style="@style/RadarValue"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/radar_el_value"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/radar_az_value"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/radar_card" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_el"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/radar_el_text"
|
|
||||||
android:textSize="@dimen/text_size_small"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/radar_az"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/radar_el_value" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_alt_value"
|
|
||||||
style="@style/RadarValue"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:text="@string/radar_alt_value"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/radar_card"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/radar_az_value" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_alt"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/radar_alt_text"
|
|
||||||
android:textSize="@dimen/text_size_small"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/radar_alt_value"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/radar_alt_value" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_dst_value"
|
|
||||||
style="@style/RadarValue"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/radar_dist_value"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/radar_alt_value"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/radar_el_value" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_dst"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/radar_dist_text"
|
|
||||||
android:textSize="@dimen/text_size_small"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/radar_alt"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/radar_dst_value" />
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/radar_list_card"
|
|
||||||
style="@style/SurfaceCard"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="@dimen/view_default_margin"
|
|
||||||
android:layout_marginTop="@dimen/view_default_margin"
|
|
||||||
android:layout_marginEnd="@dimen/view_default_margin"
|
|
||||||
android:layout_marginBottom="@dimen/view_default_margin"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/radar_coordinator"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/radar_card">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/radar_recycler"
|
|
||||||
style="@style/Recycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/radar_progress"
|
|
||||||
style="@style/RecyclerProgress" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/radar_empty_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:visibility="invisible"
|
|
||||||
tools:ignore="UseCompoundDrawables">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/radar_empty_img"
|
|
||||||
android:layout_width="64dp"
|
|
||||||
android:layout_height="64dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:contentDescription="@string/radio_no_data"
|
|
||||||
android:src="@drawable/ic_satellite" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_empty_msg"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:gravity="center"
|
|
||||||
android:includeFontPadding="false"
|
|
||||||
android:text="@string/radio_no_data"
|
|
||||||
android:textSize="@dimen/text_size_mediumLarge" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/radar_coordinator"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent">
|
|
||||||
|
|
||||||
<com.google.android.material.bottomappbar.BottomAppBar
|
|
||||||
android:id="@+id/radar_bottom_bar"
|
|
||||||
style="@style/BottomBar" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/radar_btn_notify"
|
|
||||||
style="@style/NormalButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_marginStart="@dimen/button_margin_side"
|
|
||||||
android:text="@string/btn_notify"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/radar_coordinator"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/radar_visibility"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/radar_coordinator"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/radar_coordinator"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/radar_coordinator" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radar_visibility"
|
|
||||||
style="@style/WorldMapText"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:layout_marginEnd="4dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@string/radar_visible"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/radar_btn_notify"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/radar_btn_settings"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/radar_btn_notify" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/radar_btn_settings"
|
|
||||||
style="@style/NormalButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_marginEnd="@dimen/button_margin_side"
|
|
||||||
android:text="@string/btn_settings"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/radar_btn_notify"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/radar_coordinator"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/radar_visibility" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:transitionGroup="true">
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/settings_toolbar"
|
|
||||||
style="@style/Toolbar">
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/settings_btn_back"
|
|
||||||
style="@style/ToolbarButton"
|
|
||||||
android:layout_gravity="start|center_vertical"
|
|
||||||
android:contentDescription="@string/btn_back"
|
|
||||||
android:src="@drawable/ic_prev" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/settings_title"
|
|
||||||
style="@style/ToolbarTitle"
|
|
||||||
android:text="@string/btn_settings" />
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
style="@style/Toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginStart="@dimen/view_default_margin"
|
|
||||||
android:layout_marginTop="@dimen/surface_margin_top"
|
|
||||||
android:layout_marginEnd="@dimen/view_default_margin"
|
|
||||||
android:layout_marginBottom="@dimen/surface_margin_bot"
|
|
||||||
android:backgroundTint="@color/background">
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
|
||||||
android:id="@+id/settings_scroll"
|
|
||||||
style="@style/Recycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/settings_location"
|
|
||||||
layout="@layout/card_location"
|
|
||||||
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_toTopOf="parent" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/settings_data"
|
|
||||||
layout="@layout/card_data"
|
|
||||||
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_location" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/settings_remote"
|
|
||||||
layout="@layout/card_remote"
|
|
||||||
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_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"
|
|
||||||
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_btremote" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<com.google.android.material.bottomappbar.BottomAppBar
|
|
||||||
android:id="@+id/settings_bottom_bar"
|
|
||||||
style="@style/BottomBar">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/settings_btn_github"
|
|
||||||
style="@style/NormalButton"
|
|
||||||
android:layout_width="@dimen/button_width_max"
|
|
||||||
android:layout_marginStart="@dimen/button_margin_side"
|
|
||||||
android:layout_gravity="start|center_vertical"
|
|
||||||
android:text="@string/btn_github" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/settings_btn_fdroid"
|
|
||||||
style="@style/NormalButton"
|
|
||||||
android:layout_width="@dimen/button_width_max"
|
|
||||||
android:layout_marginEnd="@dimen/button_margin_side"
|
|
||||||
android:layout_gravity="end|center_vertical"
|
|
||||||
android:text="@string/btn_fdroid" />
|
|
||||||
|
|
||||||
</com.google.android.material.bottomappbar.BottomAppBar>
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/settings_fab"
|
|
||||||
style="@style/FloatingActionButton"
|
|
||||||
android:src="@drawable/ic_checkmark"
|
|
||||||
android:contentDescription="@string/btn_donate"
|
|
||||||
app:layout_anchor="@id/settings_bottom_bar" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
<?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"
|
|
||||||
style="@style/RecyclerItemCard">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Guideline
|
|
||||||
android:id="@+id/radio_guide"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintGuide_percent="0.5" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radio_info"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="2dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="2dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="center"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@string/radio_info"
|
|
||||||
android:textSize="@dimen/text_size_small"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/radio_up_img"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/radio_down_img"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/radio_down_img"
|
|
||||||
android:layout_width="18dp"
|
|
||||||
android:layout_height="18dp"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
android:contentDescription="@string/radio_downlink"
|
|
||||||
android:rotation="90"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/radio_info"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/radio_info"
|
|
||||||
app:srcCompat="@drawable/ic_next" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radio_downlink"
|
|
||||||
style="@style/RadioFreq"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:layout_marginTop="3dp"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/radio_guide"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/radio_info" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/radio_up_img"
|
|
||||||
android:layout_width="18dp"
|
|
||||||
android:layout_height="18dp"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:contentDescription="@string/radio_uplink"
|
|
||||||
android:rotation="270"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/radio_info"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/radio_info"
|
|
||||||
app:srcCompat="@drawable/ic_next" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radio_uplink"
|
|
||||||
style="@style/RadioFreq"
|
|
||||||
android:layout_marginEnd="4dp"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/radio_downlink"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/radio_guide" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radio_mode"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:text="@string/radio_mode"
|
|
||||||
android:textSize="@dimen/text_size_small"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/radio_inverted"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/radio_downlink" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/radio_inverted"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:text="@string/radio_inverted"
|
|
||||||
android:textSize="@dimen/text_size_small"
|
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/radio_mode"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/radio_mode" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<org.osmdroid.views.MapView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/osm_map_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,175 +0,0 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<style name="Theme.Look4Sat.SplashScreen" parent="Theme.SplashScreen">
|
|
||||||
<item name="android:forceDarkAllowed" tools:targetApi="q">false</item>
|
|
||||||
<item name="postSplashScreenTheme">@style/Theme.Look4Sat.Main</item>
|
|
||||||
<item name="windowSplashScreenBackground">@color/background</item>
|
|
||||||
<item name="windowSplashScreenAnimatedIcon">@drawable/splash</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="Theme.Look4Sat.Main" parent="Theme.MaterialComponents.NoActionBar">
|
|
||||||
<item name="colorAccent">@color/accent</item>
|
|
||||||
<item name="colorControlNormal">@color/textMain</item>
|
|
||||||
<item name="colorPrimary">@color/accent</item>
|
|
||||||
<item name="colorSecondary">@color/accent</item>
|
|
||||||
<!-- Bottom bar colors-->
|
|
||||||
<!-- <item name="colorOnSecondaryContainer">@color/background</item>-->
|
|
||||||
<!-- <item name="colorOnSurfaceVariant">@color/textMain</item>-->
|
|
||||||
<!-- <item name="colorSecondaryContainer">@color/accent</item>-->
|
|
||||||
<!-- <item name="colorOnPrimaryContainer">@color/textMain</item>-->
|
|
||||||
<!-- -->
|
|
||||||
<item name="android:forceDarkAllowed" tools:targetApi="q">false</item>
|
|
||||||
<item name="android:navigationBarColor">@color/background</item>
|
|
||||||
<item name="android:textColorPrimary">@color/textMain</item>
|
|
||||||
<item name="android:statusBarColor">@color/background</item>
|
|
||||||
<item name="android:windowBackground">@color/background</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="Toolbar">
|
|
||||||
<item name="android:layout_width">match_parent</item>
|
|
||||||
<item name="android:layout_height">@dimen/toolbar_size</item>
|
|
||||||
<item name="android:layout_gravity">top|center_horizontal</item>
|
|
||||||
<item name="android:layout_marginStart">@dimen/view_default_margin</item>
|
|
||||||
<item name="android:layout_marginTop">@dimen/view_default_margin</item>
|
|
||||||
<item name="android:layout_marginEnd">@dimen/view_default_margin</item>
|
|
||||||
<item name="cardBackgroundColor">@color/toolbar</item>
|
|
||||||
<item name="cardCornerRadius">@dimen/card_corner_high</item>
|
|
||||||
<item name="cardElevation">@dimen/card_elev_low</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="ToolbarButton">
|
|
||||||
<item name="android:layout_width">@dimen/toolbar_icon_size</item>
|
|
||||||
<item name="android:layout_height">@dimen/toolbar_icon_size</item>
|
|
||||||
<item name="android:background">?actionBarItemBackground</item>
|
|
||||||
<item name="android:tint">@color/textMain</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="ToolbarTimer">
|
|
||||||
<item name="android:layout_width">wrap_content</item>
|
|
||||||
<item name="android:layout_height">wrap_content</item>
|
|
||||||
<item name="android:layout_gravity">center</item>
|
|
||||||
<item name="android:includeFontPadding">false</item>
|
|
||||||
<item name="android:text">@string/app_timer</item>
|
|
||||||
<item name="android:textColor">@color/accent</item>
|
|
||||||
<item name="android:textSize">@dimen/text_size_app_timer</item>
|
|
||||||
<item name="android:textStyle">bold</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="ToolbarTitle">
|
|
||||||
<item name="android:layout_width">wrap_content</item>
|
|
||||||
<item name="android:layout_height">wrap_content</item>
|
|
||||||
<item name="android:layout_gravity">center</item>
|
|
||||||
<item name="android:background">@color/transparent</item>
|
|
||||||
<item name="android:gravity">center</item>
|
|
||||||
<item name="android:maxLines">1</item>
|
|
||||||
<item name="android:ellipsize">end</item>
|
|
||||||
<item name="android:includeFontPadding">false</item>
|
|
||||||
<item name="android:textColor">@color/accent</item>
|
|
||||||
<item name="android:textColorHint">@color/accent</item>
|
|
||||||
<item name="android:textSize">@dimen/text_size_large</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="FloatingActionButton">
|
|
||||||
<item name="android:layout_width">wrap_content</item>
|
|
||||||
<item name="android:layout_height">wrap_content</item>
|
|
||||||
<item name="android:backgroundTint">@color/accent</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="NormalButton" parent="Widget.AppCompat.Button">
|
|
||||||
<item name="android:layout_width">wrap_content</item>
|
|
||||||
<item name="android:layout_height">wrap_content</item>
|
|
||||||
<item name="android:backgroundTint">@color/buttonRegular</item>
|
|
||||||
<item name="android:ellipsize">end</item>
|
|
||||||
<item name="android:minWidth">96dp</item>
|
|
||||||
<item name="android:maxLines">1</item>
|
|
||||||
<item name="android:textAllCaps">false</item>
|
|
||||||
<item name="android:textColor">@color/textMain</item>
|
|
||||||
<item name="android:textSize">@dimen/text_size_medium</item>
|
|
||||||
<item name="cornerRadius">@dimen/btn_corner_high</item>
|
|
||||||
<item name="iconTint">@color/textMain</item>
|
|
||||||
<item name="rippleColor">@color/textMain</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="RadioFreq">
|
|
||||||
<item name="android:layout_width">wrap_content</item>
|
|
||||||
<item name="android:layout_height">wrap_content</item>
|
|
||||||
<item name="android:includeFontPadding">false</item>
|
|
||||||
<item name="android:text">@string/radio_link_low</item>
|
|
||||||
<item name="android:textColor">@color/accent</item>
|
|
||||||
<item name="android:textSize">@dimen/text_size_frequency</item>
|
|
||||||
<item name="android:textStyle">bold</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="Recycler">
|
|
||||||
<item name="android:overScrollMode">never</item>
|
|
||||||
<item name="android:scrollbars">none</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="RecyclerProgress">
|
|
||||||
<item name="android:layout_width">72dp</item>
|
|
||||||
<item name="android:layout_height">72dp</item>
|
|
||||||
<item name="android:layout_gravity">center</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="RecyclerItemCard">
|
|
||||||
<item name="android:layout_width">match_parent</item>
|
|
||||||
<item name="android:layout_height">wrap_content</item>
|
|
||||||
<item name="cardBackgroundColor">@color/cardRegular</item>
|
|
||||||
<item name="cardCornerRadius">@dimen/card_corner_low</item>
|
|
||||||
<item name="cardElevation">@dimen/card_elev_low</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="SurfaceCard">
|
|
||||||
<item name="android:layout_width">match_parent</item>
|
|
||||||
<item name="android:layout_height">wrap_content</item>
|
|
||||||
<item name="cardBackgroundColor">@color/cardRegular</item>
|
|
||||||
<item name="cardCornerRadius">@dimen/card_corner_high</item>
|
|
||||||
<item name="cardElevation">@dimen/card_elev_low</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="BottomBar">
|
|
||||||
<item name="android:layout_width">match_parent</item>
|
|
||||||
<item name="android:layout_height">@dimen/bottom_bar_size</item>
|
|
||||||
<item name="android:layout_gravity">bottom</item>
|
|
||||||
<item name="android:layout_marginBottom">3dp</item>
|
|
||||||
<item name="android:backgroundTint">@color/bottomBar</item>
|
|
||||||
<item name="contentInsetStart">@dimen/bottom_bar_inset</item>
|
|
||||||
<item name="contentInsetEnd">@dimen/bottom_bar_inset</item>
|
|
||||||
<item name="fabCradleMargin">@dimen/view_default_margin</item>
|
|
||||||
<item name="fabCradleRoundedCornerRadius">@dimen/view_default_margin</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="RadarValue">
|
|
||||||
<item name="android:textSize">@dimen/text_size_large</item>
|
|
||||||
<item name="android:textColor">@color/textMain</item>
|
|
||||||
<item name="android:includeFontPadding">false</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="WorldMapText">
|
|
||||||
<item name="android:textSize">@dimen/text_size_medium</item>
|
|
||||||
<item name="android:textColor">@color/accent</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="SettingsText">
|
|
||||||
<item name="android:textSize">@dimen/text_size_medium</item>
|
|
||||||
<item name="android:textColor">@color/textMain</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="SettingsTitle">
|
|
||||||
<item name="android:includeFontPadding">false</item>
|
|
||||||
<item name="android:textSize">@dimen/text_size_medium</item>
|
|
||||||
<item name="android:textColor">@color/accent</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="DialogText">
|
|
||||||
<item name="android:layout_width">wrap_content</item>
|
|
||||||
<item name="android:layout_height">wrap_content</item>
|
|
||||||
<item name="android:textColor">@color/textMain</item>
|
|
||||||
<item name="android:textSize">@dimen/text_size_medium</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="DialogTitle" parent="ToolbarTitle">
|
|
||||||
<item name="android:textSize">@dimen/text_size_mediumLarge</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</resources>
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<style name="Theme.Look4Sat.SplashScreen" parent="Theme.SplashScreen">
|
||||||
|
<item name="android:forceDarkAllowed" tools:targetApi="q">false</item>
|
||||||
|
<item name="postSplashScreenTheme">@style/Theme.Look4Sat.Main</item>
|
||||||
|
<item name="windowSplashScreenBackground">@color/background</item>
|
||||||
|
<item name="windowSplashScreenAnimatedIcon">@drawable/splash</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="Theme.Look4Sat.Main" parent="android:Theme.Material.Light.NoActionBar">
|
||||||
|
<item name="android:forceDarkAllowed" tools:targetApi="q">false</item>
|
||||||
|
<item name="android:navigationBarColor">@color/background</item>
|
||||||
|
<item name="android:textColorPrimary">@color/textMain</item>
|
||||||
|
<item name="android:statusBarColor">@color/background</item>
|
||||||
|
<item name="android:windowBackground">@color/background</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample backup rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/guide/topics/data/autobackup
|
||||||
|
for details.
|
||||||
|
Note: This file is ignored for devices older that API 31
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore
|
||||||
|
-->
|
||||||
|
<full-backup-content>
|
||||||
|
<!--
|
||||||
|
<include domain="sharedpref" path="."/>
|
||||||
|
<exclude domain="sharedpref" path="device.xml"/>
|
||||||
|
-->
|
||||||
|
</full-backup-content>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample data extraction rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||||
|
for details.
|
||||||
|
-->
|
||||||
|
<data-extraction-rules>
|
||||||
|
<cloud-backup>
|
||||||
|
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
-->
|
||||||
|
</cloud-backup>
|
||||||
|
<!--
|
||||||
|
<device-transfer>
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
</device-transfer>
|
||||||
|
-->
|
||||||
|
</data-extraction-rules>
|
||||||
@@ -5,4 +5,4 @@
|
|||||||
<certificates src="user" />
|
<certificates src="user" />
|
||||||
</trust-anchors>
|
</trust-anchors>
|
||||||
</debug-overrides>
|
</debug-overrides>
|
||||||
</network-security-config>
|
</network-security-config>
|
||||||
@@ -1,31 +1,29 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
hilt_version = '2.45'
|
|
||||||
hilt_compose_version = '1.0.0'
|
|
||||||
application_version = '7.4.2'
|
application_version = '7.4.2'
|
||||||
library_version = '7.1.2'
|
library_version = '7.1.2'
|
||||||
kotlin_android_version = '1.8.10'
|
kotlin_android_version = '1.8.10'
|
||||||
core_ktx_version = '1.9.0'
|
hilt_android_version = '2.45'
|
||||||
core_splashscreen_version = '1.0.0'
|
// Android
|
||||||
constraint_version = '2.1.4'
|
splashscreen_version = '1.0.0'
|
||||||
|
room_version = '2.5.0'
|
||||||
|
// Compose
|
||||||
|
activity_compose_version = '1.6.1'
|
||||||
|
compose_version = '1.3.3'
|
||||||
|
compose_compiler_version = '1.4.3'
|
||||||
|
material3_version = '1.0.1'
|
||||||
|
hilt_compose_version = '1.0.0'
|
||||||
lifecycle_version = '2.5.1'
|
lifecycle_version = '2.5.1'
|
||||||
navigation_version = '2.5.3'
|
navigation_version = '2.5.3'
|
||||||
work_version = '2.7.1'
|
// Utility
|
||||||
room_version = '2.5.0'
|
|
||||||
timber_version = '5.0.1'
|
timber_version = '5.0.1'
|
||||||
osmdroid_version = '6.1.14'
|
osmdroid_version = '6.1.14'
|
||||||
json_version = '20220924'
|
json_version = '20220924'
|
||||||
compose_version = '1.3.3'
|
|
||||||
compose_compiler_version = '1.4.3'
|
|
||||||
activity_compose_version = '1.6.1'
|
|
||||||
material_version = '1.3.1'
|
|
||||||
material3_version = '1.0.1'
|
|
||||||
leakcanary_version = '2.10'
|
leakcanary_version = '2.10'
|
||||||
|
// Test
|
||||||
junit_version = '4.13.2'
|
junit_version = '4.13.2'
|
||||||
mockito_version = '5.1.1'
|
mockito_version = '5.1.1'
|
||||||
coroutines_test_version = '1.6.4'
|
coroutines_test_version = '1.6.4'
|
||||||
androidx_test_version = '1.5.2'
|
|
||||||
androidx_junit_version = '1.1.5'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +31,7 @@ plugins {
|
|||||||
id "com.android.application" version "$application_version" apply false
|
id "com.android.application" version "$application_version" apply false
|
||||||
id "com.android.library" version "$library_version" apply false
|
id "com.android.library" version "$library_version" apply false
|
||||||
id "org.jetbrains.kotlin.android" version "$kotlin_android_version" apply false
|
id "org.jetbrains.kotlin.android" version "$kotlin_android_version" apply false
|
||||||
id "com.google.dagger.hilt.android" version "$hilt_version" apply false
|
id "com.google.dagger.hilt.android" version "$hilt_android_version" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
|
|||||||