[UI] Refactor Grades, Notifications, Homework fragments to better match unified templates.

This commit is contained in:
Kuba Szczodrzyński 2020-03-30 18:55:28 +02:00
parent b9f83875a0
commit b004ec048e
36 changed files with 871 additions and 421 deletions

View File

@ -40,6 +40,9 @@ import androidx.core.util.forEach
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.viewpager.widget.ViewPager
import com.google.android.gms.security.ProviderInstaller import com.google.android.gms.security.ProviderInstaller
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonElement import com.google.gson.JsonElement
@ -141,6 +144,10 @@ fun CharSequence?.isNotNullNorEmpty(): Boolean {
return this != null && this.isNotEmpty() return this != null && this.isNotEmpty()
} }
fun <T> Collection<T>?.isNotNullNorEmpty(): Boolean {
return this != null && this.isNotEmpty()
}
fun CharSequence?.isNotNullNorBlank(): Boolean { fun CharSequence?.isNotNullNorBlank(): Boolean {
return this != null && this.isNotBlank() return this != null && this.isNotBlank()
} }
@ -1167,3 +1174,19 @@ fun TextView.getTextPosition(range: IntRange): Rect {
return parentTextViewRect return parentTextViewRect
} }
inline fun ViewPager.addOnPageSelectedListener(crossinline block: (position: Int) -> Unit) = addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) { block(position) }
})
val SwipeRefreshLayout.onScrollListener: RecyclerView.OnScrollListener
get() = object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (recyclerView.canScrollVertically(-1))
this@onScrollListener.isEnabled = false
if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE)
this@onScrollListener.isEnabled = true
}
}

View File

@ -61,7 +61,7 @@ import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListFragment
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
@ -70,7 +70,7 @@ import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsListFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
import pl.szczodrzynski.edziennik.ui.modules.template.TemplateFragment import pl.szczodrzynski.edziennik.ui.modules.template.TemplateFragment
@ -155,7 +155,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
.withBadgeTypeId(TYPE_EVENT) .withBadgeTypeId(TYPE_EVENT)
.isInDrawer(true) .isInDrawer(true)
list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesFragment::class) list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesListFragment::class)
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline) .withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline)
.withBadgeTypeId(TYPE_GRADE) .withBadgeTypeId(TYPE_GRADE)
.isInDrawer(true) .isInDrawer(true)
@ -187,7 +187,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
// static drawer items // static drawer items
list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsFragment::class) list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsListFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline) .withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline)
.isInDrawer(true) .isInDrawer(true)
.isStatic(true) .isStatic(true)
@ -1077,6 +1077,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
.also { if (target.icon != null) it.withIcon(target.icon!!) } .also { if (target.icon != null) it.withIcon(target.icon!!) }
.also { if (target.title != null) it.withAppTitle(getString(target.title!!)) } .also { if (target.title != null) it.withAppTitle(getString(target.title!!)) }
.also { if (target.badgeTypeId != null) it.withBadgeStyle(drawer.badgeStyle)} .also { if (target.badgeTypeId != null) it.withBadgeStyle(drawer.badgeStyle)}
.withSelectedBackgroundAnimated(false)
if (target.badgeTypeId != null) if (target.badgeTypeId != null)
drawer.addUnreadCounterType(target.badgeTypeId!!, target.id) drawer.addUnreadCounterType(target.badgeTypeId!!, target.id)

View File

@ -65,7 +65,7 @@ abstract class EventDao : BaseDao<Event, EventFull> {
fun getAllByDate(profileId: Int, date: Date) = fun getAllByDate(profileId: Int, date: Date) =
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY") getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY")
fun getAllByDateTime(profileId: Int, date: Date, time: Time) = fun getAllByDateTime(profileId: Int, date: Date, time: Time) =
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' AND eventTime = ${time.stringValue}") getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' AND eventTime = '${time.stringValue}'")
fun getNearestNotDone(profileId: Int, today: Date, limit: Int) = fun getNearestNotDone(profileId: Int, today: Date, limit: Int) =
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND $NOT_DONE AND events.profileId = $profileId AND eventDate >= '${today.stringY_m_d}' $ORDER_BY LIMIT $limit") getRaw("$QUERY WHERE $NOT_BLACKLISTED AND $NOT_DONE AND events.profileId = $profileId AND eventDate >= '${today.stringY_m_d}' $ORDER_BY LIMIT $limit")

View File

