From b004ec048e3c26a14918b806e8153d854ef66c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 30 Mar 2020 18:55:28 +0200 Subject: [PATCH] [UI] Refactor Grades, Notifications, Homework fragments to better match unified templates. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 23 ++++ .../szczodrzynski/edziennik/MainActivity.kt | 9 +- .../edziennik/data/db/dao/EventDao.kt | 2 +- .../edziennik/ui/dialogs/day/DayDialog.kt | 5 + .../ui/dialogs/event/EventListAdapter.kt | 13 +- .../dialogs/timetable/LessonDetailsDialog.kt | 5 + .../lazypager/FragmentLazyPagerAdapter.kt | 18 +++ .../ui/modules/base/lazypager/LazyFragment.kt | 16 +++ .../modules/base/lazypager/LazyViewPager.kt | 2 +- ...radesFragment.kt => GradesListFragment.kt} | 130 ++++++++---------- .../ui/modules/home/cards/HomeEventsCard.kt | 5 +- .../ui/modules/homework/HomeworkFragment.kt | 83 +++++------ .../modules/homework/HomeworkListFragment.kt | 124 ++++++++++------- .../notifications/NotificationsAdapter.kt | 71 +++++----- .../notifications/NotificationsFragment.kt | 87 ------------ .../NotificationsListFragment.kt | 101 ++++++++++++++ .../ui/modules/template/TemplateAdapter.kt | 31 +++-- .../ui/modules/template/TemplateFragment.kt | 26 +++- .../modules/template/TemplateListFragment.kt | 79 +++++++++++ .../template/TemplateListPageFragment.kt | 79 +++++++++++ ...gerFragment.kt => TemplatePageFragment.kt} | 8 +- .../modules/template/TemplatePagerAdapter.kt | 2 +- .../edziennik/utils/models/Date.java | 2 +- app/src/main/res/drawable/ic_no_homework.xml | 34 +++++ ..._fragment.xml => grades_list_fragment.xml} | 18 ++- ...ent_homework.xml => homework_fragment.xml} | 5 +- app/src/main/res/layout/homework_list.xml | 47 ------- .../res/layout/homework_list_fragment.xml | 41 ++++++ ...ns.xml => notifications_list_fragment.xml} | 33 +++-- .../res/layout/notifications_list_item.xml | 37 +++++ .../res/layout/row_notifications_item.xml | 35 ----- app/src/main/res/layout/template_fragment.xml | 2 +- .../res/layout/template_list_fragment.xml | 47 +++++++ .../main/res/layout/template_list_item.xml | 31 ++++- .../layout/template_list_page_fragment.xml | 41 ++++++ ...ragment.xml => template_page_fragment.xml} | 0 36 files changed, 871 insertions(+), 421 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/FragmentLazyPagerAdapter.kt rename app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/{GradesFragment.kt => GradesListFragment.kt} (81%) delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsFragment.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsListFragment.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateListFragment.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateListPageFragment.kt rename app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/{TemplatePagerFragment.kt => TemplatePageFragment.kt} (84%) create mode 100644 app/src/main/res/drawable/ic_no_homework.xml rename app/src/main/res/layout/{grades_fragment.xml => grades_list_fragment.xml} (67%) rename app/src/main/res/layout/{fragment_homework.xml => homework_fragment.xml} (95%) delete mode 100644 app/src/main/res/layout/homework_list.xml create mode 100644 app/src/main/res/layout/homework_list_fragment.xml rename app/src/main/res/layout/{fragment_notifications.xml => notifications_list_fragment.xml} (60%) create mode 100644 app/src/main/res/layout/notifications_list_item.xml delete mode 100644 app/src/main/res/layout/row_notifications_item.xml create mode 100644 app/src/main/res/layout/template_list_fragment.xml create mode 100644 app/src/main/res/layout/template_list_page_fragment.xml rename app/src/main/res/layout/{template_pager_fragment.xml => template_page_fragment.xml} (100%) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 7e802ae4..41968389 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -40,6 +40,9 @@ import androidx.core.util.forEach import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData import androidx.lifecycle.Observer +import androidx.recyclerview.widget.RecyclerView +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import androidx.viewpager.widget.ViewPager import com.google.android.gms.security.ProviderInstaller import com.google.gson.JsonArray import com.google.gson.JsonElement @@ -141,6 +144,10 @@ fun CharSequence?.isNotNullNorEmpty(): Boolean { return this != null && this.isNotEmpty() } +fun Collection?.isNotNullNorEmpty(): Boolean { + return this != null && this.isNotEmpty() +} + fun CharSequence?.isNotNullNorBlank(): Boolean { return this != null && this.isNotBlank() } @@ -1167,3 +1174,19 @@ fun TextView.getTextPosition(range: IntRange): Rect { return parentTextViewRect } + +inline fun ViewPager.addOnPageSelectedListener(crossinline block: (position: Int) -> Unit) = addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrollStateChanged(state: Int) {} + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} + override fun onPageSelected(position: Int) { block(position) } +}) + +val SwipeRefreshLayout.onScrollListener: RecyclerView.OnScrollListener + get() = object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + if (recyclerView.canScrollVertically(-1)) + this@onScrollListener.isEnabled = false + if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE) + this@onScrollListener.isEnabled = true + } + } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index 561f4148..6abe7be6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -61,7 +61,7 @@ import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment -import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment +import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListFragment import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment @@ -70,7 +70,7 @@ import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment -import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment +import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsListFragment import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment import pl.szczodrzynski.edziennik.ui.modules.template.TemplateFragment @@ -155,7 +155,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope { .withBadgeTypeId(TYPE_EVENT) .isInDrawer(true) - list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesFragment::class) + list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesListFragment::class) .withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline) .withBadgeTypeId(TYPE_GRADE) .isInDrawer(true) @@ -187,7 +187,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope { // static drawer items - list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsFragment::class) + list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsListFragment::class) .withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline) .isInDrawer(true) .isStatic(true) @@ -1077,6 +1077,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope { .also { if (target.icon != null) it.withIcon(target.icon!!) } .also { if (target.title != null) it.withAppTitle(getString(target.title!!)) } .also { if (target.badgeTypeId != null) it.withBadgeStyle(drawer.badgeStyle)} + .withSelectedBackgroundAnimated(false) if (target.badgeTypeId != null) drawer.addUnreadCounterType(target.badgeTypeId!!, target.id) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt index c2efdf2c..bbd7e8e9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt @@ -65,7 +65,7 @@ abstract class EventDao : BaseDao { fun getAllByDate(profileId: Int, date: Date) = getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY") fun getAllByDateTime(profileId: Int, date: Date, time: Time) = - getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' AND eventTime = ${time.stringValue}") + getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' AND eventTime = '${time.stringValue}'") fun getNearestNotDone(profileId: Int, today: Date, limit: Int) = getRaw("$QUERY WHERE $NOT_BLACKLISTED AND $NOT_DONE AND events.profileId = $profileId AND eventDate >= '${today.stringY_m_d}' $ORDER_BY LIMIT $limit") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt index b46925f0..12b7c71a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt @@ -146,6 +146,11 @@ class DayDialog( adapter = EventListAdapter( activity, + showWeekDay = false, + showDate = false, + showType = true, + showTime = true, + showSubject = true, onItemClick = { EventDetailsDialog( activity, 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 64c4b6ce..8f5ebaee 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 @@ -9,6 +9,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.data.db.full.EventFull @@ -19,8 +20,11 @@ import pl.szczodrzynski.edziennik.utils.models.Week class EventListAdapter( val context: Context, val simpleMode: Boolean = false, - val showDate: Boolean = false, val showWeekDay: Boolean = false, + val showDate: Boolean = false, + val showType: Boolean = true, + val showTime: Boolean = true, + val showSubject: Boolean = true, val onItemClick: ((event: EventFull) -> Unit)? = null, val onEventEditClick: ((event: EventFull) -> Unit)? = null ) : RecyclerView.Adapter() { @@ -52,9 +56,9 @@ class EventListAdapter( b.details.text = mutableListOf( if (showWeekDay) Week.getFullDayName(event.date.weekDay) else null, if (showDate) event.date.getRelativeString(context, 7) ?: event.date.formattedStringShort else null, - event.typeName, - if (simpleMode) null else event.time?.stringHM ?: app.getString(R.string.event_all_day), - if (simpleMode) null else event.subjectLongName + if (showType) event.typeName else null, + if (showTime) event.time?.stringHM ?: app.getString(R.string.event_all_day) else null, + if (showSubject) event.subjectLongName else null ).concat(bullet) b.addedBy.setText( @@ -73,6 +77,7 @@ class EventListAdapter( ) b.typeColor.background?.setTintColor(event.eventColor) + b.typeColor.isVisible = showType b.editButton.visibility = if (event.addedManually && !simpleMode) View.VISIBLE else View.GONE b.editButton.onClick { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/LessonDetailsDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/LessonDetailsDialog.kt index b384cb46..2957f593 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/LessonDetailsDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/LessonDetailsDialog.kt @@ -170,6 +170,11 @@ class LessonDetailsDialog( adapter = EventListAdapter( activity, + showWeekDay = false, + showDate = false, + showType = true, + showTime = true, + showSubject = true, onItemClick = { EventDetailsDialog( activity, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/FragmentLazyPagerAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/FragmentLazyPagerAdapter.kt new file mode 100644 index 00000000..4aae09ee --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/FragmentLazyPagerAdapter.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-3-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.base.lazypager + +import androidx.fragment.app.FragmentManager +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout + +class FragmentLazyPagerAdapter( + fragmentManager: FragmentManager, + swipeRefreshLayout: SwipeRefreshLayout, + private val fragments: List> +) : LazyPagerAdapter(fragmentManager, swipeRefreshLayout) { + override fun getPage(position: Int) = fragments[position].first + override fun getPageTitle(position: Int) = fragments[position].second + override fun getCount() = fragments.size +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/LazyFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/LazyFragment.kt index 4ac173f6..1d011510 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/LazyFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/LazyFragment.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.ui.modules.base.lazypager import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.RecyclerView abstract class LazyFragment : Fragment() { private var isPageCreated = false @@ -25,12 +26,27 @@ abstract class LazyFragment : Fragment() { fun disableSwipeToRefresh() = swipeRefreshLayoutCallback?.invoke(position, false) fun setSwipeToRefresh(enabled: Boolean) = swipeRefreshLayoutCallback?.invoke(position, enabled) + val onScrollListener: RecyclerView.OnScrollListener + get() = object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + if (recyclerView.canScrollVertically(-1)) + disableSwipeToRefresh() + if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE) + enableSwipeToRefresh() + } + } + internal fun createPage() { if (!isPageCreated && isAdded) { isPageCreated = onPageCreated() } } + override fun onDestroyView() { + isPageCreated = false + super.onDestroyView() + } + override fun onResume() { createPage() super.onResume() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/LazyViewPager.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/LazyViewPager.kt index 2ad94c05..7f7c4334 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/LazyViewPager.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/lazypager/LazyViewPager.kt @@ -12,7 +12,7 @@ class LazyViewPager @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : ViewPager(context, attrs) { - var pageSelection = -1 + private var pageSelection = -1 init { addOnPageChangeListener(object : OnPageChangeListener { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesListFragment.kt similarity index 81% rename from app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesFragment.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesListFragment.kt index ba125d9c..81b990a9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesListFragment.kt @@ -10,11 +10,10 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon2 import kotlinx.coroutines.* @@ -23,7 +22,7 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_GRADES_EDITOR import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE import pl.szczodrzynski.edziennik.data.db.full.GradeFull -import pl.szczodrzynski.edziennik.databinding.GradesFragmentBinding +import pl.szczodrzynski.edziennik.databinding.GradesListFragmentBinding import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages @@ -36,24 +35,20 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem import kotlin.coroutines.CoroutineContext import kotlin.math.max - -class GradesFragment : Fragment(), CoroutineScope { +class GradesListFragment : Fragment(), CoroutineScope { companion object { private const val TAG = "GradesFragment" } private lateinit var app: App private lateinit var activity: MainActivity - private lateinit var b: GradesFragmentBinding + private lateinit var b: GradesListFragmentBinding private val job: Job = Job() override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main // local/private variables go here - private val adapter by lazy { - GradesAdapter(activity) - } private val manager by lazy { app.gradesManager } private val dontCountEnabled by lazy { manager.dontCountEnabled } private val dontCountGrades by lazy { manager.dontCountGrades } @@ -63,51 +58,49 @@ class GradesFragment : Fragment(), CoroutineScope { activity = (getActivity() as MainActivity?) ?: return null context ?: return null app = activity.application as App - b = GradesFragmentBinding.inflate(inflater) + b = GradesListFragmentBinding.inflate(inflater) b.refreshLayout.setParent(activity.swipeRefreshLayout) return b.root } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - if (!isAdded) - return + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) { + if (!isAdded) return@startCoroutineTimer expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L - app.db.gradeDao() - .getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()) - .observe(this, Observer { grades -> - if (b.gradesRecyclerView.adapter == null) { - b.gradesRecyclerView.adapter = adapter - b.gradesRecyclerView.apply { - setHasFixedSize(true) - layoutManager = LinearLayoutManager(context) - //addItemDecoration(SimpleDividerItemDecoration(context)) - addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - if (recyclerView.canScrollVertically(-1)) { - b.refreshLayout.isEnabled = false - } - if (!recyclerView.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) { - b.refreshLayout.isEnabled = true - } - } - }) - } - } + val adapter = GradesAdapter(activity) + var firstRun = true - launch(Dispatchers.Default) { - processGrades(grades) - } + app.db.gradeDao().getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()).observe(this, Observer { items -> launch { + if (!isAdded) return@launch - if (grades != null && grades.isNotEmpty()) { - b.gradesRecyclerView.visibility = View.VISIBLE - b.gradesNoData.visibility = View.GONE - } else { - b.gradesRecyclerView.visibility = View.GONE - b.gradesNoData.visibility = View.VISIBLE - } - }) + // load & configure the adapter + adapter.items = withContext(Dispatchers.Default) { processGrades(items) } + if (items.isNotNullNorEmpty() && b.list.adapter == null) { + b.list.adapter = adapter + b.list.apply { + setHasFixedSize(true) + layoutManager = LinearLayoutManager(context) + addOnScrollListener(b.refreshLayout.onScrollListener) + } + } + adapter.notifyDataSetChanged() + + if (firstRun) { + expandSubject(adapter) + firstRun = false + } + + // show/hide relevant views + b.progressBar.isVisible = false + if (items.isNullOrEmpty()) { + b.list.isVisible = false + b.noData.isVisible = true + } else { + b.list.isVisible = true + b.noData.isVisible = false + } + }}) adapter.onGradeClick = { GradeDetailsDialog(activity, it) @@ -153,10 +146,30 @@ class GradesFragment : Fragment(), CoroutineScope { }) ) activity.gainAttention() + }} + + private fun expandSubject(adapter: GradesAdapter) { + var expandSubjectModel: GradesSubject? = null + if (expandSubjectId != 0L) { + expandSubjectModel = adapter.items.firstOrNull { it is GradesSubject && it.subjectId == expandSubjectId } as? GradesSubject + adapter.expandModel( + model = expandSubjectModel, + view = null, + notifyAdapter = false + ) + } + + startCoroutineTimer(500L) { + if (expandSubjectModel != null) { + b.list.smoothScrollToPosition( + adapter.items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0) + ) + } + } } @Suppress("SuspendFunctionOnCoroutineScope") - private suspend fun processGrades(grades: List) { + private fun processGrades(grades: List): MutableList { val items = mutableListOf() var subjectId = -1L @@ -284,30 +297,7 @@ class GradesFragment : Fragment(), CoroutineScope { GradesManager.ORDER_BY_DATE_ASC -> items.sortBy { it.lastAddedDate } } - adapter.items = items.toMutableList() - adapter.items.add(stats) - - var expandSubjectModel: GradesSubject? = null - if (expandSubjectId != 0L) { - expandSubjectModel = items.firstOrNull { it.subjectId == expandSubjectId } - adapter.expandModel( - model = expandSubjectModel, - view = null, - notifyAdapter = false - ) - } - - withContext(Dispatchers.Main) { - adapter.notifyDataSetChanged() - } - - startCoroutineTimer(500L, 0L) { - if (expandSubjectModel != null) { - b.gradesRecyclerView.smoothScrollToPosition( - items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0) - ) - } - } + return (items + stats).toMutableList() } private fun countGrade(grade: Grade, averages: GradesAverages) { 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 index bcff527a..b0c8ec79 100644 --- 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 @@ -60,8 +60,11 @@ class HomeEventsCard( adapter = EventListAdapter( activity, simpleMode = true, - showDate = true, showWeekDay = true, + showDate = true, + showType = true, + showTime = false, + showSubject = false, onItemClick = { EventDetailsDialog( activity, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkFragment.kt index b84d4738..30abcd02 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkFragment.kt @@ -1,3 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-3-30. + */ + package pl.szczodrzynski.edziennik.ui.modules.homework import android.os.AsyncTask @@ -7,46 +11,48 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.fragment.app.Fragment -import androidx.viewpager.widget.ViewPager import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.MainActivity -import pl.szczodrzynski.edziennik.R +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.Metadata -import pl.szczodrzynski.edziennik.databinding.FragmentHomeworkBinding +import pl.szczodrzynski.edziennik.databinding.HomeworkFragmentBinding import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog -import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment -import pl.szczodrzynski.edziennik.utils.Themes +import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem +import kotlin.coroutines.CoroutineContext -class HomeworkFragment : Fragment() { +class HomeworkFragment : Fragment(), CoroutineScope { companion object { + private const val TAG = "HomeworkFragment" var pageSelection = 0 } private lateinit var app: App private lateinit var activity: MainActivity - private lateinit var b: FragmentHomeworkBinding + private lateinit var b: HomeworkFragmentBinding + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as MainActivity?) ?: return null - if (context == null) - return null + context ?: return null app = activity.application as App - context!!.theme.applyStyle(Themes.appTheme, true) - // activity, context and profile is valid - b = FragmentHomeworkBinding.inflate(inflater) + b = HomeworkFragmentBinding.inflate(inflater) b.refreshLayout.setParent(activity.swipeRefreshLayout) return b.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - // TODO check if app, activity, b can be null - if (app.profile == null || !isAdded) - return + if (!isAdded) return activity.bottomSheet.prependItems( BottomSheetPrimaryItem(true) @@ -67,31 +73,28 @@ class HomeworkFragment : Fragment() { Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show() })) - b.viewPager.adapter = MessagesFragment.Adapter(childFragmentManager, b.refreshLayout).also { adapter -> + val pagerAdapter = FragmentLazyPagerAdapter( + fragmentManager ?: return, + b.refreshLayout, + listOf( + HomeworkListFragment().apply { + arguments = Bundle("homeworkDate" to HomeworkDate.CURRENT) + } to getString(R.string.homework_tab_current), - adapter.addFragment(HomeworkListFragment().also { fragment -> - fragment.arguments = Bundle().also { args -> - args.putInt("homeworkDate", HomeworkDate.CURRENT) - } - }, getString(R.string.homework_tab_current)) - - adapter.addFragment(HomeworkListFragment().also { fragment -> - fragment.arguments = Bundle().also { args -> - args.putInt("homeworkDate", HomeworkDate.PAST) - } - }, getString(R.string.homework_tab_past)) - } - - b.viewPager.currentItem = pageSelection - b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { - override fun onPageScrollStateChanged(state: Int) {} - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} - override fun onPageSelected(position: Int) { - pageSelection = position + HomeworkListFragment().apply { + arguments = Bundle("homeworkDate" to HomeworkDate.PAST) + } to getString(R.string.homework_tab_past) + ) + ) + b.viewPager.apply { + offscreenPageLimit = 1 + adapter = pagerAdapter + currentItem = pageSelection + addOnPageSelectedListener { + pageSelection = it } - }) - - b.tabLayout.setupWithViewPager(b.viewPager) + b.tabLayout.setupWithViewPager(this) + } activity.navView.apply { bottomBar.apply { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkListFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkListFragment.kt index 394fc2ed..2b84a596 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkListFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/homework/HomeworkListFragment.kt @@ -4,75 +4,105 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.view.isVisible import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.MainActivity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.data.db.entity.Event -import pl.szczodrzynski.edziennik.databinding.HomeworkListBinding -import pl.szczodrzynski.edziennik.getInt +import pl.szczodrzynski.edziennik.databinding.HomeworkListFragmentBinding +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.base.lazypager.LazyFragment +import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.models.Date +import kotlin.coroutines.CoroutineContext -class HomeworkListFragment : LazyFragment() { +class HomeworkListFragment : LazyFragment(), CoroutineScope { + companion object { + private const val TAG = "HomeworkListFragment" + } private lateinit var app: App private lateinit var activity: MainActivity - private lateinit var b: HomeworkListBinding + private lateinit var b: HomeworkListFragmentBinding - private var homeworkDate = HomeworkDate.CURRENT + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as MainActivity?) ?: return null context ?: return null app = activity.application as App - b = HomeworkListBinding.inflate(inflater) + b = HomeworkListFragmentBinding.inflate(inflater) return b.root } - override fun onPageCreated(): Boolean { - if (arguments != null) { - homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT) + override fun onPageCreated(): Boolean { startCoroutineTimer(100L) { + val homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT) + + val today = Date.getToday() + val filter = when(homeworkDate) { + HomeworkDate.CURRENT -> "eventDate >= '${today.stringY_m_d}'" + else -> "eventDate < '${today.stringY_m_d}'" } - val layoutManager = LinearLayoutManager(context) - layoutManager.reverseLayout = homeworkDate == HomeworkDate.PAST - layoutManager.stackFromEnd = homeworkDate == HomeworkDate.PAST - - b.homeworkView.setHasFixedSize(true) - b.homeworkView.layoutManager = layoutManager - b.homeworkView.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - if (recyclerView.canScrollVertically(-1)) { - setSwipeToRefresh(false) + val adapter = EventListAdapter( + activity, + showWeekDay = true, + showDate = true, + showType = false, + showTime = true, + showSubject = true, + onItemClick = { + EventDetailsDialog( + activity, + it + ) + }, + onEventEditClick = { + EventManualDialog( + activity, + it.profileId, + editingEvent = it + ) } - if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE) { - setSwipeToRefresh(true) + ) + + app.db.eventDao().getAllByType(App.profileId, Event.TYPE_HOMEWORK, filter).observe(this@HomeworkListFragment, Observer { items -> + if (!isAdded) return@Observer + + // load & configure the adapter + adapter.items = items + if (items.isNotNullNorEmpty() && b.list.adapter == null) { + b.list.adapter = adapter + b.list.apply { + setHasFixedSize(true) + layoutManager = LinearLayoutManager(context).apply { + reverseLayout = homeworkDate == HomeworkDate.PAST + stackFromEnd = homeworkDate == HomeworkDate.PAST + } + addItemDecoration(SimpleDividerItemDecoration(context)) + addOnScrollListener(onScrollListener) } } + adapter.notifyDataSetChanged() + + // show/hide relevant views + b.progressBar.isVisible = false + if (items.isNullOrEmpty()) { + b.list.isVisible = false + b.noData.isVisible = true + } else { + b.list.isVisible = true + b.noData.isVisible = false + } }) - - val filter = when(homeworkDate) { - HomeworkDate.CURRENT -> "eventDate >= '" + Date.getToday().stringY_m_d + "'" - else -> "eventDate < '" + Date.getToday().stringY_m_d + "'" - } - - app.db.eventDao() - .getAllByType(App.profileId, Event.TYPE_HOMEWORK, filter) - .observe(this, Observer { homeworkList -> - if (!isAdded) return@Observer - - if (homeworkList != null && homeworkList.size > 0) { - val adapter = HomeworkAdapter(context, homeworkList) - b.homeworkView.adapter = adapter - b.homeworkView.visibility = View.VISIBLE - b.homeworkNoData.visibility = View.GONE - } else { - b.homeworkView.visibility = View.GONE - b.homeworkNoData.visibility = View.VISIBLE - } - }) - return true - } + }; return true } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsAdapter.kt index 473b9855..8df33a8c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsAdapter.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsAdapter.kt @@ -1,71 +1,62 @@ package pl.szczodrzynski.edziennik.ui.modules.notifications -import android.app.Activity -import android.content.Context -import android.content.Intent import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup -import android.widget.TextView -import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.data.db.entity.Notification -import pl.szczodrzynski.edziennik.utils.Utils.d +import pl.szczodrzynski.edziennik.databinding.NotificationsListItemBinding import pl.szczodrzynski.edziennik.utils.models.Date +import kotlin.coroutines.CoroutineContext class NotificationsAdapter( - private val context: Context -) : RecyclerView.Adapter() { + private val activity: AppCompatActivity, + val onItemClick: ((item: Notification) -> Unit)? = null +) : RecyclerView.Adapter(), CoroutineScope { companion object { private const val TAG = "NotificationsAdapter" } + private val app = activity.applicationContext as App + // optional: place the manager here + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + var items = listOf() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val inflater = LayoutInflater.from(context) - val view = inflater.inflate(R.layout.row_notifications_item, parent, false) + val inflater = LayoutInflater.from(activity) + val view = NotificationsListItemBinding.inflate(inflater, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val app = context.applicationContext as App + val item = items[position] + val b = holder.b - val notification = items[position] + val date = Date.fromMillis(item.addedDate).formattedString + val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity) - val date = Date.fromMillis(notification.addedDate).formattedString - val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(context) - - holder.title.text = notification.text - holder.profileDate.text = listOf( - notification.profileName ?: "", + b.title.text = item.text + b.profileDate.text = listOf( + item.profileName ?: "", " • ", - date.asColoredSpannable(colorSecondary) - ).concat() - holder.type.text = context.getNotificationTitle(notification.type) + date + ).concat().asColoredSpannable(colorSecondary) + b.type.text = activity.getNotificationTitle(item.type) - holder.root.onClick { - val intent = Intent("android.intent.action.MAIN") - notification.fillIntent(intent) - - d(TAG, "notification with item " + notification.viewId + " extras " + if (intent.extras == null) "null" else intent.extras!!.toString()) - - //Log.d(TAG, "Got date "+intent.getLongExtra("timetableDate", 0)); - - if (notification.profileId != null && notification.profileId != -1 && notification.profileId != app.profile.id && context is Activity) { - Toast.makeText(app, app.getString(R.string.toast_changing_profile), Toast.LENGTH_LONG).show() - } - app.sendBroadcast(intent) + onItemClick?.let { listener -> + b.root.onClick { listener(item) } } } override fun getItemCount() = items.size - class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - var root = itemView - var title: TextView = itemView.findViewById(R.id.title) - var profileDate: TextView = itemView.findViewById(R.id.profileDate) - var type: TextView = itemView.findViewById(R.id.type) - } + class ViewHolder(val b: NotificationsListItemBinding) : RecyclerView.ViewHolder(b.root) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsFragment.kt deleted file mode 100644 index 74d01bd1..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsFragment.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-11-22. - */ - -package pl.szczodrzynski.edziennik.ui.modules.notifications - -import android.os.AsyncTask -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer -import androidx.recyclerview.widget.LinearLayoutManager -import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.MainActivity -import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.databinding.FragmentNotificationsBinding -import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration -import pl.szczodrzynski.edziennik.utils.Themes -import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem - - -class NotificationsFragment : Fragment() { - private lateinit var app: App - private lateinit var activity: MainActivity - private lateinit var b: FragmentNotificationsBinding - - private val adapter by lazy { - NotificationsAdapter(activity) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - activity = (getActivity() as MainActivity?) ?: return null - context ?: return null - app = activity.application as App - context!!.theme.applyStyle(Themes.appTheme, true) - if (app.profile == null) - return inflater.inflate(R.layout.fragment_loading, container, false) - // activity, context and profile is valid - b = FragmentNotificationsBinding.inflate(inflater) - return b.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - // TODO check if app, activity, b can be null - if (app.profile == null || !isAdded) - return - - activity.bottomSheet.prependItems( - BottomSheetPrimaryItem(true) - .withTitle(R.string.menu_remove_notifications) - .withIcon(CommunityMaterial.Icon.cmd_delete_sweep_outline) - .withOnClickListener(View.OnClickListener { - activity.bottomSheet.close() - AsyncTask.execute { app.db.notificationDao().clearAll() } - Toast.makeText(activity, R.string.menu_remove_notifications_success, Toast.LENGTH_SHORT).show() - })) - - app.db.notificationDao() - .getAll() - .observe(this, Observer { notifications -> - if (app.profile == null || !isAdded) return@Observer - - adapter.items = notifications - if (b.notificationsView.adapter == null) { - b.notificationsView.adapter = adapter - b.notificationsView.apply { - setHasFixedSize(true) - layoutManager = LinearLayoutManager(context) - addItemDecoration(SimpleDividerItemDecoration(context)) - } - } - adapter.notifyDataSetChanged() - - if (notifications != null && notifications.isNotEmpty()) { - b.notificationsView.visibility = View.VISIBLE - b.notificationsNoData.visibility = View.GONE - } else { - b.notificationsView.visibility = View.GONE - b.notificationsNoData.visibility = View.VISIBLE - } - }) - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsListFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsListFragment.kt new file mode 100644 index 00000000..96f25bd5 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/notifications/NotificationsListFragment.kt @@ -0,0 +1,101 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-22. + */ + +package pl.szczodrzynski.edziennik.ui.modules.notifications + +import android.app.Activity +import android.content.Intent +import android.os.AsyncTask +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.LinearLayoutManager +import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.databinding.NotificationsListFragmentBinding +import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration +import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem +import kotlin.coroutines.CoroutineContext + +class NotificationsListFragment : Fragment(), CoroutineScope { + companion object { + private const val TAG = "NotificationsListFragment" + } + + private lateinit var app: App + private lateinit var activity: MainActivity + private lateinit var b: NotificationsListFragmentBinding + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + activity = (getActivity() as MainActivity?) ?: return null + context ?: return null + app = activity.application as App + b = NotificationsListFragmentBinding.inflate(inflater) + return b.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) { + if (!isAdded) return@startCoroutineTimer + + activity.bottomSheet.prependItems( + BottomSheetPrimaryItem(true) + .withTitle(R.string.menu_remove_notifications) + .withIcon(CommunityMaterial.Icon.cmd_delete_sweep_outline) + .withOnClickListener(View.OnClickListener { + activity.bottomSheet.close() + AsyncTask.execute { app.db.notificationDao().clearAll() } + Toast.makeText(activity, R.string.menu_remove_notifications_success, Toast.LENGTH_SHORT).show() + })) + + val adapter = NotificationsAdapter(activity) { notification -> + val intent = Intent("android.intent.action.MAIN") + notification.fillIntent(intent) + + Utils.d(TAG, "notification with item " + notification.viewId + " extras " + if (intent.extras == null) "null" else intent.extras!!.toString()) + if (notification.profileId != null && notification.profileId != -1 && notification.profileId != app.profile.id && context is Activity) { + Toast.makeText(app, app.getString(R.string.toast_changing_profile), Toast.LENGTH_LONG).show() + } + app.sendBroadcast(intent) + } + + app.db.notificationDao().getAll().observe(this, Observer { items -> + if (!isAdded) return@Observer + + // load & configure the adapter + adapter.items = items + if (items.isNotNullNorEmpty() && b.list.adapter == null) { + b.list.adapter = adapter + b.list.apply { + setHasFixedSize(true) + layoutManager = LinearLayoutManager(context) + addItemDecoration(SimpleDividerItemDecoration(context)) + } + } + adapter.notifyDataSetChanged() + + // show/hide relevant views + b.progressBar.isVisible = false + if (items.isNullOrEmpty()) { + b.list.isVisible = false + b.noData.isVisible = true + } else { + b.list.isVisible = true + b.noData.isVisible = false + } + }) + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateAdapter.kt index c4810c2f..fe78396f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateAdapter.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateAdapter.kt @@ -11,16 +11,19 @@ import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.data.db.entity.Notification import pl.szczodrzynski.edziennik.databinding.TemplateListItemBinding -import pl.szczodrzynski.edziennik.onClick +import pl.szczodrzynski.edziennik.utils.models.Date import kotlin.coroutines.CoroutineContext class TemplateAdapter( val activity: AppCompatActivity, - val onItemClick: ((item: TemplateItem) -> Unit)? = null, - val onItemButtonClick: ((item: TemplateItem) -> Unit)? = null + val onItemClick: ((item: Notification) -> Unit)? = null ) : RecyclerView.Adapter(), CoroutineScope { + companion object { + private const val TAG = "TemplateAdapter" + } private val app = activity.applicationContext as App // optional: place the manager here @@ -29,7 +32,7 @@ class TemplateAdapter( override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main - var items = listOf() + var items = listOf() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(parent.context) @@ -41,19 +44,23 @@ class TemplateAdapter( val item = items[position] val b = holder.b + val date = Date.fromMillis(item.addedDate).formattedString + val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity) + + b.title.text = item.text + b.profileDate.text = listOf( + item.profileName ?: "", + " • ", + date + ).concat().asColoredSpannable(colorSecondary) + b.type.text = activity.getNotificationTitle(item.type) + onItemClick?.let { listener -> b.root.onClick { listener(item) } } - - /*b.someButton.visibility = if (buttonVisible) View.VISIBLE else View.GONE - onItemButtonClick?.let { listener -> - b.someButton.onClick { listener(item) } - }*/ } override fun getItemCount() = items.size class ViewHolder(val b: TemplateListItemBinding) : RecyclerView.ViewHolder(b.root) - - data class TemplateItem(val text: String) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateFragment.kt index 6cc9f902..1dff7db0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateFragment.kt @@ -14,12 +14,16 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.MainActivity +import pl.szczodrzynski.edziennik.addOnPageSelectedListener import pl.szczodrzynski.edziennik.databinding.TemplateFragmentBinding +import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter +import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment import kotlin.coroutines.CoroutineContext class TemplateFragment : Fragment(), CoroutineScope { companion object { private const val TAG = "TemplateFragment" + var pageSelection = 0 } private lateinit var app: App @@ -42,17 +46,29 @@ class TemplateFragment : Fragment(), CoroutineScope { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - if (!isAdded) - return + if (!isAdded) return - val pagerAdapter = TemplatePagerAdapter( + val pagerAdapter = FragmentLazyPagerAdapter( fragmentManager ?: return, - b.refreshLayout + b.refreshLayout, + listOf( + TemplatePageFragment() to "Pager 0", + TemplatePageFragment() to "Pager 1", + TemplatePageFragment() to "Pager 2", + TemplatePageFragment() to "Pager 3", + TemplateListPageFragment() to "Pager 4", + TemplateListPageFragment() to "Pager 5", + TemplateListPageFragment() to "Pager 6", + TemplateListPageFragment() to "Pager 7" + ) ) b.viewPager.apply { offscreenPageLimit = 1 adapter = pagerAdapter - currentItem = 4 + currentItem = pageSelection + addOnPageSelectedListener { + HomeworkFragment.pageSelection = it + } b.tabLayout.setupWithViewPager(this) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateListFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateListFragment.kt new file mode 100644 index 00000000..fb3b1084 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateListFragment.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-3-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.template + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.databinding.TemplateListFragmentBinding +import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration +import kotlin.coroutines.CoroutineContext + +class TemplateListFragment : Fragment(), CoroutineScope { + companion object { + private const val TAG = "TemplateListFragment" + } + + private lateinit var app: App + private lateinit var activity: MainActivity + private lateinit var b: TemplateListFragmentBinding + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + activity = (getActivity() as MainActivity?) ?: return null + context ?: return null + app = activity.application as App + b = TemplateListFragmentBinding.inflate(inflater) + b.refreshLayout.setParent(activity.swipeRefreshLayout) + return b.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) { + if (!isAdded) return@startCoroutineTimer + + val adapter = TemplateAdapter(activity) + + app.db.notificationDao().getAll().observe(this, Observer { items -> + if (!isAdded) return@Observer + + // load & configure the adapter + adapter.items = items + if (items.isNotNullNorEmpty() && b.list.adapter == null) { + b.list.adapter = adapter + b.list.apply { + setHasFixedSize(true) + layoutManager = LinearLayoutManager(context) + addItemDecoration(SimpleDividerItemDecoration(context)) + addOnScrollListener(b.refreshLayout.onScrollListener) + } + } + adapter.notifyDataSetChanged() + + // show/hide relevant views + b.progressBar.isVisible = false + if (items.isNullOrEmpty()) { + b.list.isVisible = false + b.noData.isVisible = true + } else { + b.list.isVisible = true + b.noData.isVisible = false + } + }) + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateListPageFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateListPageFragment.kt new file mode 100644 index 00000000..6908b30e --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplateListPageFragment.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-3-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.template + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.MainActivity +import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding +import pl.szczodrzynski.edziennik.isNotNullNorEmpty +import pl.szczodrzynski.edziennik.startCoroutineTimer +import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment +import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration +import kotlin.coroutines.CoroutineContext + +class TemplateListPageFragment : LazyFragment(), CoroutineScope { + companion object { + private const val TAG = "TemplateListPagerFragment" + } + + private lateinit var app: App + private lateinit var activity: MainActivity + private lateinit var b: TemplateListPageFragmentBinding + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + activity = (getActivity() as MainActivity?) ?: return null + context ?: return null + app = activity.application as App + b = TemplateListPageFragmentBinding.inflate(inflater) + return b.root + } + + override fun onPageCreated(): Boolean { startCoroutineTimer(100L) { + val adapter = TemplateAdapter(activity) + + app.db.notificationDao().getAll().observe(this, Observer { items -> + if (!isAdded) return@Observer + + // load & configure the adapter + adapter.items = items + if (items.isNotNullNorEmpty() && b.list.adapter == null) { + b.list.adapter = adapter + b.list.apply { + setHasFixedSize(true) + layoutManager = LinearLayoutManager(context) + addItemDecoration(SimpleDividerItemDecoration(context)) + addOnScrollListener(onScrollListener) + } + } + adapter.notifyDataSetChanged() + + // show/hide relevant views + b.progressBar.isVisible = false + if (items.isNullOrEmpty()) { + b.list.isVisible = false + b.noData.isVisible = true + } else { + b.list.isVisible = true + b.noData.isVisible = false + } + }) + }; return true } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplatePagerFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplatePageFragment.kt similarity index 84% rename from app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplatePagerFragment.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplatePageFragment.kt index 3bebc1e1..f2404ea4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplatePagerFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplatePageFragment.kt @@ -13,18 +13,18 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.MainActivity -import pl.szczodrzynski.edziennik.databinding.TemplatePagerFragmentBinding +import pl.szczodrzynski.edziennik.databinding.TemplatePageFragmentBinding import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment import kotlin.coroutines.CoroutineContext -class TemplatePagerFragment : LazyFragment(), CoroutineScope { +class TemplatePageFragment : LazyFragment(), CoroutineScope { companion object { private const val TAG = "TemplatePagerFragment" } private lateinit var app: App private lateinit var activity: MainActivity - private lateinit var b: TemplatePagerFragmentBinding + private lateinit var b: TemplatePageFragmentBinding private val job: Job = Job() override val coroutineContext: CoroutineContext @@ -36,7 +36,7 @@ class TemplatePagerFragment : LazyFragment(), CoroutineScope { activity = (getActivity() as MainActivity?) ?: return null context ?: return null app = activity.application as App - b = TemplatePagerFragmentBinding.inflate(inflater) + b = TemplatePageFragmentBinding.inflate(inflater) return b.root } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplatePagerAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplatePagerAdapter.kt index 26ce6c89..eaa8ba1d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplatePagerAdapter.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/template/TemplatePagerAdapter.kt @@ -9,7 +9,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyPagerAdapter class TemplatePagerAdapter(fragmentManager: FragmentManager, swipeRefreshLayout: SwipeRefreshLayout) : LazyPagerAdapter(fragmentManager, swipeRefreshLayout) { - override fun getPage(position: Int) = TemplatePagerFragment() + override fun getPage(position: Int) = TemplatePageFragment() override fun getPageTitle(position: Int) = "Page $position" override fun getCount() = 10 } 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 e1216f3c..c1d32538 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 @@ -137,7 +137,7 @@ public class Date implements Comparable { } public static int diffDays(Date d1, Date d2) { - return (int) ((d1.getInMillis() - d2.getInMillis()) / (24 * 60 * 60 * 1000)); + return Math.round((d1.getInMillis() - d2.getInMillis()) / (24 * 60 * 60 * 1000f)); } public static boolean isToday(Date date) { diff --git a/app/src/main/res/drawable/ic_no_homework.xml b/app/src/main/res/drawable/ic_no_homework.xml new file mode 100644 index 00000000..d8ba8c38 --- /dev/null +++ b/app/src/main/res/drawable/ic_no_homework.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/grades_fragment.xml b/app/src/main/res/layout/grades_list_fragment.xml similarity index 67% rename from app/src/main/res/layout/grades_fragment.xml rename to app/src/main/res/layout/grades_list_fragment.xml index a9702e8c..faf8264d 100644 --- a/app/src/main/res/layout/grades_fragment.xml +++ b/app/src/main/res/layout/grades_list_fragment.xml @@ -16,8 +16,14 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + android:visibility="gone" + app:drawableTopCompat="@drawable/ic_no_grades" + tools:visibility="visible" /> + android:visibility="gone" + tools:listitem="@layout/grades_item_subject" + tools:visibility="visible" /> diff --git a/app/src/main/res/layout/fragment_homework.xml b/app/src/main/res/layout/homework_fragment.xml similarity index 95% rename from app/src/main/res/layout/fragment_homework.xml rename to app/src/main/res/layout/homework_fragment.xml index 4e2583c3..9a015c15 100644 --- a/app/src/main/res/layout/fragment_homework.xml +++ b/app/src/main/res/layout/homework_fragment.xml @@ -1,4 +1,8 @@ + + @@ -27,7 +31,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> - diff --git a/app/src/main/res/layout/homework_list.xml b/app/src/main/res/layout/homework_list.xml deleted file mode 100644 index bfcc4960..00000000 --- a/app/src/main/res/layout/homework_list.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/homework_list_fragment.xml b/app/src/main/res/layout/homework_list_fragment.xml new file mode 100644 index 00000000..d53b9d17 --- /dev/null +++ b/app/src/main/res/layout/homework_list_fragment.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_notifications.xml b/app/src/main/res/layout/notifications_list_fragment.xml similarity index 60% rename from app/src/main/res/layout/fragment_notifications.xml rename to app/src/main/res/layout/notifications_list_fragment.xml index ea9d4d68..a5c9aa90 100644 --- a/app/src/main/res/layout/fragment_notifications.xml +++ b/app/src/main/res/layout/notifications_list_fragment.xml @@ -1,30 +1,41 @@ + + + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + - + + - \ No newline at end of file + diff --git a/app/src/main/res/layout/notifications_list_item.xml b/app/src/main/res/layout/notifications_list_item.xml new file mode 100644 index 00000000..498f7e21 --- /dev/null +++ b/app/src/main/res/layout/notifications_list_item.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/row_notifications_item.xml b/app/src/main/res/layout/row_notifications_item.xml deleted file mode 100644 index 3ef2f6d8..00000000 --- a/app/src/main/res/layout/row_notifications_item.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/template_fragment.xml b/app/src/main/res/layout/template_fragment.xml index 3aeffadb..9a015c15 100644 --- a/app/src/main/res/layout/template_fragment.xml +++ b/app/src/main/res/layout/template_fragment.xml @@ -13,7 +13,7 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/template_list_item.xml b/app/src/main/res/layout/template_list_item.xml index 7d566475..d47227b3 100644 --- a/app/src/main/res/layout/template_list_item.xml +++ b/app/src/main/res/layout/template_list_item.xml @@ -3,16 +3,39 @@ ~ Copyright (c) Kuba Szczodrzyński 2019-12-19. --> - + + android:padding="8dp"> + + + + + + - \ No newline at end of file + diff --git a/app/src/main/res/layout/template_list_page_fragment.xml b/app/src/main/res/layout/template_list_page_fragment.xml new file mode 100644 index 00000000..09f8bfda --- /dev/null +++ b/app/src/main/res/layout/template_list_page_fragment.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/template_pager_fragment.xml b/app/src/main/res/layout/template_page_fragment.xml similarity index 100% rename from app/src/main/res/layout/template_pager_fragment.xml rename to app/src/main/res/layout/template_page_fragment.xml