feat: Rework the event card UI significantly

This commit is contained in:
Peter Vacho 2024-12-30 18:56:10 +01:00
parent 58957c1792
commit fb3d7785ba
Signed by: school
GPG key ID: 8CFC3837052871B4
13 changed files with 262 additions and 66 deletions

View file

@ -2,7 +2,6 @@ package com.p_vacho.neat_calendar.activities
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.ImageButton
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
@ -14,12 +13,11 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.lifecycle.lifecycleScope
import com.p_vacho.neat_calendar.MyApplication
import com.p_vacho.neat_calendar.R
import com.p_vacho.neat_calendar.adapters.CalendarAdapter
import com.p_vacho.neat_calendar.adapters.CalendarDayItemAdapter
import com.p_vacho.neat_calendar.api.RetrofitClient
import com.p_vacho.neat_calendar.api.models.EventResponse
import com.p_vacho.neat_calendar.models.CalendarDay
import kotlinx.coroutines.launch
import java.time.ZoneOffset
import java.time.YearMonth
import java.time.ZoneId
import java.time.format.TextStyle
@ -152,7 +150,7 @@ class CalendarActivity : AppCompatActivity() {
// Set up RecyclerView
rvCalendar.layoutManager = GridLayoutManager(this@CalendarActivity, 7) // 7 columns for days of the week
rvCalendar.adapter = CalendarAdapter(days) { day -> navigateToDayActivity(day) }
rvCalendar.adapter = CalendarDayItemAdapter(days) { day -> navigateToDayActivity(day) }
}
}

View file

@ -1,7 +1,6 @@
package com.p_vacho.neat_calendar.activities
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
@ -10,8 +9,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.p_vacho.neat_calendar.R
import com.p_vacho.neat_calendar.adapters.EventAdapter
import com.p_vacho.neat_calendar.api.models.EventResponse
import com.p_vacho.neat_calendar.adapters.EventCardAdapter
import com.p_vacho.neat_calendar.models.CalendarDay
class DayViewActivity : AppCompatActivity() {
@ -31,12 +29,13 @@ class DayViewActivity : AppCompatActivity() {
@Suppress("DEPRECATION")
val calendarDay = intent.getParcelableExtra<CalendarDay>("calendarDay")!!
// Setup the UI
val tvDate: TextView = findViewById(R.id.tvDate)
val rvEvents: RecyclerView = findViewById(R.id.rvEvents)
// Set up UI
tvDate.text = calendarDay.date.toString()
rvEvents.layoutManager = LinearLayoutManager(this)
rvEvents.adapter = EventAdapter(calendarDay.events)
rvEvents.adapter = EventCardAdapter(calendarDay.events)
}
}

View file

@ -8,8 +8,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.p_vacho.neat_calendar.R
import com.p_vacho.neat_calendar.models.CalendarDay
class CalendarAdapter(private val days: List<CalendarDay?>, private val onDayClicked: (CalendarDay) -> Unit) :
RecyclerView.Adapter<CalendarAdapter.DayViewHolder>() {
class CalendarDayItemAdapter(private val days: List<CalendarDay?>, private val onDayClicked: (CalendarDay) -> Unit) :
RecyclerView.Adapter<CalendarDayItemAdapter.DayViewHolder>() {
class DayViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvDay: TextView = itemView.findViewById(R.id.tvDay)

View file

@ -0,0 +1,32 @@
package com.p_vacho.neat_calendar.adapters
import android.graphics.Color
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.p_vacho.neat_calendar.R
import androidx.core.content.ContextCompat
class CategoryChipAdapter(private val categories: List<CategoryItem>) :
RecyclerView.Adapter<CategoryChipAdapter.CategoryViewHolder>() {
data class CategoryItem(val id: Int, val name: String, val color: Color)
inner class CategoryViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_category_chip, parent, false) as TextView
return CategoryViewHolder(view)
}
override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
val category = categories[position]
holder.textView.text = category.name
holder.textView.backgroundTintList = android.content.res.ColorStateList.valueOf(category.color.toArgb())
}
override fun getItemCount(): Int = categories.size
}

View file

@ -1,32 +0,0 @@
package com.p_vacho.neat_calendar.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.p_vacho.neat_calendar.R
import com.p_vacho.neat_calendar.api.models.EventResponse
class EventAdapter(private val events: List<EventResponse>) :
RecyclerView.Adapter<EventAdapter.EventViewHolder>() {
class EventViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvTitle: TextView = itemView.findViewById(R.id.tvTitle)
val tvDescription: TextView = itemView.findViewById(R.id.tvDescription)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_event, parent, false)
return EventViewHolder(view)
}
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
val event = events[position]
holder.tvTitle.text = event.title
holder.tvDescription.text = event.description
}
override fun getItemCount(): Int = events.size
}

View file