@ -146,6 +146,11 @@ class DayDialog(
adapter = EventListAdapter( adapter = EventListAdapter(
activity, activity,
showWeekDay = false,
showDate = false,
showType = true,
showTime = true,
showSubject = true,
onItemClick = { onItemClick = {
EventDetailsDialog( EventDetailsDialog(
activity, activity,

View File

@ -9,6 +9,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.EventFull
@ -19,8 +20,11 @@ import pl.szczodrzynski.edziennik.utils.models.Week
class EventListAdapter( class EventListAdapter(
val context: Context, val context: Context,
val simpleMode: Boolean = false, val simpleMode: Boolean = false,
val showDate: Boolean = false,
val showWeekDay: Boolean = false, val showWeekDay: Boolean = false,
val showDate: Boolean = false,
val showType: Boolean = true,
val showTime: Boolean = true,
val showSubject: Boolean = true,
val onItemClick: ((event: EventFull) -> Unit)? = null, val onItemClick: ((event: EventFull) -> Unit)? = null,
val onEventEditClick: ((event: EventFull) -> Unit)? = null val onEventEditClick: ((event: EventFull) -> Unit)? = null
) : RecyclerView.Adapter<EventListAdapter.ViewHolder>() { ) : RecyclerView.Adapter<EventListAdapter.ViewHolder>() {
@ -52,9 +56,9 @@ class EventListAdapter(
b.details.text = mutableListOf<CharSequence?>( b.details.text = mutableListOf<CharSequence?>(
if (showWeekDay) Week.getFullDayName(event.date.weekDay) else null, if (showWeekDay) Week.getFullDayName(event.date.weekDay) else null,
if (showDate) event.date.getRelativeString(context, 7) ?: event.date.formattedStringShort else null, if (showDate) event.date.getRelativeString(context, 7) ?: event.date.formattedStringShort else null,
event.typeName, if (showType) event.typeName else null,
if (simpleMode) null else event.time?.stringHM ?: app.getString(R.string.event_all_day), if (showTime) event.time?.stringHM ?: app.getString(R.string.event_all_day) else null,
if (simpleMode) null else event.subjectLongName if (showSubject) event.subjectLongName else null
).concat(bullet) ).concat(bullet)
b.addedBy.setText( b.addedBy.setText(
@ -73,6 +77,7 @@ class EventListAdapter(
) )
b.typeColor.background?.setTintColor(event.eventColor) b.typeColor.background?.setTintColor(event.eventColor)
b.typeColor.isVisible = showType
b.editButton.visibility = if (event.addedManually && !simpleMode) View.VISIBLE else View.GONE b.editButton.visibility = if (event.addedManually && !simpleMode) View.VISIBLE else View.GONE
b.editButton.onClick { b.editButton.onClick {

View File

@ -170,6 +170,11 @@ class LessonDetailsDialog(
adapter = EventListAdapter( adapter = EventListAdapter(
activity, activity,
showWeekDay = false,
showDate = false,
showType = true,
showTime = true,
showSubject = true,
onItemClick = { onItemClick = {
EventDetailsDialog( EventDetailsDialog(
activity, activity,

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
*/
package pl.szczodrzynski.edziennik.ui.modules.base.lazypager
import androidx.fragment.app.FragmentManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
class FragmentLazyPagerAdapter(
fragmentManager: FragmentManager,
swipeRefreshLayout: SwipeRefreshLayout,
private val fragments: List<Pair<LazyFragment, CharSequence>>
) : LazyPagerAdapter(fragmentManager, swipeRefreshLayout) {
override fun getPage(position: Int) = fragments[position].first
override fun getPageTitle(position: Int) = fragments[position].second
override fun getCount() = fragments.size
}

View File

@ -5,6 +5,7 @@
package pl.szczodrzynski.edziennik.ui.modules.base.lazypager package pl.szczodrzynski.edziennik.ui.modules.base.lazypager
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
abstract class LazyFragment : Fragment() { abstract class LazyFragment : Fragment() {
private var isPageCreated = false private var isPageCreated = false
@ -25,12 +26,27 @@ abstract class LazyFragment : Fragment() {
fun disableSwipeToRefresh() = swipeRefreshLayoutCallback?.invoke(position, false) fun disableSwipeToRefresh() = swipeRefreshLayoutCallback?.invoke(position, false)
fun setSwipeToRefresh(enabled: Boolean) = swipeRefreshLayoutCallback?.invoke(position, enabled) fun setSwipeToRefresh(enabled: Boolean) = swipeRefreshLayoutCallback?.invoke(position, enabled)
val onScrollListener: RecyclerView.OnScrollListener
get() = object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (recyclerView.canScrollVertically(-1))
disableSwipeToRefresh()
if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE)
enableSwipeToRefresh()
}
}
internal fun createPage() { internal fun createPage() {
if (!isPageCreated && isAdded) { if (!isPageCreated && isAdded) {
isPageCreated = onPageCreated() isPageCreated = onPageCreated()
} }
} }
override fun onDestroyView() {
isPageCreated = false
super.onDestroyView()
}
override fun onResume() { override fun onResume() {
createPage() createPage()
super.onResume() super.onResume()

View File

@ -12,7 +12,7 @@ class LazyViewPager @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null context: Context, attrs: AttributeSet? = null
) : ViewPager(context, attrs) { ) : ViewPager(context, attrs) {
var pageSelection = -1 private var pageSelection = -1
init { init {
addOnPageChangeListener(object : OnPageChangeListener { addOnPageChangeListener(object : OnPageChangeListener {

View File

@ -10,11 +10,10 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon2 import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon2
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -23,7 +22,7 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_GRADES_EDITOR
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE import pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE
import pl.szczodrzynski.edziennik.data.db.full.GradeFull import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.GradesFragmentBinding import pl.szczodrzynski.edziennik.databinding.GradesListFragmentBinding
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages
@ -36,24 +35,20 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.math.max import kotlin.math.max
class GradesListFragment : Fragment(), CoroutineScope {
class GradesFragment : Fragment(), CoroutineScope {
companion object { companion object {
private const val TAG = "GradesFragment" private const val TAG = "GradesFragment"
} }
private lateinit var app: App private lateinit var app: App
private lateinit var activity: MainActivity private lateinit var activity: MainActivity
private lateinit var b: GradesFragmentBinding private lateinit var b: GradesListFragmentBinding
private val job: Job = Job() private val job: Job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main get() = job + Dispatchers.Main
// local/private variables go here // local/private variables go here
private val adapter by lazy {
GradesAdapter(activity)
}
private val manager by lazy { app.gradesManager } private val manager by lazy { app.gradesManager }
private val dontCountEnabled by lazy { manager.dontCountEnabled } private val dontCountEnabled by lazy { manager.dontCountEnabled }
private val dontCountGrades by lazy { manager.dontCountGrades } private val dontCountGrades by lazy { manager.dontCountGrades }
@ -63,51 +58,49 @@ class GradesFragment : Fragment(), CoroutineScope {
activity = (getActivity() as MainActivity?) ?: return null activity = (getActivity() as MainActivity?) ?: return null
context ?: return null context ?: return null
app = activity.application as App app = activity.application as App
b = GradesFragmentBinding.inflate(inflater) b = GradesListFragmentBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout) b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root return b.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) {
if (!isAdded) if (!isAdded) return@startCoroutineTimer
return
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
app.db.gradeDao() val adapter = GradesAdapter(activity)
.getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()) var firstRun = true
.observe(this, Observer { grades ->
if (b.gradesRecyclerView.adapter == null) { app.db.gradeDao().getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()).observe(this, Observer { items -> launch {
b.gradesRecyclerView.adapter = adapter if (!isAdded) return@launch
b.gradesRecyclerView.apply {
// load & configure the adapter
adapter.items = withContext(Dispatchers.Default) { processGrades(items) }
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
b.list.adapter = adapter
b.list.apply {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
//addItemDecoration(SimpleDividerItemDecoration(context)) addOnScrollListener(b.refreshLayout.onScrollListener)
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (recyclerView.canScrollVertically(-1)) {
b.refreshLayout.isEnabled = false
}
if (!recyclerView.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) {
b.refreshLayout.isEnabled = true
} }
} }
}) adapter.notifyDataSetChanged()
}
if (firstRun) {
expandSubject(adapter)
firstRun = false
} }
launch(Dispatchers.Default) { // show/hide relevant views
processGrades(grades) b.progressBar.isVisible = false
} if (items.isNullOrEmpty()) {
b.list.isVisible = false
if (grades != null && grades.isNotEmpty()) { b.noData.isVisible = true
b.gradesRecyclerView.visibility = View.VISIBLE
b.gradesNoData.visibility = View.GONE
} else { } else {
b.gradesRecyclerView.visibility = View.GONE b.list.isVisible = true
b.gradesNoData.visibility = View.VISIBLE b.noData.isVisible = false
} }
}) }})
adapter.onGradeClick = { adapter.onGradeClick = {
GradeDetailsDialog(activity, it) GradeDetailsDialog(activity, it)
@ -153,10 +146,30 @@ class GradesFragment : Fragment(), CoroutineScope {
}) })
) )
activity.gainAttention() activity.gainAttention()
}}
private fun expandSubject(adapter: GradesAdapter) {
var expandSubjectModel: GradesSubject? = null
if (expandSubjectId != 0L) {
expandSubjectModel = adapter.items.firstOrNull { it is GradesSubject && it.subjectId == expandSubjectId } as? GradesSubject
adapter.expandModel(
model = expandSubjectModel,
view = null,
notifyAdapter = false
)
}
startCoroutineTimer(500L) {
if (expandSubjectModel != null) {
b.list.smoothScrollToPosition(
adapter.items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0)
)
}
}
} }
@Suppress("SuspendFunctionOnCoroutineScope") @Suppress("SuspendFunctionOnCoroutineScope")
private suspend fun processGrades(grades: List<GradeFull>) { private fun processGrades(grades: List<GradeFull>): MutableList<Any> {
val items = mutableListOf<GradesSubject>() val items = mutableListOf<GradesSubject>()
var subjectId = -1L var subjectId = -1L
@ -284,30 +297,7 @@ class GradesFragment : Fragment(), CoroutineScope {
GradesManager.ORDER_BY_DATE_ASC -> items.sortBy { it.lastAddedDate } GradesManager.ORDER_BY_DATE_ASC -> items.sortBy { it.lastAddedDate }
} }
adapter.items = items.toMutableList() return (items + stats).toMutableList()
adapter.items.add(stats)
var expandSubjectModel: GradesSubject? = null
if (expandSubjectId != 0L) {
expandSubjectModel = items.firstOrNull { it.subjectId == expandSubjectId }
adapter.expandModel(
model = expandSubjectModel,
view = null,
notifyAdapter = false
)
}
withContext(Dispatchers.Main) {
adapter.notifyDataSetChanged()
}
startCoroutineTimer(500L, 0L) {
if (expandSubjectModel != null) {
b.gradesRecyclerView.smoothScrollToPosition(
items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0)
)
}
}
} }
private fun countGrade(grade: Grade, averages: GradesAverages) { private fun countGrade(grade: Grade, averages: GradesAverages) {

View File

@ -60,8 +60,11 @@ class HomeEventsCard(
adapter = EventListAdapter( adapter = EventListAdapter(
activity, activity,
simpleMode = true, simpleMode = true,
showDate = true,
showWeekDay = true, showWeekDay = true,
showDate = true,
showType = true,
showTime = false,
showSubject = false,
onItemClick = { onItemClick = {
EventDetailsDialog( EventDetailsDialog(
activity, activity,

View File

@ -1,3 +1,7 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
*/
package pl.szczodrzynski.edziennik.ui.modules.homework package pl.szczodrzynski.edziennik.ui.modules.homework
import android.os.AsyncTask import android.os.AsyncTask
@ -7,46 +11,48 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.viewpager.widget.ViewPager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
import pl.szczodrzynski.edziennik.App import kotlinx.coroutines.CoroutineScope
import pl.szczodrzynski.edziennik.MainActivity import kotlinx.coroutines.Dispatchers
import pl.szczodrzynski.edziennik.R import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.databinding.FragmentHomeworkBinding import pl.szczodrzynski.edziennik.databinding.HomeworkFragmentBinding
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
import pl.szczodrzynski.edziennik.utils.Themes
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
import kotlin.coroutines.CoroutineContext
class HomeworkFragment : Fragment() { class HomeworkFragment : Fragment(), CoroutineScope {
companion object { companion object {
private const val TAG = "HomeworkFragment"
var pageSelection = 0 var pageSelection = 0
} }
private lateinit var app: App private lateinit var app: App
private lateinit var activity: MainActivity private lateinit var activity: MainActivity
private lateinit var b: FragmentHomeworkBinding private lateinit var b: HomeworkFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null activity = (getActivity() as MainActivity?) ?: return null
if (context == null) context ?: return null
return null
app = activity.application as App app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true) b = HomeworkFragmentBinding.inflate(inflater)
// activity, context and profile is valid
b = FragmentHomeworkBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout) b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root return b.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null if (!isAdded) return
if (app.profile == null || !isAdded)
return
activity.bottomSheet.prependItems( activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true) BottomSheetPrimaryItem(true)
@ -67,31 +73,28 @@ class HomeworkFragment : Fragment() {
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show() Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
})) }))
b.viewPager.adapter = MessagesFragment.Adapter(childFragmentManager, b.refreshLayout).also { adapter -> val pagerAdapter = FragmentLazyPagerAdapter(
fragmentManager ?: return,
b.refreshLayout,
listOf(
HomeworkListFragment().apply {
arguments = Bundle("homeworkDate" to HomeworkDate.CURRENT)
} to getString(R.string.homework_tab_current),
adapter.addFragment(HomeworkListFragment().also { fragment -> HomeworkListFragment().apply {
fragment.arguments = Bundle().also { args -> arguments = Bundle("homeworkDate" to HomeworkDate.PAST)
args.putInt("homeworkDate", HomeworkDate.CURRENT) } to getString(R.string.homework_tab_past)
)
)
b.viewPager.apply {
offscreenPageLimit = 1
adapter = pagerAdapter
currentItem = pageSelection
addOnPageSelectedListener {
pageSelection = it
} }
}, getString(R.string.homework_tab_current)) b.tabLayout.setupWithViewPager(this)
adapter.addFragment(HomeworkListFragment().also { fragment ->
fragment.arguments = Bundle().also { args ->
args.putInt("homeworkDate", HomeworkDate.PAST)
} }
}, getString(R.string.homework_tab_past))
}
b.viewPager.currentItem = pageSelection
b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
pageSelection = position
}
})
b.tabLayout.setupWithViewPager(b.viewPager)
activity.navView.apply { activity.navView.apply {
bottomBar.apply { bottomBar.apply {

View File

@ -4,75 +4,105 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.CoroutineScope
import pl.szczodrzynski.edziennik.App import kotlinx.coroutines.Dispatchers
import pl.szczodrzynski.edziennik.MainActivity import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.databinding.HomeworkListBinding import pl.szczodrzynski.edziennik.databinding.HomeworkListFragmentBinding
import pl.szczodrzynski.edziennik.getInt import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext
class HomeworkListFragment : LazyFragment() { class HomeworkListFragment : LazyFragment(), CoroutineScope {
companion object {
private const val TAG = "HomeworkListFragment"
}
private lateinit var app: App private lateinit var app: App
private lateinit var activity: MainActivity private lateinit var activity: MainActivity
private lateinit var b: HomeworkListBinding private lateinit var b: HomeworkListFragmentBinding
private var homeworkDate = HomeworkDate.CURRENT private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null activity = (getActivity() as MainActivity?) ?: return null
context ?: return null context ?: return null
app = activity.application as App app = activity.application as App
b = HomeworkListBinding.inflate(inflater) b = HomeworkListFragmentBinding.inflate(inflater)
return b.root return b.root
} }
override fun onPageCreated(): Boolean { override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
if (arguments != null) { val homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT)
homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT)
}
val layoutManager = LinearLayoutManager(context)
layoutManager.reverseLayout = homeworkDate == HomeworkDate.PAST
layoutManager.stackFromEnd = homeworkDate == HomeworkDate.PAST
b.homeworkView.setHasFixedSize(true)
b.homeworkView.layoutManager = layoutManager
b.homeworkView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (recyclerView.canScrollVertically(-1)) {
setSwipeToRefresh(false)
}
if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE) {
setSwipeToRefresh(true)
}
}
})
val today = Date.getToday()
val filter = when(homeworkDate) { val filter = when(homeworkDate) {
HomeworkDate.CURRENT -> "eventDate >= '" + Date.getToday().stringY_m_d + "'" HomeworkDate.CURRENT -> "eventDate >= '${today.stringY_m_d}'"
else -> "eventDate < '" + Date.getToday().stringY_m_d + "'" else -> "eventDate < '${today.stringY_m_d}'"
} }
app.db.eventDao() val adapter = EventListAdapter(
.getAllByType(App.profileId, Event.TYPE_HOMEWORK, filter) activity,
.observe(this, Observer { homeworkList -> showWeekDay = true,
showDate = true,
showType = false,
showTime = true,
showSubject = true,
onItemClick = {
EventDetailsDialog(
activity,
it
)
},
onEventEditClick = {
EventManualDialog(
activity,
it.profileId,
editingEvent = it
)
}
)
app.db.eventDao().getAllByType(App.profileId, Event.TYPE_HOMEWORK, filter).observe(this@HomeworkListFragment, Observer { items ->
if (!isAdded) return@Observer if (!isAdded) return@Observer
if (homeworkList != null && homeworkList.size > 0) { // load & configure the adapter
val adapter = HomeworkAdapter(context, homeworkList) adapter.items = items
b.homeworkView.adapter = adapter if (items.isNotNullNorEmpty() && b.list.adapter == null) {
b.homeworkView.visibility = View.VISIBLE b.list.adapter = adapter
b.homeworkNoData.visibility = View.GONE b.list.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context).apply {
reverseLayout = homeworkDate == HomeworkDate.PAST
stackFromEnd = homeworkDate == HomeworkDate.PAST
}
addItemDecoration(SimpleDividerItemDecoration(context))
addOnScrollListener(onScrollListener)
}
}
adapter.notifyDataSetChanged()
// show/hide relevant views
b.progressBar.isVisible = false
if (items.isNullOrEmpty()) {
b.list.isVisible = false
b.noData.isVisible = true
} else { } else {
b.homeworkView.visibility = View.GONE b.list.isVisible = true
b.homeworkNoData.visibility = View.VISIBLE b.noData.isVisible = false
} }
}) })
return true }; return true }
}
} }

View File

@ -1,71 +1,62 @@
package pl.szczodrzynski.edziennik.ui.modules.notifications package pl.szczodrzynski.edziennik.ui.modules.notifications
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import androidx.appcompat.app.AppCompatActivity
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Notification import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.databinding.NotificationsListItemBinding
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext
class NotificationsAdapter( class NotificationsAdapter(
private val context: Context private val activity: AppCompatActivity,
) : RecyclerView.Adapter<NotificationsAdapter.ViewHolder>() { val onItemClick: ((item: Notification) -> Unit)? = null
) : RecyclerView.Adapter<NotificationsAdapter.ViewHolder>(), CoroutineScope {
companion object { companion object {
private const val TAG = "NotificationsAdapter" private const val TAG = "NotificationsAdapter"
} }
private val app = activity.applicationContext as App
// optional: place the manager here
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
var items = listOf<Notification>() var items = listOf<Notification>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(context) val inflater = LayoutInflater.from(activity)
val view = inflater.inflate(R.layout.row_notifications_item, parent, false) val view = NotificationsListItemBinding.inflate(inflater, parent, false)
return ViewHolder(view) return ViewHolder(view)
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val app = context.applicationContext as App val item = items[position]
val b = holder.b
val notification = items[position] val date = Date.fromMillis(item.addedDate).formattedString
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
val date = Date.fromMillis(notification.addedDate).formattedString b.title.text = item.text
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(context) b.profileDate.text = listOf(
item.profileName ?: "",
holder.title.text = notification.text
holder.profileDate.text = listOf(
notification.profileName ?: "",
"", "",
date.asColoredSpannable(colorSecondary) date
).concat() ).concat().asColoredSpannable(colorSecondary)
holder.type.text = context.getNotificationTitle(notification.type) b.type.text = activity.getNotificationTitle(item.type)
holder.root.onClick { onItemClick?.let { listener ->
val intent = Intent("android.intent.action.MAIN") b.root.onClick { listener(item) }
notification.fillIntent(intent)
d(TAG, "notification with item " + notification.viewId + " extras " + if (intent.extras == null) "null" else intent.extras!!.toString())
//Log.d(TAG, "Got date "+intent.getLongExtra("timetableDate", 0));
if (notification.profileId != null && notification.profileId != -1 && notification.profileId != app.profile.id && context is Activity) {
Toast.makeText(app, app.getString(R.string.toast_changing_profile), Toast.LENGTH_LONG).show()
}
app.sendBroadcast(intent)
} }
} }
override fun getItemCount() = items.size override fun getItemCount() = items.size
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { class ViewHolder(val b: NotificationsListItemBinding) : RecyclerView.ViewHolder(b.root)
var root = itemView
var title: TextView = itemView.findViewById(R.id.title)
var profileDate: TextView = itemView.findViewById(R.id.profileDate)
var type: TextView = itemView.findViewById(R.id.type)
}
} }

View File

@ -1,87 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-22.
*/
package pl.szczodrzynski.edziennik.ui.modules.notifications
import android.os.AsyncTask
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.FragmentNotificationsBinding
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
class NotificationsFragment : Fragment() {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentNotificationsBinding
private val adapter by lazy {
NotificationsAdapter(activity)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = FragmentNotificationsBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (app.profile == null || !isAdded)
return
activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_remove_notifications)
.withIcon(CommunityMaterial.Icon.cmd_delete_sweep_outline)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
AsyncTask.execute { app.db.notificationDao().clearAll() }
Toast.makeText(activity, R.string.menu_remove_notifications_success, Toast.LENGTH_SHORT).show()
}))
app.db.notificationDao()
.getAll()
.observe(this, Observer { notifications ->
if (app.profile == null || !isAdded) return@Observer
adapter.items = notifications
if (b.notificationsView.adapter == null) {
b.notificationsView.adapter = adapter
b.notificationsView.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context))
}
}
adapter.notifyDataSetChanged()
if (notifications != null && notifications.isNotEmpty()) {
b.notificationsView.visibility = View.VISIBLE
b.notificationsNoData.visibility = View.GONE
} else {
b.notificationsView.visibility = View.GONE
b.notificationsNoData.visibility = View.VISIBLE
}
})
}
}

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-22.
*/
package pl.szczodrzynski.edziennik.ui.modules.notifications
import android.app.Activity
import android.content.Intent
import android.os.AsyncTask
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.databinding.NotificationsListFragmentBinding
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import kotlin.coroutines.CoroutineContext
class NotificationsListFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "NotificationsListFragment"
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: NotificationsListFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = NotificationsListFragmentBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) {
if (!isAdded) return@startCoroutineTimer
activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_remove_notifications)
.withIcon(CommunityMaterial.Icon.cmd_delete_sweep_outline)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
AsyncTask.execute { app.db.notificationDao().clearAll() }
Toast.makeText(activity, R.string.menu_remove_notifications_success, Toast.LENGTH_SHORT).show()
}))
val adapter = NotificationsAdapter(activity) { notification ->
val intent = Intent("android.intent.action.MAIN")
notification.fillIntent(intent)
Utils.d(TAG, "notification with item " + notification.viewId + " extras " + if (intent.extras == null) "null" else intent.extras!!.toString())
if (notification.profileId != null && notification.profileId != -1 && notification.profileId != app.profile.id && context is Activity) {
Toast.makeText(app, app.getString(R.string.toast_changing_profile), Toast.LENGTH_LONG).show()
}
app.sendBroadcast(intent)
}
app.db.notificationDao().getAll().observe(this, Observer { items ->
if (!isAdded) return@Observer
// load & configure the adapter
adapter.items = items
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
b.list.adapter = adapter
b.list.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context))
}
}
adapter.notifyDataSetChanged()
// show/hide relevant views
b.progressBar.isVisible = false
if (items.isNullOrEmpty()) {
b.list.isVisible = false
b.noData.isVisible = true
} else {
b.list.isVisible = true
b.noData.isVisible = false
}
})
}}
}

