mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2024-11-24 19:04:38 -06:00
[Timetable] Add SwipeToRefresh. Select start&end hours based on lesson ranges.
This commit is contained in:
parent
a3e5f824c8
commit
37ea65e3fc
@ -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()
|
||||
|
@ -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 <T> LiveData<T>.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
|
||||
}
|
@ -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<Date>()
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
}}
|
||||
}
|
@ -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<Date>) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
class TimetablePagerAdapter(
|
||||
fragmentManager: FragmentManager,
|
||||
private val items: List<Date>,
|
||||
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 {
|
||||
|
@ -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<View>()
|
||||
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<List<LessonFull>> { lessons ->
|
||||
buildLessonViews(lessons)
|
||||
processLessonList(lessons)
|
||||
})
|
||||
}
|
||||
|
||||
private fun buildLessonViews(lessons: List<LessonFull>) {
|
||||
private fun processLessonList(lessons: List<LessonFull>) {
|
||||
// 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<View>()
|
||||
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<LessonFull>) {
|
||||
if (!isAdded)
|
||||
return
|
||||
|
||||
val eventViews = mutableListOf<View>()
|
||||
val eventTimeRanges = mutableListOf<DayView.EventTimeRange>()
|
||||
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -2,89 +2,96 @@
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<FrameLayout
|
||||
|
||||
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
|
||||
android:id="@+id/refreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/timetableLayout"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:visibility="gone">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorSurface"
|
||||
style="@style/Widget.MaterialComponents.AppBarLayout.Surface">
|
||||
|
||||
<com.nshmura.recyclertablayout.RecyclerTabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:background="@color/colorSurface_6dp"
|
||||
app:rtl_tabTextAppearance="@style/rtl_RecyclerTabLayout.Tab"
|
||||
app:rtl_tabIndicatorColor="?colorPrimary"
|
||||
app:rtl_tabMinWidth="90dp"
|
||||
app:rtl_tabMaxWidth="300dp"
|
||||
app:rtl_tabSelectedTextColor="?colorPrimary"
|
||||
app:rtl_tabPaddingStart="16dp"
|
||||
app:rtl_tabPaddingEnd="16dp"
|
||||
app:rtl_tabPaddingTop="12dp"
|
||||
app:rtl_tabPaddingBottom="12dp"/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/viewPager"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/timetableLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
tools:visibility="gone">
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
style="@style/Widget.MaterialComponents.AppBarLayout.Surface"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorSurface">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/timetableNotPublicLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:gravity="center"
|
||||
tools:visibility="visible">
|
||||
<com.nshmura.recyclertablayout.RecyclerTabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:background="@color/colorSurface_6dp"
|
||||
app:rtl_tabIndicatorColor="?colorPrimary"
|
||||
app:rtl_tabMaxWidth="300dp"
|
||||
app:rtl_tabMinWidth="90dp"
|
||||
app:rtl_tabPaddingBottom="12dp"
|
||||
app:rtl_tabPaddingEnd="16dp"
|
||||
app:rtl_tabPaddingStart="16dp"
|
||||
app:rtl_tabPaddingTop="12dp"
|
||||
app:rtl_tabSelectedTextColor="?colorPrimary"
|
||||
app:rtl_tabTextAppearance="@style/rtl_RecyclerTabLayout.Tab" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_no_timetable" />
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/viewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_not_public_title"
|
||||
android:textSize="24sp" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
<LinearLayout
|
||||
android:id="@+id/timetableNotPublicLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/timetable_not_public_text"
|
||||
android:textSize="16sp" />
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/timetable_not_public_hint"
|
||||
android:textSize="14sp" />
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_no_timetable" />
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_not_public_title"
|
||||
android:textSize="24sp" />
|
||||
|
||||
</FrameLayout>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/timetable_not_public_text"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/timetable_not_public_hint"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
||||
</layout>
|
@ -28,126 +28,5 @@
|
||||
tools:visibility="gone"/>
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/noLessonsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:gravity="center"
|
||||
tools:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_timetable" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_no_lessons_title"
|
||||
android:textSize="24sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/freeDayLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:gravity="center"
|
||||
tools:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_sunbed" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_free_day_title"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/timetable_free_day_text"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/freeDayText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
tools:text="Dzień wolny dla szkoły z puli dyrektorskiej z okazji obchodów Światowego Dnia Wtorku w mieście Poznań i na przedmieśiach"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/freeDayShowTimetable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/timetable_free_day_show" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/noTimetableLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:gravity="center"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_sync" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_no_timetable_title"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/timetable_no_timetable_text"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/noTimetableSync"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/timetable_no_timetable_sync" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noTimetableWeek"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/timetable_no_timetable_week"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="italic"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</layout>
|
47
app/src/main/res/layout/timetable_free_day.xml
Normal file
47
app/src/main/res/layout/timetable_free_day.xml
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-15.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/freeDayLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_sunbed"
|
||||
android:drawablePadding="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_free_day_title"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/timetable_free_day_text"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/freeDayText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
android:textSize="16sp"
|
||||
tools:text="Dzień wolny dla szkoły z puli dyrektorskiej z okazji obchodów Światowego Dnia Wtorku w mieście Poznań i na przedmieśiach" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/freeDayShowTimetable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/timetable_free_day_show" />
|
||||
|
||||
</LinearLayout>
|
14
app/src/main/res/layout/timetable_no_lessons.xml
Normal file
14
app/src/main/res/layout/timetable_no_lessons.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-15.
|
||||
-->
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawableTop="@drawable/ic_timetable"
|
||||
android:drawablePadding="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_no_lessons_title"
|
||||
android:textSize="24sp" />
|
50
app/src/main/res/layout/timetable_no_timetable.xml
Normal file
50
app/src/main/res/layout/timetable_no_timetable.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-15.
|
||||
-->
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/noTimetableLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_sync"
|
||||
android:drawablePadding="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_no_timetable_title"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/timetable_no_timetable_text"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/noTimetableSync"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/timetable_no_timetable_sync" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noTimetableWeek"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="italic"
|
||||
tools:text="@string/timetable_no_timetable_week" />
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
@ -101,6 +101,8 @@
|
||||
<item name="timetable_lesson_change_color">#ffb300</item>
|
||||
<item name="timetable_lesson_shifted_source_color">#A1887F</item>
|
||||
<item name="timetable_lesson_shifted_target_color">#4caf50</item>
|
||||
<item name="hourDividerColor">#b0b0b0</item>
|
||||
<item name="halfHourDividerColor">#e0e0e0</item>
|
||||
</style>
|
||||
<style name="AppTheme.Dark" parent="NavView.Dark">
|
||||
<item name="colorPrimary">#64b5f6</item>
|
||||
@ -131,6 +133,8 @@
|
||||
<item name="timetable_lesson_change_color">#ffb300</item>
|
||||
<item name="timetable_lesson_shifted_source_color">#A1887F</item>
|
||||
<item name="timetable_lesson_shifted_target_color">#4caf50</item>
|
||||
<item name="hourDividerColor">#7fffffff</item>
|
||||
<item name="halfHourDividerColor">#40ffffff</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user