diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..e966f83d Binary files /dev/null and b/.DS_Store differ diff --git a/SharedCode/src/androidMain/.DS_Store b/SharedCode/src/androidMain/.DS_Store new file mode 100644 index 00000000..c3e3da52 Binary files /dev/null and b/SharedCode/src/androidMain/.DS_Store differ diff --git a/SharedCode/src/commonMain/kotlin/ApplicationContract.kt b/SharedCode/src/commonMain/kotlin/ApplicationContract.kt index a30bdb86..1239b144 100644 --- a/SharedCode/src/commonMain/kotlin/ApplicationContract.kt +++ b/SharedCode/src/commonMain/kotlin/ApplicationContract.kt @@ -5,9 +5,11 @@ import kotlinx.coroutines.CoroutineScope interface ApplicationContract { interface View { fun setLabel(text: String) + fun updateSearchResults(results: List) } abstract class Presenter: CoroutineScope { abstract fun onViewTaken(view: View) + abstract fun getTrainTimes(departureStation: String, arrivalStation: String) } } diff --git a/SharedCode/src/commonMain/kotlin/ApplicationPresenter.kt b/SharedCode/src/commonMain/kotlin/ApplicationPresenter.kt index 3fce53bc..eb3c05e4 100644 --- a/SharedCode/src/commonMain/kotlin/ApplicationPresenter.kt +++ b/SharedCode/src/commonMain/kotlin/ApplicationPresenter.kt @@ -1,19 +1,74 @@ package com.jetbrains.handson.mpp.mobile +import io.ktor.client.* +import io.ktor.client.features.json.* +import io.ktor.client.features.json.serializer.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* import kotlinx.coroutines.* +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json import kotlin.coroutines.CoroutineContext +@Serializable +data class OutboundJourney(val departureTime: String, val arrivalTime: String) {} + +@Serializable +data class SerializableResponse(val numberOfAdults: Int, val numberOfChildren: Int, val outboundJourneys: List) {} + class ApplicationPresenter: ApplicationContract.Presenter() { private val dispatchers = AppDispatchersImpl() - private var view: ApplicationContract.View? = null + private lateinit var view: ApplicationContract.View private val job: Job = SupervisorJob() - override val coroutineContext: CoroutineContext - get() = dispatchers.main + job - override fun onViewTaken(view: ApplicationContract.View) { this.view = view - view.setLabel(createApplicationScreenMessage()) +// view.setLabel(createApplicationScreenMessage()) + } + + private fun serializableResponseToStringArray(serializableResponse: SerializableResponse): List { + var result: MutableList = mutableListOf() + for (journey in serializableResponse.outboundJourneys) { + result.add("${journey.departureTime} - ${journey.arrivalTime}") + } + return result + } + + val client = HttpClient() { + install(JsonFeature) { + serializer = KotlinxSerializer(kotlinx.serialization.json.Json { this.ignoreUnknownKeys = true; this.isLenient = true}) + } } + + + fun updateSearchResults(results: List) { + this.view?.setLabel("a") + this.view?.updateSearchResults(results) + } + + public override fun getTrainTimes(departureStation: String, arrivalStation: String) { + this.view.updateSearchResults(listOf("Loading...")) + launch { + val response: SerializableResponse = client.request("https://mobile-api-softwire2.lner.co.uk/v1/fares?originStation=$departureStation&destinationStation=$arrivalStation&noChanges=false&numberOfAdults=2&numberOfChildren=0&journeyType=single&outboundDateTime=2021-07-24T14%3A30%3A00.000%2B01%3A00&outboundIsArriveBy=false") + /*val response: SerializableResponse = client.request("https://mobile-api-softwire2.lner.co.uk/v1/fares/") { + method = HttpMethod.Get + parameter("originStation", departureStation) + parameter("destinationStation", arrivalStation) + parameter("noChanges", "false") + parameter("numberOfAdults", "1") + parameter("numberOfChildren", "0") + parameter("journeyType", "single") + parameter("outboundDateTime", "2021-07-24T14%3A30%3A00.000%2B01%3A00") + parameter("outboundIsArriveBy", "false") + }*/ + updateSearchResults( + serializableResponseToStringArray(response)) + } + } + + override val coroutineContext: CoroutineContext + get() = dispatchers.main + job + } diff --git a/app/build.gradle b/app/build.gradle index 229c1e3a..1d90775b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,6 +35,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.recyclerview:recyclerview:1.2.1' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b60fb5f8..7327dec3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + (R.id.stations_spinner1) as Spinner + arrivalStationSpinner = findViewById(R.id.stations_spinner2) as Spinner + departureStationSpinner.setSelection(0) + arrivalStationSpinner.setSelection(0) + +// linearLayoutManager = LinearLayoutManager(this) +// recyclerView.layoutManager = linearLayoutManager } override fun setLabel(text: String) { - findViewById(R.id.main_text).text = text + //findViewById(R.id.main_text).text = text + } + + fun onSubmitClicked(view: View) { + + var departureCode = departureStationSpinner.selectedItem.toString() + val ld = departureCode.length + departureCode = departureCode.substring(ld - 4, ld - 1) + + var arrivalCode = arrivalStationSpinner.selectedItem.toString() + val ad = arrivalCode.length + arrivalCode = arrivalCode.substring(ad - 4, ad - 1) + val url = + "https://www.lner.co.uk/travel-information/travelling-now/live-train-times/depart/$departureCode/$arrivalCode/#LiveDepResults" + + presenter.getTrainTimes(departureCode, arrivalCode) + +// println("printed msg") + +// val intent = Intent(Intent.ACTION_VIEW) +// intent.data = Uri.parse(url) +// startActivity(intent) + } + + override fun updateSearchResults(results: List) { + } } diff --git a/app/src/main/java/com/jetbrains/handson/mpp/mobile/RecyclerAdapter.kt b/app/src/main/java/com/jetbrains/handson/mpp/mobile/RecyclerAdapter.kt new file mode 100644 index 00000000..0c4f5edd --- /dev/null +++ b/app/src/main/java/com/jetbrains/handson/mpp/mobile/RecyclerAdapter.kt @@ -0,0 +1,76 @@ +/* +package com.jetbrains.handson.mpp.mobile + +import android.util.Log +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.appcompat.view.menu.ActionMenuItemView +import androidx.recyclerview.widget.RecyclerView + +class RecyclerAdapter(private val times: ArrayList): RecyclerView.Adapter() { + + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflatedView = parent.inflate(R.layout.recyclerview_item_row, false) + return ViewHolder(inflatedView) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + TODO("Not yet implemented") + } + + override fun getItemCount(): Int { + return times.size + } + + inner class ViewHolder(v: View): RecyclerView.ViewHolder(v) { + var departureTime: TextView + var arrivalTime: TextView + + init { + departureTime = itemView.findViewById(R.id.departure_time) + arrivalTime = itemView.findViewById(R.id.arrival_time) + } + } +}*/ + +package com.jetbrains.handson.mpp.mobile + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView + +class RecyclerAdapter(context: Context, journeys: List) : RecyclerView.Adapter() { + + private var mData: List = journeys + private var mInflater: LayoutInflater = LayoutInflater.from(context) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val view: View = mInflater.inflate(R.layout.recyclerview_item_row, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val outboundJourney: OutboundJourney = mData[position] + if (holder is ViewHolder) { + holder.departureTime.text = outboundJourney.departureTime + holder.arrivalTime.text = outboundJourney.arrivalTime + } + } + + override fun getItemCount(): Int { + return this.mData.size + } + + class ViewHolder(view: View): RecyclerView.ViewHolder(view) { + var departureTime: TextView = view.findViewById(R.id.departure_time) + var arrivalTime: TextView = view.findViewById(R.id.arrival_time) + } + + +} \ 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 ccc538b6..aaf480fc 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,4 +1,5 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + - + + + + @@ -40,4 +101,21 @@ + + + + + + + + + + + + + + + + + diff --git a/native/KotlinIOS/KotlinIOS/ViewController.swift b/native/KotlinIOS/KotlinIOS/ViewController.swift index 16add6c9..d69fd363 100644 --- a/native/KotlinIOS/KotlinIOS/ViewController.swift +++ b/native/KotlinIOS/KotlinIOS/ViewController.swift @@ -1,20 +1,78 @@ import UIKit import SharedCode -class ViewController: UIViewController { - +class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITableViewDelegate, UITableViewDataSource, ApplicationContractView +{ + func setLabel(text: String) { + print("zzzzžzzzzžzzzzžzzzzžzzzzžzzzzž") + } + @IBOutlet private var label: UILabel! - - private let presenter: ApplicationContractPresenter = ApplicationPresenter() + @IBOutlet private var pickerViewDeparture: UIPickerView! + @IBOutlet private var pickerViewArrival: UIPickerView! + @IBOutlet private var submitButton: UIButton! + @IBOutlet private var trainsTable: UITableView! + @IBOutlet private var trainTableViewCell: UITableViewCell! override func viewDidLoad() { super.viewDidLoad() presenter.onViewTaken(view: self) + + self.pickerViewDeparture.delegate = self + self.pickerViewDeparture.dataSource = self + + self.pickerViewArrival.delegate = self + self.pickerViewArrival.dataSource = self + + self.trainsTable.delegate = self + self.trainsTable.dataSource = self + } + + private let presenter: ApplicationContractPresenter = ApplicationPresenter() + + let stations = ["Colchester [COL]","Ipswich [IPS]","Birmingham New Street [BHM]","Kings Cross [KGX]", "Cambridge [CBG]"] + + func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 1 } -} -extension ViewController: ApplicationContractView { - func setLabel(text: String) { - label.text = text + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return stations.count + } + + func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + return stations[row] + } + + + var tableRows: [String] = [] + + func tableView(_ tableView: UITableView) -> Int { + return tableRows.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return tableRows.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) + cell.textLabel!.text = tableRows[indexPath.row] + return cell + } + + + @IBAction func buttonTapped(_ sender: UIButton) { + let arr = pickerViewArrival.selectedRow(inComponent: 0) + let dep = pickerViewDeparture.selectedRow(inComponent: 0) + let departure = stations[dep].suffix(4).prefix(3) + let arrival = stations[arr].suffix(4).prefix(3) + + presenter.getTrainTimes(departureStation: String(departure), arrivalStation: String(arrival)) + } + + func updateSearchResults(results: [String]) { + tableRows = results + self.trainsTable.reloadData() } }