View File

@ -11,16 +11,19 @@ import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.databinding.TemplateListItemBinding import pl.szczodrzynski.edziennik.databinding.TemplateListItemBinding
import pl.szczodrzynski.edziennik.onClick import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class TemplateAdapter( class TemplateAdapter(
val activity: AppCompatActivity, val activity: AppCompatActivity,
val onItemClick: ((item: TemplateItem) -> Unit)? = null, val onItemClick: ((item: Notification) -> Unit)? = null
val onItemButtonClick: ((item: TemplateItem) -> Unit)? = null
) : RecyclerView.Adapter<TemplateAdapter.ViewHolder>(), CoroutineScope { ) : RecyclerView.Adapter<TemplateAdapter.ViewHolder>(), CoroutineScope {
companion object {
private const val TAG = "TemplateAdapter"
}
private val app = activity.applicationContext as App private val app = activity.applicationContext as App
// optional: place the manager here // optional: place the manager here
@ -29,7 +32,7 @@ class TemplateAdapter(
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main get() = job + Dispatchers.Main
var items = listOf<TemplateItem>() var items = listOf<Notification>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
@ -41,19 +44,23 @@ class TemplateAdapter(
val item = items[position] val item = items[position]
val b = holder.b val b = holder.b
val date = Date.fromMillis(item.addedDate).formattedString
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
b.title.text = item.text
b.profileDate.text = listOf(
item.profileName ?: "",
"",
date
).concat().asColoredSpannable(colorSecondary)
b.type.text = activity.getNotificationTitle(item.type)
onItemClick?.let { listener -> onItemClick?.let { listener ->
b.root.onClick { listener(item) } b.root.onClick { listener(item) }
} }
/*b.someButton.visibility = if (buttonVisible) View.VISIBLE else View.GONE
onItemButtonClick?.let { listener ->
b.someButton.onClick { listener(item) }
}*/
} }
override fun getItemCount() = items.size override fun getItemCount() = items.size
class ViewHolder(val b: TemplateListItemBinding) : RecyclerView.ViewHolder(b.root) class ViewHolder(val b: TemplateListItemBinding) : RecyclerView.ViewHolder(b.root)
data class TemplateItem(val text: String)
} }

