Further classes and dependencies cleanup

pull/122/head
Arty Bishop 2023-03-03 11:21:02 +00:00
rodzic 43ce9941de
commit 172edb2763
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 5C71CFDC37AD73CC
59 zmienionych plików z 1020 dodań i 1777 usunięć

Wyświetl plik

@ -6,9 +6,8 @@ plugins {
}
android {
compileSdkVersion 33
buildToolsVersion '33.0.0'
namespace 'com.rtbishop.look4sat'
compileSdk 33
defaultConfig {
applicationId "com.rtbishop.look4sat"
@ -21,7 +20,6 @@ android {
vectorDrawables {
useSupportLibrary true
}
kapt {
arguments {
arg("room.schemaLocation", "$projectDir/schemas")
@ -31,6 +29,9 @@ android {
correctErrorTypes true
}
}
buildFeatures {
compose true
}
buildTypes {
release {
minifyEnabled true
@ -38,29 +39,25 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
buildFeatures {
compose true
viewBinding true
}
composeOptions {
kotlinCompilerExtensionVersion compose_compiler_version
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
composeOptions {
kotlinCompilerExtensionVersion compose_compiler_version
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
testOptions {
animationsDisabled true
unitTests.includeAndroidResources true
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
testOptions {
animationsDisabled true
unitTests.includeAndroidResources true
}
}
kotlin {
@ -69,43 +66,34 @@ kotlin {
dependencies {
implementation project(":base")
implementation "androidx.core:core-ktx:$core_ktx_version"
implementation "androidx.core:core-splashscreen:$core_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"
// Android
implementation "androidx.core:core-splashscreen:$splashscreen_version"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
implementation "com.jakewharton.timber:timber:$timber_version"
implementation "org.osmdroid:osmdroid-android:$osmdroid_version"
implementation "com.google.dagger:hilt-android:$hilt_android_version"
kapt "com.google.dagger:hilt-compiler:$hilt_android_version"
// Compose
implementation "androidx.activity:activity-compose:$activity_compose_version"
implementation "androidx.compose.animation:animation:$compose_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.runtime:runtime:$compose_version"
implementation "androidx.compose.runtime:runtime-livedata:$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.lifecycle:lifecycle-viewmodel-compose:$lifecycle_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-test-manifest:$compose_version"
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakcanary_version"
// Test
testImplementation "junit:junit:$junit_version"
testImplementation "org.mockito:mockito-core:$mockito_version"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_test_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"
}

Wyświetl plik

@ -1,5 +1,6 @@
<?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_FINE_LOCATION" />
@ -12,13 +13,16 @@
<application
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:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Look4Sat.Main">
android:theme="@style/Theme.Look4Sat.Main"
tools:targetApi="31">
<activity
android:name=".presentation.MainActivity"
android:exported="true"
@ -30,4 +34,4 @@
</activity>
</application>
</manifest>
</manifest>

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 25 KiB

Wyświetl plik

@ -22,8 +22,6 @@ import android.content.Context
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.os.Bundle
import android.os.SystemClock
import android.view.View
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
@ -47,15 +45,3 @@ class MainActivity : ComponentActivity() {
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()
}
})
}

Wyświetl plik

@ -12,7 +12,7 @@ import androidx.compose.material.icons.outlined.PlayArrow
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material3.*
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.saveable.rememberSaveable
import androidx.compose.ui.Alignment
@ -40,7 +40,7 @@ fun EntriesScreen(
viewModel: EntriesViewModel = 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 toggleDialog = { showDialog.value = showDialog.value.not() }

Wyświetl plik

@ -17,50 +17,59 @@
*/
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.ISettingsManager
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.model.SatItem
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import java.util.*
import javax.inject.Inject
@OptIn(ExperimentalCoroutinesApi::class)
@HiltViewModel
class EntriesViewModel @Inject constructor(
private val repository: IDataRepository,
private val settings: ISettingsManager
private val repository: IDataRepository, private val settings: ISettingsManager
) : ViewModel() {
private val satType = MutableLiveData("All")
private val satType = MutableStateFlow("All")
// private val transModes = MutableLiveData(settings.loadModesSelection())
private val currentQuery = MutableLiveData(String())
private val itemsFromRepo = liveData {
delay(250)
emit(loadEntriesWithSelection())
} as MutableLiveData
private val itemsWithType = satType.switchMap { type ->
private val currentQuery = MutableStateFlow(String())
private val itemsFromRepo = MutableStateFlow<List<SatItem>>(emptyList())
private val itemsWithType = satType.flatMapLatest { type ->
itemsFromRepo.map { items -> filterByType(items, type) }
}
// private val itemsWithModes = transModes.switchMap { 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) }
}
val satData = itemsWithQuery.map { items -> DataState.Success(items) }
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) {
satType.value = type
}
fun selectCurrentItems(selectAll: Boolean) {
itemsWithQuery.value?.let { itemsWithQuery ->
updateSelection(itemsWithQuery.map { item -> item.catnum }, selectAll)
viewModelScope.launch {
val lastValue = itemsWithQuery.last()
updateSelection(lastValue.map { item -> item.catnum }, selectAll)
}
}
@ -74,13 +83,13 @@ class EntriesViewModel @Inject constructor(
}
fun saveSelection(): List<Int> {
return itemsFromRepo.value?.let { itemsAll ->
return itemsFromRepo.value.let { itemsAll ->
itemsAll.filter { item -> item.isSelected }.map { item -> item.catnum }
} ?: emptyList()
}
}
fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
itemsFromRepo.value?.let { itemsAll ->
itemsFromRepo.value.let { itemsAll ->
val copiedList = itemsAll.map { item -> item.copy() }
catNums.forEach { catnum ->
copiedList.find { item -> item.catnum == catnum }?.isSelected = isSelected

Wyświetl plik

@ -11,7 +11,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ElevatedCard
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -63,10 +63,10 @@ fun MapScreen(viewModel: MapViewModel = hiltViewModel()) {
val context = LocalContext.current
Configuration.getInstance().load(context, viewModel.getPreferences())
viewModel.selectDefaultSatellite(-1)
val stationPos = viewModel.stationPos.observeAsState()
val positions = viewModel.positions.observeAsState()
val satTrack = viewModel.track.observeAsState()
val footprint = viewModel.footprint.observeAsState()
val stationPos = viewModel.stationPos.collectAsState(initial = null)
val positions = viewModel.positions.collectAsState(initial = null)
val satTrack = viewModel.track.collectAsState(initial = null)
val footprint = viewModel.footprint.collectAsState(initial = null)
val positionClick = { satellite: Satellite -> viewModel.selectSatellite(satellite) }
Timber.d("MapScreen recomposition")
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
private fun rememberMapViewWithLifecycle(context: Context = LocalContext.current): MapView {
// add clipToOutline = true if needed
val mapView = remember { MapView(context).apply { id = R.id.osm_map_view } }.apply {
val mapView = remember { MapView(context) }.apply {
setMultiTouchControls(true)
setTileSource(TileSourceFactory.WIKIMEDIA)
minZoomLevel = getMinZoom(resources.displayMetrics.heightPixels) + 0.25

Wyświetl plik

@ -18,7 +18,8 @@
package com.rtbishop.look4sat.presentation.mapScreen
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.ISatelliteManager
import com.rtbishop.look4sat.domain.ISettingsManager
@ -30,8 +31,12 @@ import com.rtbishop.look4sat.utility.toDegrees
import com.rtbishop.look4sat.utility.toTimerString
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.flow
import java.util.*
import javax.inject.Inject
import kotlin.collections.set
import kotlin.math.max
import kotlin.math.min
@ -50,23 +55,23 @@ class MapViewModel @Inject constructor(
private var dataUpdateRate = 1000L
private var selectedSatellite: Satellite? = null
val stationPos = liveData {
val stationPos = flow {
val osmLat = clipLat(stationPosition.lat)
val osmLon = clipLon(stationPosition.lon)
emit(GeoPos(osmLat, osmLon))
}
private val _track = MutableLiveData<List<List<GeoPos>>>()
val track: LiveData<List<List<GeoPos>>> = _track
private val _track = MutableSharedFlow<List<List<GeoPos>>>()
val track: SharedFlow<List<List<GeoPos>>> = _track
private val _footprint = MutableLiveData<SatPos>()
val footprint: LiveData<SatPos> = _footprint
private val _footprint = MutableSharedFlow<SatPos>()
val footprint: SharedFlow<SatPos> = _footprint
private val _mapData = MutableLiveData<MapData>()
val mapData: LiveData<MapData> = this._mapData
private val _mapData = MutableSharedFlow<MapData>()
val mapData: SharedFlow<MapData> = _mapData
private val _positions = MutableLiveData<Map<Satellite, GeoPos>>()
val positions: LiveData<Map<Satellite, GeoPos>> = _positions
private val _positions = MutableSharedFlow<Map<Satellite, GeoPos>>()
val positions: SharedFlow<Map<Satellite, GeoPos>> = _positions
fun getPreferences() = preferences
@ -144,7 +149,7 @@ class MapViewModel @Inject constructor(
currentTrack.add(currentPosition)
}
satTracks.add(currentTrack)
_track.postValue(satTracks)
_track.emit(satTracks)
}
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())
positions[satellite] = GeoPos(osmLat, osmLon)
}
_positions.postValue(positions)
_positions.emit(positions)
}
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
val satPos = satelliteManager.getPosition(satellite, pos, date.time)
_footprint.postValue(satPos)
_footprint.emit(satPos)
}
private suspend fun getSatData(sat: Satellite, pos: GeoPos, date: Date) {
@ -202,7 +207,7 @@ class MapViewModel @Inject constructor(
phase,
visibility
)
_mapData.postValue(satData)
_mapData.emit(satData)
}
private fun clipLat(latitude: Double): Double {

Wyświetl plik

@ -1,5 +1,3 @@
@file:OptIn(ExperimentalMaterialApi::class)
package com.rtbishop.look4sat.presentation.passesScreen
import androidx.compose.foundation.ExperimentalFoundationApi
@ -8,14 +6,9 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
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.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
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.SatPass
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.util.*
private val sdf = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun PassesScreen(navToRadar: () -> Unit, viewModel: PassesViewModel = hiltViewModel()) {
val state = viewModel.passes.observeAsState()
val timerText = viewModel.timerText.observeAsState()
val state = viewModel.passes.collectAsState(initial = null)
val timerText = viewModel.timerText.collectAsState(initial = null)
val isRefreshing = state.value is DataState.Loading
val refreshState = rememberPullRefreshState(refreshing = isRefreshing,
onRefresh = { viewModel.calculatePasses() })

Wyświetl plik

@ -17,7 +17,8 @@
*/
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.ISatelliteManager
import com.rtbishop.look4sat.domain.ISettingsManager
@ -26,6 +27,8 @@ import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.utility.toTimerString
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
@HiltViewModel
@ -35,13 +38,12 @@ class PassesViewModel @Inject constructor(
private val settings: ISettingsManager
) : 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 _timerText = MutableLiveData<Triple<String, String, String>>()
private val _timerText = MutableStateFlow(timerData)
private var passesProcessing: Job? = null
val entriesTotal: LiveData<Int> = repository.getEntriesTotal().asLiveData()
val passes: LiveData<DataState<List<SatPass>>> = _passes
val timerText: LiveData<Triple<String, String, String>> = _timerText
val passes: StateFlow<DataState<List<SatPass>>> = _passes
val timerText: StateFlow<Triple<String, String, String>?> = _timerText
fun getHoursAhead() = settings.getHoursAhead()
@ -63,18 +65,18 @@ class PassesViewModel @Inject constructor(
val name = nextPass.name
val millisBeforeStart = nextPass.aosTime.minus(timeNow)
val timerString = millisBeforeStart.toTimerString()
_timerText.postValue(Triple("Next - Id:$catNum", name, timerString))
_timerText.emit(Triple("Next - Id:$catNum", name, timerString))
} catch (e: NoSuchElementException) {
val lastPass = newPasses.last()
val catNum = lastPass.catNum
val name = lastPass.name
val millisBeforeEnd = lastPass.losTime.minus(timeNow)
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)
}
}
@ -97,7 +99,7 @@ class PassesViewModel @Inject constructor(
selection: List<Int>? = null
) {
viewModelScope.launch {
_passes.postValue(DataState.Loading)
_passes.emit(DataState.Loading)
// _timerText.postValue(timerDefaultText)
passesProcessing?.cancelAndJoin()
selection?.let { items -> settings.saveEntriesSelection(items) }

Wyświetl plik

@ -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

Wyświetl plik

@ -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
}
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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()
}
}
}

Wyświetl plik

@ -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()
// }
//}

Wyświetl plik

@ -19,7 +19,10 @@ package com.rtbishop.look4sat.presentation.radarScreen
import android.hardware.GeomagneticField
import android.util.Log
import androidx.lifecycle.*
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteManager
import com.rtbishop.look4sat.domain.ISettingsManager
@ -34,6 +37,7 @@ import com.rtbishop.look4sat.utility.round
import com.rtbishop.look4sat.utility.toDegrees
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -56,7 +60,7 @@ class RadarViewModel @Inject constructor(
val transmitters: LiveData<List<SatRadio>> = _transmitters
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 ->
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
pass?.let { satPass ->

Wyświetl plik

@ -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))
}
}
}
}

Wyświetl plik

@ -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)))
}
}

Wyświetl plik

@ -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)))
//}

