[Home/Events] Add new card to home fragment. Disable debug card swapping.

This commit is contained in:
Kuba Szczodrzyński 2020-02-28 22:38:03 +01:00
parent f998f2d956
commit 2bea18dc3c
13 changed files with 242 additions and 60 deletions

View File

@ -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 }

View File

@ -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 }
}

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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) {

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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()));

View File

@ -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();

View File

@ -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>

View File

@ -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>
</layout>

View File

@ -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>