Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
239 lines
10 KiB
Kotlin
239 lines
10 KiB
Kotlin
package com.example.shiftalarm
|
|
|
|
import android.content.Context
|
|
import android.content.res.ColorStateList
|
|
import android.graphics.Color
|
|
import android.view.LayoutInflater
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import android.widget.ImageView
|
|
import android.widget.TextView
|
|
import androidx.core.content.ContextCompat
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import java.time.LocalDate
|
|
|
|
data class DayShift(
|
|
val date: LocalDate?,
|
|
val shift: String?,
|
|
val hasMemo: Boolean = false,
|
|
val memoContent: String? = null
|
|
)
|
|
|
|
class CalendarAdapter(
|
|
var days: List<DayShift>,
|
|
private val listener: OnDayClickListener,
|
|
var showHolidays: Boolean = true
|
|
) : RecyclerView.Adapter<CalendarAdapter.ViewHolder>() {
|
|
|
|
interface OnDayClickListener {
|
|
fun onDayClick(date: LocalDate, currentShift: String)
|
|
}
|
|
|
|
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
|
val root: View = view.findViewById(R.id.dayRoot)
|
|
val dayNumber: TextView = view.findViewById(R.id.dayNumber)
|
|
val shiftChar: TextView = view.findViewById(R.id.shiftChar)
|
|
val holidayNameSmall: TextView = view.findViewById(R.id.holidayNameSmall)
|
|
val memoIndicator: ImageView = view.findViewById(R.id.memoIndicator)
|
|
val tvTide: TextView = view.findViewById(R.id.tvTide)
|
|
val memoContent: TextView = view.findViewById(R.id.memoContent)
|
|
}
|
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
|
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_day, parent, false)
|
|
return ViewHolder(view)
|
|
}
|
|
|
|
private fun dpToPx(context: Context, dp: Float): Int {
|
|
return (dp * context.resources.displayMetrics.density).toInt()
|
|
}
|
|
|
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
|
val item = days[position]
|
|
val context = holder.itemView.context
|
|
|
|
if (item.date == null) {
|
|
holder.itemView.visibility = View.INVISIBLE
|
|
return
|
|
}
|
|
|
|
holder.itemView.visibility = View.VISIBLE
|
|
|
|
// Day Number
|
|
holder.dayNumber.text = item.date.dayOfMonth.toString()
|
|
|
|
// Holiday / Weekend logic
|
|
val isSunday = item.date.dayOfWeek == java.time.DayOfWeek.SUNDAY
|
|
val isSaturday = item.date.dayOfWeek == java.time.DayOfWeek.SATURDAY
|
|
val fullHolidayName = HolidayManager.getHolidayName(item.date)
|
|
val isToday = item.date == LocalDate.now()
|
|
|
|
// Day Number Color
|
|
if (fullHolidayName != null || isSunday) {
|
|
holder.dayNumber.setTextColor(Color.parseColor("#FF5252"))
|
|
} else if (isSaturday) {
|
|
holder.dayNumber.setTextColor(Color.parseColor("#448AFF"))
|
|
} else {
|
|
holder.dayNumber.setTextColor(ContextCompat.getColor(context, R.color.text_primary))
|
|
}
|
|
|
|
// Tide Display
|
|
val prefs = context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
|
val showTide = prefs.getBoolean("show_tide", false)
|
|
val tideLocation = prefs.getString("selected_tide_location", "군산") ?: "군산"
|
|
|
|
if (showTide) {
|
|
val tide = HolidayManager.getTide(item.date, tideLocation)
|
|
if (tide.isNotEmpty()) {
|
|
holder.tvTide.visibility = View.VISIBLE
|
|
holder.tvTide.text = tide
|
|
} else {
|
|
holder.tvTide.visibility = View.GONE
|
|
}
|
|
} else {
|
|
holder.tvTide.visibility = View.GONE
|
|
}
|
|
|
|
|
|
// --- Shift & Holiday Display Logic ---
|
|
holder.shiftChar.background = null
|
|
holder.shiftChar.text = ""
|
|
holder.holidayNameSmall.visibility = View.GONE
|
|
holder.shiftChar.textSize = 15f
|
|
|
|
// "반월", "반년" (Half-Monthly, Half-Yearly) Special Logic
|
|
// These are overrides or specific shifts that user sets.
|
|
// User requested: "월", "년" text. Half-filled background (Red + Transparent).
|
|
// Check exact string or "startswith" if logic changed?
|
|
// Logic in adapter `getShift` might return "반월", "반년".
|
|
|
|
if (showHolidays && fullHolidayName != null) {
|
|
// Holiday Mode (Priority): Show full holiday name, no circle
|
|
holder.shiftChar.text = fullHolidayName
|
|
holder.shiftChar.setTextColor(Color.parseColor("#FF5252"))
|
|
holder.shiftChar.textSize = 11f
|
|
holder.shiftChar.background = null
|
|
} else if (item.shift != null && item.shift != "비번") {
|
|
// Shift Mode
|
|
|
|
// Handle specific "Half" cases first
|
|
if (item.shift == "반월" || item.shift == "반년") {
|
|
holder.shiftChar.text = if (item.shift == "반월") "월" else "년"
|
|
holder.shiftChar.setTextColor(ContextCompat.getColor(context, R.color.black)) // Black for contrast on Half Red/Transparent
|
|
holder.shiftChar.textSize = 15f
|
|
holder.shiftChar.background = ContextCompat.getDrawable(context, R.drawable.bg_shift_half_red)
|
|
} else {
|
|
// Standard Logic
|
|
val shiftAbbreviation = when (item.shift) {
|
|
"주간" -> "주"
|
|
"석간" -> "석"
|
|
"야간" -> "야"
|
|
"주간 맞교대" -> "주맞"
|
|
"야간 맞교대" -> "야맞"
|
|
"휴무", "휴가" -> "휴"
|
|
"월차" -> "월"
|
|
"연차" -> "연"
|
|
"교육" -> "교"
|
|
else -> item.shift.take(1)
|
|
}
|
|
holder.shiftChar.text = shiftAbbreviation
|
|
holder.shiftChar.textSize = 17f
|
|
holder.shiftChar.setTypeface(null, android.graphics.Typeface.BOLD)
|
|
|
|
val shiftColorRes = when (item.shift) {
|
|
"주간" -> R.color.shift_lemon
|
|
"석간" -> R.color.shift_seok
|
|
"야간" -> R.color.shift_ya
|
|
"주간 맞교대" -> R.color.shift_jumat
|
|
"야간 맞교대" -> R.color.shift_yamat
|
|
"휴무", "휴가", "월차", "연차" -> R.color.shift_red
|
|
"교육" -> R.color.primary
|
|
else -> R.color.text_secondary
|
|
}
|
|
val shiftColor = ContextCompat.getColor(context, shiftColorRes)
|
|
|
|
if (isToday) {
|
|
// Today: Solid Circle
|
|
val background = ContextCompat.getDrawable(context, R.drawable.bg_shift_solid_v4) as? android.graphics.drawable.GradientDrawable
|
|
background?.setColor(shiftColor)
|
|
holder.shiftChar.background = background
|
|
holder.shiftChar.backgroundTintList = null
|
|
|
|
if (item.shift == "주간" || item.shift == "석간") {
|
|
holder.shiftChar.setTextColor(ContextCompat.getColor(context, R.color.black))
|
|
} else {
|
|
holder.shiftChar.setTextColor(Color.WHITE)
|
|
}
|
|
} else {
|
|
// Not Today: Stroke Circle
|
|
val background = ContextCompat.getDrawable(context, R.drawable.bg_shift_stroke_v4) as? android.graphics.drawable.GradientDrawable
|
|
background?.setStroke(dpToPx(context, 1.5f), shiftColor)
|
|
background?.setColor(Color.TRANSPARENT)
|
|
|
|
holder.shiftChar.background = background
|
|
holder.shiftChar.backgroundTintList = null
|
|
|
|
holder.shiftChar.setTextColor(shiftColor)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Lunar date small display if requested or just default
|
|
if (!showHolidays && fullHolidayName != null) {
|
|
holder.holidayNameSmall.visibility = View.VISIBLE
|
|
holder.holidayNameSmall.text = fullHolidayName
|
|
} else {
|
|
// Ensure visibility GONE if not needed (e.g. standard day)
|
|
holder.holidayNameSmall.visibility = View.GONE
|
|
}
|
|
|
|
// Double check: if showHolidays=true (Holiday mode), we handled it at top block.
|
|
// But if showHolidays=true and NO holiday, we show lunar date?
|
|
// User asked: "Overlap date and holiday text".
|
|
// My item_day.xml has holidayNameSmall at bottom now.
|
|
// If showHolidays=true, CalendarAdapter usually HIDES shiftChar and shows Holiday Name?
|
|
// Wait, standard logic (lines 84-91 above):
|
|
// If showHolidays && fullHolidayName != null -> shiftChar shows Name.
|
|
// If showHolidays && fullHolidayName == null -> shiftChar shows LUNAR DATE? (Old logic had this).
|
|
|
|
if (showHolidays && fullHolidayName == null) {
|
|
// Show Lunar Date in shiftChar instead of empty?
|
|
// Or shiftChar is empty, show small text?
|
|
// Previous code:
|
|
// holder.shiftChar.text = ""
|
|
// holder.holidayNameSmall.visibility = View.VISIBLE
|
|
// holder.holidayNameSmall.text = HolidayManager.getLunarDateString(item.date)
|
|
|
|
holder.shiftChar.text = HolidayManager.getLunarDateString(item.date)
|
|
holder.shiftChar.textSize = 11f
|
|
holder.shiftChar.setTextColor(ContextCompat.getColor(context, R.color.text_tertiary))
|
|
holder.shiftChar.background = null
|
|
}
|
|
|
|
|
|
// Memo Indicator
|
|
holder.memoIndicator.visibility = View.GONE // Hide indicator, showing text instead
|
|
if (item.hasMemo && !item.memoContent.isNullOrEmpty()) {
|
|
holder.memoContent.visibility = View.VISIBLE
|
|
holder.memoContent.text = item.memoContent
|
|
} else {
|
|
holder.memoContent.visibility = View.GONE
|
|
}
|
|
|
|
// Today Border or Highlight
|
|
if (isToday) {
|
|
holder.root.setBackgroundResource(R.drawable.bg_grid_cell_today_v4)
|
|
} else {
|
|
holder.root.setBackgroundResource(R.drawable.bg_grid_cell_v4)
|
|
}
|
|
|
|
holder.itemView.setOnClickListener {
|
|
if (item.date != null && item.shift != null) {
|
|
listener.onDayClick(item.date, item.shift)
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun getItemCount(): Int = days.size
|
|
}
|