From 170126b23e42af6076c658675bdeee57f5af8b65 Mon Sep 17 00:00:00 2001 From: connorreinhold Date: Wed, 29 Jan 2020 17:35:30 -0500 Subject: [PATCH] Add MVP searchview functionality with rxjava (#25) * Add mvp searchview functionality with rxjava * Route Options searchview ui * Addressing requested changes * Adding MaxHeightListView commenting --- .gitignore | 3 + app/.DS_Store | Bin 6148 -> 6148 bytes app/build.gradle | 23 ++- app/src/main/AndroidManifest.xml | 14 +- .../ithaca_transit_android_v2/MainActivity.kt | 152 +++++++++++++---- .../ithaca_transit_android_v2/NetworkUtils.kt | 10 +- .../NetworkingAdapters.kt | 93 ++++++---- .../RouteOptionsActivity.kt | 33 ++++ .../models/Location.kt | 4 +- .../models/LocationType.kt | 5 +- .../states/SearchState.kt | 12 +- .../ui/MaxHeightListView.kt | 23 +++ .../ui_adapters/SearchViewAdapter.kt | 76 +++++++++ app/src/main/res/anim/slide_in_left.xml | 6 + app/src/main/res/anim/slide_in_right.xml | 6 + app/src/main/res/anim/slide_out_left.xml | 6 + app/src/main/res/anim/slide_out_right.xml | 6 + .../main/res/drawable/baseline_error_24.xml | 10 ++ .../main/res/drawable/baseline_forum_24.xml | 10 ++ app/src/main/res/drawable/ic_bus_stop.xml | 17 ++ app/src/main/res/drawable/ic_location.xml | 10 ++ app/src/main/res/drawable/ic_rightarrow.xml | 10 ++ app/src/main/res/drawable/ic_search.xml | 12 ++ .../main/res/drawable/ic_search_scaled.xml | 10 ++ app/src/main/res/drawable/ic_switch.xml | 10 ++ app/src/main/res/drawable/list_divider.xml | 12 ++ app/src/main/res/layout/activity_main.xml | 32 ++-- .../res/layout/activity_route_options.xml | 159 ++++++++++++++++++ .../res/layout/activity_toolbar_search.xml | 90 ++++++++++ .../main/res/layout/home_drawer_header.xml | 20 +++ app/src/main/res/layout/item_searchview.xml | 45 +++++ app/src/main/res/menu/home_drawer_menu.xml | 21 +++ app/src/main/res/values/styles.xml | 2 + ithaca-transit-android-v2.iml | 2 +- 34 files changed, 855 insertions(+), 89 deletions(-) create mode 100644 app/src/main/java/com/example/ithaca_transit_android_v2/RouteOptionsActivity.kt create mode 100644 app/src/main/java/com/example/ithaca_transit_android_v2/ui/MaxHeightListView.kt create mode 100644 app/src/main/java/com/example/ithaca_transit_android_v2/ui_adapters/SearchViewAdapter.kt create mode 100644 app/src/main/res/anim/slide_in_left.xml create mode 100644 app/src/main/res/anim/slide_in_right.xml create mode 100644 app/src/main/res/anim/slide_out_left.xml create mode 100644 app/src/main/res/anim/slide_out_right.xml create mode 100644 app/src/main/res/drawable/baseline_error_24.xml create mode 100644 app/src/main/res/drawable/baseline_forum_24.xml create mode 100644 app/src/main/res/drawable/ic_bus_stop.xml create mode 100644 app/src/main/res/drawable/ic_location.xml create mode 100644 app/src/main/res/drawable/ic_rightarrow.xml create mode 100644 app/src/main/res/drawable/ic_search.xml create mode 100644 app/src/main/res/drawable/ic_search_scaled.xml create mode 100644 app/src/main/res/drawable/ic_switch.xml create mode 100644 app/src/main/res/drawable/list_divider.xml create mode 100644 app/src/main/res/layout/activity_route_options.xml create mode 100644 app/src/main/res/layout/activity_toolbar_search.xml create mode 100644 app/src/main/res/layout/home_drawer_header.xml create mode 100644 app/src/main/res/layout/item_searchview.xml create mode 100644 app/src/main/res/menu/home_drawer_menu.xml diff --git a/.gitignore b/.gitignore index 01c9f6b..d93cfa9 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,6 @@ build/ app/src/main/res/values/google_maps_api.xml .DS_Store google-services.json + +# API keys +keys.properties diff --git a/app/.DS_Store b/app/.DS_Store index 7b755dd96bc1000ed8001c4bfae60da704dedde3..4ad6958b924c1bb82e4acf28323f7874e0c5cd14 100644 GIT binary patch delta 65 zcmZoMXfc=&&6&=S$B@rZ#E`mKkYhQsBnJZn0~doHLpnnyLkSSWr6;dok)O=ZGGX&^ M78B;p>>Pjj0s5>ECIA2c delta 66 zcmZoMXfc=&&6&%Pd5UKnm1_lNJ1_s8~ S%|}?AnI|^zY-Z>9%MSp3;u50( diff --git a/app/build.gradle b/app/build.gradle index 4f9d69e..a3e0eda 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,6 +6,10 @@ apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' +def keyFile = file('../keys.properties') +def keyProperties = new Properties() +keyProperties.load(new FileInputStream(keyFile)) + android { compileSdkVersion 29 buildToolsVersion "29.0.2" @@ -16,6 +20,7 @@ android { versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + resValue 'string', "google_maps_key", keyProperties["googleMapApiKey"] } buildTypes { release { @@ -31,18 +36,26 @@ android { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.0" + implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.50" implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.0.2' - implementation 'androidx.core:core-ktx:1.0.2' + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'com.squareup.okhttp3:okhttp:4.0.1' implementation 'net.sourceforge.streamsupport:streamsupport:1.7.0' implementation "com.squareup.moshi:moshi-adapters:1.8.0" implementation "com.squareup.moshi:moshi-kotlin:1.8.0" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2' + + implementation 'com.google.android.gms:play-services-maps:17.0.0' + implementation 'com.google.android.gms:play-services-location:17.0.0' + implementation 'com.google.android.libraries.places:places:2.1.0' + + // rxjava + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' + } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e3ef051..8235082 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,8 @@ - + + + + + + + + + diff --git a/app/src/main/java/com/example/ithaca_transit_android_v2/MainActivity.kt b/app/src/main/java/com/example/ithaca_transit_android_v2/MainActivity.kt index c9d6977..0ffdbb1 100644 --- a/app/src/main/java/com/example/ithaca_transit_android_v2/MainActivity.kt +++ b/app/src/main/java/com/example/ithaca_transit_android_v2/MainActivity.kt @@ -1,50 +1,138 @@ package com.example.ithaca_transit_android_v2 +import android.content.Intent import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher import android.util.Log +import android.view.View +import android.widget.EditText import androidx.appcompat.app.AppCompatActivity -import com.example.ithaca_transit_android_v2.models.Coordinate -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.runBlocking +import com.example.ithaca_transit_android_v2.models.Location +import com.example.ithaca_transit_android_v2.states.* +import com.example.ithaca_transit_android_v2.ui_adapters.SearchViewAdapter +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.OnMapReadyCallback +import com.google.android.gms.maps.SupportMapFragment +import io.reactivex.Observable +import io.reactivex.ObservableEmitter +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.activity_toolbar_search.* +class MainActivity : AppCompatActivity(), OnMapReadyCallback { + private lateinit var disposable: Disposable + private var mSearchLocations: List = ArrayList() + private var mSearchAdapter: SearchViewAdapter? = null -class MainActivity : AppCompatActivity() { + override fun onMapReady(map: GoogleMap?) { + map!!.setOnMapClickListener { point -> + Log.i("qwerty", "map clicked") + search_view.clearFocus() + } + } + + private fun createSearchObservable(): Observable { + val obs = Observable.create { emitter: ObservableEmitter -> + val watcher: TextWatcher = object : TextWatcher { + override fun afterTextChanged(p0: Editable?) { + } + + override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { + } + + override fun onTextChanged(searchText: CharSequence?, p1: Int, p2: Int, p3: Int) { + if (searchText!!.isEmpty()) { + emitter.onNext(EmptyInitClickState()) + } else { + emitter.onNext(InitSearchState(searchText.toString())) + } + } + } + + search_input.addTextChangedListener(watcher) + + search_input.setOnFocusChangeListener { view, hasFocus -> + if (hasFocus && (view as EditText).text.isEmpty()) { + // search input clicked on with no text + emitter.onNext(EmptyInitClickState()) + } else if (hasFocus) { + // search input clicked on with text query + emitter.onNext(InitSearchState((view as EditText).text.toString())) + } else { + // search input not focused - display the un-expanded version + emitter.onNext(SearchLaunchState()) + } + } + + emitter.setCancellable { + search_input.removeTextChangedListener(watcher) + search_input.setOnFocusChangeListener(null) + } + } + return obs.startWith(SearchLaunchState()) + } + + private fun initSearchView() { + val observable = createSearchObservable() + + disposable = observable + .observeOn(Schedulers.io()) + .map { state -> + if (state is InitSearchState) { + val locations = NetworkUtils().getSearchedLocations(state.searchText) + InitLocationsSearchState(state.searchText, locations) + } else { + state + } + } + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ state -> + when (state) { + is SearchLaunchState -> { + search_empty_state.visibility = View.GONE + search_locations_state.visibility = View.GONE + } + is EmptyInitClickState -> { + search_locations_state.visibility = View.GONE + search_empty_state.visibility = View.VISIBLE + } + is InitLocationsSearchState -> { + search_empty_state.visibility = View.GONE + search_locations_state.visibility = View.VISIBLE + if (state.searchedLocations!!.size > 0 || search_input.text.isEmpty()) { + mSearchAdapter!!.swapItems(state.searchedLocations) + } + } + } + }, { error -> Log.e("An Error Occurred", error.toString()) }) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - // ======== FOR TESTING ONLY ======== - val end = Coordinate(42.444971674516864, -76.48098092526197) - // val uid = "E4A0256E-5865-4E9F-8A5A-33747CAC7EBF" - val time = 1574292741.0 - val destinationName = "Bill & Melinda Gates Hall" - val start = Coordinate(42.44717985041025, -76.48551732274225) - val arriveBy = false - - // TODO (lesley): just for testing purposes - replace with rxjava - runBlocking { - val deferred = CoroutineScope(Dispatchers.IO).async { - NetworkUtils().getRouteOptions(start, end, time, arriveBy, destinationName) - }.await() - - // Printing out [deferred] in log for testing - printLongLog(deferred.toString()) + mSearchAdapter = SearchViewAdapter(this, mSearchLocations) + locations_list.adapter = mSearchAdapter + locations_list.setOnItemClickListener { parent, view, position, id -> + val destination = parent.getItemAtPosition(position) as Location + + val intent = Intent(this, RouteOptionsActivity::class.java) + startActivity(intent) + overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left) + + } + initSearchView() + (map_fragment as SupportMapFragment).getMapAsync(this) } - private fun printLongLog (s: String) { - val maxLogSize = 1000 - val stringLength = s.length - if (stringLength != null) { - for (i in 0..stringLength / maxLogSize) { - val start = i * maxLogSize - var end = (i + 1) * maxLogSize - end = if (end > s.length) s.length else end - Log.v("returnBody", s.substring(start, end)) - } + override fun onStop() { + super.onStop() + if (!disposable.isDisposed) { + disposable.dispose() } } } diff --git a/app/src/main/java/com/example/ithaca_transit_android_v2/NetworkUtils.kt b/app/src/main/java/com/example/ithaca_transit_android_v2/NetworkUtils.kt index b07777c..47f6a24 100644 --- a/app/src/main/java/com/example/ithaca_transit_android_v2/NetworkUtils.kt +++ b/app/src/main/java/com/example/ithaca_transit_android_v2/NetworkUtils.kt @@ -1,9 +1,11 @@ package com.example.ithaca_transit_android_v2 +import android.util.Log import com.example.ithaca_transit_android_v2.models.Coordinate import com.example.ithaca_transit_android_v2.models.Location import com.example.ithaca_transit_android_v2.models.RouteOptions import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonReader import com.squareup.moshi.Moshi import com.squareup.moshi.Types.newParameterizedType import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory @@ -23,24 +25,24 @@ class NetworkUtils { val mediaType = ("application/json; charset=utf-8").toMediaType() // Function that takes in query and returns list of Locations - fun getSearchedLocations(query: String): List { + fun getSearchedLocations(query: String): List? { val json = JSONObject() json.put("query", query) val requestBody = json.toString().toRequestBody(mediaType) val request: Request = Request.Builder() - .url(url + "search") + .url(url + "appleSearch") .post(requestBody) .build() val body = client.newCall(request).execute().body?.string() + val type = newParameterizedType(List::class.java, Location::class.java) val moshi = Moshi.Builder() .add(LocationAdapter()) .add(KotlinJsonAdapterFactory()) .build() - val adapter: JsonAdapter> = moshi.adapter(type) - return adapter.fromJson(body) ?: emptyList() + return adapter.fromJson(body.toString())?: emptyList() } fun getAllBusStops(): List { diff --git a/app/src/main/java/com/example/ithaca_transit_android_v2/NetworkingAdapters.kt b/app/src/main/java/com/example/ithaca_transit_android_v2/NetworkingAdapters.kt index 0e15c94..2c0ce24 100644 --- a/app/src/main/java/com/example/ithaca_transit_android_v2/NetworkingAdapters.kt +++ b/app/src/main/java/com/example/ithaca_transit_android_v2/NetworkingAdapters.kt @@ -1,22 +1,23 @@ package com.example.ithaca_transit_android_v2 import com.example.ithaca_transit_android_v2.models.* -import com.squareup.moshi.JsonClass -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -import com.squareup.moshi.Json -import com.squareup.moshi.JsonWriter +import com.squareup.moshi.* import java.text.SimpleDateFormat -import java.util.Date -import java.util.Calendar -import java.util.Locale -import java.util.concurrent.TimeUnit +import java.util.* +import kotlin.collections.ArrayList @JsonClass(generateAdapter = true) class LocationAdapter { + class DataLocation( - val data: List + val data: PlacesLocation? + ) + + class PlacesLocation( + val applePlaces: List?, + val busStops: List? ) + @JsonClass(generateAdapter = true) class JsonLocation( val type: LocationType, @@ -25,52 +26,86 @@ class LocationAdapter { val long: Double, val detail: String? ) + @FromJson private fun fromJson(json: DataLocation): List { - val finalLoc = json.data.map { loc -> - Location(loc.type, loc.name, Coordinate(loc.lat, loc.long), loc.detail) + if (json.data == null) { + return ArrayList() } - return finalLoc + val applePlaces = json.data.applePlaces?.let { + json.data.applePlaces.map { loc -> + Location(loc.type, loc.name, Coordinate(loc.lat, loc.long), loc.detail) + } + } ?: ArrayList() + + val busStops = json.data.busStops?.let { + json.data.busStops.map { loc -> + Location(loc.type, loc.name, Coordinate(loc.lat, loc.long), loc.detail) + } + } ?: ArrayList() + val places = ArrayList() + places.addAll(applePlaces) + places.addAll(busStops) + return places } + @ToJson - private fun toJson(json: List): DataLocation { - val final = json.map { loc -> - JsonLocation( - loc.type, - loc.name, - loc.coordinate.latitude, - loc.coordinate.longitude, - loc.detail + private fun toJson(json: List): PlacesLocation { + val applePlaces = ArrayList() + val busStops = ArrayList() + for (place in json) { + val jsonPlace = JsonLocation( + place.type, + place.name, + place.coordinate.latitude, + place.coordinate.longitude, + place.detail ) + if (place.type == LocationType.APPLE_PLACE) { + applePlaces.add(jsonPlace) + } else { + busStops.add(jsonPlace) + } } - return DataLocation(final) + return PlacesLocation(applePlaces, busStops) } } -class RouteAdapter{ + +class RouteAdapter { class JsonRoute( val directions: List, val startCoords: Coordinate, val endCoords: Coordinate, - @Json(name ="arrivalTime") + @Json(name = "arrivalTime") val arrival: Date, - @Json(name ="departureTime") + @Json(name = "departureTime") val depart: Date ) + @FromJson private fun fromJson(json: JsonRoute): Route { var firstBus = if (json.directions[0].type == DirectionType.BUS) 1 else 0 - var boardInMins: Int = if (json.directions.size != 1) Route.computeBoardInMin(json.directions[firstBus]) else 0 - return Route(json.directions, json.startCoords, json.endCoords, json.arrival, json.depart, - boardInMins) + var boardInMins: Int = + if (json.directions.size != 1) Route.computeBoardInMin(json.directions[firstBus]) else 0 + return Route( + json.directions, + json.startCoords, + json.endCoords, + json.arrival, + json.depart, + boardInMins + ) } } -class CustomDateAdapter{ + +class CustomDateAdapter { private val serverFormat = ("yyyy-MM-dd'T'HH:mm:ss'Z'") private val dateFormat = SimpleDateFormat(serverFormat, Locale.getDefault()) @FromJson fun fromJson(date: String): Date { return dateFormat.parse(date) } + @ToJson fun toJson(writer: JsonWriter, value: Date?) { value?.let { writer.value(value.toString()) } diff --git a/app/src/main/java/com/example/ithaca_transit_android_v2/RouteOptionsActivity.kt b/app/src/main/java/com/example/ithaca_transit_android_v2/RouteOptionsActivity.kt new file mode 100644 index 0000000..383463e --- /dev/null +++ b/app/src/main/java/com/example/ithaca_transit_android_v2/RouteOptionsActivity.kt @@ -0,0 +1,33 @@ +package com.example.ithaca_transit_android_v2 + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import kotlinx.android.synthetic.main.activity_route_options.* + + +class RouteOptionsActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_route_options) + display_route.visibility = View.GONE + getActionBar()?.setDisplayHomeAsUpEnabled(true); + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when(item.getItemId()) { + android.R.id.home -> { + finish() + overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right) + } + } + return super.onOptionsItemSelected(item) + } + + override fun onBackPressed() { + super.onBackPressed() + overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right) + } +} diff --git a/app/src/main/java/com/example/ithaca_transit_android_v2/models/Location.kt b/app/src/main/java/com/example/ithaca_transit_android_v2/models/Location.kt index 07ab278..f842edf 100644 --- a/app/src/main/java/com/example/ithaca_transit_android_v2/models/Location.kt +++ b/app/src/main/java/com/example/ithaca_transit_android_v2/models/Location.kt @@ -1,10 +1,12 @@ package com.example.ithaca_transit_android_v2.models +import android.os.Parcelable import com.squareup.moshi.JsonClass +import kotlinx.android.parcel.Parcelize //Location. - Represents either a bus stop or a google place. @JsonClass(generateAdapter = true) -data class Location( +data class Location ( val type: LocationType, val name: String, val coordinate: Coordinate, diff --git a/app/src/main/java/com/example/ithaca_transit_android_v2/models/LocationType.kt b/app/src/main/java/com/example/ithaca_transit_android_v2/models/LocationType.kt index a0fee51..dd998ec 100644 --- a/app/src/main/java/com/example/ithaca_transit_android_v2/models/LocationType.kt +++ b/app/src/main/java/com/example/ithaca_transit_android_v2/models/LocationType.kt @@ -5,6 +5,7 @@ import com.squareup.moshi.Json enum class LocationType { @Json(name = "busStop") BUS_STOP, - @Json(name = "googlePlace") - GOOGLE_PLACE + @Json(name = "applePlace") + APPLE_PLACE + } \ No newline at end of file diff --git a/app/src/main/java/com/example/ithaca_transit_android_v2/states/SearchState.kt b/app/src/main/java/com/example/ithaca_transit_android_v2/states/SearchState.kt index 2cd8d7f..99d0f72 100644 --- a/app/src/main/java/com/example/ithaca_transit_android_v2/states/SearchState.kt +++ b/app/src/main/java/com/example/ithaca_transit_android_v2/states/SearchState.kt @@ -19,11 +19,16 @@ class EmptyInitClickState : SearchState() */ class InitClickState : SearchState() -// Immediately following InitClickState or EmptyInitClickState when user begins typing. +// Immediately following InitClickState or EmptyInitClickState when user begins typing class InitSearchState( + val searchText:String +) : SearchState() + +// Immediately following InitSearchState after network requests are made +class InitLocationsSearchState( val searchText: String, // Network call to all locations that match the search text - val searchedLocations: List + val searchedLocations: List? ) : SearchState() // Default start location is Current Location @@ -48,4 +53,5 @@ class SearchRouteOptionState( class ChangeLocationState( searchText: String, // User Input searchedLocations: List -) : SearchState() \ No newline at end of file +) : SearchState() + diff --git a/app/src/main/java/com/example/ithaca_transit_android_v2/ui/MaxHeightListView.kt b/app/src/main/java/com/example/ithaca_transit_android_v2/ui/MaxHeightListView.kt new file mode 100644 index 0000000..b7a5c56 --- /dev/null +++ b/app/src/main/java/com/example/ithaca_transit_android_v2/ui/MaxHeightListView.kt @@ -0,0 +1,23 @@ +package com.example.ithaca_transit_android_v2.ui + +import android.content.Context +import android.util.AttributeSet +import android.util.TypedValue +import android.widget.ListView + +/* This class is a ListView that has a maximum height of 250 pixels. It is used by the main SearchView to +* display a list of available options. We can't just wrap the ListView in some layout of constant height +* because then the ListView would always have a constant height, we want it to expand up to a specific +* height. */ +class MaxHeightListView(context: Context, attributeSet: AttributeSet) : + ListView(context, attributeSet) { + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + var maxHeightMeasureSpec = heightMeasureSpec + val maxHeight = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 250f, getResources().getDisplayMetrics() + ) + maxHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight.toInt(), MeasureSpec.AT_MOST) + super.onMeasure(widthMeasureSpec, maxHeightMeasureSpec) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/ithaca_transit_android_v2/ui_adapters/SearchViewAdapter.kt b/app/src/main/java/com/example/ithaca_transit_android_v2/ui_adapters/SearchViewAdapter.kt new file mode 100644 index 0000000..d65bc4a --- /dev/null +++ b/app/src/main/java/com/example/ithaca_transit_android_v2/ui_adapters/SearchViewAdapter.kt @@ -0,0 +1,76 @@ +package com.example.ithaca_transit_android_v2.ui_adapters + +import android.content.Context +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.BaseAdapter +import android.widget.ImageView +import android.widget.TextView +import androidx.annotation.LayoutRes +import com.example.ithaca_transit_android_v2.R +import com.example.ithaca_transit_android_v2.models.Location +import com.example.ithaca_transit_android_v2.models.LocationType + +class SearchViewAdapter(context: Context, private var locations: List ): + BaseAdapter() { + + private val inflater: LayoutInflater + = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + + private class ViewHolder(element: View?) { + var placeNameTextView: TextView? = null + var placeLocTextView: TextView? = null + var placeIcon: ImageView? = null + + init { + this.placeNameTextView = element?.findViewById(R.id.name) + this.placeLocTextView = element?.findViewById(R.id.location) + this.placeIcon = element?.findViewById(R.id.location_icon) + } + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val view: View + val viewHolder: ViewHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_searchview, null) + viewHolder = ViewHolder(view) + view.tag = viewHolder + } else { + view = convertView + viewHolder = view.tag as ViewHolder + } + + val place = locations[position] + viewHolder.placeNameTextView?.text = place.name + + if (place.type == LocationType.BUS_STOP) { + viewHolder.placeIcon?.setBackgroundResource(R.drawable.ic_bus_stop) + viewHolder.placeLocTextView?.text = "Bus Stop" + } else { + viewHolder.placeIcon?.setBackgroundResource(R.drawable.ic_location) + viewHolder.placeLocTextView?.text = place.detail + } + return view + } + + override fun getItem(i: Int): Location { + return locations[i] + } + + override fun getItemId(i: Int): Long { + return i.toLong() + } + + override fun getCount(): Int { + return locations.size + } + + fun swapItems(locations: List) { + this.locations = locations + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_left.xml b/app/src/main/res/anim/slide_in_left.xml new file mode 100644 index 0000000..99b94d5 --- /dev/null +++ b/app/src/main/res/anim/slide_in_left.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_right.xml b/app/src/main/res/anim/slide_in_right.xml new file mode 100644 index 0000000..0c97d28 --- /dev/null +++ b/app/src/main/res/anim/slide_in_right.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out_left.xml b/app/src/main/res/anim/slide_out_left.xml new file mode 100644 index 0000000..21ba7bd --- /dev/null +++ b/app/src/main/res/anim/slide_out_left.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out_right.xml b/app/src/main/res/anim/slide_out_right.xml new file mode 100644 index 0000000..7af6fac --- /dev/null +++ b/app/src/main/res/anim/slide_out_right.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/baseline_error_24.xml b/app/src/main/res/drawable/baseline_error_24.xml new file mode 100644 index 0000000..a0fa7d9 --- /dev/null +++ b/app/src/main/res/drawable/baseline_error_24.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/baseline_forum_24.xml b/app/src/main/res/drawable/baseline_forum_24.xml new file mode 100644 index 0000000..af82ef4 --- /dev/null +++ b/app/src/main/res/drawable/baseline_forum_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_bus_stop.xml b/app/src/main/res/drawable/ic_bus_stop.xml new file mode 100644 index 0000000..41dd434 --- /dev/null +++ b/app/src/main/res/drawable/ic_bus_stop.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 0000000..4e802bf --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_rightarrow.xml b/app/src/main/res/drawable/ic_rightarrow.xml new file mode 100644 index 0000000..bc86377 --- /dev/null +++ b/app/src/main/res/drawable/ic_rightarrow.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000..14feb56 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_search_scaled.xml b/app/src/main/res/drawable/ic_search_scaled.xml new file mode 100644 index 0000000..31c720f --- /dev/null +++ b/app/src/main/res/drawable/ic_search_scaled.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_switch.xml b/app/src/main/res/drawable/ic_switch.xml new file mode 100644 index 0000000..ee128c1 --- /dev/null +++ b/app/src/main/res/drawable/ic_switch.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/list_divider.xml b/app/src/main/res/drawable/list_divider.xml new file mode 100644 index 0000000..74fc561 --- /dev/null +++ b/app/src/main/res/drawable/list_divider.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4fc2444..34069b0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,18 +1,28 @@ - - + + + + + + + - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_route_options.xml b/app/src/main/res/layout/activity_route_options.xml new file mode 100644 index 0000000..5759fb8 --- /dev/null +++ b/app/src/main/res/layout/activity_route_options.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_toolbar_search.xml b/app/src/main/res/layout/activity_toolbar_search.xml new file mode 100644 index 0000000..05b5ba9 --- /dev/null +++ b/app/src/main/res/layout/activity_toolbar_search.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/home_drawer_header.xml b/app/src/main/res/layout/home_drawer_header.xml new file mode 100644 index 0000000..8bc2333 --- /dev/null +++ b/app/src/main/res/layout/home_drawer_header.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_searchview.xml b/app/src/main/res/layout/item_searchview.xml new file mode 100644 index 0000000..2e09212 --- /dev/null +++ b/app/src/main/res/layout/item_searchview.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/home_drawer_menu.xml b/app/src/main/res/menu/home_drawer_menu.xml new file mode 100644 index 0000000..99fdd40 --- /dev/null +++ b/app/src/main/res/menu/home_drawer_menu.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5885930..127d3b0 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -6,6 +6,8 @@ @color/colorPrimary @color/colorPrimaryDark @color/colorAccent + 0.2 + 0.4 diff --git a/ithaca-transit-android-v2.iml b/ithaca-transit-android-v2.iml index 56b4580..392e03f 100644 --- a/ithaca-transit-android-v2.iml +++ b/ithaca-transit-android-v2.iml @@ -8,7 +8,7 @@ - +