From 2bea18dc3c0438662dd3e4f676785a58e3d067c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= <kuba@szczodrzynski.pl>
Date: Fri, 28 Feb 2020 22:38:03 +0100
Subject: [PATCH] [Home/Events] Add new card to home fragment. Disable debug
 card swapping.

---
 .../edziennik/config/ConfigUI.kt              |   6 -
 .../edziennik/config/ProfileConfigUI.kt       |   6 +
 .../edziennik/config/utils/ConfigMigration.kt |   1 -
 .../edziennik/data/db/dao/EventDao.java       |  17 +--
 .../ui/dialogs/event/EventListAdapter.kt      |   9 +-
 .../dialogs/settings/ProfileRemoveDialog.kt   |   4 -
 .../edziennik/ui/modules/home/HomeFragment.kt |  29 ++---
 .../ui/modules/home/cards/HomeEventsCard.kt   | 107 ++++++++++++++++++
 .../ui/modules/homework/HomeworkAdapter.java  |  25 +---
 .../edziennik/utils/models/Date.java          |  36 ++++++
 app/src/main/res/layout/card_home_events.xml  |  43 +++++++
 app/src/main/res/layout/event_list_item.xml   |  16 ++-
 app/src/main/res/values/strings.xml           |   3 +
 13 files changed, 242 insertions(+), 60 deletions(-)
 create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeEventsCard.kt
 create mode 100644 app/src/main/res/layout/card_home_events.xml

diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigUI.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigUI.kt
index bd876c4c..7b39383e 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigUI.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ConfigUI.kt
@@ -7,7 +7,6 @@ package pl.szczodrzynski.edziennik.config
 import pl.szczodrzynski.edziennik.config.utils.get
 import pl.szczodrzynski.edziennik.config.utils.getIntList
 import pl.szczodrzynski.edziennik.config.utils.set