View File

@ -14,12 +14,16 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.addOnPageSelectedListener
import pl.szczodrzynski.edziennik.databinding.TemplateFragmentBinding import pl.szczodrzynski.edziennik.databinding.TemplateFragmentBinding
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class TemplateFragment : Fragment(), CoroutineScope { class TemplateFragment : Fragment(), CoroutineScope {
companion object { companion object {
private const val TAG = "TemplateFragment" private const val TAG = "TemplateFragment"
var pageSelection = 0
} }
private lateinit var app: App private lateinit var app: App
@ -42,17 +46,29 @@ class TemplateFragment : Fragment(), CoroutineScope {
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded) if (!isAdded) return
return
val pagerAdapter = TemplatePagerAdapter( val pagerAdapter = FragmentLazyPagerAdapter(
fragmentManager ?: return, fragmentManager ?: return,
b.refreshLayout b.refreshLayout,
listOf(
TemplatePageFragment() to "Pager 0",
TemplatePageFragment() to "Pager 1",
TemplatePageFragment() to "Pager 2",
TemplatePageFragment() to "Pager 3",
TemplateListPageFragment() to "Pager 4",
TemplateListPageFragment() to "Pager 5",
TemplateListPageFragment() to "Pager 6",
TemplateListPageFragment() to "Pager 7"
)
) )
b.viewPager.apply { b.viewPager.apply {
offscreenPageLimit = 1 offscreenPageLimit = 1
adapter = pagerAdapter adapter = pagerAdapter
currentItem = 4 currentItem = pageSelection
addOnPageSelectedListener {
HomeworkFragment.pageSelection = it
}
b.tabLayout.setupWithViewPager(this) b.tabLayout.setupWithViewPager(this)
} }
} }

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
*/
package pl.szczodrzynski.edziennik.ui.modules.template
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.databinding.TemplateListFragmentBinding
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class TemplateListFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "TemplateListFragment"
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: TemplateListFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = TemplateListFragmentBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) {
if (!isAdded) return@startCoroutineTimer
val adapter = TemplateAdapter(activity)
app.db.notificationDao().getAll().observe(this, Observer { items ->
if (!isAdded) return@Observer
// load & configure the adapter
adapter.items = items
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
b.list.adapter = adapter
b.list.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context))
addOnScrollListener(b.refreshLayout.onScrollListener)
}
}
adapter.notifyDataSetChanged()
// show/hide relevant views
b.progressBar.isVisible = false
if (items.isNullOrEmpty()) {
b.list.isVisible = false
b.noData.isVisible = true
} else {
b.list.isVisible = true
b.noData.isVisible = false
}
})
}}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
*/
package pl.szczodrzynski.edziennik.ui.modules.template
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.startCoroutineTimer
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class TemplateListPageFragment : LazyFragment(), CoroutineScope {
companion object {
private const val TAG = "TemplateListPagerFragment"
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: TemplateListPageFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = TemplateListPageFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
val adapter = TemplateAdapter(activity)
app.db.notificationDao().getAll().observe(this, Observer { items ->
if (!isAdded) return@Observer
// load & configure the adapter
adapter.items = items
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
b.list.adapter = adapter
b.list.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context))
addOnScrollListener(onScrollListener)
}
}
adapter.notifyDataSetChanged()
// show/hide relevant views
b.progressBar.isVisible = false
if (items.isNullOrEmpty()) {
b.list.isVisible = false
b.noData.isVisible = true
} else {
b.list.isVisible = true
b.noData.isVisible = false
}
})
}; return true }
}

