diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 92f6f4c..bac4de7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,9 +1,12 @@ +# Checklist +- [ ] I ran `./gradlew test connectedAndroidTest detekt` +- [ ] I rebased off of `upstream/master` +- [ ] At least 1 approving review - **@dellisd**, **@GustavoSanMartin** + # Issues -[issueName](issueURL) +Closed # # Summary Brief summary of what you committed # Screenshots -Column1 | Column2 ---- | --- \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 5576ada..21fe067 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,6 @@ apply plugin: 'kotlin-kapt' apply plugin: 'kotlinx-serialization' apply plugin: 'com.squareup.sqldelight' apply plugin: "androidx.navigation.safeargs.kotlin" -apply plugin: "org.jlleitschuh.gradle.ktlint" apply from: '../core_dependencies.gradle' apply from: '../test_dependencies.gradle' diff --git a/app/src/main/java/ca/llamabagel/transpo/data/SearchRepository.kt b/app/src/main/java/ca/llamabagel/transpo/data/SearchRepository.kt index 2f871db..268acda 100644 --- a/app/src/main/java/ca/llamabagel/transpo/data/SearchRepository.kt +++ b/app/src/main/java/ca/llamabagel/transpo/data/SearchRepository.kt @@ -4,29 +4,76 @@ package ca.llamabagel.transpo.data +import ca.llamabagel.transpo.BuildConfig.MAPBOX_KEY +import ca.llamabagel.transpo.R +import ca.llamabagel.transpo.data.OttawaBoundaries.MAX_LAT +import ca.llamabagel.transpo.data.OttawaBoundaries.MAX_LNG +import ca.llamabagel.transpo.data.OttawaBoundaries.MIN_LAT +import ca.llamabagel.transpo.data.OttawaBoundaries.MIN_LNG import ca.llamabagel.transpo.data.db.TransitDatabase +import ca.llamabagel.transpo.di.StringsGen import ca.llamabagel.transpo.ui.search.viewholders.SearchResult +import com.mapbox.api.geocoding.v5.MapboxGeocoding import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import javax.inject.Inject -class SearchRepository @Inject constructor(private val database: TransitDatabase) { - suspend fun getStops(query: String): List = withContext(Dispatchers.IO) { - val stops = database.stopQueries.getStopsByName("$query*").executeAsList() - val routes = database.routeQueries.getRoutes("$query%").executeAsList() +object OttawaBoundaries { + const val MIN_LAT = 45.203039 + const val MIN_LNG = -76.227729 + const val MAX_LAT = 45.513359 + const val MAX_LNG = -75.351405 +} + +class SearchRepository @Inject constructor(private val database: TransitDatabase, private val strings: StringsGen) { + + suspend fun getSearchResults(query: String): List = withContext(Dispatchers.IO) { + if (query.isEmpty()) return@withContext emptyList() val searchResults = mutableListOf() - if (routes.isNotEmpty()) { - searchResults.add(SearchResult.CategoryHeader("Routes")) - searchResults.addAll(routes.map { SearchResult.RouteItem("Name", it.short_name, it.type.toString()) }) + getStops(query).takeIf { it.isNotEmpty() }?.let { stops -> + searchResults.add(SearchResult.CategoryHeader(strings.get(R.string.search_category_routes))) + searchResults.addAll(stops) } - if (stops.isNotEmpty()) { - searchResults.add(SearchResult.CategoryHeader("Stops")) - searchResults.addAll(stops.map { SearchResult.StopItem(it.name, "• ${it.code}", "No upcoming trips") }) + getRoutes(query).takeIf { it.isNotEmpty() }?.let { routes -> + searchResults.add(SearchResult.CategoryHeader(strings.get(R.string.search_category_routes))) + searchResults.addAll(routes) } - searchResults + getPlaces(query).takeIf { it.isNotEmpty() }?.let { places -> + searchResults.add(SearchResult.CategoryHeader(strings.get(R.string.search_category_places))) + searchResults.addAll(places) + } + + return@withContext searchResults + } + + private suspend fun getStops(query: String): List = withContext(Dispatchers.IO) { + database.stopQueries + .getStopsByName("$query*") + .executeAsList() + .map { SearchResult.StopItem(it.name, "• ${it.code}", strings.get(R.string.search_stop_no_trips)) } + } + + private suspend fun getRoutes(query: String): List = withContext(Dispatchers.IO) { + database.routeQueries + .getRoutes("$query%") + .executeAsList() + .map { SearchResult.RouteItem("Name", it.short_name, it.type.toString()) } // TODO: update name parameter + } + + private suspend fun getPlaces(query: String): List = withContext(Dispatchers.IO) { + MapboxGeocoding.builder() + .accessToken(MAPBOX_KEY) + .query(query) + .bbox(MIN_LNG, MIN_LAT, MAX_LNG, MAX_LAT) + .build() + .executeCall() + .body() + ?.features() + ?.map { feature -> SearchResult.PlaceItem(feature.placeName().orEmpty(), feature.text().orEmpty()) } + .orEmpty() } } \ No newline at end of file diff --git a/app/src/main/java/ca/llamabagel/transpo/data/TripsRepository.kt b/app/src/main/java/ca/llamabagel/transpo/data/TripsRepository.kt index d96f04e..de3ed21 100644 --- a/app/src/main/java/ca/llamabagel/transpo/data/TripsRepository.kt +++ b/app/src/main/java/ca/llamabagel/transpo/data/TripsRepository.kt @@ -10,6 +10,7 @@ import ca.llamabagel.transpo.data.db.TransitDatabase import ca.llamabagel.transpo.models.trips.ApiResponse import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import java.io.IOException import javax.inject.Inject class TripsRepository @Inject constructor( @@ -24,7 +25,7 @@ class TripsRepository @Inject constructor( suspend fun getTrips(stopCode: String): Result = withContext(Dispatchers.IO) { try { Result.Success(apiService.getTrips(stopCode).await()) - } catch (e: Exception) { + } catch (e: IOException) { Result.Error(e) } } diff --git a/app/src/main/java/ca/llamabagel/transpo/di/CoreModule.kt b/app/src/main/java/ca/llamabagel/transpo/di/CoreModule.kt index b9e4c18..8935a9b 100644 --- a/app/src/main/java/ca/llamabagel/transpo/di/CoreModule.kt +++ b/app/src/main/java/ca/llamabagel/transpo/di/CoreModule.kt @@ -4,6 +4,8 @@ package ca.llamabagel.transpo.di +import android.content.Context +import androidx.annotation.StringRes import ca.llamabagel.transpo.BuildConfig import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory @@ -39,5 +41,11 @@ class CoreModule { @Provides fun provideOkHttpClient(interceptor: HttpLoggingInterceptor): OkHttpClient = - OkHttpClient.Builder().addInterceptor(interceptor).build() + OkHttpClient.Builder().addInterceptor(interceptor).build() + + @Provides + @Singleton + fun provideStringsProvider(context: Context): StringsGen = object : StringsGen { + override fun get(@StringRes strResId: Int) = context.getString(strResId) + } } \ No newline at end of file diff --git a/app/src/main/java/ca/llamabagel/transpo/di/StringsGen.kt b/app/src/main/java/ca/llamabagel/transpo/di/StringsGen.kt new file mode 100644 index 0000000..ab1a510 --- /dev/null +++ b/app/src/main/java/ca/llamabagel/transpo/di/StringsGen.kt @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2019 Derek Ellis. Subject to the MIT license. + */ + +package ca.llamabagel.transpo.di + +import androidx.annotation.StringRes + +interface StringsGen { + fun get(@StringRes strResId: Int): String +} \ No newline at end of file diff --git a/app/src/main/java/ca/llamabagel/transpo/di/TransitDatabaseModule.kt b/app/src/main/java/ca/llamabagel/transpo/di/TransitDatabaseModule.kt index deebddc..a19b284 100644 --- a/app/src/main/java/ca/llamabagel/transpo/di/TransitDatabaseModule.kt +++ b/app/src/main/java/ca/llamabagel/transpo/di/TransitDatabaseModule.kt @@ -21,5 +21,6 @@ class TransitDatabaseModule { @Provides @Singleton - fun provideSqlDriver(context: Context): SqlDriver = AndroidSqliteDriver(TransitDatabase.Schema, context, "transit.db") + fun provideSqlDriver(context: Context): SqlDriver = + AndroidSqliteDriver(TransitDatabase.Schema, context, "transit.db") } \ No newline at end of file diff --git a/app/src/main/java/ca/llamabagel/transpo/di/TripsModule.kt b/app/src/main/java/ca/llamabagel/transpo/di/TripsModule.kt index eea321c..bb9512f 100644 --- a/app/src/main/java/ca/llamabagel/transpo/di/TripsModule.kt +++ b/app/src/main/java/ca/llamabagel/transpo/di/TripsModule.kt @@ -22,7 +22,11 @@ abstract class TripsModule { companion object { @Provides @JvmStatic - fun provideTripsService(adapter: CoroutineCallAdapterFactory, converter: Converter.Factory, okHttpClient: OkHttpClient): TripsService = Retrofit.Builder() + fun provideTripsService( + adapter: CoroutineCallAdapterFactory, + converter: Converter.Factory, + okHttpClient: OkHttpClient + ): TripsService = Retrofit.Builder() .baseUrl(BuildConfig.API_ENDPOINT) .client(okHttpClient) .addCallAdapterFactory(adapter) diff --git a/app/src/main/java/ca/llamabagel/transpo/ui/home/HomeFragment.kt b/app/src/main/java/ca/llamabagel/transpo/ui/home/HomeFragment.kt index c5d0ba5..b714cfa 100644 --- a/app/src/main/java/ca/llamabagel/transpo/ui/home/HomeFragment.kt +++ b/app/src/main/java/ca/llamabagel/transpo/ui/home/HomeFragment.kt @@ -50,7 +50,8 @@ class HomeFragment : Fragment() { val info = it[0] val finished = info.state.isFinished - requireView().findViewById(R.id.progress_bar).visibility = if (finished) View.INVISIBLE else View.VISIBLE + requireView().findViewById(R.id.progress_bar).visibility = + if (finished) View.INVISIBLE else View.VISIBLE }) requireView().findViewById