From d7ce31970457c6ca628e3135cb6abe73c9f13ab0 Mon Sep 17 00:00:00 2001 From: eddy Date: Wed, 14 Oct 2026 20:26:41 +0200 Subject: [PATCH] Hoofdscherm: donker thema + wekkerkaarten met grote tijden (stijl systeemwekker), lege state met instructie (les uit Alarmio) --- .../buschauffeurswekker/MainActivity.kt | 128 ++++++++++-------- app/src/main/res/layout/activity_main.xml | 93 ++++++------- app/src/main/res/layout/item_alarm_card.xml | 50 +++++++ app/src/main/res/layout/item_idle_card.xml | 35 +++++ app/src/main/res/values/colors.xml | 13 +- app/src/main/res/values/themes.xml | 9 +- 6 files changed, 210 insertions(+), 118 deletions(-) create mode 100644 app/src/main/res/layout/item_alarm_card.xml create mode 100644 app/src/main/res/layout/item_idle_card.xml diff --git a/app/src/main/java/nl/eddydevink/buschauffeurswekker/MainActivity.kt b/app/src/main/java/nl/eddydevink/buschauffeurswekker/MainActivity.kt index 9d4e98a..400d828 100644 --- a/app/src/main/java/nl/eddydevink/buschauffeurswekker/MainActivity.kt +++ b/app/src/main/java/nl/eddydevink/buschauffeurswekker/MainActivity.kt @@ -5,39 +5,33 @@ import android.content.Intent import android.content.pm.PackageManager import android.os.Build import android.os.Bundle -import android.os.Handler -import android.os.Looper +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import android.widget.PopupMenu import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope -import com.google.android.material.button.MaterialButton import kotlinx.coroutines.launch import nl.eddydevink.buschauffeurswekker.core.AlarmScheduler import nl.eddydevink.buschauffeurswekker.core.Prefs import nl.eddydevink.buschauffeurswekker.core.ShiftCheck import nl.eddydevink.buschauffeurswekker.core.ShiftPlanner +import java.time.LocalDate import java.time.LocalDateTime -import java.time.LocalTime +import java.time.ZoneId import java.time.format.DateTimeFormatter +import java.util.Locale /** - * Hoofdscherm: klok + de geplande wekkers van morgen. Instellingen zitten in - * [SettingsActivity]; de testwekker is alleen bereikbaar via launch-intent - * (--ez test true), niet via de productie-UI. + * Hoofdscherm in de stijl van de systeem-wekker-app: donker thema, één kaart + * per wekker met grote tijd, dag-label en naam. Acties zitten achter het ⋮-menu. */ class MainActivity : AppCompatActivity() { private lateinit var prefs: Prefs - private val handler = Handler(Looper.getMainLooper()) - - private val clockTick = object : Runnable { - override fun run() { - updateClock() - handler.postDelayed(this, 1000) - } - } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -46,10 +40,7 @@ class MainActivity : AppCompatActivity() { prefs = Prefs(this) requestNotificationPermission() - findViewById(R.id.btn_check).setOnClickListener { runCheck() } - findViewById(R.id.btn_settings).setOnClickListener { - startActivity(Intent(this, SettingsActivity::class.java)) - } + findViewById(R.id.tv_menu).setOnClickListener { showMenu(it) } AlarmScheduler.scheduleNoonCheck(this) @@ -92,56 +83,78 @@ class MainActivity : AppCompatActivity() { override fun onResume() { super.onResume() renderStatus() - updateClock() - handler.removeCallbacks(clockTick) - handler.post(clockTick) } - override fun onPause() { - handler.removeCallbacks(clockTick) - super.onPause() - } - - private fun updateClock() { - val now = LocalTime.now() - findViewById(R.id.tv_clock).text = HH_MM.format(now) - findViewById(R.id.tv_date).text = DATE.format(java.time.LocalDate.now()) + private fun showMenu(anchor: View) { + val popup = PopupMenu(this, anchor) + popup.menu.add(0, MENU_CHECK, 0, "Nu controleren") + popup.menu.add(0, MENU_SETTINGS, 1, "Instellingen") + popup.setOnMenuItemClickListener { item -> + when (item.itemId) { + MENU_CHECK -> runCheck() + MENU_SETTINGS -> startActivity(Intent(this, SettingsActivity::class.java)) + } + true + } + popup.show() } private fun renderStatus() { val p = prefs - val (_, alarms) = p.loadPlan() - val next = findViewById(R.id.tv_next_alarms) - val info = findViewById(R.id.tv_shift_info) + val (day, alarms) = p.loadPlan() + val container = findViewById(R.id.alarm_container) + container.removeAllViews() + val inflater = LayoutInflater.from(this) - if (alarms.isNotEmpty()) { - next.text = alarms.joinToString("\n") { "${it.name} ${HH_MM.format(it.time)}" } - val shift = p.lastShiftStartIso?.let { runCatching { LocalDateTime.parse(it) }.getOrNull() } - info.text = if (shift != null) { - val rounded = ShiftPlanner.roundDownToQuarter(shift.toLocalTime()) - "Dienst om ${HH_MM.format(shift.toLocalTime())} · afgerond ${HH_MM.format(rounded)}" + if (alarms.isEmpty()) { + // Lege state, zoals Alarmio: vertel wat de gebruiker moet doen. + if (p.config() == null) { + container.addView(idleCard(inflater, "Geen instellingen", "Open het menu (⋮) → Instellingen en vul je agenda-gegevens in")) } else { - p.lastStatusTxt.orEmpty() + container.addView(idleCard(inflater, "Geen wekkers gepland", "Morgen geen dienst")) } } else { - next.text = "Geen wekkers" - info.text = p.lastStatusTxt ?: "Nog niet gecontroleerd" + val tomorrow = LocalDate.now(ZoneId.systemDefault()).plusDays(1) + val dayLabel = when { + day == tomorrow -> "Morgen" + day != null -> "Gepland ${day.format(DAY_LABEL)}" + else -> "Gepland" + } + alarms.forEach { spec -> + container.addView( + inflater.inflate(R.layout.item_alarm_card, container, false).apply { + findViewById(R.id.tv_day).text = dayLabel + findViewById(R.id.tv_name).text = spec.name + findViewById(R.id.tv_time).text = TIME.format(spec.time) + }, + ) + } + } + + val info = findViewById(R.id.tv_shift_info) + val shift = p.lastShiftStartIso?.let { runCatching { LocalDateTime.parse(it) }.getOrNull() } + info.text = if (shift != null) { + val rounded = ShiftPlanner.roundDownToQuarter(shift.toLocalTime()) + "Dienst om ${TIME_SHORT.format(shift.toLocalTime())} · afgerond ${TIME_SHORT.format(rounded)}" + } else { + p.lastStatusTxt.orEmpty() + } + } + + /** Grijze kaart voor lege toestanden (geen instellingen / geen dienst). */ + private fun idleCard(inflater: LayoutInflater, title: String, sub: String): View { + return inflater.inflate(R.layout.item_idle_card, null, false).apply { + findViewById(R.id.tv_title).text = title + findViewById(R.id.tv_sub).text = sub } } private fun runCheck() { - val btn = findViewById(R.id.btn_check) - btn.isEnabled = false - btn.text = "Bezig…" + toast("Bezig met controleren…") lifecycleScope.launch { - try { - val result = ShiftCheck.run(this@MainActivity) - renderStatus() - toast(result.message) - } finally { - btn.isEnabled = true - btn.text = "Nu controleren" - } + val r = ShiftCheck.run(this@MainActivity) + renderStatus() + toast(r.message) } } @@ -158,11 +171,14 @@ class MainActivity : AppCompatActivity() { private companion object { const val REQ_NOTIFICATIONS = 42 + const val MENU_CHECK = 1 + const val MENU_SETTINGS = 2 const val EXTRA_SETUP_URL = "url" const val EXTRA_SETUP_USER = "user" const val EXTRA_SETUP_PASS = "pass" const val EXTRA_TEST = "test" - val HH_MM = DateTimeFormatter.ofPattern("HH:mm") - val DATE = DateTimeFormatter.ofPattern("EEEE d MMMM") + val TIME = DateTimeFormatter.ofPattern("HH:mm") + val TIME_SHORT = DateTimeFormatter.ofPattern("HH:mm") + val DAY_LABEL = DateTimeFormatter.ofPattern("EEE d MMM", Locale.getDefault()) } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index bda9b1a..36be5a0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -2,69 +2,52 @@ + android:background="@color/bg" + android:orientation="vertical"> - - - - - - - + android:gravity="center_vertical" + android:orientation="horizontal" + android:paddingStart="24dp" + android:paddingEnd="8dp" + android:paddingTop="16dp"> - + + + + + + android:orientation="vertical" /> - - - - + android:layout_marginStart="24dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="24dp" + android:textColor="@color/text_muted" + android:textSize="13sp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_alarm_card.xml b/app/src/main/res/layout/item_alarm_card.xml new file mode 100644 index 0000000..a5a3c7f --- /dev/null +++ b/app/src/main/res/layout/item_alarm_card.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_idle_card.xml b/app/src/main/res/layout/item_idle_card.xml new file mode 100644 index 0000000..45a24a9 --- /dev/null +++ b/app/src/main/res/layout/item_idle_card.xml @@ -0,0 +1,35 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8bd7789..3a1cdbd 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,6 +1,11 @@ - #00695C - #FFFFFF - #4DB6AC - #00695C + #0A0A0C + #445C96 + #1B1B1E + #EEF2FF + #A9B5D8 + #A9B5D8 + #0A0A0C + #445C96 + #445C96 \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 79e6965..4fde872 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,12 +1,15 @@ - -