Bimba.git

ref: e4d7079147c16b0d67cd39e35f5d943644d24c68

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
// 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.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 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


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>
) : Queryable, Locatable, StopAbstract {

	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) })

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

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

	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
				}
		}
	}
}