mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-01-18 04:46:44 -06:00
[UI] Enable colored app bars on scroll (#211)
* Extract fragment scroll listener to separate file * Replace canRefresh with isScrolled * Add empty helper class to animate app bar color * Implement AppBarColorAnimator * Rename getRefreshScrollingView() to getScrollingView() * Set isScrolled on drag start * Clear isScrolled on fling to top * Add getSyncParams() to fragments * Convert getAppBars() to property * Tint TabLayout background drawable
This commit is contained in:
parent
58d9dec33c
commit
37a94595c0
@ -19,6 +19,7 @@ import kotlinx.coroutines.withContext
|
|||||||
import pl.szczodrzynski.edziennik.MainActivity
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
|
import pl.szczodrzynski.edziennik.data.enums.FeatureType
|
||||||
import pl.szczodrzynski.edziennik.data.enums.MetadataType
|
import pl.szczodrzynski.edziennik.data.enums.MetadataType
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding
|
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
|
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
|
||||||
@ -45,6 +46,7 @@ class AgendaFragment : BaseFragment<ViewBinding, MainActivity>(
|
|||||||
|
|
||||||
override fun getFab() = R.string.add to CommunityMaterial.Icon3.cmd_plus
|
override fun getFab() = R.string.add to CommunityMaterial.Icon3.cmd_plus
|
||||||
override fun getMarkAsReadType() = MetadataType.EVENT
|
override fun getMarkAsReadType() = MetadataType.EVENT
|
||||||
|
override fun getSyncParams() = FeatureType.AGENDA to null
|
||||||
override fun getBottomSheetItems() = listOf(
|
override fun getBottomSheetItems() = listOf(
|
||||||
BottomSheetPrimaryItem(true)
|
BottomSheetPrimaryItem(true)
|
||||||
.withTitle(R.string.menu_add_event)
|
.withTitle(R.string.menu_add_event)
|
||||||
@ -114,6 +116,7 @@ class AgendaFragment : BaseFragment<ViewBinding, MainActivity>(
|
|||||||
private suspend fun createDefaultAgendaView(b: FragmentAgendaDefaultBinding) {
|
private suspend fun createDefaultAgendaView(b: FragmentAgendaDefaultBinding) {
|
||||||
if (!isAdded)
|
if (!isAdded)
|
||||||
return
|
return
|
||||||
|
canRefreshDisabled = true
|
||||||
checkEventTypes()
|
checkEventTypes()
|
||||||
delay(500)
|
delay(500)
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.ui.attendance
|
|||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
import pl.szczodrzynski.edziennik.MainActivity
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
|
import pl.szczodrzynski.edziennik.data.enums.FeatureType
|
||||||
import pl.szczodrzynski.edziennik.data.enums.MetadataType
|
import pl.szczodrzynski.edziennik.data.enums.MetadataType
|
||||||
import pl.szczodrzynski.edziennik.databinding.BasePagerFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.BasePagerFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ext.Bundle
|
import pl.szczodrzynski.edziennik.ext.Bundle
|
||||||
@ -32,6 +33,7 @@ class AttendanceFragment : PagerFragment<BasePagerFragmentBinding, MainActivity>
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getMarkAsReadType() = MetadataType.ATTENDANCE
|
override fun getMarkAsReadType() = MetadataType.ATTENDANCE
|
||||||
|
override fun getSyncParams() = FeatureType.ATTENDANCE to null
|
||||||
override fun getBottomSheetItems() = listOf(
|
override fun getBottomSheetItems() = listOf(
|
||||||
BottomSheetPrimaryItem(true)
|
BottomSheetPrimaryItem(true)
|
||||||
.withTitle(R.string.menu_attendance_config)
|
.withTitle(R.string.menu_attendance_config)
|
||||||
|
@ -29,7 +29,7 @@ class AttendanceListFragment : BaseFragment<AttendanceListFragmentBinding, MainA
|
|||||||
inflater = AttendanceListFragmentBinding::inflate,
|
inflater = AttendanceListFragmentBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.list
|
override fun getScrollingView() = b.list
|
||||||
|
|
||||||
private var viewType = AttendanceFragment.VIEW_DAYS
|
private var viewType = AttendanceFragment.VIEW_DAYS
|
||||||
private var expandSubjectId = 0L
|
private var expandSubjectId = 0L
|
||||||
|
@ -43,7 +43,7 @@ class AttendanceSummaryFragment : BaseFragment<AttendanceSummaryFragmentBinding,
|
|||||||
private var periodSelection = 0
|
private var periodSelection = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.scrollView
|
override fun getScrollingView() = b.scrollView
|
||||||
|
|
||||||
private val manager
|
private val manager
|
||||||
get() = app.attendanceManager
|
get() = app.attendanceManager
|
||||||
|
@ -4,11 +4,7 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.base.fragment
|
package pl.szczodrzynski.edziennik.ui.base.fragment
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -18,49 +14,6 @@ import pl.szczodrzynski.edziennik.ui.login.LoginActivity
|
|||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
internal fun BaseFragment<*, *>.setupCanRefresh() {
|
|
||||||
when (val view = getRefreshScrollingView()) {
|
|
||||||
is RecyclerView -> {
|
|
||||||
canRefresh = !view.canScrollVertically(-1)
|
|
||||||
view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
|
||||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
|
||||||
// disable refresh when scrolled down
|
|
||||||
if (recyclerView.canScrollVertically(-1))
|
|
||||||
canRefresh = false
|
|
||||||
// enable refresh when scrolled to the top and not scrolling anymore
|
|
||||||
else if (newState == RecyclerView.SCROLL_STATE_IDLE)
|
|
||||||
canRefresh = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
is View -> {
|
|
||||||
canRefresh = !view.canScrollVertically(-1)
|
|
||||||
var isTouched = false
|
|
||||||
view.setOnTouchListener { _, event ->
|
|
||||||
// keep track of the touch state
|
|
||||||
when (event.action) {
|
|
||||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> isTouched = false
|
|
||||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> isTouched = true
|
|
||||||
}
|
|
||||||
// disable refresh when scrolled down
|
|
||||||
if (view.canScrollVertically(-1))
|
|
||||||
canRefresh = false
|
|
||||||
// enable refresh when scrolled to the top and not touching anymore
|
|
||||||
else if (!isTouched)
|
|
||||||
canRefresh = true
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
// dispatch the default value to the activity
|
|
||||||
canRefresh = canRefresh
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun BaseFragment<*, *>.setupMainActivity(activity: MainActivity) {
|
internal fun BaseFragment<*, *>.setupMainActivity(activity: MainActivity) {
|
||||||
val items = getBottomSheetItems().toMutableList()
|
val items = getBottomSheetItems().toMutableList()
|
||||||
getMarkAsReadType()?.let { metadataType ->
|
getMarkAsReadType()?.let { metadataType ->
|
||||||
@ -97,6 +50,8 @@ internal fun BaseFragment<*, *>.setupMainActivity(activity: MainActivity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appBars += activity.navView.toolbar
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun BaseFragment<*, *>.setupLoginActivity(activity: LoginActivity) {}
|
internal fun BaseFragment<*, *>.setupLoginActivity(activity: LoginActivity) {}
|
||||||
|
@ -12,6 +12,7 @@ import android.view.ViewGroup
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.google.gson.JsonObject
|
||||||
import com.mikepenz.iconics.typeface.IIcon
|
import com.mikepenz.iconics.typeface.IIcon
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -19,6 +20,7 @@ import kotlinx.coroutines.Job
|
|||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.MainActivity
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
|
import pl.szczodrzynski.edziennik.data.enums.FeatureType
|
||||||
import pl.szczodrzynski.edziennik.data.enums.MetadataType
|
import pl.szczodrzynski.edziennik.data.enums.MetadataType
|
||||||
import pl.szczodrzynski.edziennik.ext.registerSafe
|
import pl.szczodrzynski.edziennik.ext.registerSafe
|
||||||
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
|
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
|
||||||
@ -38,31 +40,37 @@ abstract class BaseFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
|
|
||||||
private var isViewReady: Boolean = false
|
private var isViewReady: Boolean = false
|
||||||
private var inState: Bundle? = null
|
private var inState: Bundle? = null
|
||||||
|
private var appBarAnimator: AppBarColorAnimator? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables or disables the activity's SwipeRefreshLayout.
|
* Whether the view is currently being scrolled
|
||||||
* Use only if [getRefreshScrollingView] is not used.
|
* or is left scrolled away from the top.
|
||||||
*
|
|
||||||
* The [PagerFragment] manages its [canRefresh] state
|
|
||||||
* based on the value of the currently selected page.
|
|
||||||
*/
|
*/
|
||||||
internal var canRefresh = false
|
internal var isScrolled = false
|
||||||
set(value) {
|
set(value) { // cannot be private - PagerFragment onPageScrollStateChanged
|
||||||
field = value
|
field = value
|
||||||
(activity as? MainActivity)?.swipeRefreshLayout?.isEnabled =
|
dispatchCanRefresh()
|
||||||
!canRefreshDisabled && value
|
appBarAnimator?.dispatchLiftOnScroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forcefully disables the activity's SwipeRefreshLayout
|
* Forcefully disables the activity's SwipeRefreshLayout.
|
||||||
* if [getRefreshScrollingView] is used.
|
*
|
||||||
|
* The [PagerFragment] manages its [canRefreshDisabled] state
|
||||||
|
* based on the value of the currently selected page.
|
||||||
*/
|
*/
|
||||||
internal var canRefreshDisabled = false
|
internal var canRefreshDisabled = false
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
canRefresh = canRefresh
|
dispatchCanRefresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of views (usually app bars) that should have their
|
||||||
|
* background color elevated when the fragment is scrolled.
|
||||||
|
*/
|
||||||
|
internal var appBars = mutableSetOf<View>()
|
||||||
|
|
||||||
private var job = Job()
|
private var job = Job()
|
||||||
final override val coroutineContext: CoroutineContext
|
final override val coroutineContext: CoroutineContext
|
||||||
get() = job + Dispatchers.Main
|
get() = job + Dispatchers.Main
|
||||||
@ -83,6 +91,7 @@ abstract class BaseFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
?: return null
|
?: return null
|
||||||
isViewReady = false // reinitialize the view in onResume()
|
isViewReady = false // reinitialize the view in onResume()
|
||||||
inState = savedInstanceState // save the instance state for onResume()
|
inState = savedInstanceState // save the instance state for onResume()
|
||||||
|
appBarAnimator = AppBarColorAnimator(activity, appBars)
|
||||||
return b.root
|
return b.root
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,9 +101,17 @@ abstract class BaseFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
if (!isAdded || isViewReady)
|
if (!isAdded || isViewReady)
|
||||||
return
|
return
|
||||||
isViewReady = true
|
isViewReady = true
|
||||||
setupCanRefresh()
|
// setup the activity (bottom sheet, FAB, etc.)
|
||||||
|
// run before setupScrollListener {} to populate appBars
|
||||||
(activity as? MainActivity)?.let(::setupMainActivity)
|
(activity as? MainActivity)?.let(::setupMainActivity)
|
||||||
(activity as? LoginActivity)?.let(::setupLoginActivity)
|
(activity as? LoginActivity)?.let(::setupLoginActivity)
|
||||||
|
// listen to scroll state changes
|
||||||
|
var first = true
|
||||||
|
setupScrollListener {
|
||||||
|
if (isScrolled != it || first)
|
||||||
|
isScrolled = it
|
||||||
|
first = false
|
||||||
|
}
|
||||||
// let the UI transition for a moment
|
// let the UI transition for a moment
|
||||||
startCoroutineTimer(100L) {
|
startCoroutineTimer(100L) {
|
||||||
if (!isAdded)
|
if (!isAdded)
|
||||||
@ -141,9 +158,10 @@ abstract class BaseFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to retrieve the scrolling view contained in the fragment.
|
* Called to retrieve the scrolling view contained in the fragment.
|
||||||
* The scrolling view is configured to act nicely with the SwipeRefreshLayout.
|
* The scrolling view is configured to work nicely with the app bars
|
||||||
|
* and the SwipeRefreshLayout.
|
||||||
*/
|
*/
|
||||||
open fun getRefreshScrollingView(): View? = null
|
open fun getScrollingView(): View? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to retrieve the FAB label resource and the icon.
|
* Called to retrieve the FAB label resource and the icon.
|
||||||
@ -157,6 +175,22 @@ abstract class BaseFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
*/
|
*/
|
||||||
open fun getMarkAsReadType(): MetadataType? = null
|
open fun getMarkAsReadType(): MetadataType? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to retrieve the [FeatureType] this fragment is associated with.
|
||||||
|
* May also return arguments for the sync task.
|
||||||
|
*
|
||||||
|
* If not provided, swipe-to-refresh is disabled and the manual sync dialog
|
||||||
|
* selects all features by default.
|
||||||
|
*
|
||||||
|
* If [FeatureType] is null, all features are synced (and selected by the
|
||||||
|
* manual sync dialog).
|
||||||
|
*
|
||||||
|
* It is important to return the desired [FeatureType] from the first
|
||||||
|
* call of this method, which runs before [onViewReady]. Otherwise,
|
||||||
|
* swipe-to-refresh will not be enabled unless the view is scrolled.
|
||||||
|
*/
|
||||||
|
open fun getSyncParams(): Pair<FeatureType?, JsonObject?>? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to retrieve any extra bottom sheet items that should be displayed.
|
* Called to retrieve any extra bottom sheet items that should be displayed.
|
||||||
*/
|
*/
|
||||||
|
@ -23,8 +23,8 @@ abstract class PagerFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
inflater: ((inflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> B)?,
|
inflater: ((inflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> B)?,
|
||||||
) : BaseFragment<B, A>(inflater) {
|
) : BaseFragment<B, A>(inflater) {
|
||||||
|
|
||||||
private lateinit var pages: List<Pair<Fragment, String>>
|
private lateinit var pages: List<Pair<BaseFragment<*, *>, String>>
|
||||||
private val fragmentCache = mutableMapOf<Int, Fragment>()
|
private val fragmentCache = mutableMapOf<Int, BaseFragment<*, *>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the default page index that is activated when
|
* Stores the default page index that is activated when
|
||||||
@ -37,6 +37,18 @@ abstract class PagerFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
*/
|
*/
|
||||||
protected open var savedPageSelection = -1
|
protected open var savedPageSelection = -1
|
||||||
|
|
||||||
|
protected val currentFragment: BaseFragment<*, *>?
|
||||||
|
get() = fragmentCache[getViewPager().currentItem]
|
||||||
|
|
||||||
|
final override fun getScrollingView() = null
|
||||||
|
override fun getSyncParams() = currentFragment?.getSyncParams()
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
// add TabLayout before super's setupScrollListener {}
|
||||||
|
appBars += getTabLayout()
|
||||||
|
super.onResume()
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
||||||
if (savedPageSelection == -1)
|
if (savedPageSelection == -1)
|
||||||
savedPageSelection = savedInstanceState?.getInt("pageSelection") ?: 0
|
savedPageSelection = savedInstanceState?.getInt("pageSelection") ?: 0
|
||||||
@ -47,6 +59,7 @@ abstract class PagerFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
override fun getItemCount() = getPageCount()
|
override fun getItemCount() = getPageCount()
|
||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
val fragment = getPageFragment(position)
|
val fragment = getPageFragment(position)
|
||||||
|
fragment.appBars += getTabLayout()
|
||||||
fragmentCache[position] = fragment
|
fragmentCache[position] = fragment
|
||||||
return fragment
|
return fragment
|
||||||
}
|
}
|
||||||
@ -58,15 +71,15 @@ abstract class PagerFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
it.setCurrentItem(savedPageSelection, false)
|
it.setCurrentItem(savedPageSelection, false)
|
||||||
it.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
it.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||||
override fun onPageScrollStateChanged(state: Int) {
|
override fun onPageScrollStateChanged(state: Int) {
|
||||||
canRefresh = when (state) {
|
if (state != ViewPager2.SCROLL_STATE_IDLE) {
|
||||||
ViewPager2.SCROLL_STATE_IDLE -> {
|
// disable swipe-to-refresh during scrolling
|
||||||
val fragment =
|
canRefreshDisabled = true
|
||||||
fragmentCache[it.currentItem] as? BaseFragment<*, *>
|
return
|
||||||
fragment != null && !fragment.canRefreshDisabled && fragment.canRefresh
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> false
|
|
||||||
}
|
}
|
||||||
|
// take child fragment's values
|
||||||
|
val fragment = currentFragment
|
||||||
|
canRefreshDisabled = fragment?.canRefreshDisabled == true
|
||||||
|
isScrolled = fragment?.isScrolled == true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPageSelected(position: Int) {
|
override fun onPageSelected(position: Int) {
|
||||||
@ -125,7 +138,7 @@ abstract class PagerFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
* Only used with the default implementation of [getPageCount], [getPageFragment]
|
* Only used with the default implementation of [getPageCount], [getPageFragment]
|
||||||
* and [getPageTitle].
|
* and [getPageTitle].
|
||||||
*/
|
*/
|
||||||
open suspend fun onCreatePages() = listOf<Pair<Fragment, String>>()
|
open suspend fun onCreatePages() = listOf<Pair<BaseFragment<*, *>, String>>()
|
||||||
|
|
||||||
open fun getPageCount() = pages.size
|
open fun getPageCount() = pages.size
|
||||||
open fun getPageFragment(position: Int) = pages[position].first
|
open fun getPageFragment(position: Int) = pages[position].first
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2024-7-9.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.base.fragment
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.drawable.LayerDrawable
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.animation.LinearInterpolator
|
||||||
|
import androidx.core.widget.NestedScrollView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.color.MaterialColors
|
||||||
|
import com.google.android.material.motion.MotionUtils
|
||||||
|
import com.google.android.material.shape.MaterialShapeDrawable
|
||||||
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
|
import pl.szczodrzynski.edziennik.R
|
||||||
|
import pl.szczodrzynski.edziennik.ext.resolveAttr
|
||||||
|
import pl.szczodrzynski.edziennik.ext.setTintColor
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
internal fun BaseFragment<*, *>.setupScrollListener(setIsScrolled: (Boolean) -> Unit) {
|
||||||
|
when (val view = getScrollingView()) {
|
||||||
|
is RecyclerView -> {
|
||||||
|
setIsScrolled(view.canScrollVertically(-1))
|
||||||
|
view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||||
|
if (view.canScrollVertically(-1))
|
||||||
|
setIsScrolled(true)
|
||||||
|
else if (newState == RecyclerView.SCROLL_STATE_IDLE)
|
||||||
|
setIsScrolled(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
if (view.canScrollVertically(-1))
|
||||||
|
setIsScrolled(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
is View -> {
|
||||||
|
setIsScrolled(view.canScrollVertically(-1))
|
||||||
|
var isTouched = false
|
||||||
|
view.setOnTouchListener { _, event ->
|
||||||
|
// keep track of the touch state
|
||||||
|
when (event.action) {
|
||||||
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> isTouched = false
|
||||||
|
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> isTouched = true
|
||||||
|
}
|
||||||
|
if (view.canScrollVertically(-1))
|
||||||
|
setIsScrolled(true)
|
||||||
|
else if (!isTouched)
|
||||||
|
setIsScrolled(false)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
(view as? NestedScrollView)?.setOnScrollChangeListener(
|
||||||
|
NestedScrollView.OnScrollChangeListener { _, _, _, _, _ ->
|
||||||
|
if (!isTouched && !view.canScrollVertically(-1))
|
||||||
|
setIsScrolled(false)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> { // null
|
||||||
|
// dispatch the default value to the activity
|
||||||
|
setIsScrolled(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun BaseFragment<*, *>.dispatchCanRefresh() {
|
||||||
|
(activity as? MainActivity)?.swipeRefreshLayout?.isEnabled =
|
||||||
|
!canRefreshDisabled && !isScrolled && getSyncParams() != null
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AppBarColorAnimator(
|
||||||
|
context: Context,
|
||||||
|
private val bars: Set<View>,
|
||||||
|
) : ValueAnimator.AnimatorUpdateListener {
|
||||||
|
companion object {
|
||||||
|
// keep track of the current animation value applied globally to the view
|
||||||
|
private var currentValue = 0.0f
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var animator: ValueAnimator
|
||||||
|
|
||||||
|
private val barColor = R.attr.colorSurface.resolveAttr(context)
|
||||||
|
private val liftColor = R.attr.colorSurfaceContainer.resolveAttr(context)
|
||||||
|
|
||||||
|
context(BaseFragment<*, *>)
|
||||||
|
fun dispatchLiftOnScroll() {
|
||||||
|
if (::animator.isInitialized)
|
||||||
|
animator.cancel()
|
||||||
|
animator = ValueAnimator.ofFloat(
|
||||||
|
currentValue,
|
||||||
|
if (isScrolled) 1.0f else 0.0f,
|
||||||
|
)
|
||||||
|
animator.duration = MotionUtils.resolveThemeDuration(
|
||||||
|
activity,
|
||||||
|
R.attr.motionDurationMedium2,
|
||||||
|
resources.getInteger(R.integer.app_bar_elevation_anim_duration),
|
||||||
|
).toLong()
|
||||||
|
animator.interpolator = MotionUtils.resolveThemeInterpolator(
|
||||||
|
activity,
|
||||||
|
R.attr.motionEasingStandardInterpolator,
|
||||||
|
LinearInterpolator(),
|
||||||
|
)
|
||||||
|
animator.addUpdateListener(this)
|
||||||
|
animator.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationUpdate(animation: ValueAnimator) {
|
||||||
|
currentValue = animation.animatedValue as Float
|
||||||
|
val mixedColor = MaterialColors.layer(
|
||||||
|
barColor,
|
||||||
|
liftColor,
|
||||||
|
currentValue,
|
||||||
|
)
|
||||||
|
for (bar in bars) {
|
||||||
|
when (val drawable = bar.background) {
|
||||||
|
is MaterialShapeDrawable -> drawable.fillColor = ColorStateList.valueOf(mixedColor)
|
||||||
|
is LayerDrawable -> drawable.getDrawable(0).setTintColor(mixedColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.MainActivity
|
|||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Notice
|
import pl.szczodrzynski.edziennik.data.db.entity.Notice
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.NoticeFull
|
import pl.szczodrzynski.edziennik.data.db.full.NoticeFull
|
||||||
|
import pl.szczodrzynski.edziennik.data.enums.FeatureType
|
||||||
import pl.szczodrzynski.edziennik.data.enums.MetadataType
|
import pl.szczodrzynski.edziennik.data.enums.MetadataType
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentBehaviourBinding
|
import pl.szczodrzynski.edziennik.databinding.FragmentBehaviourBinding
|
||||||
import pl.szczodrzynski.edziennik.ext.resolveAttr
|
import pl.szczodrzynski.edziennik.ext.resolveAttr
|
||||||
@ -20,8 +21,9 @@ class BehaviourFragment : BaseFragment<FragmentBehaviourBinding, MainActivity>(
|
|||||||
inflater = FragmentBehaviourBinding::inflate,
|
inflater = FragmentBehaviourBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.noticesView
|
override fun getScrollingView() = b.noticesView
|
||||||
override fun getMarkAsReadType() = MetadataType.NOTICE
|
override fun getMarkAsReadType() = MetadataType.NOTICE
|
||||||
|
override fun getSyncParams() = FeatureType.BEHAVIOUR to null
|
||||||
|
|
||||||
private var displayMode = MODE_YEAR
|
private var displayMode = MODE_YEAR
|
||||||
private var noticeList: List<NoticeFull>? = null
|
private var noticeList: List<NoticeFull>? = null
|
||||||
|
@ -39,7 +39,7 @@ class LabPageFragment : BaseFragment<LabFragmentBinding, AppCompatActivity>(
|
|||||||
inflater = LabFragmentBinding::inflate,
|
inflater = LabFragmentBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.scrollView
|
override fun getScrollingView() = b.scrollView
|
||||||
|
|
||||||
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
||||||
b.app = app
|
b.app = app
|
||||||
|
@ -17,7 +17,7 @@ class LabPlaygroundFragment : BaseFragment<LabPlaygroundBinding, AppCompatActivi
|
|||||||
inflater = LabPlaygroundBinding::inflate,
|
inflater = LabPlaygroundBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.root
|
override fun getScrollingView() = b.root
|
||||||
|
|
||||||
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
||||||
Timber.d("textColorSecondary: ${android.R.attr.textColorSecondary.resolveAttr(activity)}")
|
Timber.d("textColorSecondary: ${android.R.attr.textColorSecondary.resolveAttr(activity)}")
|
||||||
|
@ -29,7 +29,7 @@ class LabProfileFragment : BaseFragment<TemplateListPageFragmentBinding, AppComp
|
|||||||
private const val TAG = "LabProfileFragment"
|
private const val TAG = "LabProfileFragment"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.list
|
override fun getScrollingView() = b.list
|
||||||
|
|
||||||
private lateinit var adapter: LabJsonAdapter
|
private lateinit var adapter: LabJsonAdapter
|
||||||
private val loginStore by lazy {
|
private val loginStore by lazy {
|
||||||
|
@ -18,6 +18,7 @@ import pl.szczodrzynski.edziennik.R
|
|||||||
import pl.szczodrzynski.edziennik.core.manager.GradesManager
|
import pl.szczodrzynski.edziennik.core.manager.GradesManager
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||||
|
import pl.szczodrzynski.edziennik.data.enums.FeatureType
|
||||||
import pl.szczodrzynski.edziennik.data.enums.MetadataType
|
import pl.szczodrzynski.edziennik.data.enums.MetadataType
|
||||||
import pl.szczodrzynski.edziennik.data.enums.NavTarget
|
import pl.szczodrzynski.edziennik.data.enums.NavTarget
|
||||||
import pl.szczodrzynski.edziennik.databinding.GradesListFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.GradesListFragmentBinding
|
||||||
@ -38,8 +39,9 @@ class GradesListFragment : BaseFragment<GradesListFragmentBinding, MainActivity>
|
|||||||
inflater = GradesListFragmentBinding::inflate,
|
inflater = GradesListFragmentBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.list
|
override fun getScrollingView() = b.list
|
||||||
override fun getMarkAsReadType() = MetadataType.GRADE
|
override fun getMarkAsReadType() = MetadataType.GRADE
|
||||||
|
override fun getSyncParams() = FeatureType.GRADES to null
|
||||||
override fun getBottomSheetItems() = listOf(
|
override fun getBottomSheetItems() = listOf(
|
||||||
BottomSheetPrimaryItem(true)
|
BottomSheetPrimaryItem(true)
|
||||||
.withTitle(R.string.menu_grades_config)
|
.withTitle(R.string.menu_grades_config)
|
||||||
|
@ -78,7 +78,8 @@ class HomeFragment : BaseFragment<FragmentHomeBinding, MainActivity>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.scrollView
|
override fun getScrollingView() = b.scrollView
|
||||||
|
override fun getSyncParams() = null to null
|
||||||
override fun getBottomSheetItems() = listOf(
|
override fun getBottomSheetItems() = listOf(
|
||||||
BottomSheetPrimaryItem(true)
|
BottomSheetPrimaryItem(true)
|
||||||
.withTitle(R.string.menu_add_remove_cards)
|
.withTitle(R.string.menu_add_remove_cards)
|
||||||
|
@ -7,6 +7,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.MainActivity
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||||
|
import pl.szczodrzynski.edziennik.data.enums.FeatureType
|
||||||
import pl.szczodrzynski.edziennik.databinding.HomeworkListFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.HomeworkListFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ext.getInt
|
import pl.szczodrzynski.edziennik.ext.getInt
|
||||||
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
|
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
|
||||||
@ -20,7 +21,8 @@ class HomeworkListFragment : BaseFragment<HomeworkListFragmentBinding, MainActiv
|
|||||||
inflater = HomeworkListFragmentBinding::inflate,
|
inflater = HomeworkListFragmentBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.list
|
override fun getScrollingView() = b.list
|
||||||
|
override fun getSyncParams() = FeatureType.HOMEWORK to null
|
||||||
|
|
||||||
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
||||||
val homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT)
|
val homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT)
|
||||||
|
@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.App
|
|||||||
import pl.szczodrzynski.edziennik.MainActivity
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||||
|
import pl.szczodrzynski.edziennik.data.enums.FeatureType
|
||||||
import pl.szczodrzynski.edziennik.data.enums.NavTarget
|
import pl.szczodrzynski.edziennik.data.enums.NavTarget
|
||||||
import pl.szczodrzynski.edziennik.databinding.MessagesListFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.MessagesListFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ext.Bundle
|
import pl.szczodrzynski.edziennik.ext.Bundle
|
||||||
@ -27,16 +28,24 @@ class MessagesListFragment : BaseFragment<MessagesListFragmentBinding, MainActiv
|
|||||||
inflater = MessagesListFragmentBinding::inflate,
|
inflater = MessagesListFragmentBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.list
|
override fun getScrollingView() = b.list
|
||||||
|
override fun getSyncParams() = when (messageType) {
|
||||||
|
Message.TYPE_RECEIVED -> FeatureType.MESSAGES_INBOX to null
|
||||||
|
Message.TYPE_SENT -> FeatureType.MESSAGES_SENT to null
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var adapter: MessagesAdapter
|
private lateinit var adapter: MessagesAdapter
|
||||||
private val manager
|
private val manager
|
||||||
get() = app.messageManager
|
get() = app.messageManager
|
||||||
var teachers = listOf<Teacher>()
|
var teachers = listOf<Teacher>()
|
||||||
|
|
||||||
|
private val messageType by lazy {
|
||||||
|
arguments.getInt("messageType", Message.TYPE_RECEIVED)
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
||||||
val messageType = arguments.getInt("messageType", Message.TYPE_RECEIVED)
|
|
||||||
var recyclerViewState =
|
var recyclerViewState =
|
||||||
savedInstanceState?.getParcelable<LinearLayoutManager.SavedState>("recyclerViewState")
|
savedInstanceState?.getParcelable<LinearLayoutManager.SavedState>("recyclerViewState")
|
||||||
val searchText = savedInstanceState?.getString("searchText")
|
val searchText = savedInstanceState?.getString("searchText")
|
||||||
@ -75,9 +84,6 @@ class MessagesListFragment : BaseFragment<MessagesListFragmentBinding, MainActiv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageType != Message.TYPE_RECEIVED && messageType != Message.TYPE_SENT)
|
|
||||||
canRefreshDisabled = true
|
|
||||||
|
|
||||||
// show/hide relevant views
|
// show/hide relevant views
|
||||||
b.progressBar.isVisible = false
|
b.progressBar.isVisible = false
|
||||||
b.list.isVisible = messages.isNotEmpty()
|
b.list.isVisible = messages.isNotEmpty()
|
||||||
|
@ -24,6 +24,7 @@ class NotesFragment : BaseFragment<NotesFragmentBinding, MainActivity>(
|
|||||||
inflater = NotesFragmentBinding::inflate,
|
inflater = NotesFragmentBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
override fun getScrollingView() = b.list
|
||||||
override fun getFab() =
|
override fun getFab() =
|
||||||
R.string.notes_action_add to CommunityMaterial.Icon3.cmd_text_box_plus_outline
|
R.string.notes_action_add to CommunityMaterial.Icon3.cmd_text_box_plus_outline
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ class NotificationsListFragment : BaseFragment<NotificationsListFragmentBinding,
|
|||||||
inflater = NotificationsListFragmentBinding::inflate,
|
inflater = NotificationsListFragmentBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
override fun getScrollingView() = b.list
|
||||||
override fun getBottomSheetItems() = listOf(
|
override fun getBottomSheetItems() = listOf(
|
||||||
BottomSheetPrimaryItem(true)
|
BottomSheetPrimaryItem(true)
|
||||||
.withTitle(R.string.menu_remove_notifications)
|
.withTitle(R.string.menu_remove_notifications)
|
||||||
|
@ -22,6 +22,8 @@ class TeachersListFragment : BaseFragment<TeachersListFragmentBinding, MainActiv
|
|||||||
inflater = TeachersListFragmentBinding::inflate,
|
inflater = TeachersListFragmentBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
override fun getScrollingView() = b.list
|
||||||
|
|
||||||
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
||||||
val adapter = TeachersAdapter(activity)
|
val adapter = TeachersAdapter(activity)
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ class TemplateListFragment : BaseFragment<TemplateListFragmentBinding, MainActiv
|
|||||||
inflater = TemplateListFragmentBinding::inflate,
|
inflater = TemplateListFragmentBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.list
|
override fun getScrollingView() = b.list
|
||||||
|
|
||||||
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
||||||
val adapter = TemplateAdapter(activity)
|
val adapter = TemplateAdapter(activity)
|
||||||
|
@ -18,7 +18,7 @@ class TemplateListPageFragment : BaseFragment<TemplateListPageFragmentBinding, M
|
|||||||
inflater = TemplateListPageFragmentBinding::inflate,
|
inflater = TemplateListPageFragmentBinding::inflate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.list
|
override fun getScrollingView() = b.list
|
||||||
|
|
||||||
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
||||||
val adapter = TemplateAdapter(activity)
|
val adapter = TemplateAdapter(activity)
|
||||||
|
@ -18,11 +18,11 @@ class TemplatePageFragment : BaseFragment<TemplatePageFragmentBinding, MainActiv
|
|||||||
b.text.text = "Fragment VIEW READY"
|
b.text.text = "Fragment VIEW READY"
|
||||||
b.editText.setText(savedInstanceState.getString("editText", "default"))
|
b.editText.setText(savedInstanceState.getString("editText", "default"))
|
||||||
|
|
||||||
canRefresh = true
|
canRefreshDisabled = false
|
||||||
b.button.addOnCheckedChangeListener { button, isChecked ->
|
b.button.addOnCheckedChangeListener { button, isChecked ->
|
||||||
canRefresh = isChecked
|
canRefreshDisabled = !isChecked
|
||||||
}
|
}
|
||||||
b.button.isChecked = canRefresh
|
b.button.isChecked = !canRefreshDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
@ -74,6 +74,7 @@ class TimetableDayFragment : BaseFragment<TimetableDayFragmentBinding, MainActiv
|
|||||||
private var endHour = DEFAULT_END_HOUR
|
private var endHour = DEFAULT_END_HOUR
|
||||||
private var firstEventMinute = 24 * 60
|
private var firstEventMinute = 24 * 60
|
||||||
private var paddingTop = 0
|
private var paddingTop = 0
|
||||||
|
private var syncArgs = JsonObject()
|
||||||
|
|
||||||
private var viewsRemoved = false
|
private var viewsRemoved = false
|
||||||
|
|
||||||
@ -101,7 +102,8 @@ class TimetableDayFragment : BaseFragment<TimetableDayFragmentBinding, MainActiv
|
|||||||
}
|
}
|
||||||
private val dayView by dayViewDelegate
|
private val dayView by dayViewDelegate
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.scrollView
|
override fun getScrollingView() = b.scrollView
|
||||||
|
override fun getSyncParams() = FeatureType.TIMETABLE to syncArgs
|
||||||
|
|
||||||
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
||||||
this.inflater = AsyncLayoutInflater(requireContext())
|
this.inflater = AsyncLayoutInflater(requireContext())
|
||||||
@ -110,6 +112,10 @@ class TimetableDayFragment : BaseFragment<TimetableDayFragmentBinding, MainActiv
|
|||||||
startHour = arguments?.getInt("startHour") ?: DEFAULT_START_HOUR
|
startHour = arguments?.getInt("startHour") ?: DEFAULT_START_HOUR
|
||||||
endHour = arguments?.getInt("endHour") ?: DEFAULT_END_HOUR
|
endHour = arguments?.getInt("endHour") ?: DEFAULT_END_HOUR
|
||||||
|
|
||||||
|
syncArgs = JsonObject(
|
||||||
|
"weekStart" to date.weekStart.stringY_m_d
|
||||||
|
)
|
||||||
|
|
||||||
// observe lesson database
|
// observe lesson database
|
||||||
app.db.timetableDao().getAllForDate(App.profileId, date).observe(this) { lessons ->
|
app.db.timetableDao().getAllForDate(App.profileId, date).observe(this) { lessons ->
|
||||||
launch {
|
launch {
|
||||||
|
@ -92,16 +92,10 @@
|
|||||||
<com.google.android.material.tabs.TabLayout
|
<com.google.android.material.tabs.TabLayout
|
||||||
android:id="@+id/tabLayout"
|
android:id="@+id/tabLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?actionBarSize"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:background="?colorSurfaceContainerLow"
|
|
||||||
android:foreground="@color/colorSurface_2dp"
|
|
||||||
android:minHeight="?actionBarSize"
|
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:tabIndicatorColor="?colorPrimary"
|
|
||||||
app:tabMode="auto"
|
app:tabMode="auto"
|
||||||
app:tabSelectedTextColor="?colorPrimary"
|
|
||||||
app:tabTextColor="?android:textColorPrimary"
|
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user