mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Added filter dialog, removed PassesFragment
This commit is contained in:
1 parent
a08cfc7fb8
commit
acb851d4f6
11 files changed
+74
-816
No files matched your search
@@ -1,20 +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.framework.model
|
||||
|
||||
data class DataSource(var url: String = String())
|
||||
@@ -17,54 +17,70 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.passesScreen
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.DialogFilterBinding
|
||||
import com.rtbishop.look4sat.domain.ISettingsManager
|
||||
import com.rtbishop.look4sat.presentation.setNavResult
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.rtbishop.look4sat.presentation.CardButton
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
|
||||
@AndroidEntryPoint
|
||||
class FilterDialog : AppCompatDialogFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var preferences: ISettingsManager
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.dialog_filter, group, false)
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun FilterDialogPreview() {
|
||||
MainTheme { FilterDialog(8, 16.0, {}) { _, _ -> } }
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, state: Bundle?) {
|
||||
super.onViewCreated(view, state)
|
||||
DialogFilterBinding.bind(view).apply {
|
||||
dialog?.window?.setBackgroundDrawableResource(R.color.transparent)
|
||||
dialog?.window?.setLayout(
|
||||
(resources.displayMetrics.widthPixels * 0.94).toInt(),
|
||||
WindowManager.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
filterHoursEdit.setText(preferences.getHoursAhead().toString())
|
||||
filterElevEdit.setText(preferences.getMinElevation().toString())
|
||||
filterBtnPos.setOnClickListener {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun FilterDialog(hours: Int, elevation: Double, toggle: () -> Unit, save: (Int, Double) -> Unit) {
|
||||
val hoursValue = rememberSaveable { mutableStateOf(hours) }
|
||||
val elevValue = rememberSaveable { mutableStateOf(elevation) }
|
||||
Dialog(onDismissRequest = { toggle() }) {
|
||||
ElevatedCard {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.fillMaxWidth(1f)
|
||||
) {
|
||||
Text(text = "Filter passes", color = MaterialTheme.colorScheme.primary)
|
||||
Text(text = "Show passes that occur within X hours")
|
||||
OutlinedTextField(value = hoursValue.value.toString(), onValueChange = { newValue ->
|
||||
val hoursAhead = try {
|
||||
filterHoursEdit.text.toString().toInt()
|
||||
newValue.toInt()
|
||||
} catch (exception: Exception) {
|
||||
24
|
||||
12
|
||||
}
|
||||
val minElevation = try {
|
||||
filterElevEdit.text.toString().toDouble()
|
||||
hoursValue.value = hoursAhead
|
||||
})
|
||||
Text(text = "Show passes with max elevation above")
|
||||
OutlinedTextField(value = elevValue.value.toString(), onValueChange = { newValue ->
|
||||
val maxElevation = try {
|
||||
newValue.toDouble()
|
||||
} catch (exception: Exception) {
|
||||
16.0
|
||||
}
|
||||
setNavResult("prefs", Pair(hoursAhead, minElevation))
|
||||
dismiss()
|
||||
}
|
||||
filterBtnNeg.setOnClickListener { dismiss() }
|
||||
elevValue.value = maxElevation
|
||||
})
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
CardButton(onClick = { toggle() }, text = "Cancel")
|
||||
CardButton(
|
||||
onClick = {
|
||||
save(hoursValue.value, elevValue.value)
|
||||
toggle()
|
||||
}, text = "Accept"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +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.passesScreen
|
||||
|
||||
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.ItemPassBinding
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class PassesAdapter(private val clickListener: PassesClickListener) :
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private val diffCallback = object : DiffUtil.ItemCallback<SatPass>() {
|
||||
override fun areItemsTheSame(oldItem: SatPass, newItem: SatPass): Boolean {
|
||||
return oldItem.catNum == newItem.catNum && oldItem.aosTime == newItem.aosTime
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: SatPass, newItem: SatPass): Boolean {
|
||||
return oldItem.progress == newItem.progress
|
||||
}
|
||||
}
|
||||
private val differ = AsyncListDiffer(this, diffCallback)
|
||||
private var isUTC: Boolean = false
|
||||
|
||||
interface PassesClickListener {
|
||||
fun navigateToPass(satPass: SatPass)
|
||||
}
|
||||
|
||||
fun submitList(passes: List<SatPass>) {
|
||||
differ.submitList(passes)
|
||||
}
|
||||
|
||||
fun setUTC(isUTC: Boolean) {
|
||||
this.isUTC = isUTC
|
||||
}
|
||||
|
||||
override fun getItemCount() = differ.currentList.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return PassHolder.from(parent)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
(holder as PassHolder).bind(differ.currentList[position], clickListener, isUTC)
|
||||
}
|
||||
|
||||
class PassHolder private constructor(private val binding: ItemPassBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
private val satIdFormat = itemView.context.getString(R.string.pass_satId)
|
||||
private val aosAzFormat = itemView.context.getString(R.string.pass_aosAz)
|
||||
private val altFormat = itemView.context.getString(R.string.pass_altitude)
|
||||
private val losAzFormat = itemView.context.getString(R.string.pass_losAz)
|
||||
private val startTimeFormat = itemView.context.getString(R.string.pass_startTime)
|
||||
private val elevFormat = itemView.context.getString(R.string.pass_elevation)
|
||||
private val endTimeFormat = itemView.context.getString(R.string.pass_endTime)
|
||||
private val placeholder = itemView.context.getString(R.string.pass_placeholder)
|
||||
private val timeZoneUTC = TimeZone.getTimeZone("UTC")
|
||||
private val startFormat = SimpleDateFormat(startTimeFormat, Locale.getDefault())
|
||||
private val endFormat = SimpleDateFormat(endTimeFormat, Locale.getDefault())
|
||||
|
||||
fun bind(satPass: SatPass, listener: PassesClickListener, shouldUseUTC: Boolean) {
|
||||
binding.apply {
|
||||
if (satPass.isDeepSpace) {
|
||||
passName.text = satPass.name
|
||||
passId.text = String.format(satIdFormat, satPass.catNum)
|
||||
passAos.text = String.format(aosAzFormat, satPass.aosAzimuth)
|
||||
passAltitude.text = String.format(altFormat, satPass.altitude)
|
||||
passLos.text = String.format(losAzFormat, satPass.losAzimuth)
|
||||
passStart.text = placeholder
|
||||
passElev.text = String.format(elevFormat, satPass.maxElevation)
|
||||
passEnd.text = placeholder
|
||||
passProgress.progress = 100
|
||||
} else {
|
||||
if (shouldUseUTC) {
|
||||
startFormat.timeZone = timeZoneUTC
|
||||
endFormat.timeZone = timeZoneUTC
|
||||
}
|
||||
passName.text = satPass.name
|
||||
passId.text = String.format(satIdFormat, satPass.catNum)
|
||||
passAos.text = String.format(aosAzFormat, satPass.aosAzimuth)
|
||||
passAltitude.text = String.format(altFormat, satPass.altitude)
|
||||
passLos.text = String.format(losAzFormat, satPass.losAzimuth)
|
||||
passStart.text = startFormat.format(Date(satPass.aosTime))
|
||||
passElev.text = String.format(elevFormat, satPass.maxElevation)
|
||||
passEnd.text = endFormat.format(Date(satPass.losTime))
|
||||
passProgress.progress = satPass.progress.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
itemView.setOnClickListener {
|
||||
listener.navigateToPass(satPass)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun from(parent: ViewGroup): PassHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return PassHolder(ItemPassBinding.inflate(inflater, parent, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,167 +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.passesScreen
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.FragmentPassesBinding
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.presentation.clickWithDebounce
|
||||
import com.rtbishop.look4sat.presentation.getNavResult
|
||||
import com.rtbishop.look4sat.utility.toTimerString
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesClickListener {
|
||||
|
||||
private val viewModel: PassesViewModel by viewModels()
|
||||
private val passesAdapter = PassesAdapter(this)
|
||||
private var binding: FragmentPassesBinding? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentPassesBinding.bind(view).apply {
|
||||
passesAdapter.setUTC(viewModel.shouldUseUTC())
|
||||
passesRecycler.apply {
|
||||
setHasFixedSize(true)
|
||||
adapter = passesAdapter
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
addItemDecoration(DividerItemDecoration(requireContext(), 1))
|
||||
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
if (dy > 0 && passesFab.visibility == View.VISIBLE) passesFab.hide()
|
||||
else if (dy < 0 && passesFab.visibility != View.VISIBLE) passesFab.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
passesBtnRefresh.clickWithDebounce { viewModel.calculatePasses() }
|
||||
passesBtnMap.clickWithDebounce {
|
||||
val dir = PassesFragmentDirections.globalToMap()
|
||||
findNavController().navigate(dir)
|
||||
}
|
||||
passesBtnFilter.clickWithDebounce {
|
||||
val dir = PassesFragmentDirections.passesToFilter()
|
||||
findNavController().navigate(dir)
|
||||
}
|
||||
passesBtnSettings.clickWithDebounce {
|
||||
val dir = PassesFragmentDirections.globalToSettings()
|
||||
findNavController().navigate(dir)
|
||||
}
|
||||
setupObservers()
|
||||
}
|
||||
}
|
||||
|
||||
override fun navigateToPass(satPass: SatPass) {
|
||||
if (satPass.progress < 100) satPass.run {
|
||||
val dir = PassesFragmentDirections.globalToRadar(this.catNum, this.aosTime)
|
||||
findNavController().navigate(dir)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
binding?.passesRecycler?.adapter = null
|
||||
binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
viewModel.entriesTotal.observe(viewLifecycleOwner) { number ->
|
||||
handleEntriesTotal(number)
|
||||
}
|
||||
viewModel.passes.observe(viewLifecycleOwner) { passesResult ->
|
||||
handleNewPasses(passesResult)
|
||||
}
|
||||
getNavResult<Pair<Int, Double>>(R.id.nav_passes, "prefs") { prefs ->
|
||||
viewModel.calculatePasses(prefs.first, prefs.second)
|
||||
}
|
||||
getNavResult<List<Int>>(R.id.nav_passes, "selection") { items ->
|
||||
viewModel.calculatePasses(selection = items)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleEntriesTotal(number: Int) {
|
||||
binding?.run {
|
||||
if (number > 0) {
|
||||
passesFab.clickWithDebounce {
|
||||
val direction = PassesFragmentDirections.globalToEntries()
|
||||
findNavController().navigate(direction)
|
||||
}
|
||||
} else {
|
||||
passesFab.clickWithDebounce {
|
||||
val errorMessage = getString(R.string.passes_empty_db)
|
||||
Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleNewPasses(state: DataState<List<SatPass>>) {
|
||||
binding?.run {
|
||||
when (state) {
|
||||
is DataState.Success -> {
|
||||
passesAdapter.submitList(state.data)
|
||||
tickMainTimer(state.data)
|
||||
if (state.data.isNotEmpty()) { // show new passes list
|
||||
passesEmpty.visibility = View.INVISIBLE
|
||||
passesProgress.visibility = View.INVISIBLE
|
||||
} else { // show no passes message
|
||||
passesEmpty.visibility = View.VISIBLE
|
||||
passesProgress.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
is DataState.Loading -> {
|
||||
passesEmpty.visibility = View.INVISIBLE
|
||||
passesProgress.visibility = View.VISIBLE
|
||||
passesTimer.text = 0L.toTimerString()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tickMainTimer(passes: List<SatPass>) {
|
||||
binding?.run {
|
||||
if (passes.isNotEmpty()) {
|
||||
val timeNow = System.currentTimeMillis()
|
||||
try {
|
||||
val nextPass = passes.first { it.aosTime.minus(timeNow) > 0 }
|
||||
val millisBeforeStart = nextPass.aosTime.minus(timeNow)
|
||||
passesTimer.text = millisBeforeStart.toTimerString()
|
||||
} catch (e: NoSuchElementException) {
|
||||
val lastPass = passes.last()
|
||||
val millisBeforeEnd = lastPass.losTime.minus(timeNow)
|
||||
passesTimer.text = millisBeforeEnd.toTimerString()
|
||||
}
|
||||
} else {
|
||||
passesTimer.text = 0L.toTimerString()
|
||||
}
|
||||
passesBtnRefresh.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ 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.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
@@ -42,14 +44,23 @@ fun PassesScreen(viewModel: PassesViewModel = hiltViewModel()) {
|
||||
val state = viewModel.passes.observeAsState()
|
||||
val timerText = viewModel.timerText.observeAsState()
|
||||
val isRefreshing = state.value is DataState.Loading
|
||||
val refreshState = rememberPullRefreshState(
|
||||
refreshing = isRefreshing,
|
||||
val refreshState = rememberPullRefreshState(refreshing = isRefreshing,
|
||||
onRefresh = { viewModel.calculatePasses() })
|
||||
|
||||
val value = state.value
|
||||
var passes = emptyList<SatPass>()
|
||||
if (value is DataState.Success) passes = value.data
|
||||
|
||||
val showDialog = rememberSaveable { mutableStateOf(false) }
|
||||
val toggleDialog = { showDialog.value = showDialog.value.not() }
|
||||
if (showDialog.value) {
|
||||
FilterDialog(viewModel.getHoursAhead(),
|
||||
viewModel.getMinElevation(),
|
||||
{ toggleDialog() }) { hours, elev ->
|
||||
viewModel.calculatePasses(hours, elev)
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
ElevatedCard(modifier = Modifier.height(52.dp)) {
|
||||
Row(
|
||||
@@ -57,7 +68,7 @@ fun PassesScreen(viewModel: PassesViewModel = hiltViewModel()) {
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
IconButton(onClick = {}) {
|
||||
IconButton(onClick = { toggleDialog() }) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_filter),
|
||||
contentDescription = null
|
||||
|
||||
@@ -43,6 +43,10 @@ class PassesViewModel @Inject constructor(
|
||||
val passes: LiveData<DataState<List<SatPass>>> = _passes
|
||||
val timerText: LiveData<Triple<String, String, String>> = _timerText
|
||||
|
||||
fun getHoursAhead() = settings.getHoursAhead()
|
||||
|
||||
fun getMinElevation() = settings.getMinElevation()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
satelliteManager.calculatedPasses.collect { passes ->
|
||||
|
||||
@@ -1,131 +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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/filter_title"
|
||||
style="@style/DialogTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:minHeight="48dp"
|
||||
android:text="@string/filter_title"
|
||||
app:layout_constraintBottom_toTopOf="@+id/filter_hours_text"
|
||||
app:layout_constraintEnd_toEndOf="@+id/filter_btn_pos"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="@+id/filter_btn_neg"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/filter_hours_text"
|
||||
style="@style/DialogText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@string/filter_hours_text"
|
||||
app:layout_constraintBottom_toTopOf="@+id/filter_hours_layout"
|
||||
app:layout_constraintEnd_toEndOf="@+id/filter_title"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="@+id/filter_title"
|
||||
app:layout_constraintTop_toBottomOf="@+id/filter_title" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/filter_hours_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="@string/filter_hours_hint"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="number"
|
||||
android:minHeight="48dp"
|
||||
app:endIconMode="clear_text"
|
||||
app:layout_constraintBottom_toTopOf="@+id/filter_elev_text"
|
||||
app:layout_constraintEnd_toEndOf="@+id/filter_hours_text"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="@+id/filter_hours_text"
|
||||
app:layout_constraintTop_toBottomOf="@+id/filter_hours_text">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/filter_hours_edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:inputType="textUri" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/filter_elev_text"
|
||||
style="@style/DialogText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/filter_elev_text"
|
||||
app:layout_constraintBottom_toTopOf="@+id/filter_elev_layout"
|
||||
app:layout_constraintEnd_toEndOf="@+id/filter_hours_text"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="@+id/filter_hours_text"
|
||||
app:layout_constraintTop_toBottomOf="@+id/filter_hours_layout" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/filter_elev_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="@string/filter_elev_hint"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="number|numberDecimal"
|
||||
android:minHeight="48dp"
|
||||
app:endIconMode="clear_text"
|
||||
app:layout_constraintBottom_toTopOf="@+id/filter_btn_neg"
|
||||
app:layout_constraintEnd_toEndOf="@+id/filter_hours_layout"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="@+id/filter_hours_layout"
|
||||
app:layout_constraintTop_toBottomOf="@+id/filter_elev_text">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/filter_elev_edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:inputType="textUri" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/filter_btn_neg"
|
||||
style="@style/NormalButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="@string/btn_cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/filter_btn_pos"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/filter_elev_layout" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/filter_btn_pos"
|
||||
style="@style/NormalButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/btn_accept"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/filter_btn_neg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/filter_btn_neg" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
@@ -1,84 +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"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sources_title"
|
||||
style="@style/DialogTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="22dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@string/sources_title"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/sources_btn_add"
|
||||
app:layout_constraintEnd_toStartOf="@+id/sources_btn_add"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toStartOf="@+id/sources_btn_neg" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/sources_btn_add"
|
||||
style="@style/NormalButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/btn_add"
|
||||
app:layout_constraintEnd_toEndOf="@+id/sources_btn_pos"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/sources_title"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/sources_recycler"
|
||||
style="@style/Recycler"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/sources_btn_pos"
|
||||
app:layout_constraintDimensionRatio="w,1:1"
|
||||
app:layout_constraintEnd_toEndOf="@+id/sources_btn_pos"
|
||||
app:layout_constraintStart_toStartOf="@+id/sources_btn_neg"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sources_btn_add" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/sources_btn_neg"
|
||||
style="@style/NormalButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="@string/btn_cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/sources_btn_pos"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/sources_btn_pos"
|
||||
style="@style/NormalButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/btn_accept"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/sources_btn_neg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/sources_btn_neg" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
@@ -1,116 +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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:transitionGroup="true">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/passes_toolbar"
|
||||
style="@style/Toolbar">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/passes_btn_refresh"
|
||||
style="@style/ToolbarButton"
|
||||
android:rotation="45"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:contentDescription="@string/btn_refresh"
|
||||
android:src="@drawable/ic_refresh" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/passes_timer"
|
||||
style="@style/ToolbarTimer" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/passes_btn_map"
|
||||
style="@style/ToolbarButton"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:contentDescription="@string/btn_map"
|
||||
android:src="@drawable/ic_map" />
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
style="@style/SurfaceCard"
|
||||
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">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/passes_recycler"
|
||||
style="@style/Recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/passes_progress"
|
||||
style="@style/RecyclerProgress" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/passes_empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="48dp"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/passes_empty_img"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:contentDescription="@string/passes_error"
|
||||
android:src="@drawable/ic_list"
|
||||
app:tint="@color/textDisabled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/passes_empty_msg"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center"
|
||||
android:text="@string/passes_error"
|
||||
android:textSize="@dimen/text_size_mediumLarge" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<com.google.android.material.bottomappbar.BottomAppBar
|
||||
android:id="@+id/passes_bottomBar"
|
||||
style="@style/BottomBar">
|
||||
|
||||
<Button
|
||||
android:id="@+id/passes_btn_filter"
|
||||
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_filter" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/passes_btn_settings"
|
||||
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_settings" />
|
||||
|
||||
</com.google.android.material.bottomappbar.BottomAppBar>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/passes_fab"
|
||||
style="@style/FloatingActionButton"
|
||||
android:src="@drawable/ic_satellite"
|
||||
android:contentDescription="@string/btn_satellites"
|
||||
app:layout_anchor="@id/passes_bottomBar" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -1,115 +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:id="@+id/pass_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pass_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/pass_satName"
|
||||
android:textColor="@color/accent"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pass_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@string/pass_satId"
|
||||
android:textColor="@color/accent"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/pass_name"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pass_aos"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="@string/pass_aosAz"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintEnd_toStartOf="@+id/pass_los"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toStartOf="@+id/pass_name"
|
||||
app:layout_constraintTop_toBottomOf="@+id/pass_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pass_altitude"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pass_altitude"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/pass_aos"
|
||||
app:layout_constraintEnd_toEndOf="@+id/pass_los"
|
||||
app:layout_constraintStart_toStartOf="@+id/pass_aos" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pass_los"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pass_losAz"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/pass_aos"
|
||||
app:layout_constraintEnd_toEndOf="@+id/pass_id"
|
||||
app:layout_constraintStart_toEndOf="@+id/pass_aos" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pass_start"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="@string/pass_startTime"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintEnd_toStartOf="@+id/pass_end"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toStartOf="@+id/pass_aos"
|
||||
app:layout_constraintTop_toBottomOf="@+id/pass_aos" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pass_elev"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pass_elevation"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/pass_start"
|
||||
app:layout_constraintEnd_toEndOf="@+id/pass_end"
|
||||
app:layout_constraintStart_toStartOf="@+id/pass_start" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pass_end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pass_endTime"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/pass_start"
|
||||
app:layout_constraintEnd_toEndOf="@+id/pass_los"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/pass_start" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pass_progress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/pass_end"
|
||||
app:layout_constraintStart_toStartOf="@+id/pass_start"
|
||||
app:layout_constraintTop_toBottomOf="@+id/pass_elev" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
@@ -3,19 +3,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/nav_root"
|
||||
app:startDestination="@id/nav_passes">
|
||||
<fragment
|
||||
android:id="@+id/nav_passes"
|
||||
android:name="com.rtbishop.look4sat.presentation.passesScreen.PassesFragment"
|
||||
tools:layout="@layout/fragment_passes">
|
||||
<action
|
||||
android:id="@+id/passes_to_filter"
|
||||
app:destination="@id/nav_filter"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
</fragment>
|
||||
app:startDestination="@id/nav_radar">
|
||||
<fragment
|
||||
android:id="@+id/nav_radar"
|
||||
android:name="com.rtbishop.look4sat.presentation.radarScreen.RadarFragment"
|
||||
@@ -46,10 +34,6 @@
|
||||
android:id="@+id/nav_modes"
|
||||
android:name="com.rtbishop.look4sat.presentation.entriesScreen.ModesDialog"
|
||||
tools:layout="@layout/dialog_modes" />
|
||||
<dialog
|
||||
android:id="@+id/nav_filter"
|
||||
android:name="com.rtbishop.look4sat.presentation.passesScreen.FilterDialog"
|
||||
tools:layout="@layout/dialog_filter" />
|
||||
<dialog
|
||||
android:id="@+id/nav_position"
|
||||
android:name="com.rtbishop.look4sat.presentation.settingsScreen.PositionDialog"
|
||||
|
||||
Reference in new issue
Block a user