Wyświetl plik

@ -18,7 +18,6 @@
package com.rtbishop.look4sat.presentation.settingsScreen
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ILocationManager
import com.rtbishop.look4sat.domain.ISettingsManager
@ -35,8 +34,8 @@ class SettingsViewModel @Inject constructor(
private val settings: ISettingsManager
) : ViewModel() {
val entriesTotal = repository.getEntriesTotal().asLiveData()
val radiosTotal = repository.getRadiosTotal().asLiveData()
val entriesTotal = repository.getEntriesTotal()
val radiosTotal = repository.getRadiosTotal()
fun updateFromFile(uri: String) = repository.updateFromFile(uri)

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="512"
android:viewportHeight="512">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="287.97"
android:viewportHeight="287.97">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="128"
android:viewportHeight="128">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path

Wyświetl plik

@ -2,7 +2,6 @@
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path

Wyświetl plik

@ -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>

Wyświetl plik

@ -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_file"
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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -1,236 +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"
android:textColor="@color/textDisabled"
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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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" />

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 2.2 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 3.8 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 1.5 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 2.4 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.5 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 5.2 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 3.1 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 5.4 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 4.8 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 8.5 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 6.6 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 12 KiB

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -5,4 +5,4 @@
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>
</network-security-config>

Wyświetl plik

@ -1,31 +1,29 @@
buildscript {
ext {
hilt_version = '2.45'
hilt_compose_version = '1.0.0'
application_version = '7.4.2'
library_version = '7.1.2'
kotlin_android_version = '1.8.10'
core_ktx_version = '1.9.0'
core_splashscreen_version = '1.0.0'
constraint_version = '2.1.4'
hilt_android_version = '2.45'
// Android
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'
navigation_version = '2.5.3'
work_version = '2.7.1'
room_version = '2.5.0'
// Utility
timber_version = '5.0.1'
osmdroid_version = '6.1.14'
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'
// Test
junit_version = '4.13.2'
mockito_version = '5.1.1'
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.library" version "$library_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) {