diff --git a/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/MainGnssFragment.kt b/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/MainGnssFragment.kt index a1b7e33f..0173f4f7 100644 --- a/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/MainGnssFragment.kt +++ b/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/MainGnssFragment.kt @@ -13,6 +13,8 @@ import androidx.core.app.ActivityCompat import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 import com.craxiom.networksurvey.Application @@ -35,6 +37,8 @@ class MainGnssFragment : Fragment(), MenuProvider { private var menu: Menu? = null private var selectedTab: Int = 0 + private val _tabChangeLiveData = MutableLiveData() + val tabChangeLiveData: LiveData get() = _tabChangeLiveData @ExperimentalCoroutinesApi val viewModel: SignalInfoViewModel by activityViewModels() @@ -66,6 +70,7 @@ class MainGnssFragment : Fragment(), MenuProvider { // Update menu based on the selected tab menu?.setGroupVisible(R.id.gnss_status_group, position == 0) selectedTab = position + _tabChangeLiveData.value = position } }) } diff --git a/networksurvey/src/main/java/com/craxiom/networksurvey/ui/main/AlertDialogs.kt b/networksurvey/src/main/java/com/craxiom/networksurvey/ui/main/AlertDialogs.kt index 22d04053..ed541b48 100644 --- a/networksurvey/src/main/java/com/craxiom/networksurvey/ui/main/AlertDialogs.kt +++ b/networksurvey/src/main/java/com/craxiom/networksurvey/ui/main/AlertDialogs.kt @@ -1,22 +1,31 @@ package com.craxiom.networksurvey.ui.main +import android.content.Context +import android.content.SharedPreferences import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.selection.selectable import androidx.compose.material3.AlertDialog import androidx.compose.material3.Checkbox +import androidx.compose.material3.RadioButton import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringArrayResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.craxiom.networksurvey.Application import com.craxiom.networksurvey.R @@ -55,10 +64,8 @@ fun GnssFilterDialog( onSave: () -> Unit ) { val context = LocalContext.current - val selectedChecks = remember { mutableStateListOf(*initialChecks.toTypedArray()) } - AlertDialog( onDismissRequest = onDismissRequest, title = { Text(text = context.getString(R.string.filter_dialog_title)) }, @@ -112,3 +119,72 @@ fun GnssFilterDialog( ) } +@Composable +fun GnssSortByDialog( + onDismissRequest: () -> Unit, + onSave: () -> Unit +) { + val context = LocalContext.current + + val prefs = Application.getPrefs() + val currentSatOrder = PreferenceUtils.getSatSortOrderFromPreferences(context, prefs) + var selectedOptionIndex by remember { mutableStateOf(currentSatOrder) } + val sortOptions = stringArrayResource(id = R.array.sort_sats) + + AlertDialog( + onDismissRequest = onDismissRequest, + title = { Text(text = stringResource(id = R.string.menu_option_sort_by)) }, + text = { + Column { + sortOptions.forEachIndexed { index, option -> + Row( + modifier = Modifier + .fillMaxWidth() + .selectable( + selected = (index == selectedOptionIndex), + onClick = { selectedOptionIndex = index } + ) + .padding(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton( + selected = (index == selectedOptionIndex), + onClick = { selectedOptionIndex = index } + ) + Spacer(modifier = Modifier.width(8.dp)) + Text(text = option) + } + } + } + }, + confirmButton = { + TextButton( + onClick = { + // Save selected sort option to preferences + setSortByClause(context, selectedOptionIndex, prefs) + onSave() + onDismissRequest() + } + ) { + Text(text = stringResource(id = R.string.save)) + } + }, + dismissButton = { + TextButton(onClick = onDismissRequest) { + Text(text = stringResource(id = android.R.string.cancel)) + } + } + ) +} + +/** + * Saves the "sort by" order to preferences. + */ +private fun setSortByClause(context: Context, index: Int, prefs: SharedPreferences) { + val sortOptions = context.resources.getStringArray(R.array.sort_sats) + PreferenceUtils.saveString( + prefs, + context.resources.getString(R.string.pref_key_default_sat_sort), + sortOptions[index] + ) +} diff --git a/networksurvey/src/main/java/com/craxiom/networksurvey/ui/main/HomeScreen.kt b/networksurvey/src/main/java/com/craxiom/networksurvey/ui/main/HomeScreen.kt index d06e7e85..9d2b1ffb 100644 --- a/networksurvey/src/main/java/com/craxiom/networksurvey/ui/main/HomeScreen.kt +++ b/networksurvey/src/main/java/com/craxiom/networksurvey/ui/main/HomeScreen.kt @@ -9,15 +9,19 @@ import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.viewinterop.AndroidViewBinding +import androidx.lifecycle.Observer +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.navigation.NavController import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController @@ -49,7 +53,10 @@ fun HomeScreen( ) { val bottomNavController: NavHostController = rememberNavController() var currentScreen by remember { mutableStateOf(MainScreens.Dashboard) } + var currentGnssScreen by remember { mutableStateOf(GnssScreen.GNSS_DETAILS) } var showGnssFilterDialog by remember { mutableStateOf(false) } + var showGnssSortDialog by remember { mutableStateOf(false) } + Scaffold( topBar = { @@ -58,8 +65,10 @@ fun HomeScreen( title = getAppBarTitle(currentScreen), appBarActions = getAppBarActions( currentScreen, + currentGnssScreen, mainNavController, - showGnssFilterDialog = { showGnssFilterDialog = it }) + showGnssFilterDialog = { showGnssFilterDialog = it }, + showGnssSortDialog = { showGnssSortDialog = true }) ) }, bottomBar = { @@ -89,7 +98,9 @@ fun HomeScreen( } composable(MainScreens.Gnss.route) { currentScreen = MainScreens.Gnss - GnssFragmentInCompose() + GnssFragmentInCompose(onGnssScreenChange = { newScreen -> + currentGnssScreen = newScreen + }) } } } @@ -100,6 +111,13 @@ fun HomeScreen( onSave = { showGnssFilterDialog = false } ) } + + if (showGnssSortDialog) { + GnssSortByDialog( + onDismissRequest = { showGnssSortDialog = false }, + onSave = { showGnssFilterDialog = false } + ) + } } @Composable @@ -155,8 +173,10 @@ fun getAppBarTitle(currentScreen: MainScreens): Int { @Composable fun getAppBarActions( currentScreen: MainScreens, + currentGnssScreen: GnssScreen, navController: NavController, - showGnssFilterDialog: (Boolean) -> Unit + showGnssFilterDialog: (Boolean) -> Unit, + showGnssSortDialog: (Boolean) -> Unit ): List { return when (currentScreen) { MainScreens.Cellular -> listOf( @@ -179,20 +199,28 @@ fun getAppBarActions( ) ) - MainScreens.Gnss -> listOf( - AppBarAction( - icon = R.drawable.ic_sort, - description = R.string.menu_option_sort_by, - onClick = { - // TODO Finish me - } - ), - AppBarAction( - icon = R.drawable.ic_filter, - description = R.string.menu_option_filter_content_description, - onClick = { showGnssFilterDialog(true) } - ) - ) + MainScreens.Gnss -> { + return when (currentGnssScreen) { + GnssScreen.GNSS_DETAILS -> listOf( + AppBarAction( + icon = R.drawable.ic_sort, + description = R.string.menu_option_sort_by, + onClick = { showGnssSortDialog(true) } + ), + AppBarAction( + icon = R.drawable.ic_filter, + description = R.string.menu_option_filter_content_description, + onClick = { showGnssFilterDialog(true) } + ) + ) + + GnssScreen.GNSS_SKY_VIEW -> listOf(AppBarAction( + icon = R.drawable.ic_filter, + description = R.string.menu_option_filter_content_description, + onClick = { showGnssFilterDialog(true) } + )) + } + } else -> emptyList() } @@ -204,7 +232,7 @@ fun ShowSatsFilterDialog( onSave: () -> Unit ) { val context = LocalContext.current - val gnssTypes = GnssType.values() + val gnssTypes = GnssType.entries.toTypedArray() val len = gnssTypes.size // Retrieve the current filter from SharedPreferences @@ -234,6 +262,11 @@ sealed class MainScreens(val route: String) { data object Gnss : MainScreens("gnss_route") } +enum class GnssScreen { + GNSS_DETAILS, + GNSS_SKY_VIEW +} + data class BottomNavItem( val label: String = "", @DrawableRes val icon: Int = R.drawable.ic_dashboard, @@ -299,8 +332,26 @@ fun BluetoothFragmentInCompose() { } @Composable -fun GnssFragmentInCompose() { +fun GnssFragmentInCompose(onGnssScreenChange: (GnssScreen) -> Unit) { + var fragment: MainGnssFragment? = null + + val lifecycleOwner = LocalLifecycleOwner.current + val tabChangeObserver = rememberUpdatedState(newValue = { position: Int -> + val newScreen = if (position == 0) GnssScreen.GNSS_DETAILS else GnssScreen.GNSS_SKY_VIEW + onGnssScreenChange(newScreen) + }) + + DisposableEffect(lifecycleOwner) { + val observer = Observer { position -> + tabChangeObserver.value(position) + } + fragment?.tabChangeLiveData?.observe(lifecycleOwner, observer) + onDispose { + fragment?.tabChangeLiveData?.removeObserver(observer) + } + } + AndroidViewBinding(ContainerGnssFragmentBinding::inflate) { - val fragment = gnssFragmentContainerView.getFragment() + fragment = gnssFragmentContainerView.getFragment() } }