Author: Adam Evyčędo <git@apiote.xyz>
fix display of alerts and departures updates progress bar
%!v(PANIC=String method: strings: negative Repeat count)
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 7b334c3d4efecdfa7b18e8d98c1de5bb9d8e1e6c..498c142721249c64f9bc154b7c78a7e3b5760618 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 @@ -14,12 +14,14 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.widget.TooltipCompat import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -33,15 +35,16 @@ import org.osmdroid.views.overlay.TilesOverlay import org.osmdroid.views.overlay.gestures.RotationGestureOverlay import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.dpToPixelI +import xyz.apiote.bimba.czwek.repo.Alert import xyz.apiote.bimba.czwek.repo.CongestionLevel 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 java.time.ZoneId import java.time.ZonedDateTime - -class BimbaDepartureViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { +class BimbaDepartureViewHolder(itemView: View) : ViewHolder(itemView) { val root: View = itemView.findViewById(R.id.departure) val lineIcon: ImageView = itemView.findViewById(R.id.line_icon) val departureTime: TextView = itemView.findViewById(R.id.departure_time) @@ -75,85 +78,151 @@ } } } +class BimbaAlertViewHolder(itemView: View) : ViewHolder(itemView) { + val root: View = itemView.findViewById(R.id.alerts) + val text: TextView = itemView.findViewById(R.id.alerts_text) + val moreButton: Button = itemView.findViewById(R.id.more_button) + + companion object { + fun bind( + alerts: List<Alert>, + holder: BimbaAlertViewHolder?, + context: Context? + ) { + val alertDescriptions = alerts.map { it.description }.filter { it != "" } + .joinToString(separator = "\n") + holder?.moreButton?.setOnClickListener{ + MaterialAlertDialogBuilder(context!!) + .setTitle("Alerts") + .setPositiveButton(R.string.ok) { _, _ -> } + .setMessage(alertDescriptions) + .show() + } + holder?.moreButton?.visibility = if (alertDescriptions == "") View.GONE else View.VISIBLE + holder?.text?.text = alerts.map { + it.header.ifEmpty { + context!!.getString(R.string.alert_header) + } + }.toSet().joinToString(separator = "\n") + } + } +} + class BimbaDeparturesAdapter( private val inflater: LayoutInflater, private val context: Context?, - private var departures: List<Departure>, + private var items: List<DepartureItem>, private val onClickListener: ((Departure) -> Unit) ) : - RecyclerView.Adapter<BimbaDepartureViewHolder>() { + RecyclerView.Adapter<ViewHolder>() { var lastUpdate: ZonedDateTime = ZonedDateTime.of(0, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()) private set private var showAsTime: Boolean = false inner class DiffUtilCallback( - private val oldDepartures: List<Departure>, - private val newDepartures: List<Departure>, - private val showAsTimeChanged: Boolean + private val oldDepartures: List<DepartureItem>, + private val newDepartures: List<DepartureItem>, + private val showAsTimeChanged: Boolean, ) : DiffUtil.Callback() { override fun getOldListSize() = oldDepartures.size override fun getNewListSize() = newDepartures.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = - oldDepartures[oldItemPosition].ID == newDepartures[newItemPosition].ID + (oldDepartures[oldItemPosition].departure?.ID + ?: "alert") == (newDepartures[newItemPosition].departure?.ID ?: "alert") override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { val oldDeparture = oldDepartures[oldItemPosition] val newDeparture = newDepartures[newItemPosition] - return oldDeparture.vehicle.Line == newDeparture.vehicle.Line && oldDeparture.vehicle.Headsign == newDeparture.vehicle.Headsign && - oldDeparture.statusText(context, false, lastUpdate) == newDeparture.statusText( - context, + return if (oldDeparture.departure != null && newDeparture.departure != null) { + oldDeparture.departure.vehicle.Line == newDeparture.departure.vehicle.Line && + oldDeparture.departure.vehicle.Headsign == newDeparture.departure.vehicle.Headsign && + oldDeparture.departure.statusText( + context, + false, + lastUpdate + ) == newDeparture.departure.statusText(context, false) && !showAsTimeChanged + } else if (oldDeparture.alert.isNotEmpty() && newDeparture.alert.isEmpty()) { + oldDeparture.alert == newDeparture.alert + } else { false - ) && !showAsTimeChanged + } } } private var departuresPositions: MutableMap<String, Int> = HashMap() init { - departures.forEachIndexed { i, departure -> - departuresPositions[departure.ID] = i + items.forEachIndexed { i, departure -> + departuresPositions[departure.departure?.ID ?: "alert"] = i } } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BimbaDepartureViewHolder { - val rowView = inflater.inflate(R.layout.departure, parent, false) - return BimbaDepartureViewHolder(rowView) + override fun getItemViewType(position: Int): Int { + return if (items[position].departure != null) { + 0 + } else { + 1 + } } - override fun onBindViewHolder(holder: BimbaDepartureViewHolder, position: Int) { - BimbaDepartureViewHolder.bind( - departures[position], - holder, - context, - showAsTime, - onClickListener - ) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + if (viewType == 0) { + val rowView = inflater.inflate(R.layout.departure, parent, false) + return BimbaDepartureViewHolder(rowView) + } else { + val rowView = inflater.inflate(R.layout.alert, parent, false) + return BimbaAlertViewHolder(rowView) + } + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + if (holder is BimbaDepartureViewHolder) { + BimbaDepartureViewHolder.bind( + items[position].departure!!, + holder, + context, + showAsTime, + onClickListener + ) + } else { + BimbaAlertViewHolder.bind(items[position].alert, holder as BimbaAlertViewHolder, context) + } } - override fun getItemCount(): Int = departures.size + override fun getItemCount(): Int = items.size - fun get(id: String): Departure? { + fun get(id: String): DepartureItem? { val position = departuresPositions[id] return if (position == null) { null } else { - departures[position] + items[position] } } - fun update(departures: List<Departure>, showAsTime: Boolean, areNewObserved: Boolean = false) { + fun update( + departures: List<DepartureItem>, + showAsTime: Boolean, + areNewObserved: Boolean = false + ) { val newPositions: MutableMap<String, Int> = HashMap() departures.forEachIndexed { i, departure -> - newPositions[departure.ID] = i + newPositions[departure.departure?.ID ?: "alert"] = i } - val diff = DiffUtil.calculateDiff(DiffUtilCallback(this.departures, departures, this.showAsTime != showAsTime)) + val diff = DiffUtil.calculateDiff( + DiffUtilCallback( + this.items, + departures, + this.showAsTime != showAsTime + ) + ) this.showAsTime = showAsTime - this.departures = departures + this.items = departures departuresPositions = newPositions if (areNewObserved) { lastUpdate = ZonedDateTime.now() @@ -162,7 +231,7 @@ diff.dispatchUpdatesTo(this) } fun refreshItems() { - update(this.departures, showAsTime) + update(this.items, showAsTime) } } 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 0f51af73cb0cdf09a9ec701a42a8192b6e230560..be0393f6b640339f4117d8582a6f3826c5027550 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 @@ -16,10 +16,14 @@ import android.os.CountDownTimer import android.text.format.DateUtils import android.text.format.DateUtils.MINUTE_IN_MILLIS import android.view.View +import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.res.ResourcesCompat +import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -31,7 +35,7 @@ import com.google.android.material.timepicker.TimeFormat import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.api.Error import xyz.apiote.bimba.czwek.databinding.ActivityDeparturesBinding -import xyz.apiote.bimba.czwek.repo.Departure +import xyz.apiote.bimba.czwek.repo.DepartureItem import xyz.apiote.bimba.czwek.repo.Stop import xyz.apiote.bimba.czwek.units.Millisecond import xyz.apiote.bimba.czwek.units.Second @@ -62,22 +66,36 @@ private val linesFilterTemporary = mutableMapOf() private var alertDescriptions: String = "" // TODO [elizabeth] millisInFuture from header Cache-Control max-age - private val countdown = object : CountDownTimer(Millisecond(Second(30)).millis, Millisecond(Tim(1)).millis) { - override fun onTick(millisUntilFinished: Long) { - val timsUntillFinished = Tim(Millisecond(millisUntilFinished)) - binding.departuresUpdatesProgress.progress = timsUntillFinished.tims - } + private val countdown = + object : CountDownTimer(Millisecond(Second(30)).millis, Millisecond(Tim(1)).millis) { + override fun onTick(millisUntilFinished: Long) { + val timsUntillFinished = Tim(Millisecond(millisUntilFinished)) + binding.departuresUpdatesProgress.progress = timsUntillFinished.tims + } - override fun onFinish() { - getDepartures() + override fun onFinish() { + getDepartures() + } } - } override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() super.onCreate(savedInstanceState) _binding = ActivityDeparturesBinding.inflate(layoutInflater) setContentView(binding.root) + // TODO camera inset + ViewCompat.setOnApplyWindowInsetsListener(binding.departuresRecycler) { v, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updatePadding(bottom = insets.bottom) + WindowInsetsCompat.CONSUMED + } + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updatePadding(right = insets.right, left = insets.left) + windowInsets + } + viewModel = ViewModelProvider(this)[DeparturesViewModel::class.java] getLine()?.let { viewModel.linesFilter[it] = true @@ -94,41 +112,23 @@ linesFilterTemporary.forEach { viewModel.linesFilter[it.key] = it.value } getDepartures() } - binding.moreButton.setOnClickListener { - MaterialAlertDialogBuilder(this) - .setTitle("Alerts") - .setPositiveButton(R.string.ok) { _, _ -> } - .setMessage(alertDescriptions) - .show() - } - viewModel.departures.observe(this) { stopDepartures -> + val items = mutableListOf<DepartureItem>() if (stopDepartures.alerts.isNotEmpty()) { - binding.alerts.visibility = View.VISIBLE - binding.alertsText.text = stopDepartures.alerts.map { - it.header.ifEmpty { - getString(R.string.alert_header) - } - }.toSet().joinToString(separator = "\n") - alertDescriptions = stopDepartures.alerts.map { it.description }.filter { it != "" } - .joinToString(separator = "\n") - binding.moreButton.visibility = if (alertDescriptions == "") View.GONE else View.VISIBLE - - } else { - binding.alerts.visibility = View.GONE + items.add(DepartureItem(stopDepartures.alerts)) } - updateItems( - stopDepartures.departures - .filter { d -> - viewModel.linesFilter.values.all { !it } or (viewModel.linesFilter[d.vehicle.Line.name] ?: false) - } - .filter { d -> - val t = LocalTime.of(d.time.Hour.toInt(), d.time.Minute.toInt()) - t >= viewModel.startTime && t <= viewModel.endTime - }, - stopDepartures.stop - ) - viewModel.openBottomSheet?.departureID()?.let { adapter.get(it) }?.let { viewModel.openBottomSheet?.update(it) } + items.addAll(stopDepartures.departures + .filter { d -> + viewModel.linesFilter.values.all { !it } or (viewModel.linesFilter[d.vehicle.Line.name] + ?: false) + } + .filter { d -> + val t = LocalTime.of(d.time.Hour.toInt(), d.time.Minute.toInt()) + t >= viewModel.startTime && t <= viewModel.endTime + }.map { DepartureItem(it) }) + updateItems(items, stopDepartures.stop) + viewModel.openBottomSheet?.departureID()?.let { adapter.get(it) } + ?.let { it.departure?.let { departure -> viewModel.openBottomSheet?.update(departure) } } val lines = stopDepartures.departures.map { it.vehicle.Line.name }.sortedWith { s1, s2 -> val s1n = s1.toIntOrNull() @@ -185,7 +185,9 @@ } R.id.departures_filter_byline -> { linesFilterTemporary.clear() - viewModel.linesFilter.forEach { filter -> linesFilterTemporary[filter.key] = filter.value } + viewModel.linesFilter.forEach { filter -> + linesFilterTemporary[filter.key] = filter.value + } linePicker?.show() true } @@ -245,10 +247,10 @@ } } ) adapter = BimbaDeparturesAdapter(layoutInflater, this, listOf()) { - DepartureBottomSheet(it).apply { - show(supportFragmentManager, DepartureBottomSheet.TAG) - viewModel.openBottomSheet = this - setOnCancel { viewModel.openBottomSheet = null } + DepartureBottomSheet(it).apply { + show(supportFragmentManager, DepartureBottomSheet.TAG) + viewModel.openBottomSheet = this + setOnCancel { viewModel.openBottomSheet = null } } } binding.departuresRecycler.adapter = adapter @@ -357,13 +359,11 @@ private fun showLoading() { binding.departuresOverlay.visibility = View.VISIBLE binding.departuresProgress.visibility = View.VISIBLE - binding.departuresRecycler.visibility = View.GONE binding.errorImage.visibility = View.GONE binding.errorText.visibility = View.GONE - binding.departuresUpdatesProgress.visibility = View.GONE } - private fun updateItems(departures: List<Departure>, stop: Stop) { + private fun updateItems(departures: List<DepartureItem>, stop: Stop) { setupSnackbar() binding.departuresProgress.visibility = View.GONE // TODO [elizabeth] max, progress from header Cache-Control max-age 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 358a7bbd7ed88d19af4e2e9fdb6a26ff7774cb6f..e90ce512e3b7eba9e635c9514268ee246ddc9db8 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 @@ -22,6 +22,20 @@ import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit + +class DepartureItem { + private constructor(d: Departure?, a: List<Alert>) { + departure = d + alert = a + } + + constructor(d: Departure) : this(d, emptyList()) + constructor(a: List<Alert>) : this(null, a) + + val departure: Departure? + val alert: List<Alert> +} + enum class AlertCause { UNKNOWN, OTHER, TECHNICAL_PROBLEM, STRIKE, DEMONSTRATION, ACCIDENT, HOLIDAY, WEATHER, MAINTENANCE, CONSTRUCTION, POLICE_ACTIVITY, MEDICAL_EMERGENCY; @@ -61,7 +75,7 @@ AlertEffectV1.SIGNIFICANT_DELAYS -> valueOf("SIGNIFICANT_DELAYS") AlertEffectV1.DETOUR -> valueOf("DETOUR") AlertEffectV1.ADDITIONAL_SERVICE -> valueOf("ADDITIONAL_SERVICE") AlertEffectV1.MODIFIED_SERVICE -> valueOf("MODIFIED_SERVICE") - AlertEffectV1.STOP_MOVED -> valueOf("STOP_MOVED") + AlertEffectV1.STOP_MOVED -> valueOf("STOP_MOVED") AlertEffectV1.NONE -> valueOf("NONE") AlertEffectV1.ACCESSIBILITY_ISSUE -> valueOf("ACCESSIBILITY_ISSUE") } @@ -76,7 +90,13 @@ val url: String, val cause: AlertCause, val effect: AlertEffect ) { - constructor(a: AlertV1) : this(a.header, a.Description, a.Url, AlertCause.of(a.Cause), AlertEffect.of(a.Effect)) + constructor(a: AlertV1) : this( + a.header, + a.Description, + a.Url, + AlertCause.of(a.Cause), + AlertEffect.of(a.Effect) + ) } data class StopDepartures( diff --git a/app/src/main/res/layout/activity_departures.xml b/app/src/main/res/layout/activity_departures.xml index c5c97475c1c6fd8e75e36117e95b5c5d0de71570..d4361d70d4f149c2a311b179544e52b0c7994183 100644 --- a/app/src/main/res/layout/activity_departures.xml +++ b/app/src/main/res/layout/activity_departures.xml @@ -13,66 +13,51 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="16dp"> - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/departures_recycler" + <com.google.android.material.appbar.AppBarLayout + android:id="@+id/app_bar_layout" android:layout_width="match_parent" - android:layout_height="match_parent" - android:clipToPadding="false" - android:fitsSystemWindows="true" - android:visibility="gone" - app:layout_behavior="@string/appbar_scrolling_view_behavior" /> + android:layout_height="wrap_content" + android:fitsSystemWindows="true"> - <com.google.android.material.card.MaterialCardView - android:id="@+id/alerts" - android:layout_width="match_parent" - android:layout_height="100dp" - android:backgroundTint="@color/safety" - android:visibility="gone" - app:layout_anchor="@id/app_bar_layout" - app:layout_anchorGravity="bottom"> - - <androidx.constraintlayout.widget.ConstraintLayout + <com.google.android.material.appbar.CollapsingToolbarLayout + android:id="@+id/collapsing_layout" + style="?attr/collapsingToolbarLayoutMediumStyle" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="?attr/collapsingToolbarLayoutMediumSize" + app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" + app:maxLines="2"> - <ImageView - android:id="@+id/imageView" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:importantForAccessibility="no" - android:src="@drawable/warning" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@+id/alerts_text" - app:tint="@color/black" /> + <com.google.android.material.appbar.MaterialToolbar + android:id="@+id/departures_app_bar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:elevation="0dp" + app:layout_collapseMode="pin" + app:menu="@menu/departures_menu" /> - <com.google.android.material.textview.MaterialTextView - android:id="@+id/alerts_text" - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_marginStart="8dp" - android:layout_marginTop="58dp" - android:layout_marginEnd="8dp" - android:ellipsize="end" - android:maxLines="2" - android:textColor="@color/black" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/more_button" - app:layout_constraintStart_toEndOf="@+id/imageView" - app:layout_constraintTop_toTopOf="parent" - tool:text="Warning: Serious blockade on Piastowska towards Wojska Polskiego. Lines 5, 14, 163 diverted. Change for other means of transport, e.g. lines \n\naaaaa" /> + </com.google.android.material.appbar.CollapsingToolbarLayout> - <com.google.android.material.button.MaterialButton - android:id="@+id/more_button" - style="@style/Widget.Material3.Button.TextButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/more" - android:textColor="@color/link" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" /> - </androidx.constraintlayout.widget.ConstraintLayout> - </com.google.android.material.card.MaterialCardView> + <com.google.android.material.progressindicator.LinearProgressIndicator + android:id="@+id/departures_updates_progress" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="4dp" + android:layout_marginEnd="4dp" + android:indeterminate="true" + android:visibility="gone" /> + + </com.google.android.material.appbar.AppBarLayout> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/departures_recycler" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="8dp" + android:clipToPadding="false" + app:layout_behavior="@string/appbar_scrolling_view_behavior" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/alerts" /> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/departures_overlay" @@ -116,42 +101,5 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/error_image" tool:text="No connection" /> </androidx.constraintlayout.widget.ConstraintLayout> - - <com.google.android.material.appbar.AppBarLayout - android:id="@+id/app_bar_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:fitsSystemWindows="true"> - - <com.google.android.material.appbar.CollapsingToolbarLayout - android:id="@+id/collapsing_layout" - style="?attr/collapsingToolbarLayoutMediumStyle" - android:layout_width="match_parent" - android:layout_height="?attr/collapsingToolbarLayoutMediumSize" - app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" - app:maxLines="2"> - - <com.google.android.material.appbar.MaterialToolbar - android:id="@+id/departures_app_bar" - android:layout_width="match_parent" - android:layout_height="?attr/actionBarSize" - android:elevation="0dp" - app:layout_collapseMode="pin" - app:menu="@menu/departures_menu" /> - - </com.google.android.material.appbar.CollapsingToolbarLayout> - - </com.google.android.material.appbar.AppBarLayout> - - <com.google.android.material.progressindicator.LinearProgressIndicator - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:id="@+id/departures_updates_progress" - app:layout_anchor="@id/app_bar_layout" - app:layout_anchorGravity="bottom" - android:visibility="gone" - android:indeterminate="true" - android:layout_marginEnd="4dp" - android:layout_marginStart="4dp" /> </androidx.coordinatorlayout.widget.CoordinatorLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/alert.xml b/app/src/main/res/layout/alert.xml new file mode 100644 index 0000000000000000000000000000000000000000..643fd32e34735a592742d869500910aa13314b87 --- /dev/null +++ b/app/src/main/res/layout/alert.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tool="http://schemas.android.com/tools" + android:id="@+id/alerts" + android:layout_width="match_parent" + android:layout_height="64dp" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:backgroundTint="@color/safety"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ImageView + android:id="@+id/imageView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:importantForAccessibility="no" + android:src="@drawable/warning" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/alerts_text" + app:tint="@color/black" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/alerts_text" + tool:text="Warning: Serious blockade on Piastowska towards Wojska Polskiego. Lines 5, 14, 163 diverted. Change for other means of transport, e.g. lines \n\naaaaa" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:ellipsize="end" + android:maxLines="2" + android:textColor="@color/black" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/more_button" + app:layout_constraintStart_toEndOf="@+id/imageView" + app:layout_constraintTop_toTopOf="parent" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/more_button" + style="@style/Widget.Material3.Button.TextButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/more" + android:textColor="@color/link" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" /> + </androidx.constraintlayout.widget.ConstraintLayout> +</com.google.android.material.card.MaterialCardView> \ No newline at end of file