From 37ea65e3fc38fd522299ffeb390128ef4ac47684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 16 Nov 2019 21:16:18 +0100 Subject: [PATCH] [Timetable] Add SwipeToRefresh. Select start&end hours based on lesson ranges. --- app/build.gradle | 2 +- .../pl/szczodrzynski/edziennik/Extensions.kt | 51 +++++- .../modules/timetable/v2/TimetableFragment.kt | 89 +++++++--- .../timetable/v2/TimetablePagerAdapter.kt | 9 +- .../timetable/v2/day/TimetableDayFragment.kt | 165 +++++++++++++----- .../edziennik/utils/ListenerScrollView.kt | 46 +++++ .../main/res/layout/fragment_timetable_v2.xml | 145 +++++++-------- .../res/layout/fragment_timetable_v2_day.xml | 121 ------------- .../main/res/layout/timetable_free_day.xml | 47 +++++ .../main/res/layout/timetable_no_lessons.xml | 14 ++ .../res/layout/timetable_no_timetable.xml | 50 ++++++ app/src/main/res/values/styles.xml | 4 + 12 files changed, 475 insertions(+), 268 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/utils/ListenerScrollView.kt create mode 100644 app/src/main/res/layout/timetable_free_day.xml create mode 100644 app/src/main/res/layout/timetable_no_lessons.xml create mode 100644 app/src/main/res/layout/timetable_no_timetable.xml diff --git a/app/build.gradle b/app/build.gradle index 9407367d..11c43b78 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -169,7 +169,7 @@ dependencies { implementation 'com.github.kuba2k2:RecyclerTabLayout:700f980584' - implementation 'com.github.kuba2k2:Tachyon:bbd421f994' + implementation 'com.github.kuba2k2:Tachyon:551943a6b5' } repositories { mavenCentral() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 7ec257fd..1632a3bf 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -4,7 +4,9 @@ import android.Manifest import android.app.Activity import android.content.Context import android.content.pm.PackageManager +import android.content.res.Resources import android.graphics.Typeface +import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle import android.text.* @@ -13,9 +15,10 @@ import android.text.style.StrikethroughSpan import android.text.style.StyleSpan import android.util.LongSparseArray import android.util.SparseArray +import android.util.TypedValue import android.view.View import android.widget.TextView -import androidx.annotation.StringRes +import androidx.annotation.* import androidx.core.app.ActivityCompat import androidx.core.util.forEach import androidx.lifecycle.LifecycleOwner @@ -446,3 +449,49 @@ fun LiveData.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observ } }) } + +/** + * Convert a value in dp to pixels. + */ +val Int.dp: Int + get() = (this * Resources.getSystem().displayMetrics.density).toInt() +/** + * Convert a value in pixels to dp. + */ +val Int.px: Int + get() = (this / Resources.getSystem().displayMetrics.density).toInt() + +@ColorInt +fun @receiver:AttrRes Int.resolveAttr(context: Context?): Int { + val typedValue = TypedValue() + context?.theme?.resolveAttribute(this, typedValue, true) + return typedValue.data +} +@ColorInt +fun @receiver:ColorRes Int.resolveColor(context: Context): Int { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + context.resources.getColor(this, context.theme) + } + else { + context.resources.getColor(this) + } +} +fun @receiver:DrawableRes Int.resolveDrawable(context: Context): Drawable { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + context.resources.getDrawable(this, context.theme) + } + else { + context.resources.getDrawable(this) + } +} + +fun View.findParentById(targetId: Int): View? { + if (id == targetId) { + return this + } + val viewParent = this.parent ?: return null + if (viewParent is View) { + return viewParent.findParentById(targetId) + } + return null +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt index 56627dc0..e97421fd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt @@ -5,12 +5,14 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.viewpager.widget.ViewPager import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import kotlinx.coroutines.* import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.R @@ -18,16 +20,25 @@ import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2Binding import pl.szczodrzynski.edziennik.utils.Themes import pl.szczodrzynski.edziennik.utils.models.Date +import kotlin.coroutines.CoroutineContext -class TimetableFragment : Fragment() { +class TimetableFragment : Fragment(), CoroutineScope { companion object { private const val TAG = "TimetableFragment" const val ACTION_SCROLL_TO_DATE = "pl.szczodrzynski.edziennik.timetable.SCROLL_TO_DATE" + const val DEFAULT_START_HOUR = 6 + const val DEFAULT_END_HOUR = 19 + var pageSelection: Date? = null } private lateinit var app: App private lateinit var activity: MainActivity private lateinit var b: FragmentTimetableV2Binding + + private lateinit var job: Job + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + private var fabShown = false private val items = mutableListOf() @@ -36,11 +47,13 @@ class TimetableFragment : Fragment() { if (context == null) return null app = activity.application as App + job = Job() 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 = FragmentTimetableV2Binding.inflate(inflater) + b.refreshLayout.setParent(activity.swipeRefreshLayout) return b.root } @@ -62,48 +75,65 @@ class TimetableFragment : Fragment() { activity.unregisterReceiver(broadcastReceiver) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { launch { // TODO check if app, activity, b can be null if (app.profile == null || !isAdded) - return + return@launch if (app.profile.loginStoreType == LOGIN_TYPE_LIBRUS && app.profile.getLoginData("timetableNotPublic", false)) { b.timetableLayout.visibility = View.GONE b.timetableNotPublicLayout.visibility = View.VISIBLE - return + return@launch } b.timetableLayout.visibility = View.VISIBLE b.timetableNotPublicLayout.visibility = View.GONE - items.clear() - - val monthDayCount = listOf(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) - val today = Date.getToday().value - val yearStart = app.profile.dateSemester1Start?.clone() ?: return - val yearEnd = app.profile.dateYearEnd ?: return - while (yearStart.value <= yearEnd.value) { - items += yearStart.clone() - var maxDays = monthDayCount[yearStart.month-1] - if (yearStart.month == 2 && yearStart.isLeap) - maxDays++ - yearStart.day++ - if (yearStart.day > maxDays) { - yearStart.day = 1 - yearStart.month++ - } - if (yearStart.month > 12) { - yearStart.month = 1 - yearStart.year++ - } - } + var startHour = DEFAULT_START_HOUR + var endHour = DEFAULT_END_HOUR + val deferred = async(Dispatchers.Default) { + items.clear() - val pagerAdapter = TimetablePagerAdapter(fragmentManager ?: return, items) + val monthDayCount = listOf(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) + + val yearStart = app.profile.dateSemester1Start?.clone() ?: return@async + val yearEnd = app.profile.dateYearEnd ?: return@async + while (yearStart.value <= yearEnd.value) { + items += yearStart.clone() + var maxDays = monthDayCount[yearStart.month-1] + if (yearStart.month == 2 && yearStart.isLeap) + maxDays++ + yearStart.day++ + if (yearStart.day > maxDays) { + yearStart.day = 1 + yearStart.month++ + } + if (yearStart.month > 12) { + yearStart.month = 1 + yearStart.year++ + } + } + + val lessonRanges = app.db.lessonRangeDao().getAllNow(App.profileId) + startHour = lessonRanges.map { it.startTime.hour }.min() ?: DEFAULT_START_HOUR + endHour = lessonRanges.map { it.endTime.hour }.max()?.plus(1) ?: DEFAULT_END_HOUR + } + deferred.await() + + val pagerAdapter = TimetablePagerAdapter( + fragmentManager ?: return@launch, + items, + startHour, + endHour + ) b.viewPager.offscreenPageLimit = 2 b.viewPager.adapter = pagerAdapter b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(state: Int) { - + if (b.refreshLayout != null) { + Log.d(TAG, "State $state") + b.refreshLayout.isEnabled = state == ViewPager.SCROLL_STATE_IDLE + } } override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { @@ -111,6 +141,7 @@ class TimetableFragment : Fragment() { } override fun onPageSelected(position: Int) { + pageSelection = items[position] activity.navView.bottomBar.fabEnable = items[position].value != today if (activity.navView.bottomBar.fabEnable && !fabShown) { activity.gainAttentionFAB() @@ -123,10 +154,10 @@ class TimetableFragment : Fragment() { b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, false) //activity.navView.bottomBar.fabEnable = true - activity.navView.bottomBar.fabExtendedText = getString(pl.szczodrzynski.edziennik.R.string.timetable_today) + activity.navView.bottomBar.fabExtendedText = getString(R.string.timetable_today) activity.navView.bottomBar.fabIcon = CommunityMaterial.Icon.cmd_calendar_today activity.navView.setFabOnClickListener(View.OnClickListener { b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, true) }) - } + }} } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetablePagerAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetablePagerAdapter.kt index 45a5e5d1..645a2b3e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetablePagerAdapter.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetablePagerAdapter.kt @@ -8,7 +8,12 @@ import pl.szczodrzynski.edziennik.ui.modules.timetable.v2.day.TimetableDayFragme import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Week -class TimetablePagerAdapter(val fragmentManager: FragmentManager, val items: List) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { +class TimetablePagerAdapter( + fragmentManager: FragmentManager, + private val items: List, + private val startHour: Int, + private val endHour: Int +) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { companion object { private const val TAG = "TimetablePagerAdapter" } @@ -21,6 +26,8 @@ class TimetablePagerAdapter(val fragmentManager: FragmentManager, val items: Lis return TimetableDayFragment().apply { arguments = Bundle().apply { putInt("date", items[position].value) + putInt("startHour", startHour) + putInt("endHour", endHour) } } /*return TimetableDayFragment().apply { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt index 953cd7d7..3ca45828 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt @@ -7,42 +7,97 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import android.widget.TextView +import androidx.asynclayoutinflater.view.AsyncLayoutInflater +import androidx.core.view.setPadding import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import com.linkedin.android.tachyon.DayView +import com.linkedin.android.tachyon.DayViewConfig +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull -import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2DayBinding import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding +import pl.szczodrzynski.edziennik.databinding.TimetableNoTimetableBinding import pl.szczodrzynski.edziennik.ui.dialogs.timetable.LessonDetailsDialog +import pl.szczodrzynski.edziennik.ui.modules.timetable.v2.TimetableFragment.Companion.DEFAULT_END_HOUR +import pl.szczodrzynski.edziennik.ui.modules.timetable.v2.TimetableFragment.Companion.DEFAULT_START_HOUR +import pl.szczodrzynski.edziennik.utils.ListenerScrollView import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.navlib.getColorFromAttr import java.util.* +import kotlin.coroutines.CoroutineContext import kotlin.math.min -class TimetableDayFragment() : Fragment() { +class TimetableDayFragment : Fragment(), CoroutineScope { companion object { private const val TAG = "TimetableDayFragment" } private lateinit var app: App private lateinit var activity: MainActivity - private lateinit var b: FragmentTimetableV2DayBinding + private lateinit var inflater: AsyncLayoutInflater + + private lateinit var job: Job + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + private lateinit var date: Date + private var startHour = DEFAULT_START_HOUR + private var endHour = DEFAULT_END_HOUR + private var firstEventMinute = 24*60 + + // find SwipeRefreshLayout in the hierarchy + private val refreshLayout by lazy { view?.findParentById(R.id.refreshLayout) } + // the day ScrollView + private val dayScrollDelegate = lazy { + val dayScroll = ListenerScrollView(context!!) + dayScroll.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + dayScroll.setOnRefreshLayoutEnabledListener { enabled -> + refreshLayout?.isEnabled = enabled + } + dayScroll + } + private val dayScroll by dayScrollDelegate + // the lesson DayView + private val dayView by lazy { + val dayView = DayView(context!!, DayViewConfig( + startHour = startHour, + endHour = endHour, + dividerHeight = 1.dp, + halfHourHeight = 60.dp, + hourDividerColor = R.attr.hourDividerColor.resolveAttr(context), + halfHourDividerColor = R.attr.halfHourDividerColor.resolveAttr(context), + hourLabelWidth = 40.dp, + hourLabelMarginEnd = 10.dp, + eventMargin = 2.dp + ), true) + dayView.setPadding(10.dp) + dayScroll.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + dayScroll.addView(dayView) + dayView + } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as MainActivity?) ?: return null if (context == null) return null app = activity.application as App - b = FragmentTimetableV2DayBinding.inflate(inflater) + job = Job() + this.inflater = AsyncLayoutInflater(context!!) date = arguments?.getInt("date")?.let { Date.fromValue(it) } ?: Date.getToday() - return b.root + startHour = arguments?.getInt("startHour") ?: DEFAULT_START_HOUR + endHour = arguments?.getInt("endHour") ?: DEFAULT_END_HOUR + return FrameLayout(activity).apply { + layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -52,45 +107,42 @@ class TimetableDayFragment() : Fragment() { Log.d(TAG, "onViewCreated, date=$date") - // Inflate a label view for each hour the day view will display - val hourLabelViews = ArrayList() - for (i in b.day.startHour..b.day.endHour) { - val hourLabelView = layoutInflater.inflate(R.layout.timetable_hour_label, b.day, false) as TextView - hourLabelView.text = "$i:00" - hourLabelViews.add(hourLabelView) - } - b.day.setHourLabelViews(hourLabelViews) - + // observe lesson database app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer> { lessons -> - buildLessonViews(lessons) + processLessonList(lessons) }) } - private fun buildLessonViews(lessons: List) { + private fun processLessonList(lessons: List) { + // no lessons - timetable not downloaded yet if (lessons.isEmpty()) { - b.dayScroll.visibility = View.GONE - b.noTimetableLayout.visibility = View.VISIBLE - b.noLessonsLayout.visibility = View.GONE - val weekStart = date.clone().stepForward(0, 0, -date.weekDay).stringY_m_d - b.noTimetableSync.onClick { - it.isEnabled = false - EdziennikTask.syncProfile( - profileId = App.profileId, - viewIds = listOf( - DRAWER_ITEM_TIMETABLE to 0 - ), - arguments = JsonObject( - "weekStart" to weekStart - ) - ).enqueue(activity) + inflater.inflate(R.layout.timetable_no_timetable, view as FrameLayout) { view, _, parent -> + parent?.removeAllViews() + parent?.addView(view) + val b = TimetableNoTimetableBinding.bind(view) + val weekStart = date.clone().stepForward(0, 0, -date.weekDay).stringY_m_d + b.noTimetableSync.onClick { + it.isEnabled = false + EdziennikTask.syncProfile( + profileId = App.profileId, + viewIds = listOf( + DRAWER_ITEM_TIMETABLE to 0 + ), + arguments = JsonObject( + "weekStart" to weekStart + ) + ).enqueue(activity) + } + b.noTimetableWeek.setText(R.string.timetable_no_timetable_week, weekStart) } - b.noTimetableWeek.setText(R.string.timetable_no_timetable_week, weekStart) return } + // one lesson indicating a day without lessons if (lessons.size == 1 && lessons[0].type == Lesson.TYPE_NO_LESSONS) { - b.dayScroll.visibility = View.GONE - b.noTimetableLayout.visibility = View.GONE - b.noLessonsLayout.visibility = View.VISIBLE + inflater.inflate(R.layout.timetable_no_lessons, view as FrameLayout) { view, _, parent -> + parent?.removeAllViews() + parent?.addView(view) + } return } @@ -101,23 +153,37 @@ class TimetableDayFragment() : Fragment() { return } - b.dayScroll.visibility = View.VISIBLE - b.noTimetableLayout.visibility = View.GONE - b.noLessonsLayout.visibility = View.GONE + // clear the root view and add the ScrollView + (view as FrameLayout).removeAllViews() + (view as FrameLayout).addView(dayScroll) - var firstEventMinute = 24*60 + // Inflate a label view for each hour the day view will display + val hourLabelViews = ArrayList() + for (i in dayView.startHour..dayView.endHour) { + val hourLabelView = layoutInflater.inflate(R.layout.timetable_hour_label, dayView, false) as TextView + hourLabelView.text = "$i:00" + hourLabelViews.add(hourLabelView) + } + dayView.setHourLabelViews(hourLabelViews) + + buildLessonViews(lessons) + } + + private fun buildLessonViews(lessons: List) { + if (!isAdded) + return val eventViews = mutableListOf() val eventTimeRanges = mutableListOf() // Reclaim all of the existing event views so we can reuse them if needed, this process // can be useful if your day view is hosted in a recycler view for example - val recycled = b.day.removeEventViews() + val recycled = dayView.removeEventViews() var remaining = recycled?.size ?: 0 val arrowRight = " → " val bullet = " • " - val colorSecondary = getColorFromAttr(activity, android.R.attr.textColorSecondary) + val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity) for (lesson in lessons) { val startTime = lesson.displayStartTime ?: continue @@ -127,7 +193,7 @@ class TimetableDayFragment() : Fragment() { // Try to recycle an existing event view if there are enough left, otherwise inflate // a new one - val eventView = (if (remaining > 0) recycled?.get(--remaining) else layoutInflater.inflate(R.layout.timetable_lesson, b.day, false)) + val eventView = (if (remaining > 0) recycled?.get(--remaining) else layoutInflater.inflate(R.layout.timetable_lesson, dayView, false)) ?: continue val lb = TimetableLessonBinding.bind(eventView) eventViews += eventView @@ -274,9 +340,16 @@ class TimetableDayFragment() : Fragment() { eventTimeRanges.add(DayView.EventTimeRange(startMinute, endMinute)) } - val minuteHeight = (b.day.getHourTop(1) - b.day.getHourTop(0)).toFloat() / 60f - val firstEventTop = (firstEventMinute - b.day.startHour * 60) * minuteHeight - b.day.setEventViews(eventViews, eventTimeRanges) - b.dayScroll.scrollTo(0, firstEventTop.toInt()) + dayView.setEventViews(eventViews, eventTimeRanges) + val firstEventTop = (firstEventMinute - dayView.startHour * 60) * dayView.minuteHeight + dayScroll.scrollTo(0, firstEventTop.toInt()) + } + + override fun onResume() { + super.onResume() + if (dayScrollDelegate.isInitialized()) { + val firstEventTop = (firstEventMinute - dayView.startHour * 60) * dayView.minuteHeight + dayScroll.scrollTo(0, firstEventTop.toInt()) + } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/ListenerScrollView.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/ListenerScrollView.kt new file mode 100644 index 00000000..71e566aa --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/ListenerScrollView.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-16. + */ + +package pl.szczodrzynski.edziennik.utils + +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import android.widget.ScrollView + +class ListenerScrollView( + context: Context, + attrs: AttributeSet? = null +) : ScrollView(context, attrs) { + + private var onScrollChangedListener: ((v: ListenerScrollView, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int) -> Unit)? = null + private var onRefreshLayoutEnabledListener: ((enabled: Boolean) -> Unit)? = null + private var refreshLayoutEnabled = true + + init { + setOnTouchListener { _, event -> + if (event.action == MotionEvent.ACTION_UP) { + refreshLayoutEnabled = scrollY < 10 + onRefreshLayoutEnabledListener?.invoke(refreshLayoutEnabled) + } + false + } + } + + override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) { + onScrollChangedListener?.invoke(this, l, t, oldl, oldt) + if (t > 10 && refreshLayoutEnabled) { + refreshLayoutEnabled = false + onRefreshLayoutEnabledListener?.invoke(refreshLayoutEnabled) + } + } + + fun setOnScrollChangedListener(l: ((v: ListenerScrollView, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int) -> Unit)?) { + onScrollChangedListener = l + } + + fun setOnRefreshLayoutEnabledListener(l: ((enabled: Boolean) -> Unit)?) { + onRefreshLayoutEnabledListener = l + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_timetable_v2.xml b/app/src/main/res/layout/fragment_timetable_v2.xml index 72c22fdb..8b30d15b 100644 --- a/app/src/main/res/layout/fragment_timetable_v2.xml +++ b/app/src/main/res/layout/fragment_timetable_v2.xml @@ -2,89 +2,96 @@ - - + android:layout_height="match_parent"> - - - - - - + tools:visibility="gone"> - + - + + - + - + - + android:orientation="vertical" + android:visibility="gone" + tools:visibility="visible"> - + - + - + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_timetable_v2_day.xml b/app/src/main/res/layout/fragment_timetable_v2_day.xml index 36ccd46e..a94602d3 100644 --- a/app/src/main/res/layout/fragment_timetable_v2_day.xml +++ b/app/src/main/res/layout/fragment_timetable_v2_day.xml @@ -28,126 +28,5 @@ tools:visibility="gone"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/timetable_free_day.xml b/app/src/main/res/layout/timetable_free_day.xml new file mode 100644 index 00000000..96067d65 --- /dev/null +++ b/app/src/main/res/layout/timetable_free_day.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/timetable_no_lessons.xml b/app/src/main/res/layout/timetable_no_lessons.xml new file mode 100644 index 00000000..a15aa42a --- /dev/null +++ b/app/src/main/res/layout/timetable_no_lessons.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/timetable_no_timetable.xml b/app/src/main/res/layout/timetable_no_timetable.xml new file mode 100644 index 00000000..b69d762b --- /dev/null +++ b/app/src/main/res/layout/timetable_no_timetable.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b2164075..54fcb154 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -101,6 +101,8 @@ #ffb300 #A1887F #4caf50 + #b0b0b0 + #e0e0e0