@ -0,0 +1,93 @@
package com.p_vacho.neat_calendar.adapters
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.p_vacho.neat_calendar.R
import com.p_vacho.neat_calendar.api.models.EventResponse
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.Locale
class EventCardAdapter(private val events: List<EventResponse>) :
RecyclerView.Adapter<EventCardAdapter.EventViewHolder>() {
inner class EventViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val tvTitle: TextView = view.findViewById(R.id.tvTitle)
val tvTime: TextView = view.findViewById(R.id.tvTime)
val tvDescription: TextView = view.findViewById(R.id.tvDescription)
val rvCategories: RecyclerView = view.findViewById(R.id.categoryChipRecyclerView)
val eventColorIndicator: View = view.findViewById(R.id.eventColorIndicator)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_event_card, parent, false)
return EventViewHolder(view)
}
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
val event = events[position]
// Set event details
holder.tvTitle.text = event.title
holder.tvTime.text = formatEventTime(event.start_time, event.end_time)
holder.eventColorIndicator.setBackgroundColor(event.color.toArgb())
// Handle empty description
if (event.description.isEmpty()) {
holder.tvDescription.visibility = View.GONE
} else {
holder.tvDescription.visibility = View.VISIBLE
holder.tvDescription.text = event.description
}
// TODO: Fetch the categoreis for this event from the API
val categories = listOf(
CategoryChipAdapter.CategoryItem(id = 1, name = "Work", color = Color.valueOf(Color.parseColor("#33A733"))), // Red
CategoryChipAdapter.CategoryItem(id = 2, name = "Personal", color = Color.valueOf(Color.parseColor("#3498DB"))), // Blue
CategoryChipAdapter.CategoryItem(id = 3, name = "Urgent", color = Color.valueOf(Color.parseColor("#E74C3C"))), // Bright Red
CategoryChipAdapter.CategoryItem(id = 4, name = "Fitness", color = Color.valueOf(Color.parseColor("#FFB400"))), // Orange
CategoryChipAdapter.CategoryItem(id = 5, name = "Travel", color = Color.valueOf(Color.parseColor("#7D3C98"))), // Purple
CategoryChipAdapter.CategoryItem(id = 6, name = "Shopping", color = Color.valueOf(Color.parseColor("#85C1E9"))), // Light Blue
CategoryChipAdapter.CategoryItem(id = 7, name = "Finance", color = Color.valueOf(Color.parseColor("#2ECC71"))), // Bright Green
CategoryChipAdapter.CategoryItem(id = 8, name = "Health", color = Color.valueOf(Color.parseColor("#F39C12"))), // Gold
CategoryChipAdapter.CategoryItem(id = 9, name = "Meetings", color = Color.valueOf(Color.parseColor("#C0392B"))), // Crimson
CategoryChipAdapter.CategoryItem(id = 10, name = "Hobbies", color = Color.valueOf(Color.parseColor("#8E44AD"))), // Deep Purple
CategoryChipAdapter.CategoryItem(id = 11, name = "Study", color = Color.valueOf(Color.parseColor("#1ABC9C"))), // Teal
CategoryChipAdapter.CategoryItem(id = 12, name = "Events", color = Color.valueOf(Color.parseColor("#34495E"))), // Navy Blue
)
holder.rvCategories.layoutManager =
LinearLayoutManager(holder.itemView.context, LinearLayoutManager.HORIZONTAL, false)
holder.rvCategories.adapter = CategoryChipAdapter(categories)
}
override fun getItemCount(): Int = events.size
private fun formatEventTime(startTime: OffsetDateTime, endTime: OffsetDateTime): String {
val timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
.withLocale(Locale.getDefault())
val dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.withLocale(Locale.getDefault())
return when {
// Instantaneous event
startTime == endTime -> startTime.format(timeFormatter)
// Cross-day event
startTime.toLocalDate() != endTime.toLocalDate() -> {
"${startTime.format(dateTimeFormatter)} - ${endTime.format(dateTimeFormatter)}"
}
// Same-day event
else -> {
"${startTime.format(timeFormatter)} - ${endTime.format(timeFormatter)}"
}
}
}
}

View file

@ -0,0 +1,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent" />
<stroke android:width="1dp" android:color="?android:attr/textColorPrimary" />
<corners android:radius="16dp" />
</shape>

View file

@ -0,0 +1,19 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Outer Border -->
<item>
<shape android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<stroke
android:width="2dp"
android:color="?attr/colorOutline" />
<corners android:radius="8dp" />
</shape>
</item>
<!-- Inner Background -->
<item android:left="2dp" android:top="2dp" android:right="2dp" android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="?android:attr/colorBackground" />
<corners android:radius="6dp" />
</shape>
</item>
</layer-list>

View file

@ -22,5 +22,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvEvents"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/item_event_card"
tools:itemCount="5"
tools:visibility="visible" />
</LinearLayout>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:background="@drawable/bg_category_chip"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:textSize="12sp"
android:textColor="?android:attr/textColorPrimary"
android:backgroundTint="@android:color/holo_blue_dark"
tools:text="Work" />

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:paddingTop="4dp" />
</LinearLayout>

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp"
app:cardBackgroundColor="?android:attr/colorBackground"
android:background="@drawable/card_border">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- Event Title Row -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<!-- Event Color Indicator -->
<View
android:id="@+id/eventColorIndicator"
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:background="@color/event_indicator_color"
android:contentDescription="@string/event_color_indicator" />
<!-- Event Title -->
<TextView
android:id="@+id/tvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="?android:attr/textColorPrimary"
android:ellipsize="end"
android:maxLines="1"
tools:text="Event Title" />
</LinearLayout>
<!-- Event Time -->
<TextView
android:id="@+id/tvTime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:paddingTop="4dp"
tools:text="10:00 AM - 11:00 AM" />
<!-- Event Description -->
<TextView
android:id="@+id/tvDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:paddingTop="8dp"
android:ellipsize="end"
android:maxLines="2"
tools:text="This is a sample description for the event. It gives an overview of what the event is about." />
<!-- Categories Section -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/categoryChipRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
tools:listitem="@layout/item_category_chip"
tools:itemCount="3"
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:orientation="horizontal" />
</LinearLayout>
</androidx.cardview.widget.CardView>

View file

@ -25,4 +25,5 @@
<string name="friday_short">Fr</string>
<string name="saturday_short">Sa</string>
<string name="sunday_short">Su</string>
<string name="event_color_indicator">Event Color Indicator</string>
</resources>