Author: Adam Evyčędo <git@apiote.xyz>
add units
%!v(PANIC=String method: strings: negative Repeat count)
diff --git a/app/build.gradle b/app/build.gradle index 54d50b5a32b00c98bde44d662ce7e94bfbe0df97..ac26e06a71c52aa0be420a23dec120bb67e4d13d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -78,6 +78,7 @@ implementation 'org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3' implementation 'com.github.jershell:kbson:0.5.0' implementation project(path: ':fruchtfleisch') + implementation 'androidx.preference:preference:1.2.0' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f7c0314f0b78f5e5a72bccc33f8d7b940088a0d9..3cd49cd487e5d6f55d771451b68bbea53a362c70 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,11 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> - -<!-- -SPDX-FileCopyrightText: Adam Evyčędo - -SPDX-License-Identifier: GPL-3.0-or-later ---> - <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tool="http://schemas.android.com/tools"> @@ -27,6 +20,10 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Bimba.Style" tool:targetApi="33"> + <activity + android:name=".settings.SettingsActivity" + android:exported="false" + android:label="@string/title_activity_settings" /> <activity android:name=".AboutActivity" android:exported="false" /> diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt index b2a20c6ca37251d8b153b0789dbfa920fa015eb3..b12c34a0e996cefb965993b6374e59bed1b73f99 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt @@ -39,6 +39,7 @@ import xyz.apiote.bimba.czwek.dashboard.ui.voyage.VoyageFragment import xyz.apiote.bimba.czwek.databinding.ActivityMainBinding import xyz.apiote.bimba.czwek.search.ResultsActivity import xyz.apiote.bimba.czwek.settings.ServerChooserActivity +import xyz.apiote.bimba.czwek.settings.SettingsActivity import xyz.apiote.bimba.czwek.settings.feeds.FeedChooserActivity @@ -107,6 +108,10 @@ } R.id.drawer_cities -> { startActivity(Intent(this, FeedChooserActivity::class.java)) + } + + R.id.drawer_settings -> { + startActivity(Intent(this, SettingsActivity::class.java)) } R.id.drawer_about -> { diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/HomeFragment.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/HomeFragment.kt index d3d3c1afc89244763f309441eb5c24275862d30d..e22468daa531581c6de54bf84b14ccc24d153874 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/HomeFragment.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/home/HomeFragment.kt @@ -29,7 +29,6 @@ import xyz.apiote.bimba.czwek.databinding.FragmentHomeBinding import xyz.apiote.bimba.czwek.dpToPixelI import xyz.apiote.bimba.czwek.repo.Favourite import xyz.apiote.bimba.czwek.search.BimbaResultsAdapter -import xyz.apiote.bimba.czwek.units.Millisecond import xyz.apiote.bimba.czwek.units.Second class HomeFragment : Fragment() { @@ -41,7 +40,7 @@ private lateinit var favouritesAdapter: BimbaFavouritesAdapter private lateinit var viewModel: HomeViewModel private val countdown = - object : CountDownTimer(Millisecond(Second(30)).millis, Millisecond(Second(10)).millis) { + object : CountDownTimer(Second(30).milliseconds(), Second(10).milliseconds()) { override fun onTick(millisUntilFinished: Long) { } diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/map/MapViewModel.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/map/MapViewModel.kt index f53541d8f75292860a260fd389f57fe047e1bf32..f046fad8cedf37d2d2164eff4e63ba9e40af7781 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/map/MapViewModel.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/map/MapViewModel.kt @@ -35,6 +35,8 @@ import xyz.apiote.bimba.czwek.repo.Position import xyz.apiote.bimba.czwek.repo.Stop import xyz.apiote.bimba.czwek.repo.TrafficResponseException import xyz.apiote.bimba.czwek.repo.Vehicle +import xyz.apiote.bimba.czwek.units.UnitSystem + class MapViewModel : ViewModel() { @@ -79,9 +81,11 @@ content.findViewById(R.id.rt_icon).visibility = View.GONE // TODO vehicle accessible content.findViewById<ImageView>(R.id.wheelchair_icon).visibility = View.GONE - // todo units -- [3.2] settings or system-based - content.findViewById<TextView>(R.id.speed_text).text = - ctx.getString(R.string.speed_in_km_per_h, vehicle.Speed * 3.6) + Log.i("unit", "${vehicle.Speed.mps}") + UnitSystem.getSelected(requireContext()).let { us -> + content.findViewById<TextView>(R.id.speed_text).text = + us.toString(requireContext(), us.speedUnit(vehicle.Speed)) + } content.findViewById<LinearLayout>(R.id.congestion).visibility = if (vehicle.congestionLevel == CongestionLevel.UNKNOWN) View.GONE else View.VISIBLE diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt b/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt index b9b2871dd953aab86b88c501729cc02801e8f705..4c9945746d503aeed8749870d75dcca08a0d2d1a 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt @@ -41,6 +41,7 @@ import xyz.apiote.bimba.czwek.repo.Departure import xyz.apiote.bimba.czwek.repo.DepartureItem import xyz.apiote.bimba.czwek.repo.OccupancyStatus import xyz.apiote.bimba.czwek.repo.Vehicle +import xyz.apiote.bimba.czwek.units.UnitSystem import java.time.ZoneId import java.time.ZonedDateTime @@ -91,7 +92,7 @@ context: Context? ) { val alertDescriptions = alerts.map { it.description }.filter { it != "" } .joinToString(separator = "\n") - holder?.moreButton?.setOnClickListener{ + holder?.moreButton?.setOnClickListener { MaterialAlertDialogBuilder(context!!) .setTitle("Alerts") .setPositiveButton(R.string.ok) { _, _ -> } @@ -303,9 +304,10 @@ ) } findViewById<TextView>(R.id.boarding_text).text = departure.boardingText(ctx) - // todo units -- [3.2] settings or system-based - findViewById<TextView>(R.id.speed_text).text = - getString(R.string.speed_in_km_per_h, departure.vehicle.Speed * 3.6) + UnitSystem.getSelected(requireContext()).let { us -> + findViewById<TextView>(R.id.speed_text).text = + us.toString(context, us.speedUnit(departure.vehicle.Speed)) + } findViewById<LinearLayout>(R.id.congestion).visibility = if (departure.vehicle.congestionLevel == CongestionLevel.UNKNOWN) View.GONE else View.VISIBLE diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt index 783606925186535e8a5ba30761e3b1ae932b6b2d..c31627a8ae83a48e330ab7b3a3408901e40135b4 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt @@ -44,7 +44,6 @@ import xyz.apiote.bimba.czwek.repo.DepartureItem import xyz.apiote.bimba.czwek.repo.Favourite import xyz.apiote.bimba.czwek.repo.OfflineRepository import xyz.apiote.bimba.czwek.repo.Stop -import xyz.apiote.bimba.czwek.units.Millisecond import xyz.apiote.bimba.czwek.units.Second import xyz.apiote.bimba.czwek.units.Tim import java.time.Instant @@ -73,10 +72,10 @@ private val linesFilterTemporary = mutableMapOf () // TODO [elizabeth] millisInFuture from header Cache-Control max-age private val countdown = - object : CountDownTimer(Millisecond(Second(30)).millis, Millisecond(Tim(1)).millis) { + object : CountDownTimer(Second(30).milliseconds(), Tim(1).milliseconds()) { override fun onTick(millisUntilFinished: Long) { - val timsUntillFinished = Tim(Millisecond(millisUntilFinished)) - binding.departuresUpdatesProgress.progress = timsUntillFinished.tims + val timsUntillFinished = Tim(Second(millisUntilFinished.toDouble()/1000)) + binding.departuresUpdatesProgress.progress = timsUntillFinished.tims.toInt() } override fun onFinish() { @@ -454,8 +453,8 @@ // TODO [elizabeth] max, progress from header Cache-Control max-age binding.departuresUpdatesProgress.apply { visibility = View.VISIBLE isIndeterminate = false - max = Tim(Second(30)).tims - progress = Tim(Second(30)).tims + max = Tim(Second(30)).tims.toInt() + progress = Tim(Second(30)).tims.toInt() } countdown.cancel() countdown.start() diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Departure.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Departure.kt index e90ce512e3b7eba9e635c9514268ee246ddc9db8..930c33fd4f177d77db3a0129bcafb98dfe86305a 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Departure.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Departure.kt @@ -16,6 +16,8 @@ import xyz.apiote.bimba.czwek.api.DepartureV3 import xyz.apiote.bimba.czwek.api.DepartureV4 import xyz.apiote.bimba.czwek.api.Time import xyz.apiote.bimba.czwek.api.UnknownResourceVersionException +import xyz.apiote.bimba.czwek.units.Second +import xyz.apiote.bimba.czwek.units.UnitSystem import java.time.Instant import java.time.ZoneId import java.time.ZonedDateTime @@ -169,12 +171,20 @@ if (departureTime.isBefore(now) && r < 3u) { r = 0u } return when (r) { - 0u -> DateUtils.getRelativeTimeSpanString( - departureTime.toEpochSecond() * 1000, - now.toEpochSecond() * 1000, - DateUtils.MINUTE_IN_MILLIS, - DateUtils.FORMAT_ABBREV_RELATIVE - ).toString() + 0u -> if (context != null && UnitSystem.getSelected(context).base == 12) { + val us = UnitSystem.getSelected(context) + us.toString( + context, + us.timeUnit(Second((departureTime.toEpochSecond() - now.toEpochSecond()).toInt())) + ) + } else { + DateUtils.getRelativeTimeSpanString( + departureTime.toEpochSecond() * 1000, + now.toEpochSecond() * 1000, + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_RELATIVE + ).toString() + } 1u -> context?.getString(R.string.departure_momentarily) ?: "momentarily" 2u -> context?.getString(R.string.departure_now) ?: "now" diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Vehicle.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Vehicle.kt index b4ae2e175d1ed72cf2b38980d9da95693764bd80..047b8c576a927a9c8359d9214a08d6df1ac10d34 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Vehicle.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Vehicle.kt @@ -5,12 +5,14 @@ package xyz.apiote.bimba.czwek.repo import android.content.Context +import android.util.Log import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.api.CongestionLevelV1 import xyz.apiote.bimba.czwek.api.OccupancyStatusV1 import xyz.apiote.bimba.czwek.api.VehicleV1 import xyz.apiote.bimba.czwek.api.VehicleV2 import xyz.apiote.bimba.czwek.api.VehicleV3 +import xyz.apiote.bimba.czwek.units.Mps enum class CongestionLevel { UNKNOWN, SMOOTH, STOP_AND_GO, SIGNIFICANT, SEVERE; @@ -73,7 +75,7 @@ data class Vehicle( val ID: String, val Position: Position, val Capabilities: UShort, - val Speed: Float, + val Speed: Mps, val Line: LineStub, val Headsign: String, val congestionLevel: CongestionLevel, @@ -83,34 +85,40 @@ constructor(v: VehicleV1) : this( v.ID, Position(v.Position), v.Capabilities, - v.Speed, + Mps(v.Speed.toDouble()), LineStub(v.Line), v.Headsign, CongestionLevel.of(v.CongestionLevel), OccupancyStatus.of(v.OccupancyStatus) - ) + ) { + Log.i("unit", "veh: ${v.Speed}") + } constructor(v: VehicleV2) : this( v.ID, Position(v.Position), v.Capabilities, - v.Speed, + Mps(v.Speed.toDouble()), LineStub(v.Line), v.Headsign, CongestionLevel.of(v.CongestionLevel), OccupancyStatus.of(v.OccupancyStatus) - ) + ) { + Log.i("unit", "veh: ${v.Speed.toDouble()}") + } constructor(v: VehicleV3) : this( v.ID, Position(v.Position), v.Capabilities, - v.Speed, + Mps(v.Speed.toDouble()), LineStub(v.Line), v.Headsign, CongestionLevel.of(v.CongestionLevel), OccupancyStatus.of(v.OccupancyStatus) - ) + ) { + Log.i("unit", "veh: ${v.Speed.toDouble()}") + } enum class Capability(val bit: UShort) { RAMP(0b0001u), LOW_FLOOR(0b0010u), LOW_ENTRY(0b0001_0000_0000u), AC(0b0100u), BIKE(0b1000u), VOICE( diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/settings/SettingsActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/settings/SettingsActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..be458c29a7f4d36e99f1e0d0189c4eb7879aa16e --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/settings/SettingsActivity.kt @@ -0,0 +1,45 @@ +package xyz.apiote.bimba.czwek.settings + +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams +import androidx.preference.PreferenceFragmentCompat +import xyz.apiote.bimba.czwek.R + +class SettingsActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + setContentView(R.layout.settings_activity) + if (savedInstanceState == null) { + supportFragmentManager + .beginTransaction() + .replace(R.id.settings, SettingsFragment()) + .commit() + } + supportActionBar?.setDisplayHomeAsUpEnabled(true) + // TODO insets + + val root = findViewById<View>(R.id.settings) + + ViewCompat.setOnApplyWindowInsetsListener(root) { v, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updateLayoutParams<ViewGroup.MarginLayoutParams> { + topMargin = insets.top + } + windowInsets + } + } + + class SettingsFragment : PreferenceFragmentCompat() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.root_preferences, rootKey) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/units/TGM.kt b/app/src/main/java/xyz/apiote/bimba/czwek/units/TGM.kt new file mode 100644 index 0000000000000000000000000000000000000000..ca2c62ff76293c56f939852f078c0e1514eae3cf --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/units/TGM.kt @@ -0,0 +1,82 @@ +package xyz.apiote.bimba.czwek.units + +import android.content.Context +import xyz.apiote.bimba.czwek.R +import java.text.NumberFormat +import kotlin.math.abs +import kotlin.math.pow + + +class TGM(base: Int) : UnitSystem(base) { + override fun timeUnit(count: Long): TimeUnit = Tim(count) + override fun timeUnit(other: TimeUnit): TimeUnit = Tim(other) + + override fun speedUnit(count: Double): SpeedUnit = Vlos(count) + override fun speedUnit(other: SpeedUnit): SpeedUnit = Vlos(other) + override fun toString(context: Context, s: SpeedUnit): String = s.toString(context, base) + override fun toString(context: Context, t: TimeUnit): String = t.toString(context, base) +} + +class Tim(val tims: Long) : TimeUnit { + constructor(other: TimeUnit) : this((other.milliseconds() * 144 / 25 / 1000)) + + override fun milliseconds(): Long = tims * 25 * 1000 / 144 + override fun toString(context: Context, base: Int): String { + val res = if (tims < 0) { + R.string.time_in_tm_past + } else { + R.string.time_in_tm + } + val (t,m) = if (tims > base.toDouble().pow(4)) { + Pair(tims/base.toDouble().pow(4).toInt(), "⁴") + } else { + Pair(tims/base.toDouble().pow(2).toInt(), "²") + } + return if (base == 10) { + context.getString(res, NumberFormat.getInstance().format(abs(t)), m) + } else { + context.getString( + res, + abs(t).toString(12).lowercase().replace("a", "↊").replace("b", "↋"), + m + ) + } + } + + override fun contentDescription(context: Context, base: Int): String = + context.resources.getQuantityString(R.plurals.time_in_tm_cd, tims.toInt(), tims) +} + +class Vlos(val vlos: Double) : SpeedUnit { + constructor(other: SpeedUnit) : this(other.mps() / 1.703133986928105) + + override fun mps(): Double = vlos * 1.703133986928105 + override fun contentDescription(context: Context, base: Int): String { + // TODO + return "" + } + + override fun toString(context: Context, base: Int): String { + return context.getString(R.string.speed_in_vl, vlos.toString(base, 3).lowercase()) + } +} + +fun Double.toString(radix: Int, precision: Int): String { + var x = this + var result = "" + if (x < 0) { + result = "-" + x = -x + } + result += x.toInt().toString(radix) + var frac = (x - x.toInt()) + var digits = 0 + while (frac > 0 && digits < precision) { + if (digits == 0) result += if (radix == 12) "·" else "." // TODO separator based on locale + frac *= radix + result += frac.toInt().toString(radix) + frac -= frac.toInt() + digits++ + } + return result.lowercase().replace("a", "↊").replace("b", "↋") +} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/units/Time.kt b/app/src/main/java/xyz/apiote/bimba/czwek/units/Time.kt deleted file mode 100644 index 9456eae31f27301d561789f4e07ca44dd9519bef..0000000000000000000000000000000000000000 --- a/app/src/main/java/xyz/apiote/bimba/czwek/units/Time.kt +++ /dev/null @@ -1,17 +0,0 @@ -package xyz.apiote.bimba.czwek.units - -interface TimeUnit - -data class Second(val secs: Long) { - constructor(t: Tim) : this(t.tims.toLong() * 25 / 144) -} - -data class Millisecond(val millis: Long) { - constructor(t: Tim) : this(t.tims.toLong() * 25 * 1000 / 144) - constructor(s: Second) : this(s.secs * 1000) -} - -data class Tim(val tims: Int) { - constructor(s: Second) : this((s.secs * 144 / 25).toInt()) - constructor(m: Millisecond) : this((m.millis * 144 / 25 / 1000).toInt()) -} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/units/UnitSystem.kt b/app/src/main/java/xyz/apiote/bimba/czwek/units/UnitSystem.kt new file mode 100644 index 0000000000000000000000000000000000000000..2af9ab4bc7f3e06e89f8f5710b05770dcbe1b287 --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/units/UnitSystem.kt @@ -0,0 +1,74 @@ +package xyz.apiote.bimba.czwek.units + +import android.content.Context +import android.icu.util.LocaleData +import android.icu.util.LocaleData.MeasurementSystem +import android.icu.util.ULocale +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.preference.PreferenceManager +import java.util.Locale + +abstract class UnitSystem(val base: Int) { + companion object { + @RequiresApi(Build.VERSION_CODES.P) + private fun forMeasureSystem(ms: MeasurementSystem) = + when (ms) { + MeasurementSystem.SI -> { + Metric + } + + MeasurementSystem.UK -> { + Imperial + } + + MeasurementSystem.US -> { + USCustomary + } + + else -> { + Metric + } + } + + private fun forLocale(country: String): UnitSystem = Metric // TODO + + private fun getDefault() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + forMeasureSystem(LocaleData.getMeasurementSystem(ULocale.forLocale(Locale.getDefault()))) + } else { + forLocale(Locale.getDefault().country) + } + + fun getSelected(context: Context): UnitSystem = + when (PreferenceManager.getDefaultSharedPreferences(context) + .getString("unit_system", "default")) { + "default" -> getDefault() + "metric" -> Metric + "imperial" -> Imperial + "customary" -> USCustomary + "tgm10" -> TGM(10) + "tgm12" -> TGM(12) + else -> getDefault() + } + } + + abstract fun timeUnit(count: Long): TimeUnit + abstract fun timeUnit(other: TimeUnit): TimeUnit + abstract fun speedUnit(count: Double): SpeedUnit + abstract fun speedUnit(other: SpeedUnit): SpeedUnit + + abstract fun toString(context: Context, s: SpeedUnit): String + abstract fun toString(context: Context, t: TimeUnit): String +} + +interface TimeUnit { + fun milliseconds(): Long + fun toString(context: Context, base: Int): String + fun contentDescription(context: Context, base: Int): String +} + +interface SpeedUnit { + fun mps(): Double + fun toString(context: Context, base: Int): String + fun contentDescription(context: Context, base: Int): String +} diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/units/imperial.kt b/app/src/main/java/xyz/apiote/bimba/czwek/units/imperial.kt new file mode 100644 index 0000000000000000000000000000000000000000..964a97973865f2c5ba98e432f3fdd3427cf1ca8d --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/units/imperial.kt @@ -0,0 +1,26 @@ +package xyz.apiote.bimba.czwek.units + +import android.content.Context +import xyz.apiote.bimba.czwek.R +import java.text.NumberFormat + +object Imperial : UnitSystem(10) { + override fun timeUnit(count: Long): TimeUnit = Second(count.toDouble()) + override fun timeUnit(other: TimeUnit): TimeUnit = Second(other) + + override fun speedUnit(count: Double): SpeedUnit = Miph(count) + override fun speedUnit(other: SpeedUnit): SpeedUnit = Miph(other) + override fun toString(context: Context, s: SpeedUnit): String = s.toString(context, base) + override fun toString(context: Context, t: TimeUnit): String = t.toString(context, base) +} + +class Miph(val miph: Double) : SpeedUnit { + constructor(s: Int) : this(s.toDouble()) + constructor(other: SpeedUnit) : this(other.mps() / 0.44704) + + override fun mps(): Double = miph * 0.44704 + override fun toString(context: Context, base: Int): String = context.getString(R.string.speed_in_mi_per_h, NumberFormat.getInstance().format(miph)) + override fun contentDescription(context: Context, base: Int): String = context.resources.getQuantityString( + R.plurals.speed_in_mi_per_h_cd, miph.toInt(), miph) + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/units/metric.kt b/app/src/main/java/xyz/apiote/bimba/czwek/units/metric.kt new file mode 100644 index 0000000000000000000000000000000000000000..2492c4d0ff00a56037de882cdb0888bdc1ad8144 --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/units/metric.kt @@ -0,0 +1,44 @@ +package xyz.apiote.bimba.czwek.units + +import android.content.Context +import xyz.apiote.bimba.czwek.R +import java.text.NumberFormat + +object Metric : UnitSystem(10) { + + override fun timeUnit(count: Long): TimeUnit = Second(count.toDouble()) + override fun timeUnit(other: TimeUnit): TimeUnit = Second(other) + + override fun speedUnit(count: Double): SpeedUnit = Kmph(count) + override fun speedUnit(other: SpeedUnit): SpeedUnit = Kmph(other) + override fun toString(context: Context, s: SpeedUnit): String = s.toString(context, base) + override fun toString(context: Context, t: TimeUnit): String = t.toString(context, base) +} + +class Second(val seconds: Double) : TimeUnit { + constructor(s: Int) : this(s.toDouble()) + constructor(other: TimeUnit) : this((other.milliseconds().toDouble() / 1000)) + + override fun milliseconds(): Long = (seconds * 1000).toLong() + override fun toString(context: Context, base: Int): String = context.getString(R.string.time_in_s, NumberFormat.getInstance().format(seconds)) + override fun contentDescription(context: Context, base: Int): String = context.resources.getQuantityString(R.plurals.time_in_s_cd, seconds.toInt(), seconds) +} + +class Mps(val mps: Double) : SpeedUnit { + constructor(s: Int) : this(s.toDouble()) + constructor(other: SpeedUnit) : this(other.mps()) + + override fun mps(): Double = mps + override fun toString(context: Context, base: Int): String = context.getString(R.string.speed_in_m_per_s, NumberFormat.getInstance().format(mps)) + override fun contentDescription(context: Context, base: Int): String = context.resources.getQuantityString(R.plurals.speed_in_m_per_s_cd, mps.toInt(), mps) +} + +class Kmph(val kmph: Double) : SpeedUnit { + constructor(s: Int) : this(s.toDouble()) + constructor(other: SpeedUnit) : this(other.mps() * 3.6) + + override fun mps(): Double = kmph / 3.6 + override fun toString(context: Context, base: Int): String = context.getString(R.string.speed_in_km_per_h, NumberFormat.getInstance().format(kmph)) + override fun contentDescription(context: Context, base: Int): String = context.resources.getQuantityString(R.plurals.speed_in_km_per_h_cd, kmph.toInt(), kmph) + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/units/usCustomary.kt b/app/src/main/java/xyz/apiote/bimba/czwek/units/usCustomary.kt new file mode 100644 index 0000000000000000000000000000000000000000..8b2317362a850d7bde70b60e0f2c41216bf4d3f4 --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/units/usCustomary.kt @@ -0,0 +1,13 @@ +package xyz.apiote.bimba.czwek.units + +import android.content.Context + +object USCustomary: UnitSystem(10) { + override fun timeUnit(count: Long): TimeUnit = Second(count.toDouble()) + override fun timeUnit(other: TimeUnit): TimeUnit = Second(other) + + override fun speedUnit(count: Double): SpeedUnit = Miph(count) + override fun speedUnit(other: SpeedUnit): SpeedUnit = Miph(other) + override fun toString(context: Context, s: SpeedUnit): String = s.toString(context, base) + override fun toString(context: Context, t: TimeUnit): String = t.toString(context, base) +} diff --git a/app/src/main/res/drawable/units.xml b/app/src/main/res/drawable/units.xml new file mode 100644 index 0000000000000000000000000000000000000000..65250dd0441defb5c597d591f4d9a9416857b4c6 --- /dev/null +++ b/app/src/main/res/drawable/units.xml @@ -0,0 +1,10 @@ +<!-- +SPDX-FileCopyrightText: Google + +SPDX-License-Identifier: Apache-2.0 +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorOnSurface" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> + + <path android:fillColor="@android:color/white" android:pathData="M7,15H5.5v-4.5H4V9h3V15zM13.5,13.5h-3v-1h2c0.55,0 1,-0.45 1,-1V10c0,-0.55 -0.45,-1 -1,-1H9v1.5h3v1h-2c-0.55,0 -1,0.45 -1,1V15h4.5V13.5zM19.5,14v-4c0,-0.55 -0.45,-1 -1,-1H15v1.5h3v1h-2v1h2v1h-3V15h3.5C19.05,15 19.5,14.55 19.5,14z"/> + +</vector> diff --git a/app/src/main/res/font/yellowcircle8.otf b/app/src/main/res/font/yellowcircle8.otf index 31e8706da286d9575e653938bfd72b7fdba1676d..bfd91f587ddefb3c1f81d7750fd41162c882ae6f 100644 Binary files a/app/src/main/res/font/yellowcircle8.otf and b/app/src/main/res/font/yellowcircle8.otf differ diff --git a/app/src/main/res/layout/settings_activity.xml b/app/src/main/res/layout/settings_activity.xml new file mode 100644 index 0000000000000000000000000000000000000000..b5b5089cad3cc4480dae8e346bc72ab4d18096c9 --- /dev/null +++ b/app/src/main/res/layout/settings_activity.xml @@ -0,0 +1,9 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <FrameLayout + android:id="@+id/settings" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/menu/drawer.xml b/app/src/main/res/menu/drawer.xml index 6aa4cd40617cbc62655fc57af593549f6d4016a1..1d876c0cf374716191449dcfaf664152687c4623 100644 --- a/app/src/main/res/menu/drawer.xml +++ b/app/src/main/res/menu/drawer.xml @@ -25,6 +25,9 @@ </item> <!-- other settings --> <item + android:id="@+id/drawer_settings" + android:title="@string/title_settings"/> + <item android:id="@+id/drawer_about" android:title="@string/title_about"/> </menu> \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000000000000000000000000000000000000..5769aed0a30f247b1dd5735e17b142d669ee65ad --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,19 @@ +<resources> + <string-array name="unit_entries"> + <item>@string/units_locale_based</item> + <item>@string/units_metric</item> + <item>@string/units_imperial</item> + <item>@string/units_customary</item> + <item>@string/units_tgm10</item> + <item>@string/units_tgm12</item> + </string-array> + + <string-array name="unit_values"> + <item>locale</item> + <item>metric</item> + <item>imperial</item> + <item>customary</item> + <item>tgm10</item> + <item>tgm12</item> + </string-array> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 19afd0e9e16147ad1463025d7342a239b206452a..a32435ac389fb8240427984e8b571754f5b7f833 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -27,7 +27,37 @@ Cannot obtain current location <string name="no_departures">No departures</string> <string name="waiting_position">waiting for position</string> <string name="vehicle_headsign">%1$s » %2$s</string> - <string name="speed_in_km_per_h">%1$.3f km/h</string> + <string name="time_in_s">%1$s s</string> + <plurals name="time_in_s_cd"> + <item quantity="one">%1$d second</item> + <item quantity="other">%1$d seconds</item> + </plurals> + <string name="time_in_tm">%1$s %2$sTm</string> + <string name="time_in_tm_past">%1$s %2$sTm ago</string> + <plurals name="time_in_tm_cd"> + <item quantity="one">%1$d tim</item> + <item quantity="other">%1$d tims</item> + </plurals> + <string name="speed_in_km_per_h">%1$s km/h</string> + <string name="speed_in_m_per_s">%1$s m/s</string> + <string name="speed_in_mi_per_h">%1$s mph</string> + <string name="speed_in_vl">%1$s Vl</string> + <plurals name="speed_in_m_per_s_cd"> + <item quantity="one">%1$d meter per second</item> + <item quantity="other">%1$d meters per second</item> + </plurals> + <plurals name="speed_in_km_per_h_cd"> + <item quantity="one">%1$d kilometer per hour</item> + <item quantity="other">%1$d kilometers per hour</item> + </plurals> + <plurals name="speed_in_mi_per_h_cd"> + <item quantity="one">%1$d mile per hour</item> + <item quantity="other">%1$d mile per hour</item> + </plurals> + <plurals name="speed_in_vl_cd"> + <item quantity="one">%1$s vlos</item> + <item quantity="other">%1$s vlos</item> + </plurals> <string name="congestion_unknown">unknown</string> <string name="congestion_smooth">smooth</string> <string name="congestion_stop_and_go">stop and go</string> @@ -136,5 +166,14 @@No more departures <string name="loading">loading…</string> <string name="favourite_deleted">Favourite deleted</string> <string name="undo">Undo</string> + <string name="title_activity_settings">SettingsActivity</string> + <string name="units_title">Unit system</string> + <string name="units_locale_based">Locale based</string> + <string name="units_metric">Metric (SI)</string> + <string name="units_imperial">Imperial (UK)</string> + <string name="units_customary">US Customary</string> + <string name="units_tgm10">TGM (base 10)</string> + <string name="units_tgm12">TGM (base 12)</string> + <string name="title_settings">Settings</string> </resources> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3b2cafc7373811872ce3e4dc2a3e795908a0e2a9..c05c82928aa6e68cb4fd603ae8558e5f65972f90 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -10,7 +10,7 @@GPS-Symbol <string name="title_activity_results">Ergebnisse</string> <string name="cont">Fortsetzen</string> <string name="vehicle_headsign">%1$s » %2$s</string> - <string name="speed_in_km_per_h">%1$.3f km/h</string> + <string name="speed_in_km_per_h">%1$s km/h</string> <string name="congestion_unknown">unbekannt</string> <string name="congestion_congestion">Stau</string> <string name="occupancy_unknown">unbekannt</string> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 4b8162ad24cae62e539542f5747503263a3424a7..a7ad7ccc620202f1698b8fa2859f47bb1c5276ce 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -27,7 +27,7 @@Nessune partenze <string name="waiting_position">In attesa della posizione</string> <string name="vehicle_headsign">%1$s » %2$s</string> <string name="vehicle_headsign_content_description">%1$s verso %2$s</string> - <string name="speed_in_km_per_h">%1$.3f km/h</string> + <string name="speed_in_km_per_h">%1$s km/h</string> <string name="congestion_unknown">sconosciuta</string> <string name="congestion_smooth">fluido</string> <string name="congestion_stop_and_go">fermarsi e andare</string> diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 541705d81a56a03233dbe01c4f898b4090b4da74..1f933108d6c66a0dabfa1dd07ac8ddf53f05fc9f 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -27,7 +27,7 @@Brak odjazdów <string name="waiting_position">oczekiwanie na pozycję</string> <string name="vehicle_headsign">%1$s » %2$s</string> <string name="vehicle_headsign_content_description">%1$s w kierunku przystanku %2$s</string> - <string name="speed_in_km_per_h">%1$.3f km/h</string> + <string name="speed_in_km_per_h">%1$s km/h</string> <string name="congestion_unknown">nieznane</string> <string name="congestion_smooth">płynne</string> <string name="congestion_stop_and_go">przestoje</string> diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml new file mode 100644 index 0000000000000000000000000000000000000000..25c5c6c8cb3b58a4c1a14b613141d5004941b2ab --- /dev/null +++ b/app/src/main/res/xml/root_preferences.xml @@ -0,0 +1,10 @@ +<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"> + <ListPreference + app:icon="@drawable/units" + app:defaultValue="default" + app:entries="@array/unit_entries" + app:entryValues="@array/unit_values" + app:key="unit_system" + app:title="@string/units_title" + app:useSimpleSummaryProvider="true" /> +</PreferenceScreen> \ No newline at end of file