View File

@ -13,18 +13,18 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.databinding.TemplatePagerFragmentBinding import pl.szczodrzynski.edziennik.databinding.TemplatePageFragmentBinding
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class TemplatePagerFragment : LazyFragment(), CoroutineScope { class TemplatePageFragment : LazyFragment(), CoroutineScope {
companion object { companion object {
private const val TAG = "TemplatePagerFragment" private const val TAG = "TemplatePagerFragment"
} }
private lateinit var app: App private lateinit var app: App
private lateinit var activity: MainActivity private lateinit var activity: MainActivity
private lateinit var b: TemplatePagerFragmentBinding private lateinit var b: TemplatePageFragmentBinding
private val job: Job = Job() private val job: Job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
@ -36,7 +36,7 @@ class TemplatePagerFragment : LazyFragment(), CoroutineScope {
activity = (getActivity() as MainActivity?) ?: return null activity = (getActivity() as MainActivity?) ?: return null
context ?: return null context ?: return null
app = activity.application as App app = activity.application as App
b = TemplatePagerFragmentBinding.inflate(inflater) b = TemplatePageFragmentBinding.inflate(inflater)
return b.root return b.root
} }

View File

@ -9,7 +9,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyPagerAdapter import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyPagerAdapter
class TemplatePagerAdapter(fragmentManager: FragmentManager, swipeRefreshLayout: SwipeRefreshLayout) : LazyPagerAdapter(fragmentManager, swipeRefreshLayout) { class TemplatePagerAdapter(fragmentManager: FragmentManager, swipeRefreshLayout: SwipeRefreshLayout) : LazyPagerAdapter(fragmentManager, swipeRefreshLayout) {
override fun getPage(position: Int) = TemplatePagerFragment() override fun getPage(position: Int) = TemplatePageFragment()
override fun getPageTitle(position: Int) = "Page $position" override fun getPageTitle(position: Int) = "Page $position"
override fun getCount() = 10 override fun getCount() = 10
} }

View File

@ -137,7 +137,7 @@ public class Date implements Comparable<Date> {
} }
public static int diffDays(Date d1, Date d2) { public static int diffDays(Date d1, Date d2) {
return (int) ((d1.getInMillis() - d2.getInMillis()) / (24 * 60 * 60 * 1000)); return Math.round((d1.getInMillis() - d2.getInMillis()) / (24 * 60 * 60 * 1000f));
} }
public static boolean isToday(Date date) { public static boolean isToday(Date date) {

View File

@ -0,0 +1,34 @@
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m102,24h-74c-4.42,0 -8,3.58 -8,8v72c0,4.42 3.58,8 8,8h74c4.42,0 8,-3.58 8,-8v-72c0,-4.42 -3.58,-8 -8,-8z"
android:fillColor="#70d0f3"/>
<path
android:pathData="m20,32v8h90v-8c0,-4.42 -3.58,-8 -8,-8h-74c-4.42,0 -8,3.58 -8,8z"
android:fillColor="#3281e6"/>
<path
android:pathData="m88,92h-30c-2.21,0 -4,-1.79 -4,-4 0,-2.21 1.79,-4 4,-4h30c2.21,0 4,1.79 4,4 0,2.21 -1.79,4 -4,4zM92,74c0,-2.21 -1.79,-4 -4,-4h-30c-2.21,0 -4,1.79 -4,4 0,2.21 1.79,4 4,4h30c2.21,0 4,-1.79 4,-4zM92,60c0,-2.21 -1.79,-4 -4,-4h-30c-2.21,0 -4,1.79 -4,4 0,2.21 1.79,4 4,4h30c2.21,0 4,-1.79 4,-4zM48,88c0,-2.21 -1.79,-4 -4,-4h-2c-2.21,0 -4,1.79 -4,4 0,2.21 1.79,4 4,4h2c2.21,0 4,-1.79 4,-4zM48,74c0,-2.21 -1.79,-4 -4,-4h-2c-2.21,0 -4,1.79 -4,4 0,2.21 1.79,4 4,4h2c2.21,0 4,-1.79 4,-4zM48,60c0,-2.21 -1.79,-4 -4,-4h-2c-2.21,0 -4,1.79 -4,4 0,2.21 1.79,4 4,4h2c2.21,0 4,-1.79 4,-4z"
android:fillColor="#2b83d5"/>
<path
android:pathData="m127,86.3 l-7.42,-7.42c-1.12,-1.13 -2.96,-1.13 -4.09,0l-3.5,3.49 11.5,11.5 3.49,-3.5c1.13,-1.13 1.13,-2.96 0,-4.09"
android:fillColor="#fc657c"/>
<path
android:pathData="m93.4,124 l-11.5,-11.5 24.5,-24.5 11.5,11.5z"
android:fillColor="#ffa859"/>
<path
android:pathData="m118,99.6 l-11.5,-11.5 5.75,-5.76 11.5,11.5z"
android:fillColor="#c8c8c8"/>
<path
android:pathData="m81.9,113 l-3.88,15.4 15.4,-3.88z"
android:fillColor="#ffcd98"/>
<path
android:pathData="m80,120 l-1.96,7.78 7.78,-1.96z"
android:fillColor="#818181"/>
</vector>

View File

@ -16,8 +16,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/gradesNoData" android:id="@+id/noData"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
@ -25,13 +31,17 @@
android:fontFamily="sans-serif-light" android:fontFamily="sans-serif-light"
android:text="@string/grades_no_data" android:text="@string/grades_no_data"
android:textSize="24sp" android:textSize="24sp"
app:drawableTopCompat="@drawable/ic_no_grades" /> android:visibility="gone"
app:drawableTopCompat="@drawable/ic_no_grades"
tools:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/gradesRecyclerView" android:id="@+id/list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:listitem="@layout/grades_item_subject" /> android:visibility="gone"
tools:listitem="@layout/grades_item_subject"
tools:visibility="visible" />
</FrameLayout> </FrameLayout>
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator> </pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
</layout> </layout>

View File

@ -1,4 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
@ -27,7 +31,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</LinearLayout> </LinearLayout>
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator> </pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
</layout> </layout>

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/homeworkView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
<LinearLayout
android:id="@+id/homeworkNoData"
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center"
android:layout_height="match_parent"
android:visibility="gone"
tools:visibility="visible">
<com.mikepenz.iconics.view.IconicsImageView
android:layout_width="match_parent"
android:layout_height="92dp"
app:iiv_color="?android:textColorPrimary"
app:iiv_icon="szf-file-document-edit"
app:iiv_size="92dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:gravity="center"
android:text="@string/homework_no_data"
android:textSize="18sp"
android:textStyle="italic" />
</LinearLayout>
</LinearLayout>
</layout>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
-->
<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
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/noData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="16dp"
android:fontFamily="sans-serif-light"
android:text="@string/homework_no_data"
android:textSize="24sp"
android:visibility="gone"
app:drawableTopCompat="@drawable/ic_no_homework"
tools:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:listitem="@layout/event_list_item"
tools:visibility="visible" />
</FrameLayout>
</layout>

View File

@ -1,30 +1,41 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2019-11-22.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:tools="http://schemas.android.com/tools">
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView <ProgressBar
android:id="@+id/notificationsView" android:id="@+id/progressBar"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
tools:listitem="@layout/row_notifications_item" android:layout_gravity="center" />
tools:visibility="gone" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/notificationsNoData" android:id="@+id/noData"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
app:drawableTopCompat="@drawable/ic_no_notifications"
android:drawablePadding="16dp" android:drawablePadding="16dp"
android:fontFamily="sans-serif-light" android:fontFamily="sans-serif-light"
android:text="@string/notifications_no_data" android:text="@string/notifications_no_data"
android:textSize="24sp" android:textSize="24sp"
android:visibility="gone" android:visibility="gone"
app:drawableTopCompat="@drawable/ic_no_notifications"
tools:visibility="visible" /> tools:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:listitem="@layout/notifications_list_item"
tools:visibility="visible" />
</FrameLayout> </FrameLayout>
</layout> </layout>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/NavView.TextView.Medium"
tools:text="Dzisiaj 1 to szczęśliwy numerek" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/profileDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:text="Władca Androida • 22 listopada" />
<TextView
android:id="@+id/type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Szczęśliwy numerek" />
</LinearLayout>
</LinearLayout>
</layout>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp"
android:background="?selectableItemBackground">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/NavView.TextView.Medium"
tools:text="Dzisiaj 1 to szczęśliwy numerek" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/profileDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:text="Władca Androida • 22 listopada" />
<TextView
android:id="@+id/type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Szczęśliwy numerek" />
</LinearLayout>
</LinearLayout>

View File

@ -13,7 +13,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
-->
<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">
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/noData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="16dp"
android:fontFamily="sans-serif-light"
android:text="@string/grades_no_data"
android:textSize="24sp"
android:visibility="gone"
app:drawableTopCompat="@drawable/ic_no_grades"
tools:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:listitem="@layout/grades_item_subject"
tools:visibility="visible" />
</FrameLayout>
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
</layout>

View File

@ -3,16 +3,39 @@
~ Copyright (c) Kuba Szczodrzyński 2019-12-19. ~ Copyright (c) Kuba Szczodrzyński 2019-12-19.
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="8dp" android:background="?selectableItemBackground"
android:orientation="vertical" android:orientation="vertical"
android:background="?selectableItemBackground"> android:padding="8dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/NavView.TextView.Medium"
tools:text="Dzisiaj 1 to szczęśliwy numerek" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/profileDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:text="Władca Androida • 22 listopada" />
<TextView
android:id="@+id/type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Szczęśliwy numerek" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</layout> </layout>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
-->
<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
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/noData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="16dp"
android:fontFamily="sans-serif-light"
android:text="@string/grades_no_data"
android:textSize="24sp"
android:visibility="gone"
app:drawableTopCompat="@drawable/ic_no_grades"
tools:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:listitem="@layout/grades_item_subject"
tools:visibility="visible" />
</FrameLayout>
</layout>