From 6d07721cf9537093cfc0f7d3c1f3d93aba940bbe Mon Sep 17 00:00:00 2001 From: kari-ts Date: Fri, 6 Dec 2024 14:18:12 -0800 Subject: [PATCH] android: create SearchView The Material3 search bar doesn't open up a full screen view, but opens a view underneath the bar. To accomplish the full screen search page, we create a new SearchView and use a decoy search bar on the main view that navigates the user to the SearchView Tapping on a search suggestion / result navigates to the PeerDetails, so this also updates the navigation for PeerDetails to use a back stack instead of always navigating back to the main screen Updates tailscale/corp#18973 Signed-off-by: kari-ts --- .../com/tailscale/ipn/ui/view/MainView.kt | 35 +++++++++---------- .../com/tailscale/ipn/ui/view/SearchView.kt | 11 +++--- android/src/main/res/values/strings.xml | 1 + 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt index 2c1ef27..49fa5ed 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt @@ -88,6 +88,7 @@ import com.tailscale.ipn.ui.theme.short import com.tailscale.ipn.ui.theme.surfaceContainerListItem import com.tailscale.ipn.ui.theme.warningButton import com.tailscale.ipn.ui.theme.warningListItem +import com.tailscale.ipn.ui.util.AndroidTVUtil.isAndroidTV import com.tailscale.ipn.ui.util.AutoResizingText import com.tailscale.ipn.ui.util.Lists import com.tailscale.ipn.ui.util.LoadingIndicator @@ -137,7 +138,7 @@ fun MainView( val showKeyExpiry by viewModel.showExpiry.collectAsState(initial = false) // Hide the header only on Android TV when the user needs to login - val hideHeader = (/*isAndroidTV() && */ state == Ipn.State.NeedsLogin) + val hideHeader = (isAndroidTV() && state == Ipn.State.NeedsLogin) ListItem( colors = MaterialTheme.colorScheme.surfaceContainerListItem, @@ -529,11 +530,11 @@ fun PeerList( var isListFocussed by remember { mutableStateOf(false) } val expandedPeer = viewModel.expandedMenuPeer.collectAsState() val localClipboardManager = LocalClipboardManager.current - val enableSearch = true // !isAndroidTV() + val enableSearch = !isAndroidTV() Column(modifier = Modifier.fillMaxSize()) { if (enableSearch) { - SearchWithDynamicSuggestions(viewModel, onSearchBarClick) + Search(onSearchBarClick) Spacer(modifier = Modifier.height(if (showNoResults) 0.dp else 8.dp)) } @@ -570,11 +571,11 @@ fun PeerList( } first = false - // if (isAndroidTV()) { - item { NodesSectionHeader(peerSet = peerSet) } - /* } else { + if (isAndroidTV()) { + item { NodesSectionHeader(peerSet = peerSet) } + } else { stickyHeader { NodesSectionHeader(peerSet = peerSet) } - }*/ + } itemsWithDividers(peerSet.peers, key = { it.StableID }) { peer -> ListItem( @@ -694,8 +695,7 @@ fun PromptPermissionsIfNecessary() { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SearchWithDynamicSuggestions( - viewModel: MainViewModel, +fun Search( onSearchBarClick: () -> Unit // Callback for navigating to SearchView ) { // Prevent multiple taps @@ -705,30 +705,27 @@ fun SearchWithDynamicSuggestions( Box( modifier = Modifier.fillMaxWidth() - .height(56.dp) // Height matching Material Design search bar - .clip(RoundedCornerShape(28.dp)) // Fully rounded edges - .background(MaterialTheme.colorScheme.surface) // Surface background + .height(56.dp) + .clip(RoundedCornerShape(28.dp)) + .background(MaterialTheme.colorScheme.surface) .clickable(enabled = !isNavigating) { // Intercept taps isNavigating = true onSearchBarClick() // Trigger navigation } - .padding(horizontal = 16.dp) // Padding for a clean look - ) { + .padding(horizontal = 16.dp)) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxSize()) { - // Search Icon Icon( imageVector = Icons.Default.Search, - contentDescription = "Search", + contentDescription = stringResource(R.string.search), tint = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(start = 16.dp)) Spacer(modifier = Modifier.width(8.dp)) // Placeholder Text Text( - text = "Search...", + text = stringResource(R.string.search_ellipsis), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.weight(1f) // Fill remaining space - ) + modifier = Modifier.weight(1f)) } } } diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/SearchView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/SearchView.kt index 20f6204..1a74775 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/SearchView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/SearchView.kt @@ -44,15 +44,12 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.unit.dp import androidx.navigation.NavController +import com.tailscale.ipn.R import com.tailscale.ipn.ui.viewModel.MainViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SearchView( - viewModel: MainViewModel, - navController: NavController, // Use NavController for navigation - onNavigateBack: () -> Unit -) { +fun SearchView(viewModel: MainViewModel, navController: NavController, onNavigateBack: () -> Unit) { val searchTerm by viewModel.searchTerm.collectAsState() val filteredPeers by viewModel.peers.collectAsState() val netmap by viewModel.netmap.collectAsState() @@ -84,7 +81,7 @@ fun SearchView( focusManager.clearFocus() keyboardController?.hide() }, - placeholder = { Text("Search") }, + placeholder = { R.string.search }, leadingIcon = { IconButton( onClick = { @@ -138,7 +135,7 @@ fun SearchView( colors = ListItemDefaults.colors(containerColor = Color.Transparent), modifier = Modifier.clickable { - navController.navigate("peerDetails/${peer.StableID}") + navController.navigate("peerDetails/${peer.StableID}") } .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 4.dp)) diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml index 8d6657e..c2eb7df 100644 --- a/android/src/main/res/values/strings.xml +++ b/android/src/main/res/values/strings.xml @@ -18,6 +18,7 @@ Continue Warning Search + Search... Dismiss No results