Bimba.git

ref: 8693f82819b7dc79e894edcff7d67d69c7f68b2b

app/src/main/java/xyz/apiote/bimba/czwek/repo/Stop.kt


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: GPL-3.0-or-later

package xyz.apiote.bimba.czwek.repo

import android.content.Context
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.os.Parcelable
import android.text.Annotation
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.SpannedString
import android.text.style.StyleSpan
import androidx.preference.PreferenceManager
import kotlinx.parcelize.Parcelize
import xyz.apiote.bimba.czwek.R
import xyz.apiote.bimba.czwek.RoundedBackgroundSpan
import xyz.apiote.bimba.czwek.api.StopV1
import xyz.apiote.bimba.czwek.api.StopV2
import xyz.apiote.bimba.czwek.api.StopV3
import xyz.apiote.bimba.czwek.api.transitous.model.Match
import xyz.apiote.bimba.czwek.api.transitous.model.Place


@Parcelize
data class Stop(
	val code: String,
	val name: String,
	val nodeName: String,
	val zone: String,
	val feedID: String?,
	val position: Position,
	val changeOptions: List<ChangeOption>,
	val description: String?
) : Queryable, Locatable, StopAbstract, Parcelable {
	companion object {
		fun distanceComparator(centre: Position) = object : Comparator<Stop> {
			override fun compare(
				o1: Stop?,
				o2: Stop?
			): Int {
				return Position.comparator(centre).compare(o1?.location(), o2?.location())
			}

		}
	}

	override fun icon(context: Context, scale: Float): Drawable {
		return super.icon(context, nodeName, scale)
	}

	override fun id(): String = code

	override fun location(): Position = position

	constructor(s: StopV1) : this(
		s.code,
		s.name,
		s.name,
		s.zone,
		null,
		Position(s.position),
		s.changeOptions.map { ChangeOption(it) },
		null
	)

	constructor(s: StopV2) : this(
		s.code,
		s.name,
		s.nodeName,
		s.zone,
		s.feedID,
		Position(s.position),
		s.changeOptions.map { ChangeOption(it) },
		null
	)

	constructor(s: StopV3) : this(
		s.code,
		s.name,
		s.nodeName,
		s.zone,
		s.feedID,
		Position(s.position),
		s.changeOptions.map { ChangeOption(it) },
		null
	)

	constructor(s: Match) : this(
		s.id,
		s.name,
		s.areas.sortedBy { it.adminLevel }.map { it.name }.distinct().joinToString() + s.name,
		"",
		"transitous",
		Position(s.lat.toDouble(), s.lon.toDouble()),
		emptyList(),
		s.areas.sortedBy { it.adminLevel }.map { it.name }.distinct().joinToString()
	)

	constructor(s: Place) : this(
		s.stopId ?: "",
		s.name,
		s.name,
		"",
		"transitous",
		Position(s.lat.toDouble(), s.lon.toDouble()),
		emptyList(),
		""
	)

	fun changeOptions(context: Context, decoration: LineDecoration): Pair<Spannable, String> {
		return Pair(changeOptions.groupBy { it.line }
			.map {
				Pair(
					it.key,
					it.value.flatMap { co -> co.headsigns }.sortedBy { headsign -> headsign }.joinToString()
				)
			}.fold(SpannableStringBuilder("")) { acc, p ->
				if (acc.toString() != "") {
					acc.append("; ")
				}
				var str = SpannableStringBuilder(
					context.getText(
						R.string.vehicle_headsign
					) as SpannedString
				)
				str = applyAnnotations(str, decoration, p.first, p.first.name, p.second)
				str = applyAnnotations(str, decoration, p.first)
				acc.append(str)
				acc
			},
			changeOptions.groupBy { it.line }
				.map {
					Pair(
						it.key,
						it.value.flatMap { co -> co.headsigns }.sortedBy { headsign -> headsign }.joinToString()
					)
				}.joinToString {
					context.getString(
						R.string.vehicle_headsign_content_description, it.first, it.second
					)
				})
	}

	private fun applyAnnotations(
		s: SpannableStringBuilder,
		decoration: LineDecoration,
		line: LineStub,
		vararg args: Any
	): SpannableStringBuilder {
		val str = SpannableStringBuilder(s)
		val annotations = str.getSpans(0, str.length, Annotation::class.java)
		annotations.forEach {
			when (it.key) {
				"arg" -> {
					if (args.isEmpty()) {
						return@forEach
					}
					val argIndex = Integer.parseInt(it.value)
					str.replace(str.getSpanStart(it), str.getSpanEnd(it), args[argIndex] as String)
				}

				"decoration" -> {
					if (args.isNotEmpty()) {
						return@forEach
					}
					// TODO rounded corners/padding
					val background = RoundedBackgroundSpan(line.colour.toInt(), line.textColour(line.colour))
					val ital = StyleSpan(Typeface.ITALIC)
					when (decoration) {
						LineDecoration.ITALICS -> str.setSpan(
							ital,
							str.getSpanStart(it),
							str.getSpanEnd(it),
							Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
						)

						LineDecoration.COLOUR -> {
							str.setSpan(
								background,
								str.getSpanStart(it),
								str.getSpanEnd(it),
								Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
							)
							// str.setSpan(foreground, str.getSpanStart(it), str.getSpanEnd(it), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
						}

						LineDecoration.NONE -> {}
					}
				}
			}
		}
		return str
	}

	fun changeOptionsString(): String = changeOptions.groupBy { it.line }
		.map {
			Pair(
				it.key,
				it.value.flatMap { co -> co.headsigns }.sortedBy { headsign -> headsign }.joinToString()
			)
		}.joinToString("; ")

	override fun toString(): String {
		return "$name ($code) [$zone] $position\n${changeOptionsString()}"
	}

	enum class LineDecoration {
		NONE, ITALICS, COLOUR;

		companion object {
			fun fromPreferences(context: Context) =
				when (PreferenceManager.getDefaultSharedPreferences(context)
					.getString("line_decoration", "italics")) {
					"italics" -> Stop.LineDecoration.ITALICS
					"colour" -> Stop.LineDecoration.COLOUR
					"none" -> Stop.LineDecoration.NONE
					else -> Stop.LineDecoration.ITALICS
				}
		}
	}
}