-import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
 
 class ConfigUI(private val config: Config) {
     private var mTheme: Int? = null
@@ -45,11 +44,6 @@ class ConfigUI(private val config: Config) {
         get() { mOpenDrawerOnBackPressed = mOpenDrawerOnBackPressed ?: config.values.get("openDrawerOnBackPressed", false); return mOpenDrawerOnBackPressed ?: false }
         set(value) { config.set("openDrawerOnBackPressed", value); mOpenDrawerOnBackPressed = value }
 
-    private var mHomeCards: List<HomeCardModel>? = null
-    var homeCards: List<HomeCardModel>
-        get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }
-        set(value) { config.set("homeCards", value); mHomeCards = value }
-
     private var mSnowfall: Boolean? = null
     var snowfall: Boolean
         get() { mSnowfall = mSnowfall ?: config.values.get("snowfall", false); return mSnowfall ?: false }
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigUI.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigUI.kt
index 00904447..5ce237a0 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigUI.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigUI.kt
@@ -7,10 +7,16 @@ package pl.szczodrzynski.edziennik.config
 import pl.szczodrzynski.edziennik.config.utils.get
 import pl.szczodrzynski.edziennik.config.utils.set
 import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
+import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
 
 class ProfileConfigUI(private val config: ProfileConfig) {
     private var mAgendaViewType: Int? = null
     var agendaViewType: Int
         get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: AGENDA_DEFAULT }
         set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
+
+    private var mHomeCards: List<HomeCardModel>? = null
+    var homeCards: List<HomeCardModel>
+        get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }
+        set(value) { config.set("homeCards", value); mHomeCards = value }
 }
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/utils/ConfigMigration.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/utils/ConfigMigration.kt
index 39e4bd0a..8359ed0c 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/config/utils/ConfigMigration.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/utils/ConfigMigration.kt
@@ -63,7 +63,6 @@ class ConfigMigration(app: App, config: Config) {
 
         if (dataVersion < 10) {
             ui.openDrawerOnBackPressed = false
-            ui.homeCards = listOf()
             ui.snowfall = false
             ui.bottomSheetOpened = false
             sync.dontShowAppManagerDialog = false
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.java
index 8d11b11b..083b499a 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.java
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.java
@@ -61,7 +61,7 @@ public abstract class EventDao {
 
     @RawQuery(observedEntities = {Event.class})
     abstract LiveData<List<EventFull>> getAll(SupportSQLiteQuery query);
-    public LiveData<List<EventFull>> getAll(int profileId, String filter) {
+    public LiveData<List<EventFull>> getAll(int profileId, String filter, String limit) {
         String query = "SELECT \n" +
                 "*, \n" +
                 "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName,\n" +
@@ -75,24 +75,24 @@ public abstract class EventDao {
                 "LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = "+profileId+"\n" +
                 "WHERE events.profileId = "+profileId+" AND events.eventBlacklisted = 0 AND "+filter+"\n" +
                 "GROUP BY eventId\n" +
-                "ORDER BY eventDate, eventStartTime ASC";
+                "ORDER BY eventDate, eventStartTime ASC "+limit;
         Log.d("DB", query);
         return getAll(new SimpleSQLiteQuery(query));
     }
     public LiveData<List<EventFull>> getAll(int profileId) {
-        return getAll(profileId, "1");
+        return getAll(profileId, "1", "");
     }
     public List<EventFull> getAllNow(int profileId) {
         return getAllNow(profileId, "1");
     }
     public LiveData<List<EventFull>> getAllWhere(int profileId, String filter) {
-        return getAll(profileId, filter);
+        return getAll(profileId, filter, "");
     }
     public LiveData<List<EventFull>> getAllByType(int profileId, long type, String filter) {
-        return getAll(profileId, "eventType = "+type+" AND "+filter);
+        return getAll(profileId, "eventType = "+type+" AND "+filter, "");
     }
     public LiveData<List<EventFull>> getAllByDate(int profileId, @NonNull Date date) {
-        return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"'");
+        return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"'", "");
     }
     public List<EventFull> getAllByDateNow(int profileId, @NonNull Date date) {
         return getAllNow(profileId, "eventDate = '"+date.getStringY_m_d()+"'");
@@ -100,7 +100,10 @@ public abstract class EventDao {
     public LiveData<List<EventFull>> getAllByDateTime(int profileId, @NonNull Date date, Time time) {
         if (time == null)
             return getAllByDate(profileId, date);
-        return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"' AND eventStartTime = '"+time.getStringValue()+"'");
+        return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"' AND eventStartTime = '"+time.getStringValue()+"'", "");
+    }
+    public LiveData<List<EventFull>> getAllNearest(int profileId, @NonNull Date today, int limit) {
+        return getAll(profileId, "eventDate >= '"+today.getStringY_m_d()+"'", "LIMIT "+limit);
     }
 
     @RawQuery
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventListAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventListAdapter.kt
index 064502ba..ef69a86f 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventListAdapter.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventListAdapter.kt
@@ -16,6 +16,8 @@ import pl.szczodrzynski.edziennik.utils.models.Date
 
 class EventListAdapter(
         val context: Context,
+        val simpleMode: Boolean = false,
+        val showDate: Boolean = false,
         val onItemClick: ((event: EventFull) -> Unit)? = null,
         val onEventEditClick: ((event: EventFull) -> Unit)? = null
 ) : RecyclerView.Adapter<EventListAdapter.ViewHolder>() {
@@ -40,12 +42,15 @@ class EventListAdapter(
 
         val bullet = " • "
 
+        b.simpleMode = simpleMode
+
         b.topic.text = event.topic
 
         b.details.text = mutableListOf<CharSequence?>(
+                if (showDate) event.eventDate.getRelativeString(context, 7) ?: event.eventDate.formattedStringShort else null,
                 event.typeName,
-                event.startTime?.stringHM ?: app.getString(R.string.event_all_day),
-                event.subjectLongName
+                if (simpleMode) null else event.startTime?.stringHM ?: app.getString(R.string.event_all_day),
+                if (simpleMode) null else event.subjectLongName
         ).concat(bullet)
 
         b.addedBy.setText(
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/ProfileRemoveDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/ProfileRemoveDialog.kt
index 4edb5c58..14321317 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/ProfileRemoveDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/ProfileRemoveDialog.kt
@@ -75,10 +75,6 @@ class ProfileRemoveDialog(
             app.db.teamDao().clear(profileId)
             app.db.timetableDao().clear(profileId)
 
-            val homeCards = app.config.ui.homeCards.toMutableList()
-            homeCards.removeAll { it.profileId == profileId }
-            app.config.ui.homeCards = homeCards
-
             val loginStoreId = profileObject.loginStoreId
             val profilesUsingLoginStore = app.db.profileDao().getIdsByLoginStoreIdNow(loginStoreId)
             if (profilesUsingLoginStore.size == 1) {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.kt
index 65fad773..9c0f7799 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.kt
@@ -26,10 +26,7 @@ import pl.szczodrzynski.edziennik.R
 import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
 import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding
 import pl.szczodrzynski.edziennik.ui.dialogs.home.StudentNumberDialog
-import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeDebugCard
-import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeGradesCard
-import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeLuckyNumberCard
-import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeTimetableCard
+import pl.szczodrzynski.edziennik.ui.modules.home.cards.*
 import pl.szczodrzynski.edziennik.utils.Themes
 import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
 import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
@@ -40,16 +37,21 @@ class HomeFragment : Fragment(), CoroutineScope {
         private const val TAG = "HomeFragment"
 
         fun swapCards(fromPosition: Int, toPosition: Int, cardAdapter: HomeCardAdapter) {
-            val homeCards = App.config.ui.homeCards.toMutableList()
-            val fromPair = homeCards[fromPosition]
-            homeCards[fromPosition] = homeCards[toPosition]
-            homeCards[toPosition] = fromPair
-            App.config.ui.homeCards = homeCards
-
             val fromCard = cardAdapter.items[fromPosition]
+            val toCard = cardAdapter.items[toPosition]
+            if (fromCard.id == 100 || toCard.id == 100) {
+                // debug card is not swappable
+                return
+            }
             cardAdapter.items[fromPosition] = cardAdapter.items[toPosition]
             cardAdapter.items[toPosition] = fromCard
             cardAdapter.notifyItemMoved(fromPosition, toPosition)
+
+            val homeCards = App.config.forProfile().ui.homeCards.toMutableList()
+            val fromPair = homeCards[fromPosition]
+            homeCards[fromPosition] = homeCards[toPosition]
+            homeCards[toPosition] = fromPair
+            App.config.forProfile().ui.homeCards = homeCards
         }
     }
 
@@ -107,15 +109,15 @@ class HomeFragment : Fragment(), CoroutineScope {
 
         val showUnified = false
 
-        val cards = app.config.ui.homeCards.filter { it.profileId == app.profile.id }.toMutableList()
+        val cards = app.config.forProfile().ui.homeCards.filter { it.profileId == app.profile.id }.toMutableList()
         if (cards.isEmpty()) {
             cards += listOf(
                     HomeCardModel(app.profile.id, HomeCard.CARD_LUCKY_NUMBER),
                     HomeCardModel(app.profile.id, HomeCard.CARD_TIMETABLE),
-                    /*HomeCardModel(app.profile.id, HomeCard.CARD_EVENTS),*/
+                    HomeCardModel(app.profile.id, HomeCard.CARD_EVENTS),
                     HomeCardModel(app.profile.id, HomeCard.CARD_GRADES)
             )
-            app.config.ui.homeCards = app.config.ui.homeCards.toMutableList().also { it.addAll(cards) }
+            app.config.forProfile().ui.homeCards = app.config.forProfile().ui.homeCards.toMutableList().also { it.addAll(cards) }
         }
 
         val items = mutableListOf<HomeCard>()
@@ -124,6 +126,7 @@ class HomeFragment : Fragment(), CoroutineScope {
                 HomeCard.CARD_LUCKY_NUMBER -> HomeLuckyNumberCard(it.cardId, app, activity, this, app.profile)
                 HomeCard.CARD_TIMETABLE -> HomeTimetableCard(it.cardId, app, activity, this, app.profile)
                 HomeCard.CARD_GRADES -> HomeGradesCard(it.cardId, app, activity, this, app.profile)
+                HomeCard.CARD_EVENTS -> HomeEventsCard(it.cardId, app, activity, this, app.profile)
                 else -> null
             }
         }
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeEventsCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeEventsCard.kt
new file mode 100644
index 00000000..bd77efba
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeEventsCard.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-2-28.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.home.cards
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.core.view.plusAssign
+import androidx.core.view.setMargins
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.LinearLayoutManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.MainActivity
+import pl.szczodrzynski.edziennik.data.db.entity.Profile
+import pl.szczodrzynski.edziennik.databinding.CardHomeEventsBinding
+import pl.szczodrzynski.edziennik.dp
+import pl.szczodrzynski.edziennik.onClick
+import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
+import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
+import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
+import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
+import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
+import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
+import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
+import pl.szczodrzynski.edziennik.utils.models.Date
+import kotlin.coroutines.CoroutineContext
+
+class HomeEventsCard(
+        override val id: Int,
+        val app: App,
+        val activity: MainActivity,
+        val fragment: HomeFragment,
+        val profile: Profile
+) : HomeCard, CoroutineScope {
+    companion object {
+        private const val TAG = "HomeEventsCard"
+    }
+
+    private var job: Job = Job()
+    override val coroutineContext: CoroutineContext
+        get() = job + Dispatchers.Main
+
+    private lateinit var adapter: EventListAdapter
+
+    override fun bind(position: Int, holder: HomeCardAdapter.ViewHolder) { launch {
+        holder.root.removeAllViews()
+        val b = CardHomeEventsBinding.inflate(LayoutInflater.from(holder.root.context))
+        b.root.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
+            setMargins(8.dp)
+        }
+        holder.root += b.root
+
+        adapter = EventListAdapter(
+                activity,
+                simpleMode = true,
+                showDate = true,
+                onItemClick = {
+                    EventDetailsDialog(
+                            activity,
+                            it
+                    )
+                },
+                onEventEditClick = {
+                    EventManualDialog(
+                            activity,
+                            it.profileId,
+                            editingEvent = it
+                    )
+                }
+        )
+
+        app.db.eventDao().getAllNearest(App.profileId, Date.getToday(), 4).observe(activity, Observer { events ->
+            adapter.items = events
+            if (b.eventsView.adapter == null) {
+                b.eventsView.adapter = adapter
+                b.eventsView.apply {
+                    isNestedScrollingEnabled = false
+                    setHasFixedSize(true)
+                    layoutManager = LinearLayoutManager(context)
+                    addItemDecoration(SimpleDividerItemDecoration(context))
+                }
+            }
+            adapter.notifyDataSetChanged()
+
+            if (events != null && events.isNotEmpty()) {
+                b.eventsView.visibility = View.VISIBLE
+                b.eventsNoData.visibility = View.GONE
+            } else {
+                b.eventsView.visibility = View.GONE
+                b.eventsNoData.visibility = View.VISIBLE
+            }
+        })
+
+        holder.root.onClick {
+            activity.loadTarget(MainActivity.DRAWER_ITEM_AGENDA)
+        }
+    }}
+
+    override fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder) = Unit
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkAdapter.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkAdapter.java
index 3460dd6b..c8b42fbb 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkAdapter.java
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkAdapter.java
@@ -17,7 +17,6 @@ import androidx.recyclerview.widget.RecyclerView;
 import java.util.List;
 
 import pl.szczodrzynski.edziennik.App;
-import pl.szczodrzynski.edziennik.ExtensionsKt;
 import pl.szczodrzynski.edziennik.MainActivity;
 import pl.szczodrzynski.edziennik.R;
 import pl.szczodrzynski.edziennik.data.db.full.EventFull;
@@ -45,28 +44,6 @@ public class HomeworkAdapter extends RecyclerView.Adapter<HomeworkAdapter.ViewHo
         return new ViewHolder(view);
     }
 
-    public static String dayDiffString(Context context, int dayDiff) {
-        if (dayDiff > 0) {
-            if (dayDiff == 1) {
-                return context.getString(R.string.tomorrow);
-            }
-            else if (dayDiff == 2) {
-                return context.getString(R.string.the_day_after);
-            }
-            return ExtensionsKt.plural(context, R.plurals.time_till_days, Math.abs(dayDiff));
-        }
-        else if (dayDiff < 0) {
-            if (dayDiff == -1) {
-                return context.getString(R.string.yesterday);
-            }
-            else if (dayDiff == -2) {
-                return context.getString(R.string.the_day_before);
-            }
-            return context.getString(R.string.ago_format, ExtensionsKt.plural(context, R.plurals.time_till_days, Math.abs(dayDiff)));
-        }
-        return context.getString(R.string.today);
-    }
-
     @Override
     public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
         App app = (App) context.getApplicationContext();
@@ -75,7 +52,7 @@ public class HomeworkAdapter extends RecyclerView.Adapter<HomeworkAdapter.ViewHo
 
         int diffDays = Date.diffDays(homework.eventDate, Date.getToday());
 
-        holder.homeworkItemHomeworkDate.setText(app.getString(R.string.date_relative_format, homework.eventDate.getFormattedString(), dayDiffString(context, diffDays)));
+        holder.homeworkItemHomeworkDate.setText(app.getString(R.string.date_relative_format, homework.eventDate.getFormattedString(), Date.dayDiffString(context, diffDays)));
         holder.homeworkItemTopic.setText(homework.topic);
         holder.homeworkItemSubjectTeacher.setText(context.getString(R.string.homework_subject_teacher_format, bs(homework.subjectLongName), bs(homework.teacherFullName)));
         holder.homeworkItemTeamDate.setText(context.getString(R.string.homework_team_date_format, bs(homework.teamName), Date.fromMillis(homework.addedDate).getFormattedStringShort()));
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java
index a1327817..3ca6aec8 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java
@@ -1,5 +1,7 @@
 package pl.szczodrzynski.edziennik.utils.models;
 
+import android.content.Context;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
@@ -7,6 +9,9 @@ import java.text.DateFormat;
 import java.util.Calendar;
 import java.util.Locale;
 
+import pl.szczodrzynski.edziennik.ExtensionsKt;
+import pl.szczodrzynski.edziennik.R;
+
 public class Date implements Comparable<Date> {
     public int year = 0;
     public int month = 0;
@@ -283,6 +288,37 @@ public class Date implements Comparable<Date> {
         }
     }
 
+    public static String dayDiffString(Context context, int dayDiff) {
+        if (dayDiff > 0) {
+            if (dayDiff == 1) {
+                return context.getString(R.string.tomorrow);
+            }
+            else if (dayDiff == 2) {
+                return context.getString(R.string.the_day_after);
+            }
+            return context.getString(R.string.in_format, ExtensionsKt.plural(context, R.plurals.time_till_days, Math.abs(dayDiff)));
+        }
+        else if (dayDiff < 0) {
+            if (dayDiff == -1) {
+                return context.getString(R.string.yesterday);
+            }
+            else if (dayDiff == -2) {
+                return context.getString(R.string.the_day_before);
+            }
+            return context.getString(R.string.ago_format, ExtensionsKt.plural(context, R.plurals.time_till_days, Math.abs(dayDiff)));
+        }
+        return context.getString(R.string.today);
+    }
+
+    @Nullable
+    public String getRelativeString(Context context, int maxDiff) {
+        int diffDays = Date.diffDays(this, Date.getToday());
+        if (maxDiff != 0 && diffDays > maxDiff) {
+            return null;
+        }
+        return dayDiffString(context, diffDays);
+    }
+
     @Override
     public int compareTo(@NonNull Date o) {
         return this.getValue() - o.getValue();
diff --git a/app/src/main/res/layout/card_home_events.xml b/app/src/main/res/layout/card_home_events.xml
new file mode 100644
index 00000000..598cb3bd
--- /dev/null
+++ b/app/src/main/res/layout/card_home_events.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) Kuba Szczodrzyński 2020-2-28.
+  -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/NavView.TextView.Title"
+            android:text="@string/card_events_header_title" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/eventsNoData"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_margin="16dp"
+            android:gravity="center"
+            android:drawablePadding="16dp"
+            android:fontFamily="sans-serif-light"
+            android:text="@string/events_no_nearest"
+            android:textSize="16sp" />
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/eventsView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:clipToPadding="false"
+            tools:visibility="visible"
+            tools:listitem="@layout/event_list_item"
+            tools:itemCount="3"/>
+    </LinearLayout>
+</layout>
+
diff --git a/app/src/main/res/layout/event_list_item.xml b/app/src/main/res/layout/event_list_item.xml
index 8b19f9d1..c5d48310 100644
--- a/app/src/main/res/layout/event_list_item.xml
+++ b/app/src/main/res/layout/event_list_item.xml
@@ -6,6 +6,13 @@
 <layout xmlns:tools="http://schemas.android.com/tools"
     xmlns:android="http://schemas.android.com/apk/res/android">
 
+    <data>
+        <import type="android.view.View"/>
+        <variable
+            name="simpleMode"
+            type="Boolean" />
+    </data>
+
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -49,8 +56,9 @@
                 android:layout_height="wrap_content"
                 android:layout_weight="1"
                 android:textAppearance="@style/NavView.TextView.Medium"
-                android:maxLines="3"
+                android:maxLines="@{simpleMode ? 2 : 3}"
                 android:ellipsize="end"
+                tools:maxLines="3"
                 tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia. Nie wiem co się dzieje w tym roku nie będzie już religii w szkołach podstawowych w Polsce i Europie zachodniej Afryki" />
 
             <com.google.android.material.button.MaterialButton
@@ -61,7 +69,8 @@
                 style="@style/Widget.MaterialComponents.Button.OutlinedButton"
                 android:text="\uFC92"
                 android:textSize="20sp"
-                android:fontFamily="@font/community_material_font_v3_5_95_1"/>
+                android:fontFamily="@font/community_material_font_v3_5_95_1"
+                android:visibility="@{simpleMode ? View.GONE : View.VISIBLE}"/>
 
         </LinearLayout>
 
@@ -72,6 +81,7 @@
             android:textAppearance="@style/NavView.TextView.Helper"
             android:singleLine="true"
             android:ellipsize="middle"
+            android:visibility="@{simpleMode ? View.GONE : View.VISIBLE}"
             tools:text="Udostępniono 10 grudnia przez Ktoś Z Twojej Klasy • 2B3T" />
     </LinearLayout>
-</layout>
\ No newline at end of file
+</layout>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e7f1a2a7..154897ea 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1189,4 +1189,7 @@
     <string name="dialog_day_lessons_info">%s - %s (%s lekcji - %s godzin %s minut)</string>
     <string name="developer_mode">Developer mode</string>
     <string name="dev_mode_enable_warning">Te ustawienia nie są przeznaczone dla zwykłych użytkowników, wyłącznie dla twórcy tej aplikacji.\n\nNie są nawet w żaden sposób opisane, nie wiadomo co robią, więc możesz nawet nie wiedzieć kiedy coś zepsujesz.\n\nWłączenie tych opcji może spowodować utratę danych w aplikacji, uszkodzenie twojego systemu lub nawet uruchomienie wirusa na baterii.\n\nLepiej uważaj.</string>
+    <string name="card_events_header_title">Najbliższe wydarzenia</string>
+    <string name="events_no_nearest">Nie ma więcej wydarzeń w kalendarzu.</string>
+    <string name="in_format">za %s</string>
 </resources>