Author: Adam Evyčędo <git@apiote.xyz>
colour lines in journeys summaries
app/src/main/java/xyz/apiote/bimba/czwek/RoundedBackgroundSpan.kt | 33 app/src/main/java/xyz/apiote/bimba/czwek/journeys/Journeys.kt | 23 app/src/main/java/xyz/apiote/bimba/czwek/repo/LineStub.kt | 51 app/src/main/java/xyz/apiote/bimba/czwek/repo/Stop.kt | 36 app/src/main/res/layout/journey.xml | 16
diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/RoundedBackgroundSpan.kt b/app/src/main/java/xyz/apiote/bimba/czwek/RoundedBackgroundSpan.kt index e38daad84e9aba836e3195be5c442c0abf6aa678..b410764e4abb270111a89221bf2c8fe6b5198b4d 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/RoundedBackgroundSpan.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/RoundedBackgroundSpan.kt @@ -9,7 +9,13 @@ import android.graphics.Paint import android.graphics.RectF import android.text.style.ReplacementSpan -class RoundedBackgroundSpan(private val bgColour: Int, private val fgColour: Int) : ReplacementSpan() { +class RoundedBackgroundSpan(private val bgColour: Int, private val fgColour: Int) : + ReplacementSpan() { + + companion object { + const val PADDING = 20 // TODO based on small text, make dependent on eg. em-dash length + } + override fun getSize( paint: Paint, text: CharSequence, @@ -17,7 +23,15 @@ start: Int, end: Int, fm: Paint.FontMetricsInt? ): Int { - return (paint.measureText(text, start, end)+20).toInt() + val l = paint.measureText(text, start, end) + PADDING + if (fm != null) { + fm.ascent = paint.fontMetrics.ascent.toInt() + fm.bottom = paint.fontMetrics.bottom.toInt() + fm.descent = paint.fontMetrics.descent.toInt() + fm.leading = paint.fontMetrics.leading.toInt() + fm.top = paint.fontMetrics.top.toInt() + } + return l.toInt() } override fun draw( @@ -31,12 +45,19 @@ y: Int, bottom: Int, paint: Paint ) { - val length = paint.measureText(text, start, end) + 20 - val rect = RectF(x, top.toFloat() - 5f, x + length, y.toFloat() + 5f) + val length = paint.measureText(text, start, end) + PADDING + /* + Log.i("BackgroundSpan", "x: $x, y: $y, start: $start, end: $end, top: $top, bottom: $bottom") + Log.i("BackgroundSpan", "text: $text, length: $length") + Log.i("BackgroundSpan", "fontMetrics:: bottom: ${paint.fontMetricsInt.bottom}, top: ${paint.fontMetricsInt.top}, ascent: ${paint.fontMetricsInt.ascent}, descent: ${paint.fontMetricsInt.descent}, leading: ${paint.fontMetricsInt.leading}") + Log.i("BackgroundSpan", "fontSpacing: ${paint.fontSpacing}; 1dp=${dpToPixelI(1f)}") + */ + val height = y - top + val rect = RectF(x, top.toFloat()-(height/6), x + length, y.toFloat()+(height/6)) paint.color = bgColour - canvas.drawRoundRect(rect, 10f, 10f, paint) + canvas.drawRoundRect(rect, height/4f, height/4f, paint) paint.color = fgColour paint.textAlign = Paint.Align.CENTER - canvas.drawText(text, start, end, x+(length/2), y.toFloat(), paint) + canvas.drawText(text, start, end, x + (length / 2), y.toFloat(), paint) } } \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/journeys/Journeys.kt b/app/src/main/java/xyz/apiote/bimba/czwek/journeys/Journeys.kt index ff1d71676d397e60dcf7203f80d6cfa180069dc9..2f4d18ccb8bbebd0cd076fefb7d802efd9acc97e 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/journeys/Journeys.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/journeys/Journeys.kt @@ -6,6 +6,7 @@ package xyz.apiote.bimba.czwek.journeys import android.annotation.SuppressLint import android.content.Context +import android.text.SpannableStringBuilder import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -17,6 +18,7 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.google.android.material.card.MaterialCardView import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.repo.Journey +import xyz.apiote.bimba.czwek.repo.Stop.LineDecoration import xyz.apiote.bimba.czwek.units.UnitSystem class JourneysViewHolder(itemView: View) : ViewHolder(itemView) { @@ -43,8 +45,19 @@ holder.startTime.text = context.getString(R.string.time, journey.startTime.hour, journey.startTime.minute) holder.endTime.text = context.getString(R.string.time, journey.endTime.hour, journey.endTime.minute) - holder.lines.text = - journey.legs.map { it.start.vehicle.Line.name }.filter { it.isNotBlank() }.joinToString() + holder.lines.text = journey.legs.filter { it.start.vehicle.Line.name.isNotBlank() } + .fold(SpannableStringBuilder("")) { acc, leg -> + if (acc.isNotBlank()) { + acc.append(" ") + } + val s = leg.start.vehicle.Line.decorate( + SpannableStringBuilder(leg.start.vehicle.Line.name), + LineDecoration.COLOUR, + null + ) + acc.append(s) + acc + } holder.legs.removeAllViews() journey.legs.forEach { @@ -98,7 +111,11 @@ context.getString(R.string.journey_stops_headsign, stops, headsign) } val legDestination = legView.findViewById<TextView>(R.id.leg_destination) - if (it.destination.stop.name.isBlank() || it.destination.stop.name in arrayOf("START", "END")) { + if (it.destination.stop.name.isBlank() || it.destination.stop.name in arrayOf( + "START", + "END" + ) + ) { legDestination.visibility = View.GONE } else { legDestination.apply { diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineStub.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineStub.kt index 86565ae24cf1322fba82acae25063e71bb026d28..26b4b729f25698d95be373a282a99c6066da3fb9 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineStub.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/LineStub.kt @@ -5,13 +5,21 @@ package xyz.apiote.bimba.czwek.repo import android.content.Context -import android.graphics.drawable.BitmapDrawable +import android.graphics.Typeface import android.graphics.drawable.Drawable import android.os.Parcelable +import android.text.Annotation +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.style.StyleSpan +import android.util.Log +import androidx.core.graphics.drawable.toDrawable import kotlinx.parcelize.Parcelize +import xyz.apiote.bimba.czwek.RoundedBackgroundSpan import xyz.apiote.bimba.czwek.api.LineStubV1 import xyz.apiote.bimba.czwek.api.LineStubV2 import xyz.apiote.bimba.czwek.api.LineStubV3 +import xyz.apiote.bimba.czwek.repo.Stop.LineDecoration @Parcelize data class LineStub(val name: String, val kind: LineType, val colour: Colour) : LineAbstract, Parcelable { @@ -20,6 +28,45 @@ constructor(l: LineStubV2) : this(l.name, LineType.of(l.kind), Colour(l.colour)) constructor(l: LineStubV3) : this(l.name, LineType.of(l.kind), Colour(l.colour)) fun icon(context: Context, scale: Float = 1f): Drawable { - return BitmapDrawable(context.resources, super.icon(context, kind, colour, scale)) + return super.icon(context, kind, colour, scale).toDrawable(context.resources) + } + + fun decorate( + str: SpannableStringBuilder, + decoration: LineDecoration, + annotation: Annotation? + ): SpannableStringBuilder { + val spanStart = if (annotation != null) { + str.getSpanStart(annotation) + } else { + 0 + } + val spanEnd = if (annotation != null) { + str.getSpanEnd(annotation) + } else { + str.length + } + + val background = RoundedBackgroundSpan(colour.toInt(), textColour(colour)) + val ital = StyleSpan(Typeface.ITALIC) + when (decoration) { + LineDecoration.ITALICS -> str.setSpan( + ital, + spanStart, spanEnd, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + + LineDecoration.COLOUR -> { + str.setSpan( + background, + spanStart, spanEnd, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + + LineDecoration.NONE -> {} + } + + return str } } \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Stop.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Stop.kt index 1f0f19b8282e80f48a826ec163380a09b270afc6..6359c8153f193f226c7a4665a8760bd266f4c79c 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Stop.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Stop.kt @@ -5,19 +5,15 @@ 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 @@ -167,29 +163,7 @@ "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 -> {} - } + line.decorate(str, decoration, it) } } } @@ -215,10 +189,10 @@ 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 + "italics" -> ITALICS + "colour" -> COLOUR + "none" -> NONE + else -> ITALICS } } } diff --git a/app/src/main/res/layout/journey.xml b/app/src/main/res/layout/journey.xml index be5120d120ac8514a2a112c47feef904733d0a6b..3ee0cb0a56ec6137a7dc092b3796f6ac35ed865d 100644 --- a/app/src/main/res/layout/journey.xml +++ b/app/src/main/res/layout/journey.xml @@ -25,17 +25,21 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="11:25" /> + <!-- TODO line height multiplier --> <com.google.android.material.textview.MaterialTextView android:id="@+id/lines" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" + android:layout_marginStart="8dp" + android:layout_marginTop="4dp" + android:layout_marginEnd="8dp" + android:lineSpacingExtra="18sp" + android:padding="4dp" android:textAlignment="center" android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" - app:layout_constraintEnd_toStartOf="@+id/end_time" - app:layout_constraintStart_toEndOf="@+id/start_time" - app:layout_constraintTop_toTopOf="@+id/start_time" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/start_time" tools:text="Metropolitan, Circle, Hammersmith&City" /> <com.google.android.material.textview.MaterialTextView @@ -44,7 +48,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@+id/lines" + app:layout_constraintTop_toTopOf="@+id/start_time" tools:text="12:30" /> <LinearLayout