mirror of
https://github.com/ev-map/EVMap.git
synced 2026-05-19 04:16:24 -04:00
Add some stuff in detail view
This commit is contained in:
@@ -2,10 +2,12 @@ package com.johan.evmap
|
||||
|
||||
import android.app.Application
|
||||
import com.facebook.stetho.Stetho
|
||||
import com.jakewharton.threetenabp.AndroidThreeTen
|
||||
|
||||
class EvMapApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
AndroidThreeTen.init(this);
|
||||
Stetho.initializeWithDefaults(this);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.johan.evmap
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
@@ -20,8 +22,10 @@ import com.google.android.gms.maps.model.BitmapDescriptorFactory
|
||||
import com.google.android.gms.maps.model.LatLng
|
||||
import com.google.android.gms.maps.model.Marker
|
||||
import com.google.android.gms.maps.model.MarkerOptions
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.maps.android.ui.IconGenerator
|
||||
import com.johan.evmap.adapter.ConnectorAdapter
|
||||
import com.johan.evmap.adapter.DetailAdapter
|
||||
import com.johan.evmap.adapter.GalleryAdapter
|
||||
import com.johan.evmap.api.*
|
||||
import com.johan.evmap.databinding.ActivityMapsBinding
|
||||
@@ -51,21 +55,9 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
|
||||
mapFragment.getMapAsync(this)
|
||||
api = GoingElectricApi.create(getString(R.string.goingelectric_key))
|
||||
|
||||
setupAdapters()
|
||||
|
||||
val behavior = BottomSheetBehaviorGoogleMapsLike.from(binding.bottomSheet)
|
||||
|
||||
binding.gallery.adapter = GalleryAdapter(this)
|
||||
binding.gallery.layoutManager =
|
||||
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
||||
binding.gallery.addItemDecoration(DividerItemDecoration(
|
||||
this, LinearLayoutManager.HORIZONTAL
|
||||
).apply {
|
||||
setDrawable(getDrawable(R.drawable.gallery_divider)!!)
|
||||
})
|
||||
|
||||
binding.detailView.connectors.adapter = ConnectorAdapter()
|
||||
binding.detailView.connectors.layoutManager =
|
||||
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
||||
|
||||
binding.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
|
||||
var previousCharger = binding.charger
|
||||
|
||||
@@ -100,7 +92,62 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
|
||||
enableLocation(true)
|
||||
}
|
||||
}
|
||||
binding.fabDirections.setOnClickListener {
|
||||
val charger = binding.charger
|
||||
if (charger != null) {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
val coord = charger.coordinates
|
||||
|
||||
// google maps navigation
|
||||
intent.data = Uri.parse("google.navigation:q=${coord.lat},${coord.lng}")
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
// fallback: generic geo intent
|
||||
intent.data = Uri.parse("geo:${coord.lat},${coord.lng}")
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
Snackbar.make(
|
||||
binding.root,
|
||||
R.string.no_maps_app_found,
|
||||
Snackbar.LENGTH_SHORT
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAdapters() {
|
||||
binding.gallery.apply {
|
||||
adapter = GalleryAdapter(this@MapsActivity)
|
||||
layoutManager =
|
||||
LinearLayoutManager(this@MapsActivity, LinearLayoutManager.HORIZONTAL, false)
|
||||
addItemDecoration(DividerItemDecoration(
|
||||
this@MapsActivity, LinearLayoutManager.HORIZONTAL
|
||||
).apply {
|
||||
setDrawable(getDrawable(R.drawable.gallery_divider)!!)
|
||||
})
|
||||
}
|
||||
|
||||
binding.detailView.connectors.apply {
|
||||
adapter = ConnectorAdapter()
|
||||
layoutManager =
|
||||
LinearLayoutManager(this@MapsActivity, LinearLayoutManager.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
binding.detailView.details.apply {
|
||||
adapter = DetailAdapter()
|
||||
layoutManager =
|
||||
LinearLayoutManager(this@MapsActivity, LinearLayoutManager.VERTICAL, false)
|
||||
addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
this@MapsActivity,
|
||||
LinearLayoutManager.VERTICAL
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.johan.evmap.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.databinding.DataBindingUtil
|
||||
@@ -9,6 +10,7 @@ import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.johan.evmap.BR
|
||||
import com.johan.evmap.R
|
||||
import com.johan.evmap.api.ChargeLocation
|
||||
import com.johan.evmap.api.Chargepoint
|
||||
|
||||
interface Equatable {
|
||||
@@ -46,4 +48,35 @@ abstract class DataBindingAdapter<T : Equatable>() :
|
||||
|
||||
class ConnectorAdapter : DataBindingAdapter<Chargepoint>() {
|
||||
override fun getItemViewType(position: Int): Int = R.layout.item_connector
|
||||
}
|
||||
}
|
||||
|
||||
class DetailAdapter : DataBindingAdapter<DetailAdapter.Detail>() {
|
||||
data class Detail(val icon: Int, val contentDescription: Int, val text: CharSequence) :
|
||||
Equatable
|
||||
|
||||
override fun getItemViewType(position: Int): Int = R.layout.item_detail
|
||||
}
|
||||
|
||||
fun buildDetails(loc: ChargeLocation?, ctx: Context): List<DetailAdapter.Detail> {
|
||||
if (loc == null) return emptyList()
|
||||
|
||||
return listOfNotNull(
|
||||
DetailAdapter.Detail(R.drawable.ic_address, R.string.address, loc.address.toString()),
|
||||
if (loc.operator != null) DetailAdapter.Detail(
|
||||
R.drawable.ic_operator,
|
||||
R.string.operator,
|
||||
loc.operator
|
||||
) else null,
|
||||
if (loc.network != null) DetailAdapter.Detail(
|
||||
R.drawable.ic_network,
|
||||
R.string.network,
|
||||
loc.network
|
||||
) else null,
|
||||
// TODO: separate layout for opening hours with expandable details
|
||||
if (loc.openinghours != null) DetailAdapter.Detail(
|
||||
R.drawable.ic_hours,
|
||||
R.string.hours,
|
||||
loc.openinghours.getStatusText(ctx)
|
||||
) else null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.johan.evmap.api
|
||||
|
||||
import com.squareup.moshi.*
|
||||
import org.threeten.bp.LocalTime
|
||||
import java.lang.reflect.Type
|
||||
|
||||
|
||||
@@ -101,3 +102,31 @@ private fun hasJsonObjectOrFalseAnnotation(annotations: Set<Annotation>?) =
|
||||
annotation class JsonObjectOrFalse {
|
||||
|
||||
}
|
||||
|
||||
internal class HoursAdapter {
|
||||
private val regex = Regex("from (.*) till (.*)")
|
||||
|
||||
@FromJson
|
||||
fun fromJson(str: String): Hours? {
|
||||
if (str == "closed") {
|
||||
return Hours(null, null)
|
||||
} else {
|
||||
val match = regex.find(str)
|
||||
?: throw IllegalArgumentException("$str does not match hours format")
|
||||
return Hours(
|
||||
LocalTime.parse(match.groupValues[1]),
|
||||
LocalTime.parse(match.groupValues[2])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ToJson
|
||||
fun toJson(value: Hours): String {
|
||||
if (value.start == null || value.end == null) {
|
||||
return "closed"
|
||||
} else {
|
||||
return "from ${value.start} till ${value.end}"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,6 +38,7 @@ interface GoingElectricApi {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(ChargepointListItemJsonAdapterFactory())
|
||||
.add(JsonObjectOrFalseAdapter.Factory())
|
||||
.add(HoursAdapter())
|
||||
.build()
|
||||
|
||||
val retrofit = Retrofit.Builder()
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
package com.johan.evmap.api
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.text.HtmlCompat
|
||||
import com.johan.evmap.R
|
||||
import com.johan.evmap.adapter.Equatable
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.threeten.bp.DayOfWeek
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalTime
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargepointList(
|
||||
@@ -26,8 +32,9 @@ data class ChargeLocation(
|
||||
// only shown in details:
|
||||
@JsonObjectOrFalse val operator: String?,
|
||||
@Json(name = "general_information") @JsonObjectOrFalse val generalInformation: String?,
|
||||
val photos: List<ChargerPhoto>?
|
||||
val photos: List<ChargerPhoto>?,
|
||||
//val chargecards: Boolean?
|
||||
val openinghours: OpeningHours?
|
||||
) : ChargepointListItem() {
|
||||
val maxPower: Double
|
||||
get() {
|
||||
@@ -41,6 +48,79 @@ data class ChargeLocation(
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OpeningHours(
|
||||
@Json(name = "24/7") val twentyfourSeven: Boolean,
|
||||
@JsonObjectOrFalse val description: String?,
|
||||
val days: OpeningHoursDays?
|
||||
) {
|
||||
fun getStatusText(ctx: Context): CharSequence {
|
||||
if (twentyfourSeven) {
|
||||
return HtmlCompat.fromHtml(ctx.getString(R.string.open_247), 0)
|
||||
} else if (days != null) {
|
||||
val hours = days.getHoursForDate(LocalDate.now())
|
||||
if (hours.start == null || hours.end == null) {
|
||||
return HtmlCompat.fromHtml(ctx.getString(R.string.closed), 0)
|
||||
}
|
||||
|
||||
val now = LocalTime.now()
|
||||
if (hours.start.isBefore(now) && hours.end.isAfter(now)) {
|
||||
return HtmlCompat.fromHtml(
|
||||
ctx.getString(
|
||||
R.string.open_closesat,
|
||||
hours.end.toString()
|
||||
), 0
|
||||
)
|
||||
} else if (hours.end.isBefore(now)) {
|
||||
return HtmlCompat.fromHtml(ctx.getString(R.string.closed), 0)
|
||||
} else {
|
||||
return HtmlCompat.fromHtml(
|
||||
ctx.getString(
|
||||
R.string.closed_opensat,
|
||||
hours.start.toString()
|
||||
), 0
|
||||
)
|
||||
}
|
||||
} else if (description != null) {
|
||||
return description
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OpeningHoursDays(
|
||||
val monday: Hours,
|
||||
val tuesday: Hours,
|
||||
val wednesday: Hours,
|
||||
val thursday: Hours,
|
||||
val friday: Hours,
|
||||
val saturday: Hours,
|
||||
val sunday: Hours,
|
||||
val holiday: Hours
|
||||
) {
|
||||
fun getHoursForDate(date: LocalDate): Hours {
|
||||
// TODO: check for holidays
|
||||
|
||||
@Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA")
|
||||
return when (date.dayOfWeek) {
|
||||
DayOfWeek.MONDAY -> monday
|
||||
DayOfWeek.TUESDAY -> tuesday
|
||||
DayOfWeek.WEDNESDAY -> wednesday
|
||||
DayOfWeek.THURSDAY -> thursday
|
||||
DayOfWeek.FRIDAY -> friday
|
||||
DayOfWeek.SATURDAY -> saturday
|
||||
DayOfWeek.SUNDAY -> sunday
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Hours(
|
||||
val start: LocalTime?,
|
||||
val end: LocalTime?
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargerPhoto(val id: String)
|
||||
|
||||
|
||||
@@ -49,4 +49,14 @@ fun getConnectorItem(view: ImageView, type: String) {
|
||||
else -> 0
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@BindingAdapter("srcCompat")
|
||||
fun setImageResource(imageView: ImageView, resource: Int) {
|
||||
imageView.setImageResource(resource)
|
||||
}
|
||||
|
||||
@BindingAdapter("android:contentDescription")
|
||||
fun setContentDescriptionResource(imageView: ImageView, resource: Int) {
|
||||
imageView.contentDescription = imageView.context.getString(resource)
|
||||
}
|
||||
Reference in New Issue
Block a user