Skip to content

Commit

Permalink
Search in Android Nearby Transit (#584)
Browse files Browse the repository at this point in the history
  • Loading branch information
JackVCurtis authored Dec 17, 2024
1 parent a8e3e16 commit 2fb7f43
Show file tree
Hide file tree
Showing 16 changed files with 383 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ import com.mbta.tid.mbta_app.repositories.IPinnedRoutesRepository
import com.mbta.tid.mbta_app.repositories.IPredictionsRepository
import com.mbta.tid.mbta_app.repositories.IRailRouteShapeRepository
import com.mbta.tid.mbta_app.repositories.ISchedulesRepository
import com.mbta.tid.mbta_app.repositories.ISearchResultRepository
import com.mbta.tid.mbta_app.repositories.IVehiclesRepository
import com.mbta.tid.mbta_app.repositories.MockRailRouteShapeRepository
import com.mbta.tid.mbta_app.repositories.MockScheduleRepository
import com.mbta.tid.mbta_app.repositories.MockSearchResultRepository
import com.mbta.tid.mbta_app.repositories.MockVehiclesRepository
import com.mbta.tid.mbta_app.usecases.TogglePinnedRouteUsecase
import io.github.dellisd.spatialk.geojson.Position
Expand Down Expand Up @@ -249,6 +251,7 @@ class NearbyTransitPageTest : KoinTest {
single<IRailRouteShapeRepository> { MockRailRouteShapeRepository() }
single<TogglePinnedRouteUsecase> { TogglePinnedRouteUsecase(get()) }
single<IVehiclesRepository> { MockVehiclesRepository() }
single<ISearchResultRepository> { MockSearchResultRepository() }
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.mbta.tid.mbta_app.repositories.ISettingsRepository
import com.mbta.tid.mbta_app.repositories.MockSettingsRepository
import com.mbta.tid.mbta_app.repositories.Settings
import kotlin.test.assertTrue
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.koin.compose.KoinContext
Expand All @@ -29,8 +30,7 @@ class MorePageTests : KoinTest {
}

@Test
fun testSettings() {

fun testSettings() = runTest {
var hideMapToggleCalled = false

val koinApplication = koinApplication {
Expand All @@ -55,6 +55,8 @@ class MorePageTests : KoinTest {
composeTestRule.onNodeWithText("Settings").assertIsDisplayed()
composeTestRule.onNodeWithText("Hide Maps").performClick()

composeTestRule.awaitIdle()

assertTrue { hideMapToggleCalled }
}

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package com.mbta.tid.mbta_app.android.search

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.navigation.NavBackStackEntry
import com.mbta.tid.mbta_app.android.R
import com.mbta.tid.mbta_app.android.search.results.StopResultsView
import com.mbta.tid.mbta_app.android.state.getSearchResultsVm

@ExperimentalMaterial3Api
@Composable
fun SearchBarOverlay(
onStopNavigation: (stopId: String) -> Unit,
currentNavEntry: NavBackStackEntry?,
content: @Composable () -> Unit
) {
var visible =
remember(currentNavEntry) {
currentNavEntry?.arguments?.getString("stopId")?.isBlank() ?: true
}
var expanded by rememberSaveable { mutableStateOf(false) }
var searchInputState by rememberSaveable { mutableStateOf("") }
val searchResultsVm = getSearchResultsVm()
val searchResults = searchResultsVm.searchResults.collectAsState(initial = null).value

val buttonColors =
ButtonColors(
containerColor = colorResource(R.color.fill3),
disabledContainerColor = colorResource(R.color.fill3),
contentColor = colorResource(R.color.deemphasized),
disabledContentColor = colorResource(R.color.deemphasized),
)
LaunchedEffect(searchInputState) { searchResultsVm.getSearchResults(searchInputState) }

Box(contentAlignment = Alignment.TopCenter) {
Box(
modifier =
Modifier.absoluteOffset {
if (expanded) IntOffset(0, 0) else IntOffset(0, 12.dp.roundToPx())
}
.zIndex(1f),
contentAlignment = Alignment.Center,
) {
if (visible) {
Box(
modifier =
Modifier.absoluteOffset(y = 3.5.dp)
.height(64.dp)
.width(364.dp)
.border(2.dp, colorResource(R.color.halo), RoundedCornerShape(10.dp))
)
SearchBar(
shape = RoundedCornerShape(10.dp),
colors =
SearchBarDefaults.colors(containerColor = colorResource(R.color.fill3)),
inputField = {
SearchBarDefaults.InputField(
colors =
SearchBarDefaults.inputFieldColors(
focusedTextColor = colorResource(R.color.deemphasized),
unfocusedTextColor = colorResource(R.color.deemphasized),
focusedPlaceholderColor = colorResource(R.color.deemphasized),
unfocusedPlaceholderColor = colorResource(R.color.deemphasized),
),
query = searchInputState,
placeholder = { Text(stringResource(R.string.search_by_stop)) },
expanded = expanded,
onQueryChange = { searchInputState = it },
onExpandedChange = { expanded = it },
onSearch = {},
leadingIcon = {
Icon(
painterResource(R.drawable.magnifying_glass),
"Search",
tint = colorResource(R.color.deemphasized)
)
},
trailingIcon = {
if (expanded) {
Button(
colors = buttonColors,
onClick = { expanded = false },
) {
Icon(
painterResource(R.drawable.fa_xmark),
"Voice Search",
tint = colorResource(R.color.deemphasized)
)
}
}
}
)
},
expanded = expanded,
onExpandedChange = {},
) {
LazyColumn {
items(searchResults?.stops ?: emptyList()) { stop ->
StopResultsView(stop, onStopNavigation)
}
}
}
}
}

content()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.mbta.tid.mbta_app.android.search.results

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import com.mbta.tid.mbta_app.android.R
import com.mbta.tid.mbta_app.model.StopResult

@Composable
fun StopResultsView(stop: StopResult, handleSearch: (String) -> Unit) {
Row(
modifier =
Modifier.clickable { handleSearch(stop.id) }
.background(colorResource(id = R.color.fill2))
.fillMaxWidth()
) {
Text(
text = stop.name,
modifier = Modifier.padding(top = 11.dp, bottom = 11.dp, start = 16.dp, end = 8.dp),
style = MaterialTheme.typography.headlineSmall
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.mbta.tid.mbta_app.android.state

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.mbta.tid.mbta_app.model.SearchResults
import com.mbta.tid.mbta_app.model.response.ApiResult
import com.mbta.tid.mbta_app.repositories.ISearchResultRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import org.koin.compose.koinInject

@OptIn(kotlinx.coroutines.FlowPreview::class)
class SearchResultsViewModel(
private val searchResultRepository: ISearchResultRepository,
) : ViewModel() {
private var _searchResults: MutableStateFlow<SearchResults?> = MutableStateFlow(null)
private var lastClickTime: Long? = null
private var job: Job? = null
val searchResults: StateFlow<SearchResults?> = _searchResults

fun getSearchResults(query: String) {
val currentTime = System.currentTimeMillis()
if (lastClickTime != null && currentTime - lastClickTime!! > 500) {
job?.cancel()
job =
CoroutineScope(Dispatchers.IO).launch {
when (val data = searchResultRepository.getSearchResults(query)) {
is ApiResult.Ok -> _searchResults.emit(data.data)
is ApiResult.Error -> _searchResults.emit(null)
null -> {}
}
}
}
lastClickTime = currentTime
}
}

@Composable
fun getSearchResultsVm(
searchResultRepository: ISearchResultRepository = koinInject()
): SearchResultsViewModel {
val viewModel = remember { SearchResultsViewModel(searchResultRepository) }
return viewModel
}
9 changes: 9 additions & 0 deletions androidApp/src/main/res/drawable/magnifying_glass.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M17,10.5C17,11.934 16.534,13.259 15.75,14.334L19.706,18.294C20.097,18.684 20.097,19.319 19.706,19.709C19.316,20.1 18.681,20.1 18.291,19.709L14.334,15.75C13.259,16.538 11.934,17 10.5,17C6.909,17 4,14.091 4,10.5C4,6.909 6.909,4 10.5,4C14.091,4 17,6.909 17,10.5ZM10.5,15C11.091,15 11.676,14.884 12.222,14.658C12.768,14.431 13.264,14.1 13.682,13.682C14.1,13.264 14.431,12.768 14.658,12.222C14.884,11.676 15,11.091 15,10.5C15,9.909 14.884,9.324 14.658,8.778C14.431,8.232 14.1,7.736 13.682,7.318C13.264,6.9 12.768,6.569 12.222,6.343C11.676,6.116 11.091,6 10.5,6C9.909,6 9.324,6.116 8.778,6.343C8.232,6.569 7.736,6.9 7.318,7.318C6.9,7.736 6.569,8.232 6.343,8.778C6.116,9.324 6,9.909 6,10.5C6,11.091 6.116,11.676 6.343,12.222C6.569,12.768 6.9,13.264 7.318,13.682C7.736,14.1 8.232,14.431 8.778,14.658C9.324,14.884 9.909,15 10.5,15Z"
android:fillColor="#8A9299"/>
</vector>
9 changes: 9 additions & 0 deletions androidApp/src/main/res/drawable/microphone.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,4C10.344,4 9,5.344 9,7V12C9,13.656 10.344,15 12,15C13.656,15 15,13.656 15,12V7C15,5.344 13.656,4 12,4ZM8,10.75C8,10.334 7.666,10 7.25,10C6.834,10 6.5,10.334 6.5,10.75V12C6.5,14.784 8.569,17.084 11.25,17.45V18.5H9.75C9.334,18.5 9,18.834 9,19.25C9,19.666 9.334,20 9.75,20H12H14.25C14.666,20 15,19.666 15,19.25C15,18.834 14.666,18.5 14.25,18.5H12.75V17.45C15.431,17.084 17.5,14.784 17.5,12V10.75C17.5,10.334 17.166,10 16.75,10C16.334,10 16,10.334 16,10.75V12C16,14.209 14.209,16 12,16C9.791,16 8,14.209 8,12V10.75Z"
android:fillColor="#8A9299"/>
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<string name="resources_link_mticket">Boletos de tren de cercanías y ferri</string>
<string name="resources_link_mticket_note">mTicket App</string>
<string name="resources_link_trip_planner">Planificador de viaje</string>
<string name="search_by_stop">Buscar por parada</string>
<string name="service_ended">Servicio finalizado</string>
<string name="setting_toggle_hide_maps">Ocultar Mapas</string>
<string name="settings_link">Configuración</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<string name="resources_link_mticket">Tikè Tren Vil la ak Bato</string>
<string name="resources_link_mticket_note">Aplikasyon mTicket</string>
<string name="resources_link_trip_planner">Zouti pou planifye vwayaj</string>
<string name="search_by_stop">Chèche pa kanpe</string>
<string name="service_ended">Sèvis ki fini</string>
<string name="setting_toggle_hide_maps">Kache Kat yo</string>
<string name="settings_link">Paramèt</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<string name="resources_link_mticket">Bilhetes de transporte diário por trem e balsa</string>
<string name="resources_link_mticket_note">Aplicativo mTicket</string>
<string name="resources_link_trip_planner">Planejador de trajetos</string>
<string name="search_by_stop">Pesquisar por parada</string>
<string name="service_ended">Serviço encerrado</string>
<string name="setting_toggle_hide_maps">Ocultar mapas</string>
<string name="settings_link">Configurações</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<string name="resources_link_mticket">Vé phà vé đường sắt ngoại ô (Commuter Rail)</string>
<string name="resources_link_mticket_note">Ứng dụng mTicket</string>
<string name="resources_link_trip_planner">Lập kế hoạch chuyến đi</string>
<string name="search_by_stop">Tìm kiếm theo điểm dừng</string>
<string name="service_ended">Dịch vụ đã kết thúc</string>
<string name="setting_toggle_hide_maps">Ẩn bản đồ</string>
<string name="settings_link">Cài đặt</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<string name="resources_link_mticket">通勤列车和轮渡票</string>
<string name="resources_link_mticket_note">mTicket应用程序</string>
<string name="resources_link_trip_planner">Trip Planner</string>
<string name="search_by_stop">按站点搜索</string>
<string name="service_ended">服务已结束</string>
<string name="setting_toggle_hide_maps">隐藏地图</string>
<string name="settings_link">设置</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<string name="resources_link_mticket">通勤列車和輪渡票</string>
<string name="resources_link_mticket_note">mTicket應用程式</string>
<string name="resources_link_trip_planner">Trip Planner</string>
<string name="search_by_stop">按站點搜尋</string>
<string name="service_ended">服務已結束</string>
<string name="setting_toggle_hide_maps">隱藏地圖</string>
<string name="settings_link">設定</string>
Expand Down
1 change: 1 addition & 0 deletions androidApp/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
<string name="resources_link_mticket">Commuter Rail and Ferry tickets</string>
<string name="resources_link_mticket_note">mTicket App</string>
<string name="resources_link_trip_planner">Trip Planner</string>
<string name="search_by_stop">Search by stop</string>
<string name="service_ended">Service ended</string>
<string name="setting_toggle_hide_maps">Hide Maps</string>
<string name="settings_link">Settings</string>
Expand Down
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[versions]
accompanist = "0.36.0"
android-material3 = "1.3.1"
androidx-activityCompose = "1.9.1"
androidx-navigationCompose = "2.8.1"
androidx-test-monitor = "1.7.2"
Expand Down Expand Up @@ -38,7 +39,7 @@ androidx-test-monitor = { module = "androidx.test:monitor", version.ref = "andro
androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test-rules" }
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
compose-foundation = { module = "androidx.compose.foundation:foundation" }
compose-material3 = { module = "androidx.compose.material3:material3" }
compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "android-material3" }
compose-ui = { module = "androidx.compose.ui:ui" }
compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4" }
compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" }
Expand Down

0 comments on commit 2fb7f43

Please sign in to comment.