mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-02-20 21:14:44 +01:00
Remove flexible adapter (#781)
This commit is contained in:
parent
a1f864b35e
commit
4a3b746d48
@ -80,6 +80,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
viewBinding {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'HardwareIds'
|
||||
}
|
||||
@ -118,7 +122,6 @@ ext {
|
||||
work_manager = "2.3.4"
|
||||
room = "2.2.5"
|
||||
dagger = "2.27"
|
||||
// don't update https://github.com/ChuckerTeam/chucker/issues/242
|
||||
chucker = "3.2.0"
|
||||
mockk = "1.9.2"
|
||||
}
|
||||
@ -167,13 +170,11 @@ dependencies {
|
||||
implementation "com.squareup.inject:assisted-inject-annotations-dagger2:0.5.2"
|
||||
kapt "com.squareup.inject:assisted-inject-processor-dagger2:0.5.2"
|
||||
|
||||
implementation "eu.davidea:flexible-adapter:5.1.0"
|
||||
implementation "eu.davidea:flexible-adapter-ui:1.0.0"
|
||||
implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
|
||||
implementation "com.ncapdevi:frag-nav:3.3.0"
|
||||
implementation "com.github.YarikSOffice:lingver:1.2.1"
|
||||
implementation "com.github.YarikSOffice:lingver:1.2.2"
|
||||
|
||||
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6"
|
||||
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.8"
|
||||
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.19"
|
||||
|
||||
|
@ -10,8 +10,6 @@ import com.jakewharton.threetenabp.AndroidThreeTen
|
||||
import com.yariksoffice.lingver.Lingver
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.support.DaggerApplication
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.utils.Log
|
||||
import fr.bipi.tressence.file.FileLoggerTree
|
||||
import io.github.wulkanowy.di.DaggerAppComponent
|
||||
import io.github.wulkanowy.services.sync.SyncWorkerFactory
|
||||
@ -57,7 +55,6 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
|
||||
|
||||
private fun initLogging() {
|
||||
if (appInfo.isDebug) {
|
||||
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
|
||||
Timber.plant(DebugLogTree())
|
||||
Timber.plant(FileLoggerTree.Builder()
|
||||
.withFileName("wulkanowy.%g.log")
|
||||
|
@ -1,3 +0,0 @@
|
||||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
class AppCreator(val displayName: String, val githubUsername: String)
|
@ -0,0 +1,3 @@
|
||||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
class Contributor(val displayName: String, val githubUsername: String)
|
@ -2,18 +2,18 @@ package io.github.wulkanowy.data.repositories.appcreator
|
||||
|
||||
import android.content.res.AssetManager
|
||||
import com.google.gson.Gson
|
||||
import io.github.wulkanowy.data.pojos.AppCreator
|
||||
import io.github.wulkanowy.data.pojos.Contributor
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AppCreatorRepository @Inject constructor(private val assets: AssetManager) {
|
||||
fun getAppCreators(): Single<List<AppCreator>> {
|
||||
return Single.fromCallable<List<AppCreator>> {
|
||||
fun getAppCreators(): Single<List<Contributor>> {
|
||||
return Single.fromCallable<List<Contributor>> {
|
||||
Gson().fromJson(
|
||||
assets.open("contributors.json").bufferedReader().use { it.readText() },
|
||||
Array<AppCreator>::class.java
|
||||
Array<Contributor>::class.java
|
||||
).toList()
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ import android.content.Context
|
||||
import com.yariksoffice.lingver.Lingver
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.WulkanowyApp
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
@ -23,9 +21,6 @@ internal class AppModule {
|
||||
@Provides
|
||||
fun provideSchedulersProvider() = SchedulersProvider()
|
||||
|
||||
@Provides
|
||||
fun provideFlexibleAdapter() = FlexibleAdapter<AbstractFlexibleItem<*>>(null, null, true)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context)
|
||||
|
@ -0,0 +1,58 @@
|
||||
package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.util.DisplayMetrics
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
abstract class BaseExpandableAdapter<T : RecyclerView.ViewHolder> : RecyclerView.Adapter<T>() {
|
||||
|
||||
companion object {
|
||||
private const val MILLISECONDS_PER_INCH = 100f
|
||||
private const val AUTO_SCROLL_DELAY = 150L
|
||||
}
|
||||
|
||||
private var recyclerView: RecyclerView? = null
|
||||
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onAttachedToRecyclerView(recyclerView)
|
||||
this.recyclerView = recyclerView
|
||||
}
|
||||
|
||||
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onDetachedFromRecyclerView(recyclerView)
|
||||
this.recyclerView = null
|
||||
}
|
||||
|
||||
// original: https://github.com/davideas/FlexibleAdapter/blob/5.1.0/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/FlexibleAdapter.java#L4984-L5011
|
||||
protected fun scrollToHeaderWithSubItems(position: Int, subItemsCount: Int) {
|
||||
val layoutManager = recyclerView!!.layoutManager as LinearLayoutManager
|
||||
val firstVisibleItem = layoutManager.findFirstCompletelyVisibleItemPosition()
|
||||
val lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition()
|
||||
val itemsToShow = position + subItemsCount - lastVisibleItem
|
||||
if (itemsToShow > 0) {
|
||||
val scrollMax: Int = position - firstVisibleItem
|
||||
val scrollMin = max(0, position + subItemsCount - lastVisibleItem)
|
||||
val scrollBy = min(scrollMax, scrollMin)
|
||||
val scrollTo = firstVisibleItem + scrollBy
|
||||
scrollToPosition(scrollTo)
|
||||
} else if (position < firstVisibleItem) {
|
||||
scrollToPosition(position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun scrollToPosition(position: Int) {
|
||||
recyclerView?.run {
|
||||
postDelayed({
|
||||
layoutManager?.startSmoothScroll(object : LinearSmoothScroller(context) {
|
||||
override fun getVerticalSnapPreference() = SNAP_TO_START
|
||||
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics) = MILLISECONDS_PER_INCH / displayMetrics.densityDpi
|
||||
}.apply {
|
||||
targetPosition = position
|
||||
})
|
||||
}, AUTO_SCROLL_DELAY)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.databinding.ItemAccountBinding
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import javax.inject.Inject
|
||||
|
||||
class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<WidgetConfigureAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = emptyList<Pair<Student, Boolean>>()
|
||||
|
||||
var onClickListener: (Student) -> Unit = {}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemAccountBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val (student, isCurrent) = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
accountItemName.text = "${student.studentName} ${student.className}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
|
||||
with(accountItemImage) {
|
||||
val colorImage = if (isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
|
||||
else context.getThemeAttrColor(R.attr.colorOnSurface, 153)
|
||||
|
||||
setColorFilter(colorImage, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
root.setOnClickListener { onClickListener(student) }
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemAccountBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package io.github.wulkanowy.ui.modules.about
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.databinding.ItemAboutBinding
|
||||
import io.github.wulkanowy.databinding.ScrollableHeaderAboutBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class AboutAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private enum class ViewType(val id: Int) {
|
||||
ITEM_HEADER(1),
|
||||
ITEM_ELEMENT(2)
|
||||
}
|
||||
|
||||
var items = emptyList<Triple<String, String, Drawable?>>()
|
||||
|
||||
var onClickListener: (name: String) -> Unit = {}
|
||||
|
||||
override fun getItemCount() = items.size + 1
|
||||
|
||||
override fun getItemViewType(position: Int) = when (position) {
|
||||
0 -> ViewType.ITEM_HEADER.id
|
||||
else -> ViewType.ITEM_ELEMENT.id
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (viewType) {
|
||||
ViewType.ITEM_HEADER.id -> HeaderViewHolder(ScrollableHeaderAboutBinding.inflate(inflater, parent, false))
|
||||
ViewType.ITEM_ELEMENT.id -> ItemViewHolder(ItemAboutBinding.inflate(inflater, parent, false))
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is HeaderViewHolder -> bindHeaderViewHolder(holder.binding)
|
||||
is ItemViewHolder -> bindItemViewHolder(holder.binding, position - 1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindHeaderViewHolder(binding: ScrollableHeaderAboutBinding) {
|
||||
with(binding.aboutScrollableHeaderIcon) {
|
||||
setImageDrawable(ResourcesCompat.getDrawableForDensity(
|
||||
context.resources, context.applicationInfo.icon, 640, null)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindItemViewHolder(binding: ItemAboutBinding, position: Int) {
|
||||
val (title, summary, image) = items[position]
|
||||
|
||||
with(binding) {
|
||||
aboutItemImage.setImageDrawable(image)
|
||||
aboutItemTitle.text = title
|
||||
aboutItemSummary.text = summary
|
||||
|
||||
root.setOnClickListener { onClickListener(title) }
|
||||
}
|
||||
}
|
||||
|
||||
private class HeaderViewHolder(val binding: ScrollableHeaderAboutBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
private class ItemViewHolder(val binding: ItemAboutBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -5,9 +5,7 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment
|
||||
@ -19,7 +17,6 @@ import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.getCompatDrawable
|
||||
import io.github.wulkanowy.utils.openEmailClient
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_about.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -29,7 +26,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
lateinit var presenter: AboutPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var aboutAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var aboutAdapter: AboutAdapter
|
||||
|
||||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
@ -90,19 +87,18 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
aboutAdapter.setOnItemClickListener(presenter::onItemSelected)
|
||||
aboutAdapter.onClickListener = presenter::onItemSelected
|
||||
|
||||
with(aboutRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = aboutAdapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(header: AboutScrollableHeader, items: List<AboutItem>) {
|
||||
override fun updateData(data: List<Triple<String, String, Drawable?>>) {
|
||||
with(aboutAdapter) {
|
||||
removeAllScrollableHeaders()
|
||||
addScrollableHeader(header)
|
||||
updateDataSet(items)
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,56 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.about
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_about.*
|
||||
|
||||
class AboutItem(
|
||||
val title: String,
|
||||
private val summary: String,
|
||||
private val image: Drawable?
|
||||
) : AbstractFlexibleItem<AboutItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_about
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
with(holder) {
|
||||
aboutItemImage.setImageDrawable(image)
|
||||
aboutItemTitle.text = title
|
||||
aboutItemSummary.text = summary
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as AboutItem
|
||||
|
||||
if (title != other.title) return false
|
||||
if (summary != other.summary) return false
|
||||
if (image != other.image) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = title.hashCode()
|
||||
result = 31 * result + summary.hashCode()
|
||||
result = 31 * result + (image?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View? get() = contentView
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.about
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
@ -23,10 +22,9 @@ class AboutPresenter @Inject constructor(
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item !is AboutItem) return
|
||||
fun onItemSelected(name: String) {
|
||||
view?.run {
|
||||
when (item.title) {
|
||||
when (name) {
|
||||
versionRes?.first -> {
|
||||
Timber.i("Opening log viewer")
|
||||
openLogViewer()
|
||||
@ -73,15 +71,16 @@ class AboutPresenter @Inject constructor(
|
||||
|
||||
private fun loadData() {
|
||||
view?.run {
|
||||
updateData(AboutScrollableHeader(), listOfNotNull(
|
||||
versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
creatorsRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
faqRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
homepageRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
licensesRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
privacyRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }))
|
||||
updateData(listOfNotNull(
|
||||
versionRes,
|
||||
creatorsRes,
|
||||
feedbackRes,
|
||||
faqRes,
|
||||
discordRes,
|
||||
homepageRes,
|
||||
licensesRes,
|
||||
privacyRes
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.about
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.scrollable_header_about.*
|
||||
|
||||
class AboutScrollableHeader : AbstractFlexibleItem<AboutScrollableHeader.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.scrollable_header_about
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
with(holder) {
|
||||
val context = contentView.context
|
||||
val drawable = ResourcesCompat.getDrawableForDensity(context.resources, context.applicationInfo.icon, 640, null)
|
||||
|
||||
aboutScrollableHeaderIcon.setImageDrawable(drawable)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = javaClass.hashCode()
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View? get() = contentView
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ interface AboutView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(header: AboutScrollableHeader, items: List<AboutItem>)
|
||||
fun updateData(data: List<Triple<String, String, Drawable?>>)
|
||||
|
||||
fun openLogViewer()
|
||||
|
||||
|
@ -0,0 +1,41 @@
|
||||
package io.github.wulkanowy.ui.modules.about.contributor
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.api.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import io.github.wulkanowy.data.pojos.Contributor
|
||||
import io.github.wulkanowy.databinding.ItemContributorBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class ContributorAdapter @Inject constructor() :
|
||||
RecyclerView.Adapter<ContributorAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = emptyList<Contributor>()
|
||||
|
||||
var onClickListener: (Contributor) -> Unit = {}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemContributorBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
creatorItemName.text = item.displayName
|
||||
creatorItemAvatar.load("https://github.com/${item.githubUsername}.png") {
|
||||
transformations(RoundedCornersTransformation(8f))
|
||||
crossfade(true)
|
||||
}
|
||||
|
||||
root.setOnClickListener { onClickListener(item) }
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemContributorBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -6,15 +6,13 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.Contributor
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_creator.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -24,7 +22,7 @@ class ContributorFragment : BaseFragment(), ContributorView, MainView.TitledView
|
||||
lateinit var presenter: ContributorPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var creatorsAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var creatorsAdapter: ContributorAdapter
|
||||
|
||||
override val titleStringId get() = R.string.contributors_title
|
||||
|
||||
@ -43,18 +41,19 @@ class ContributorFragment : BaseFragment(), ContributorView, MainView.TitledView
|
||||
|
||||
override fun initView() {
|
||||
with(creatorRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = creatorsAdapter
|
||||
addItemDecoration(FlexibleItemDecoration(context)
|
||||
.withDefaultDivider()
|
||||
.withDrawDividerOnLastItem(false))
|
||||
addItemDecoration(DividerItemDecoration(context))
|
||||
}
|
||||
creatorsAdapter.setOnItemClickListener(presenter::onItemSelected)
|
||||
creatorsAdapter.onClickListener = presenter::onItemSelected
|
||||
creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<ContributorItem>) {
|
||||
creatorsAdapter.updateDataSet(data)
|
||||
override fun updateData(data: List<Contributor>) {
|
||||
with(creatorsAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openUserGithubPage(username: String) {
|
||||
|
@ -1,51 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.about.contributor
|
||||
|
||||
import android.view.View
|
||||
import coil.api.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.AppCreator
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_contributor.*
|
||||
|
||||
class ContributorItem(val creator: AppCreator) :
|
||||
AbstractFlexibleItem<ContributorItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_contributor
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
with(holder) {
|
||||
creatorItemName.text = creator.displayName
|
||||
|
||||
creatorItemAvatar.load("https://github.com/${creator.githubUsername}.png") {
|
||||
transformations(RoundedCornersTransformation(8f))
|
||||
crossfade(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ContributorItem
|
||||
|
||||
if (creator != other.creator) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = creator.hashCode()
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View? get() = contentView
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.about.contributor
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.pojos.Contributor
|
||||
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
@ -21,9 +21,8 @@ class ContributorPresenter @Inject constructor(
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item !is ContributorItem) return
|
||||
view?.openUserGithubPage(item.creator.githubUsername)
|
||||
fun onItemSelected(contributor: Contributor) {
|
||||
view?.openUserGithubPage(contributor.githubUsername)
|
||||
}
|
||||
|
||||
fun onSeeMoreClick() {
|
||||
@ -32,7 +31,6 @@ class ContributorPresenter @Inject constructor(
|
||||
|
||||
private fun loadData() {
|
||||
disposable.add(appCreatorRepository.getAppCreators()
|
||||
.map { it.map { creator -> ContributorItem(creator) } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally { view?.showProgress(false) }
|
||||
|
@ -1,12 +1,13 @@
|
||||
package io.github.wulkanowy.ui.modules.about.contributor
|
||||
|
||||
import io.github.wulkanowy.data.pojos.Contributor
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface ContributorView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<ContributorItem>)
|
||||
fun updateData(data: List<Contributor>)
|
||||
|
||||
fun openUserGithubPage(username: String)
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
package io.github.wulkanowy.ui.modules.about.license
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.mikepenz.aboutlibraries.entity.Library
|
||||
import io.github.wulkanowy.databinding.ItemLicenseBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class LicenseAdapter @Inject constructor() : RecyclerView.Adapter<LicenseAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = emptyList<Library>()
|
||||
|
||||
var onClickListener: (Library) -> Unit = {}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemLicenseBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
licenseItemName.text = item.libraryName
|
||||
licenseItemSummary.text = item.license?.licenseName?.takeIf { it.isNotBlank() } ?: item.libraryVersion
|
||||
|
||||
root.setOnClickListener { onClickListener(item) }
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemLicenseBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -8,16 +8,13 @@ import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.mikepenz.aboutlibraries.Libs
|
||||
import com.mikepenz.aboutlibraries.entity.Library
|
||||
import dagger.Lazy
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_license.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -27,7 +24,7 @@ class LicenseFragment : BaseFragment(), LicenseView, MainView.TitledView {
|
||||
lateinit var presenter: LicensePresenter
|
||||
|
||||
@Inject
|
||||
lateinit var licenseAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var licenseAdapter: LicenseAdapter
|
||||
|
||||
@Inject
|
||||
lateinit var libs: Lazy<Libs>
|
||||
@ -53,15 +50,19 @@ class LicenseFragment : BaseFragment(), LicenseView, MainView.TitledView {
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
licenseAdapter.onClickListener = presenter::onItemSelected
|
||||
|
||||
with(licenseRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = licenseAdapter
|
||||
}
|
||||
licenseAdapter.setOnItemClickListener(presenter::onItemSelected)
|
||||
}
|
||||
|
||||
override fun updateData(data: List<LicenseItem>) {
|
||||
licenseAdapter.updateDataSet(data)
|
||||
override fun updateData(data: List<Library>) {
|
||||
with(licenseAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openLicense(licenseHtml: String) {
|
||||
|
@ -1,44 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.about.license
|
||||
|
||||
import android.view.View
|
||||
import com.mikepenz.aboutlibraries.entity.Library
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_license.*
|
||||
|
||||
class LicenseItem(val library: Library) : AbstractFlexibleItem<LicenseItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_license
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
with(holder) {
|
||||
licenseItemName.text = library.libraryName
|
||||
licenseItemSummary.text = library.license?.licenseName?.takeIf { it.isNotBlank() } ?: library.libraryVersion
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as LicenseItem
|
||||
|
||||
if (library != other.library) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = library.hashCode()
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View? get() = contentView
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.about.license
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import com.mikepenz.aboutlibraries.entity.Library
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
@ -20,14 +20,12 @@ class LicensePresenter @Inject constructor(
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item !is LicenseItem) return
|
||||
view?.run { item.library.license?.licenseDescription?.let { openLicense(it) } }
|
||||
fun onItemSelected(library: Library) {
|
||||
view?.run { library.license?.licenseDescription?.let { openLicense(it) } }
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
disposable.add(Single.fromCallable { view?.appLibraries }
|
||||
.map { it.map { library -> LicenseItem(library) } }
|
||||
disposable.add(Single.fromCallable { view?.appLibraries.orEmpty() }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doOnEvent { _, _ -> view?.showProgress(false) }
|
||||
|
@ -9,7 +9,7 @@ interface LicenseView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<LicenseItem>)
|
||||
fun updateData(data: List<Library>)
|
||||
|
||||
fun openLicense(licenseHtml: String)
|
||||
|
||||
|
@ -0,0 +1,46 @@
|
||||
package io.github.wulkanowy.ui.modules.account
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.databinding.ItemAccountBinding
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountAdapter @Inject constructor() : RecyclerView.Adapter<AccountAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = emptyList<Student>()
|
||||
|
||||
var onClickListener: (Student) -> Unit = {}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemAccountBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val student = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
accountItemName.text = "${student.studentName} ${student.className}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
|
||||
with(accountItemImage) {
|
||||
val colorImage = if (student.isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
|
||||
else context.getThemeAttrColor(R.attr.colorOnSurface, 153)
|
||||
|
||||
setColorFilter(colorImage, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
root.setOnClickListener { onClickListener(student) }
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemAccountBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -7,13 +7,11 @@ import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import android.widget.Toast.LENGTH_LONG
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.dialog_account.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -23,7 +21,7 @@ class AccountDialog : BaseDialogFragment(), AccountView {
|
||||
lateinit var presenter: AccountPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var accountAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var accountAdapter: AccountAdapter
|
||||
|
||||
companion object {
|
||||
fun newInstance() = AccountDialog()
|
||||
@ -44,18 +42,21 @@ class AccountDialog : BaseDialogFragment(), AccountView {
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
accountAdapter.setOnItemClickListener { presenter.onItemSelected(it) }
|
||||
accountAdapter.onClickListener = presenter::onItemSelected
|
||||
|
||||
accountDialogAdd.setOnClickListener { presenter.onAddSelected() }
|
||||
accountDialogRemove.setOnClickListener { presenter.onRemoveSelected() }
|
||||
accountDialogRecycler.apply {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = accountAdapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(data: List<AccountItem>) {
|
||||
accountAdapter.updateDataSet(data)
|
||||
override fun updateData(data: List<Student>) {
|
||||
with(accountAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun showError(text: String, error: Throwable) {
|
||||
|
@ -1,59 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.account
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.View
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_account.*
|
||||
|
||||
class AccountItem(val student: Student) : AbstractFlexibleItem<AccountItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_account
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
val context = holder.contentView.context
|
||||
|
||||
val colorImage = if (student.isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
|
||||
else ColorUtils.setAlphaComponent(context.getThemeAttrColor(R.attr.colorOnSurface), 153)
|
||||
|
||||
with(holder) {
|
||||
accountItemName.text = "${student.studentName} ${student.className}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
accountItemImage.setColorFilter(colorImage, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as AccountItem
|
||||
|
||||
if (student != other.student) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = student.hashCode()
|
||||
result = 31 * result + student.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View? get() = contentView
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.account
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
@ -63,32 +63,29 @@ class AccountPresenter @Inject constructor(
|
||||
}))
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item is AccountItem) {
|
||||
Timber.i("Select student item ${item.student.id}")
|
||||
if (item.student.isCurrent) {
|
||||
view?.dismissView()
|
||||
} else {
|
||||
Timber.i("Attempt to change a student")
|
||||
disposable.add(studentRepository.switchStudent(item.student)
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally { view?.dismissView() }
|
||||
.subscribe({
|
||||
Timber.i("Change a student result: Success")
|
||||
view?.recreateMainView()
|
||||
}, {
|
||||
Timber.i("Change a student result: An exception occurred")
|
||||
errorHandler.dispatch(it)
|
||||
}))
|
||||
}
|
||||
fun onItemSelected(student: Student) {
|
||||
Timber.i("Select student item ${student.id}")
|
||||
if (student.isCurrent) {
|
||||
view?.dismissView()
|
||||
} else {
|
||||
Timber.i("Attempt to change a student")
|
||||
disposable.add(studentRepository.switchStudent(student)
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally { view?.dismissView() }
|
||||
.subscribe({
|
||||
Timber.i("Change a student result: Success")
|
||||
view?.recreateMainView()
|
||||
}, {
|
||||
Timber.i("Change a student result: An exception occurred")
|
||||
errorHandler.dispatch(it)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
Timber.i("Loading account data started")
|
||||
disposable.add(studentRepository.getSavedStudents(false)
|
||||
.map { it.map { item -> AccountItem(item) } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.subscribe({
|
||||
@ -100,4 +97,3 @@ class AccountPresenter @Inject constructor(
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,13 @@
|
||||
package io.github.wulkanowy.ui.modules.account
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface AccountView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<AccountItem>)
|
||||
fun updateData(data: List<Student>)
|
||||
|
||||
fun dismissView()
|
||||
|
||||
|
@ -1,12 +1,80 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus
|
||||
import io.github.wulkanowy.databinding.ItemAttendanceBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class AttendanceAdapter<T : IFlexible<*>> : FlexibleAdapter<T>(null, null, true) {
|
||||
class AttendanceAdapter @Inject constructor() :
|
||||
RecyclerView.Adapter<AttendanceAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = emptyList<Attendance>()
|
||||
|
||||
var excuseActionMode: Boolean = false
|
||||
|
||||
var onClickListener: (Attendance) -> Unit = {}
|
||||
|
||||
var onExcuseCheckboxSelect: (attendanceItem: Attendance, checked: Boolean) -> Unit = { _, _ -> }
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemAttendanceBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
attendanceItemNumber.text = item.number.toString()
|
||||
attendanceItemSubject.text = item.subject
|
||||
attendanceItemDescription.text = item.name
|
||||
attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE }
|
||||
attendanceItemNumber.visibility = View.GONE
|
||||
attendanceItemExcuseInfo.visibility = View.GONE
|
||||
attendanceItemExcuseCheckbox.visibility = View.GONE
|
||||
attendanceItemExcuseCheckbox.isChecked = false
|
||||
attendanceItemExcuseCheckbox.setOnCheckedChangeListener { _, checked ->
|
||||
onExcuseCheckboxSelect(item, checked)
|
||||
}
|
||||
|
||||
when (if (item.excuseStatus != null) SentExcuseStatus.valueOf(item.excuseStatus) else null) {
|
||||
SentExcuseStatus.WAITING -> {
|
||||
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting)
|
||||
attendanceItemExcuseInfo.visibility = View.VISIBLE
|
||||
attendanceItemAlert.visibility = View.INVISIBLE
|
||||
}
|
||||
SentExcuseStatus.DENIED -> {
|
||||
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_denied)
|
||||
attendanceItemExcuseInfo.visibility = View.VISIBLE
|
||||
}
|
||||
else -> {
|
||||
if (item.excusable && excuseActionMode) {
|
||||
attendanceItemNumber.visibility = View.GONE
|
||||
attendanceItemExcuseCheckbox.visibility = View.VISIBLE
|
||||
} else {
|
||||
attendanceItemNumber.visibility = View.VISIBLE
|
||||
attendanceItemExcuseCheckbox.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
root.setOnClickListener {
|
||||
onClickListener(item)
|
||||
|
||||
with(attendanceItemExcuseCheckbox) {
|
||||
if (excuseActionMode && isVisible) {
|
||||
isChecked = !isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemAttendanceBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
|
@ -13,19 +13,17 @@ import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.dialog_excuse.*
|
||||
import kotlinx.android.synthetic.main.fragment_attendance.*
|
||||
import org.threeten.bp.LocalDate
|
||||
@ -38,7 +36,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
lateinit var presenter: AttendancePresenter
|
||||
|
||||
@Inject
|
||||
lateinit var attendanceAdapter: AttendanceAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var attendanceAdapter: AttendanceAdapter
|
||||
|
||||
override val excuseSuccessString: String
|
||||
get() = getString(R.string.attendance_excuse_success)
|
||||
@ -54,7 +52,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
|
||||
override val titleStringId get() = R.string.attendance_title
|
||||
|
||||
override val isViewEmpty get() = attendanceAdapter.isEmpty
|
||||
override val isViewEmpty get() = attendanceAdapter.items.isEmpty()
|
||||
|
||||
override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize
|
||||
|
||||
@ -102,15 +100,15 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
attendanceAdapter.setOnItemClickListener(presenter::onAttendanceItemSelected)
|
||||
attendanceAdapter.onExcuseCheckboxSelect = presenter::onExcuseCheckboxSelect
|
||||
with(attendanceAdapter) {
|
||||
onClickListener = presenter::onAttendanceItemSelected
|
||||
onExcuseCheckboxSelect = presenter::onExcuseCheckboxSelect
|
||||
}
|
||||
|
||||
with(attendanceRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = attendanceAdapter
|
||||
addItemDecoration(FlexibleItemDecoration(context)
|
||||
.withDefaultDivider()
|
||||
.withDrawDividerOnLastItem(false))
|
||||
addItemDecoration(DividerItemDecoration(context))
|
||||
}
|
||||
|
||||
attendanceSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||
@ -135,8 +133,11 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
else false
|
||||
}
|
||||
|
||||
override fun updateData(data: List<AttendanceItem>) {
|
||||
attendanceAdapter.updateDataSet(data, true)
|
||||
override fun updateData(data: List<Attendance>) {
|
||||
with(attendanceAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateNavigationDay(date: String) {
|
||||
@ -144,7 +145,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
}
|
||||
|
||||
override fun clearData() {
|
||||
attendanceAdapter.clear()
|
||||
with(attendanceAdapter) {
|
||||
items = emptyList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetView() {
|
||||
|
@ -1,97 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.core.view.isVisible
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_attendance.*
|
||||
|
||||
class AttendanceItem(val attendance: Attendance) :
|
||||
AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_attendance
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.apply {
|
||||
attendanceItemNumber.text = attendance.number.toString()
|
||||
attendanceItemSubject.text = attendance.subject
|
||||
attendanceItemDescription.text = attendance.name
|
||||
attendanceItemAlert.visibility = attendance.run { if (absence && !excused) VISIBLE else INVISIBLE }
|
||||
attendanceItemNumber.visibility = GONE
|
||||
attendanceItemExcuseInfo.visibility = GONE
|
||||
attendanceItemExcuseCheckbox.visibility = GONE
|
||||
attendanceItemExcuseCheckbox.isChecked = false
|
||||
attendanceItemExcuseCheckbox.setOnCheckedChangeListener { _, checked ->
|
||||
(adapter as AttendanceAdapter).onExcuseCheckboxSelect(attendance, checked)
|
||||
}
|
||||
|
||||
when (if (attendance.excuseStatus != null) SentExcuseStatus.valueOf(attendance.excuseStatus) else null) {
|
||||
SentExcuseStatus.WAITING -> {
|
||||
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting)
|
||||
attendanceItemExcuseInfo.visibility = VISIBLE
|
||||
attendanceItemAlert.visibility = INVISIBLE
|
||||
}
|
||||
SentExcuseStatus.DENIED -> {
|
||||
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_denied)
|
||||
attendanceItemExcuseInfo.visibility = VISIBLE
|
||||
}
|
||||
else -> {
|
||||
if (attendance.excusable && (adapter as AttendanceAdapter).excuseActionMode) {
|
||||
attendanceItemNumber.visibility = GONE
|
||||
attendanceItemExcuseCheckbox.visibility = VISIBLE
|
||||
} else {
|
||||
attendanceItemNumber.visibility = VISIBLE
|
||||
attendanceItemExcuseCheckbox.visibility = GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as AttendanceItem
|
||||
|
||||
if (attendance != other.attendance) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = attendance.hashCode()
|
||||
result = 31 * result + attendance.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, val adapter: FlexibleAdapter<*>) :
|
||||
FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
|
||||
override fun onClick(view: View?) {
|
||||
super.onClick(view)
|
||||
attendanceItemExcuseCheckbox.apply {
|
||||
if ((adapter as AttendanceAdapter).excuseActionMode && isVisible) {
|
||||
isChecked = !isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
|
||||
@Module
|
||||
class AttendanceModule {
|
||||
|
||||
@Provides
|
||||
fun provideAttendanceFlexibleAdapter() = AttendanceAdapter<AbstractFlexibleItem<*>>()
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
@ -111,11 +110,11 @@ class AttendancePresenter @Inject constructor(
|
||||
view?.finishActionMode()
|
||||
}
|
||||
|
||||
fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
fun onAttendanceItemSelected(attendance: Attendance) {
|
||||
view?.apply {
|
||||
if (item is AttendanceItem && !excuseActionMode) {
|
||||
Timber.i("Select attendance item ${item.attendance.id}")
|
||||
showAttendanceDialog(item.attendance)
|
||||
if (!excuseActionMode) {
|
||||
Timber.i("Select attendance item ${attendance.id}")
|
||||
showAttendanceDialog(attendance)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -197,9 +196,7 @@ class AttendancePresenter @Inject constructor(
|
||||
if (prefRepository.isShowPresent) list
|
||||
else list.filter { !it.presence }
|
||||
}
|
||||
.delay(200, MILLISECONDS)
|
||||
.map { items -> items.map { AttendanceItem(it) } }
|
||||
.map { items -> items.sortedBy { it.attendance.number } }
|
||||
.map { items -> items.sortedBy { it.number } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally {
|
||||
@ -216,7 +213,7 @@ class AttendancePresenter @Inject constructor(
|
||||
showEmpty(it.isEmpty())
|
||||
showErrorView(false)
|
||||
showContent(it.isNotEmpty())
|
||||
showExcuseButton(it.any { item -> item.attendance.excusable })
|
||||
showExcuseButton(it.any { item -> item.excusable })
|
||||
}
|
||||
analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh)
|
||||
}) {
|
||||
@ -236,7 +233,6 @@ class AttendancePresenter @Inject constructor(
|
||||
attendanceRepository.excuseForAbsence(student, semester, toExcuseList, reason)
|
||||
}
|
||||
}
|
||||
.delay(200, MILLISECONDS)
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doOnSubscribe {
|
||||
|
@ -18,7 +18,7 @@ interface AttendanceView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<AttendanceItem>)
|
||||
fun updateData(data: List<Attendance>)
|
||||
|
||||
fun updateNavigationDay(date: String)
|
||||
|
||||
|
@ -0,0 +1,101 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance.summary
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||
import io.github.wulkanowy.databinding.ItemAttendanceSummaryBinding
|
||||
import io.github.wulkanowy.databinding.ScrollableHeaderAttendanceSummaryBinding
|
||||
import io.github.wulkanowy.utils.calculatePercentage
|
||||
import io.github.wulkanowy.utils.getFormattedName
|
||||
import org.threeten.bp.Month
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
class AttendanceSummaryAdapter @Inject constructor() :
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private enum class ViewType(val id: Int) {
|
||||
HEADER(1),
|
||||
ITEM(2)
|
||||
}
|
||||
|
||||
var items = emptyList<AttendanceSummary>()
|
||||
|
||||
override fun getItemCount() = items.size + 2
|
||||
|
||||
override fun getItemViewType(position: Int) = when (position) {
|
||||
0 -> ViewType.HEADER.id
|
||||
else -> ViewType.ITEM.id
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (viewType) {
|
||||
ViewType.HEADER.id -> HeaderViewHolder(ScrollableHeaderAttendanceSummaryBinding.inflate(inflater, parent, false))
|
||||
ViewType.ITEM.id -> ItemViewHolder(ItemAttendanceSummaryBinding.inflate(inflater, parent, false))
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is HeaderViewHolder -> bindHeaderViewHolder(holder.binding)
|
||||
is ItemViewHolder -> bindItemViewHolder(holder.binding, position - 2)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindHeaderViewHolder(binding: ScrollableHeaderAttendanceSummaryBinding) {
|
||||
binding.attendanceSummaryScrollableHeaderPercentage.text = formatPercentage(items.calculatePercentage())
|
||||
}
|
||||
|
||||
private fun bindItemViewHolder(binding: ItemAttendanceSummaryBinding, position: Int) {
|
||||
val item = if (position == -1) getTotalItem() else items[position]
|
||||
|
||||
with(binding) {
|
||||
attendanceSummaryMonth.text = when (position) {
|
||||
-1 -> root.context.getString(R.string.attendance_summary_total)
|
||||
else -> item.month.getFormattedName()
|
||||
}
|
||||
attendanceSummaryPercentage.text = when (position) {
|
||||
-1 -> formatPercentage(items.calculatePercentage())
|
||||
else -> formatPercentage(item.calculatePercentage())
|
||||
}
|
||||
|
||||
attendanceSummaryPresent.text = item.presence.toString()
|
||||
attendanceSummaryAbsenceUnexcused.text = item.absence.toString()
|
||||
attendanceSummaryAbsenceExcused.text = item.absenceExcused.toString()
|
||||
attendanceSummaryAbsenceSchool.text = item.absenceForSchoolReasons.toString()
|
||||
attendanceSummaryExemption.text = item.exemption.toString()
|
||||
attendanceSummaryLatenessUnexcused.text = item.lateness.toString()
|
||||
attendanceSummaryLatenessExcused.text = item.latenessExcused.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTotalItem() = AttendanceSummary(
|
||||
month = Month.APRIL,
|
||||
presence = items.sumBy { it.presence },
|
||||
absence = items.sumBy { it.absence },
|
||||
absenceExcused = items.sumBy { it.absenceExcused },
|
||||
absenceForSchoolReasons = items.sumBy { it.absenceForSchoolReasons },
|
||||
exemption = items.sumBy { it.exemption },
|
||||
lateness = items.sumBy { it.lateness },
|
||||
latenessExcused = items.sumBy { it.latenessExcused },
|
||||
diaryId = -1,
|
||||
studentId = -1,
|
||||
subjectId = -1
|
||||
)
|
||||
|
||||
private fun formatPercentage(percentage: Double): String {
|
||||
return if (percentage == 0.0) "0%"
|
||||
else "${String.format(Locale.FRANCE, "%.2f", percentage)}%"
|
||||
}
|
||||
|
||||
class HeaderViewHolder(val binding: ScrollableHeaderAttendanceSummaryBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class ItemViewHolder(val binding: ItemAttendanceSummaryBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -9,10 +9,9 @@ import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
@ -26,7 +25,7 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie
|
||||
lateinit var presenter: AttendanceSummaryPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var attendanceSummaryAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var attendanceSummaryAdapter: AttendanceSummaryAdapter
|
||||
|
||||
private lateinit var subjectsAdapter: ArrayAdapter<String>
|
||||
|
||||
@ -36,11 +35,9 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie
|
||||
fun newInstance() = AttendanceSummaryFragment()
|
||||
}
|
||||
|
||||
override val totalString get() = getString(R.string.attendance_summary_total)
|
||||
|
||||
override val titleStringId get() = R.string.attendance_title
|
||||
|
||||
override val isViewEmpty get() = attendanceSummaryAdapter.isEmpty
|
||||
override val isViewEmpty get() = attendanceSummaryAdapter.items.isEmpty()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_attendance_summary, container, false)
|
||||
@ -54,7 +51,7 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie
|
||||
|
||||
override fun initView() {
|
||||
with(attendanceSummaryRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = attendanceSummaryAdapter
|
||||
}
|
||||
|
||||
@ -81,16 +78,18 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateDataSet(data: List<AttendanceSummaryItem>, header: AttendanceSummaryScrollableHeader) {
|
||||
override fun updateDataSet(data: List<AttendanceSummary>) {
|
||||
with(attendanceSummaryAdapter) {
|
||||
updateDataSet(data, true)
|
||||
removeAllScrollableHeaders()
|
||||
addScrollableHeader(header)
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearView() {
|
||||
attendanceSummaryAdapter.clear()
|
||||
with(attendanceSummaryAdapter) {
|
||||
items = emptyList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun showEmpty(show: Boolean) {
|
||||
|
@ -1,80 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance.summary
|
||||
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_attendance_summary.*
|
||||
|
||||
class AttendanceSummaryItem(
|
||||
private val month: String,
|
||||
private val percentage: String,
|
||||
private val present: String,
|
||||
private val absence: String,
|
||||
private val excusedAbsence: String,
|
||||
private val schoolAbsence: String,
|
||||
private val exemption: String,
|
||||
private val lateness: String,
|
||||
private val excusedLateness: String
|
||||
) : AbstractFlexibleItem<AttendanceSummaryItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_attendance_summary
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.apply {
|
||||
attendanceSummaryMonth.text = month
|
||||
attendanceSummaryPercentage.text = percentage
|
||||
attendanceSummaryPresent.text = present
|
||||
attendanceSummaryAbsenceUnexcused.text = absence
|
||||
attendanceSummaryAbsenceExcused.text = excusedAbsence
|
||||
attendanceSummaryAbsenceSchool.text = schoolAbsence
|
||||
attendanceSummaryExemption.text = exemption
|
||||
attendanceSummaryLatenessUnexcused.text = lateness
|
||||
attendanceSummaryLatenessExcused.text = excusedLateness
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as AttendanceSummaryItem
|
||||
|
||||
if (month != other.month) return false
|
||||
if (percentage != other.percentage) return false
|
||||
if (present != other.present) return false
|
||||
if (absence != other.absence) return false
|
||||
if (excusedAbsence != other.excusedAbsence) return false
|
||||
if (schoolAbsence != other.schoolAbsence) return false
|
||||
if (exemption != other.exemption) return false
|
||||
if (lateness != other.lateness) return false
|
||||
if (excusedLateness != other.excusedLateness) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = month.hashCode()
|
||||
result = 31 * result + percentage.hashCode()
|
||||
result = 31 * result + present.hashCode()
|
||||
result = 31 * result + absence.hashCode()
|
||||
result = 31 * result + excusedAbsence.hashCode()
|
||||
result = 31 * result + schoolAbsence.hashCode()
|
||||
result = 31 * result + exemption.hashCode()
|
||||
result = 31 * result + lateness.hashCode()
|
||||
result = 31 * result + excusedLateness.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance.summary
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||
import io.github.wulkanowy.data.db.entities.Subject
|
||||
import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
@ -10,13 +9,8 @@ import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.calculatePercentage
|
||||
import io.github.wulkanowy.utils.getFormattedName
|
||||
import org.threeten.bp.Month
|
||||
import timber.log.Timber
|
||||
import java.lang.String.format
|
||||
import java.util.Locale.FRANCE
|
||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
import javax.inject.Inject
|
||||
|
||||
class AttendanceSummaryPresenter @Inject constructor(
|
||||
@ -88,8 +82,7 @@ class AttendanceSummaryPresenter @Inject constructor(
|
||||
attendanceSummaryRepository.getAttendanceSummary(student, it, subjectId, forceRefresh)
|
||||
}
|
||||
}
|
||||
.map { createAttendanceSummaryItems(it) to AttendanceSummaryScrollableHeader(formatPercentage(it.calculatePercentage())) }
|
||||
.delay(200, MILLISECONDS)
|
||||
.map { items -> items.sortedByDescending { if (it.month.value <= Month.JUNE.value) it.month.value + 12 else it.month.value } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally {
|
||||
@ -102,11 +95,11 @@ class AttendanceSummaryPresenter @Inject constructor(
|
||||
.subscribe({
|
||||
Timber.i("Loading attendance summary result: Success")
|
||||
view?.apply {
|
||||
showEmpty(it.first.isEmpty())
|
||||
showContent(it.first.isNotEmpty())
|
||||
updateDataSet(it.first, it.second)
|
||||
showEmpty(it.isEmpty())
|
||||
showContent(it.isNotEmpty())
|
||||
updateDataSet(it)
|
||||
}
|
||||
analytics.logEvent("load_attendance_summary", "items" to it.first.size, "force_refresh" to forceRefresh, "item_id" to subjectId)
|
||||
analytics.logEvent("load_attendance_summary", "items" to it.size, "force_refresh" to forceRefresh, "item_id" to subjectId)
|
||||
}) {
|
||||
Timber.i("Loading attendance summary result: An exception occurred")
|
||||
errorHandler.dispatch(it)
|
||||
@ -150,42 +143,4 @@ class AttendanceSummaryPresenter @Inject constructor(
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
private fun createAttendanceSummaryTotalItem(attendanceSummary: List<AttendanceSummary>): AttendanceSummaryItem {
|
||||
return AttendanceSummaryItem(
|
||||
month = view?.totalString.orEmpty(),
|
||||
percentage = formatPercentage(attendanceSummary.calculatePercentage()),
|
||||
present = attendanceSummary.sumBy { it.presence }.toString(),
|
||||
absence = attendanceSummary.sumBy { it.absence }.toString(),
|
||||
excusedAbsence = attendanceSummary.sumBy { it.absenceExcused }.toString(),
|
||||
schoolAbsence = attendanceSummary.sumBy { it.absenceForSchoolReasons }.toString(),
|
||||
exemption = attendanceSummary.sumBy { it.exemption }.toString(),
|
||||
lateness = attendanceSummary.sumBy { it.lateness }.toString(),
|
||||
excusedLateness = attendanceSummary.sumBy { it.latenessExcused }.toString()
|
||||
)
|
||||
}
|
||||
|
||||
private fun createAttendanceSummaryItems(attendanceSummary: List<AttendanceSummary>): List<AttendanceSummaryItem> {
|
||||
if (attendanceSummary.isEmpty()) return emptyList()
|
||||
return listOf(createAttendanceSummaryTotalItem(attendanceSummary)) + attendanceSummary.sortedByDescending {
|
||||
if (it.month.value <= Month.JUNE.value) it.month.value + 12 else it.month.value
|
||||
}.map {
|
||||
AttendanceSummaryItem(
|
||||
month = it.month.getFormattedName(),
|
||||
percentage = formatPercentage(it.calculatePercentage()),
|
||||
present = it.presence.toString(),
|
||||
absence = it.absence.toString(),
|
||||
excusedAbsence = it.absenceExcused.toString(),
|
||||
schoolAbsence = it.absenceForSchoolReasons.toString(),
|
||||
exemption = it.exemption.toString(),
|
||||
lateness = it.lateness.toString(),
|
||||
excusedLateness = it.latenessExcused.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatPercentage(percentage: Double): String {
|
||||
return if (percentage == 0.0) "0%"
|
||||
else "${format(FRANCE, "%.2f", percentage)}%"
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance.summary
|
||||
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.scrollable_header_attendance_summary.*
|
||||
|
||||
class AttendanceSummaryScrollableHeader(private val percentage: String) :
|
||||
AbstractFlexibleItem<AttendanceSummaryScrollableHeader.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.scrollable_header_attendance_summary
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, position: Int, payloads: MutableList<Any>?) {
|
||||
holder?.apply { attendanceSummaryScrollableHeaderPercentage.text = percentage }
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as AttendanceSummaryScrollableHeader
|
||||
|
||||
if (percentage != other.percentage) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return percentage.hashCode()
|
||||
}
|
||||
|
||||
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View?
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -1,11 +1,10 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance.summary
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface AttendanceSummaryView : BaseView {
|
||||
|
||||
val totalString: String
|
||||
|
||||
val isViewEmpty: Boolean
|
||||
|
||||
fun initView()
|
||||
@ -24,7 +23,7 @@ interface AttendanceSummaryView : BaseView {
|
||||
|
||||
fun setErrorDetails(message: String)
|
||||
|
||||
fun updateDataSet(data: List<AttendanceSummaryItem>, header: AttendanceSummaryScrollableHeader)
|
||||
fun updateDataSet(data: List<AttendanceSummary>)
|
||||
|
||||
fun updateSubjects(data: ArrayList<String>)
|
||||
|
||||
|
@ -0,0 +1,65 @@
|
||||
package io.github.wulkanowy.ui.modules.exam
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.data.db.entities.Exam
|
||||
import io.github.wulkanowy.databinding.HeaderExamBinding
|
||||
import io.github.wulkanowy.databinding.ItemExamBinding
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.github.wulkanowy.utils.weekDayName
|
||||
import org.threeten.bp.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
||||
class ExamAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
var items = emptyList<ExamItem<*>>()
|
||||
|
||||
var onClickListener: (Exam) -> Unit = {}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun getItemViewType(position: Int) = items[position].viewType.id
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (viewType) {
|
||||
ExamItem.ViewType.HEADER.id -> HeaderViewHolder(HeaderExamBinding.inflate(inflater, parent, false))
|
||||
ExamItem.ViewType.ITEM.id -> ItemViewHolder(ItemExamBinding.inflate(inflater, parent, false))
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is HeaderViewHolder -> bindHeaderViewHolder(holder.binding, items[position].value as LocalDate)
|
||||
is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position].value as Exam)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private fun bindHeaderViewHolder(binding: HeaderExamBinding, date: LocalDate) {
|
||||
with(binding) {
|
||||
examHeaderDay.text = date.weekDayName.capitalize()
|
||||
examHeaderDate.text = date.toFormattedString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindItemViewHolder(binding: ItemExamBinding, exam: Exam) {
|
||||
with(binding) {
|
||||
examItemSubject.text = exam.subject
|
||||
examItemTeacher.text = exam.teacher
|
||||
examItemType.text = exam.type
|
||||
|
||||
root.setOnClickListener { onClickListener(exam) }
|
||||
}
|
||||
}
|
||||
|
||||
private class HeaderViewHolder(val binding: HeaderExamBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
private class ItemViewHolder(val binding: ItemExamBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -7,17 +7,14 @@ import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Exam
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_exam.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -27,7 +24,7 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
|
||||
lateinit var presenter: ExamPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var examAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var examAdapter: ExamAdapter
|
||||
|
||||
companion object {
|
||||
private const val SAVED_DATE_KEY = "CURRENT_DATE"
|
||||
@ -37,7 +34,7 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
|
||||
|
||||
override val titleStringId get() = R.string.exam_title
|
||||
|
||||
override val isViewEmpty get() = examAdapter.isEmpty
|
||||
override val isViewEmpty get() = examAdapter.items.isEmpty()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_exam, container, false)
|
||||
@ -50,14 +47,12 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
examAdapter.setOnItemClickListener(presenter::onExamItemSelected)
|
||||
examAdapter.onClickListener = presenter::onExamItemSelected
|
||||
|
||||
with(examRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = examAdapter
|
||||
addItemDecoration(FlexibleItemDecoration(context)
|
||||
.withDefaultDivider(R.layout.item_exam)
|
||||
.withDrawDividerOnLastItem(false))
|
||||
addItemDecoration(DividerItemDecoration(context))
|
||||
}
|
||||
|
||||
examSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||
@ -74,8 +69,11 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
|
||||
examSwipe.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun updateData(data: List<ExamItem>) {
|
||||
examAdapter.updateDataSet(data, true)
|
||||
override fun updateData(data: List<ExamItem<*>>) {
|
||||
with(examAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateNavigationWeek(date: String) {
|
||||
@ -83,7 +81,10 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
|
||||
}
|
||||
|
||||
override fun clearData() {
|
||||
examAdapter.clear()
|
||||
with(examAdapter) {
|
||||
items = emptyList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetView() {
|
||||
|
@ -1,52 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.exam
|
||||
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.ExpandableViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.github.wulkanowy.utils.weekDayName
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.header_exam.*
|
||||
import org.threeten.bp.LocalDate
|
||||
|
||||
class ExamHeader(private val date: LocalDate) : AbstractHeaderItem<ExamHeader.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.header_exam
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder,
|
||||
position: Int, payloads: MutableList<Any>?) {
|
||||
holder.run {
|
||||
examHeaderDay.text = date.weekDayName.capitalize()
|
||||
examHeaderDate.text = date.toFormattedString()
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ExamHeader
|
||||
|
||||
if (date != other.date) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return date.hashCode()
|
||||
}
|
||||
|
||||
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : ExpandableViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -1,50 +1,9 @@
|
||||
package io.github.wulkanowy.ui.modules.exam
|
||||
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractSectionableItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Exam
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_exam.*
|
||||
data class ExamItem<out T>(val value: T, val viewType: ViewType) {
|
||||
|
||||
class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem<ExamItem.ViewHolder, ExamHeader>(header) {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_exam
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.run {
|
||||
examItemSubject.text = exam.subject
|
||||
examItemTeacher.text = exam.teacher
|
||||
examItemType.text = exam.type
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ExamItem
|
||||
|
||||
if (exam != other.exam) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = exam.hashCode()
|
||||
result = 31 * result + exam.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
enum class ViewType(val id: Int) {
|
||||
HEADER(1),
|
||||
ITEM(2)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.exam
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Exam
|
||||
import io.github.wulkanowy.data.repositories.exam.ExamRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
@ -19,7 +18,6 @@ import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import org.threeten.bp.LocalDate.ofEpochDay
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
import javax.inject.Inject
|
||||
|
||||
class ExamPresenter @Inject constructor(
|
||||
@ -75,11 +73,9 @@ class ExamPresenter @Inject constructor(
|
||||
view?.showErrorDetailsDialog(lastError)
|
||||
}
|
||||
|
||||
fun onExamItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is ExamItem) {
|
||||
Timber.i("Select exam item ${item.exam.id}")
|
||||
view?.showExamDialog(item.exam)
|
||||
}
|
||||
fun onExamItemSelected(exam: Exam) {
|
||||
Timber.i("Select exam item ${exam.id}")
|
||||
view?.showExamDialog(exam)
|
||||
}
|
||||
|
||||
fun onViewReselected() {
|
||||
@ -117,8 +113,6 @@ class ExamPresenter @Inject constructor(
|
||||
examRepository.getExams(student, semester, currentDate.monday, currentDate.friday, forceRefresh)
|
||||
}
|
||||
}
|
||||
.delay(200, MILLISECONDS)
|
||||
.map { it.groupBy { exam -> exam.date }.toSortedMap() }
|
||||
.map { createExamItems(it) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
@ -156,12 +150,12 @@ class ExamPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun createExamItems(items: Map<LocalDate, List<Exam>>): List<ExamItem> {
|
||||
return items.flatMap {
|
||||
ExamHeader(it.key).let { header ->
|
||||
it.value.reversed().map { item -> ExamItem(header, item) }
|
||||
private fun createExamItems(items: List<Exam>): List<ExamItem<*>> {
|
||||
return items.groupBy { it.date }.toSortedMap().map { (date, exams) ->
|
||||
listOf(ExamItem(date, ExamItem.ViewType.HEADER)) + exams.reversed().map { exam ->
|
||||
ExamItem(exam, ExamItem.ViewType.ITEM)
|
||||
}
|
||||
}
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
private fun reloadView() {
|
||||
|
@ -9,7 +9,7 @@ interface ExamView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<ExamItem>)
|
||||
fun updateData(data: List<ExamItem<*>>)
|
||||
|
||||
fun updateNavigationWeek(date: String)
|
||||
|
||||
|
@ -0,0 +1,176 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.details
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.Resources
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.databinding.HeaderGradeDetailsBinding
|
||||
import io.github.wulkanowy.databinding.ItemGradeDetailsBinding
|
||||
import io.github.wulkanowy.ui.base.BaseExpandableAdapter
|
||||
import io.github.wulkanowy.utils.getBackgroundColor
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import javax.inject.Inject
|
||||
|
||||
class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private var headers = mutableListOf<GradeDetailsItem>()
|
||||
|
||||
private var items = mutableListOf<GradeDetailsItem>()
|
||||
|
||||
private var expandedPosition = RecyclerView.NO_POSITION
|
||||
|
||||
private var isExpandable = false
|
||||
|
||||
var onClickListener: (Grade, position: Int) -> Unit = { _, _ -> }
|
||||
|
||||
var colorTheme = ""
|
||||
|
||||
fun setDataItems(data: List<GradeDetailsItem>, isExpanded: Boolean = isExpandable) {
|
||||
headers = data.filter { it.viewType == ViewType.HEADER }.toMutableList()
|
||||
items = if (isExpanded) headers else data.toMutableList()
|
||||
isExpandable = isExpanded
|
||||
expandedPosition = RecyclerView.NO_POSITION
|
||||
}
|
||||
|
||||
fun updateDetailsItem(position: Int, grade: Grade) {
|
||||
items[position] = GradeDetailsItem(grade, ViewType.ITEM)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
|
||||
fun getHeaderItem(subject: String): GradeDetailsItem {
|
||||
return headers.single { (it.value as GradeDetailsHeader).subject == subject }
|
||||
}
|
||||
|
||||
fun updateHeaderItem(item: GradeDetailsItem) {
|
||||
headers[headers.indexOf(item)] = item
|
||||
items[items.indexOf(item)] = item
|
||||
notifyItemChanged(items.indexOf(item))
|
||||
}
|
||||
|
||||
fun collapseAll() {
|
||||
if (expandedPosition != -1) {
|
||||
refreshList(headers)
|
||||
expandedPosition = RecyclerView.NO_POSITION
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun refreshList(newItems: List<GradeDetailsItem>) {
|
||||
val diffCallback = GradeDetailsDiffUtil(items, newItems)
|
||||
val diffResult = DiffUtil.calculateDiff(diffCallback)
|
||||
items = newItems.toMutableList()
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun getItemViewType(position: Int) = items[position].viewType.id
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (viewType) {
|
||||
ViewType.HEADER.id -> HeaderViewHolder(HeaderGradeDetailsBinding.inflate(inflater, parent, false))
|
||||
ViewType.ITEM.id -> ItemViewHolder(ItemGradeDetailsBinding.inflate(inflater, parent, false))
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is HeaderViewHolder -> bindHeaderViewHolder(
|
||||
binding = holder.binding,
|
||||
header = items[position].value as GradeDetailsHeader,
|
||||
headerPosition = headers.indexOf(items[position]),
|
||||
adapterPosition = position
|
||||
)
|
||||
is ItemViewHolder -> bindItemViewHolder(
|
||||
binding = holder.binding,
|
||||
grade = items[position].value as Grade,
|
||||
position = position
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindHeaderViewHolder(binding: HeaderGradeDetailsBinding, header: GradeDetailsHeader, headerPosition: Int, adapterPosition: Int) {
|
||||
with(binding) {
|
||||
gradeHeaderDivider.visibility = if (adapterPosition == 0) View.GONE else View.VISIBLE
|
||||
gradeHeaderSubject.apply {
|
||||
text = header.subject
|
||||
maxLines = if (headerPosition == expandedPosition) 2 else 1
|
||||
}
|
||||
gradeHeaderAverage.text = formatAverage(header.average, root.context.resources)
|
||||
gradeHeaderPointsSum.text = root.context.getString(R.string.grade_points_sum, header.pointsSum)
|
||||
gradeHeaderPointsSum.visibility = if (!header.pointsSum.isNullOrEmpty()) View.VISIBLE else View.GONE
|
||||
gradeHeaderNumber.text = root.context.resources.getQuantityString(R.plurals.grade_number_item, header.number, header.number)
|
||||
gradeHeaderNote.visibility = if (header.newGrades > 0) View.VISIBLE else View.GONE
|
||||
if (header.newGrades > 0) gradeHeaderNote.text = header.newGrades.toString(10)
|
||||
|
||||
gradeHeaderContainer.isEnabled = isExpandable
|
||||
gradeHeaderContainer.setOnClickListener {
|
||||
expandedPosition = if (expandedPosition == adapterPosition) -1 else adapterPosition
|
||||
|
||||
if (expandedPosition != RecyclerView.NO_POSITION) {
|
||||
refreshList(headers.toMutableList().apply {
|
||||
addAll(headerPosition + 1, header.grades)
|
||||
})
|
||||
scrollToHeaderWithSubItems(headerPosition, header.grades.size)
|
||||
} else {
|
||||
refreshList(headers)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatAverage(average: Double?, resources: Resources): String {
|
||||
return if (average == null || average == .0) resources.getString(R.string.grade_no_average)
|
||||
else resources.getString(R.string.grade_average, average)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindItemViewHolder(binding: ItemGradeDetailsBinding, grade: Grade, position: Int) {
|
||||
with(binding) {
|
||||
gradeItemValue.run {
|
||||
text = grade.entry
|
||||
setBackgroundResource(grade.getBackgroundColor(colorTheme))
|
||||
}
|
||||
gradeItemDescription.text = when {
|
||||
grade.description.isNotBlank() -> grade.description
|
||||
grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol
|
||||
else -> root.context.getString(R.string.all_no_description)
|
||||
}
|
||||
gradeItemDate.text = grade.date.toFormattedString()
|
||||
gradeItemWeight.text = "${root.context.getString(R.string.grade_weight)}: ${grade.weight}"
|
||||
gradeItemNote.visibility = if (!grade.isRead) View.VISIBLE else View.GONE
|
||||
|
||||
root.setOnClickListener { onClickListener(grade, position) }
|
||||
}
|
||||
}
|
||||
|
||||
private class HeaderViewHolder(val binding: HeaderGradeDetailsBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
private class ItemViewHolder(val binding: ItemGradeDetailsBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class GradeDetailsDiffUtil(private val old: List<GradeDetailsItem>, private val new: List<GradeDetailsItem>) :
|
||||
DiffUtil.Callback() {
|
||||
|
||||
override fun getOldListSize() = old.size
|
||||
|
||||
override fun getNewListSize() = new.size
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
return old[oldItemPosition] == new[newItemPosition]
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
return old[oldItemPosition] == new[newItemPosition]
|
||||
}
|
||||
}
|
||||
}
|
@ -10,18 +10,13 @@ import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IExpandable
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeView
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_grade_details.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -31,7 +26,7 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
||||
lateinit var presenter: GradeDetailsPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var gradeDetailsAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var gradeDetailsAdapter: GradeDetailsAdapter
|
||||
|
||||
private var gradeDetailsMenu: Menu? = null
|
||||
|
||||
@ -39,23 +34,8 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
||||
fun newInstance() = GradeDetailsFragment()
|
||||
}
|
||||
|
||||
override val emptyAverageString: String
|
||||
get() = getString(R.string.grade_no_average)
|
||||
|
||||
override val averageString: String
|
||||
get() = getString(R.string.grade_average)
|
||||
|
||||
override val pointsSumString: String
|
||||
get() = getString(R.string.grade_points_sum)
|
||||
|
||||
override val weightString: String
|
||||
get() = getString(R.string.grade_weight)
|
||||
|
||||
override val noDescriptionString: String
|
||||
get() = getString(R.string.all_no_description)
|
||||
|
||||
override val isViewEmpty
|
||||
get() = gradeDetailsAdapter.isEmpty
|
||||
get() = gradeDetailsAdapter.itemCount == 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -79,18 +59,11 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
gradeDetailsAdapter.run {
|
||||
isAutoCollapseOnExpand = true
|
||||
isAutoScrollOnExpand = true
|
||||
setOnItemClickListener { presenter.onGradeItemSelected(it) }
|
||||
}
|
||||
gradeDetailsAdapter.onClickListener = presenter::onGradeItemSelected
|
||||
|
||||
gradeDetailsRecycler.run {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = gradeDetailsAdapter
|
||||
addItemDecoration(GradeDetailsHeaderItemDecoration(context)
|
||||
.withDefaultDivider(R.layout.header_grade_details)
|
||||
)
|
||||
}
|
||||
gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
gradeDetailsErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
@ -102,16 +75,23 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
||||
else false
|
||||
}
|
||||
|
||||
override fun updateData(data: List<GradeDetailsHeader>) {
|
||||
gradeDetailsAdapter.updateDataSet(data, true)
|
||||
override fun updateData(data: List<GradeDetailsItem>, isGradeExpandable: Boolean, gradeColorTheme: String) {
|
||||
with(gradeDetailsAdapter) {
|
||||
colorTheme = gradeColorTheme
|
||||
setDataItems(data, isGradeExpandable)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateItem(item: AbstractFlexibleItem<*>) {
|
||||
gradeDetailsAdapter.updateItem(item)
|
||||
override fun updateItem(item: Grade, position: Int) {
|
||||
gradeDetailsAdapter.updateDetailsItem(position, item)
|
||||
}
|
||||
|
||||
override fun clearView() {
|
||||
gradeDetailsAdapter.clear()
|
||||
with(gradeDetailsAdapter) {
|
||||
setDataItems(mutableListOf())
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun collapseAllItems() {
|
||||
@ -119,15 +99,15 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
||||
}
|
||||
|
||||
override fun scrollToStart() {
|
||||
gradeDetailsRecycler.scrollToPosition(0)
|
||||
gradeDetailsRecycler.smoothScrollToPosition(0)
|
||||
}
|
||||
|
||||
override fun getHeaderOfItem(item: AbstractFlexibleItem<*>): IExpandable<*, out IFlexible<*>>? {
|
||||
return gradeDetailsAdapter.getExpandableOf(item)
|
||||
override fun getHeaderOfItem(subject: String): GradeDetailsItem {
|
||||
return gradeDetailsAdapter.getHeaderItem(subject)
|
||||
}
|
||||
|
||||
override fun getGradeNumberString(number: Int): String {
|
||||
return resources.getQuantityString(R.plurals.grade_number_item, number, number)
|
||||
override fun updateHeaderItem(item: GradeDetailsItem) {
|
||||
gradeDetailsAdapter.updateHeaderItem(item)
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
|
@ -1,94 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.details
|
||||
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractExpandableItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.ExpandableViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.header_grade_details.*
|
||||
|
||||
class GradeDetailsHeader(
|
||||
private val subject: String,
|
||||
private val number: String,
|
||||
private val average: String,
|
||||
private val pointsSum: String,
|
||||
var newGrades: Int,
|
||||
private val isExpandable: Boolean
|
||||
) : AbstractExpandableItem<GradeDetailsHeader.ViewHolder, GradeDetailsItem>() {
|
||||
|
||||
init {
|
||||
isExpanded = !isExpandable
|
||||
}
|
||||
|
||||
override fun getLayoutRes() = R.layout.header_grade_details
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.run {
|
||||
gradeHeaderSubject.apply {
|
||||
text = subject
|
||||
maxLines = if (isExpanded) 2 else 1
|
||||
}
|
||||
gradeHeaderAverage.text = average
|
||||
gradeHeaderPointsSum.text = pointsSum
|
||||
gradeHeaderPointsSum.visibility = if (pointsSum.isNotEmpty()) VISIBLE else GONE
|
||||
gradeHeaderNumber.text = number
|
||||
gradeHeaderNote.visibility = if (newGrades > 0) VISIBLE else GONE
|
||||
if (newGrades > 0) gradeHeaderNote.text = newGrades.toString(10)
|
||||
gradeHeaderContainer.isEnabled = isExpandable
|
||||
|
||||
isViewExpandable = isExpandable
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as GradeDetailsHeader
|
||||
|
||||
if (subject != other.subject) return false
|
||||
if (number != other.number) return false
|
||||
if (average != other.average) return false
|
||||
if (isExpandable != other.isExpandable) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = subject.hashCode()
|
||||
result = 31 * result + number.hashCode()
|
||||
result = 31 * result + average.hashCode()
|
||||
result = 31 * result + isExpandable.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) :
|
||||
ExpandableViewHolder(view, adapter), LayoutContainer {
|
||||
|
||||
var isViewExpandable = true
|
||||
|
||||
init {
|
||||
contentView.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
|
||||
override fun isViewCollapsibleOnClick() = isViewExpandable
|
||||
|
||||
override fun isViewExpandableOnClick() = isViewExpandable
|
||||
|
||||
override fun onClick(view: View?) {
|
||||
super.onClick(view)
|
||||
mAdapter.getItem(adapterPosition)?.let { mAdapter.updateItem(it) }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.details
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
|
||||
class GradeDetailsHeaderItemDecoration(context: Context) : FlexibleItemDecoration(context) {
|
||||
|
||||
override fun drawVertical(canvas: Canvas, parent: RecyclerView) {
|
||||
canvas.save()
|
||||
val left: Int
|
||||
val right: Int
|
||||
if (parent.clipToPadding) {
|
||||
left = parent.paddingLeft
|
||||
right = parent.width - parent.paddingRight
|
||||
canvas.clipRect(left, parent.paddingTop, right,
|
||||
parent.height - parent.paddingBottom)
|
||||
} else {
|
||||
left = 0
|
||||
right = parent.width
|
||||
}
|
||||
|
||||
val itemCount = parent.childCount
|
||||
for (i in 1 until itemCount) {
|
||||
val child = parent.getChildAt(i)
|
||||
val viewHolder = parent.getChildViewHolder(child)
|
||||
if (shouldDrawDivider(viewHolder)) {
|
||||
parent.getDecoratedBoundsWithMargins(child, mBounds)
|
||||
val bottom = mBounds.top + Math.round(child.translationY)
|
||||
val top = bottom - mDivider.intrinsicHeight
|
||||
mDivider.setBounds(left, top, right, bottom)
|
||||
mDivider.draw(canvas)
|
||||
}
|
||||
}
|
||||
canvas.restore()
|
||||
}
|
||||
}
|
@ -1,74 +1,20 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.details
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_grade_details.*
|
||||
|
||||
class GradeDetailsItem(
|
||||
val grade: Grade,
|
||||
private val valueBgColor: Int,
|
||||
private val weightString: String,
|
||||
private val noDescriptionString: String
|
||||
) : AbstractFlexibleItem<GradeDetailsItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_grade_details
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.run {
|
||||
gradeItemValue.run {
|
||||
text = grade.entry
|
||||
setBackgroundResource(valueBgColor)
|
||||
}
|
||||
gradeItemDescription.text = when {
|
||||
grade.description.isNotBlank() -> grade.description
|
||||
grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol
|
||||
else -> noDescriptionString
|
||||
}
|
||||
gradeItemDate.text = grade.date.toFormattedString()
|
||||
gradeItemWeight.text = "$weightString: ${grade.weight}"
|
||||
gradeItemNote.visibility = if (!grade.isRead) VISIBLE else GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as GradeDetailsItem
|
||||
|
||||
if (grade != other.grade) return false
|
||||
if (grade.id != other.grade.id) return false
|
||||
if (weightString != other.weightString) return false
|
||||
if (valueBgColor != other.valueBgColor) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = grade.hashCode()
|
||||
result = 31 * result + grade.id.toInt()
|
||||
result = 31 * result + weightString.hashCode()
|
||||
result = 31 * result + valueBgColor
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
enum class ViewType(val id: Int) {
|
||||
HEADER(1),
|
||||
ITEM(2)
|
||||
}
|
||||
|
||||
data class GradeDetailsItem(
|
||||
val value: Any,
|
||||
val viewType: ViewType
|
||||
)
|
||||
|
||||
data class GradeDetailsHeader(
|
||||
val subject: String,
|
||||
val number: Int,
|
||||
val average: Double?,
|
||||
val pointsSum: String?,
|
||||
var newGrades: Int,
|
||||
val grades: List<GradeDetailsItem>
|
||||
)
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.details
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
@ -11,7 +10,6 @@ import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.getBackgroundColor
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -43,24 +41,20 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
loadData(semesterId, forceRefresh)
|
||||
}
|
||||
|
||||
fun onGradeItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is GradeDetailsItem) {
|
||||
Timber.i("Select grade item ${item.grade.id}")
|
||||
view?.apply {
|
||||
showGradeDialog(item.grade, preferencesRepository.gradeColorTheme)
|
||||
if (!item.grade.isRead) {
|
||||
item.grade.isRead = true
|
||||
updateItem(item)
|
||||
getHeaderOfItem(item)?.let { header ->
|
||||
if (header is GradeDetailsHeader) {
|
||||
header.newGrades--
|
||||
updateItem(header)
|
||||
}
|
||||
}
|
||||
newGradesAmount--
|
||||
updateMarkAsDoneButton()
|
||||
updateGrade(item.grade)
|
||||
fun onGradeItemSelected(grade: Grade, position: Int) {
|
||||
Timber.i("Select grade item ${grade.id}")
|
||||
view?.apply {
|
||||
showGradeDialog(grade, preferencesRepository.gradeColorTheme)
|
||||
if (!grade.isRead) {
|
||||
grade.isRead = true
|
||||
updateItem(grade, position)
|
||||
getHeaderOfItem(grade.subject).let { header ->
|
||||
(header.value as GradeDetailsHeader).newGrades--
|
||||
updateHeaderItem(header)
|
||||
}
|
||||
newGradesAmount--
|
||||
updateMarkAsDoneButton()
|
||||
updateGrade(grade)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,13 +128,11 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
disposable.add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semester -> it to semester } }
|
||||
.flatMap { (student, semesters) ->
|
||||
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh)
|
||||
.flatMap { averages ->
|
||||
gradeRepository.getGrades(student, semesters.first { it.semesterId == semesterId }, forceRefresh)
|
||||
.map { it.sortedByDescending { grade -> grade.date } }
|
||||
.map { it.groupBy { grade -> grade.subject }.toSortedMap() }
|
||||
.map { createGradeItems(it, averages) }
|
||||
}
|
||||
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh).flatMap { averages ->
|
||||
gradeRepository.getGrades(student, semesters.first { it.semesterId == semesterId }, forceRefresh)
|
||||
.map { it.sortedByDescending { grade -> grade.date } }
|
||||
.map { createGradeItems(it, averages) }
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
@ -152,17 +144,23 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
notifyParentDataLoaded(semesterId)
|
||||
}
|
||||
}
|
||||
.subscribe({
|
||||
.subscribe({ grades ->
|
||||
Timber.i("Loading grade details result: Success")
|
||||
newGradesAmount = it.sumBy { gradeDetailsHeader -> gradeDetailsHeader.newGrades }
|
||||
newGradesAmount = grades
|
||||
.filter { it.viewType == ViewType.HEADER }
|
||||
.sumBy { item -> (item.value as GradeDetailsHeader).newGrades }
|
||||
updateMarkAsDoneButton()
|
||||
view?.run {
|
||||
showEmpty(it.isEmpty())
|
||||
showEmpty(grades.isEmpty())
|
||||
showErrorView(false)
|
||||
showContent(it.isNotEmpty())
|
||||
updateData(it)
|
||||
showContent(grades.isNotEmpty())
|
||||
updateData(
|
||||
data = grades,
|
||||
isGradeExpandable = preferencesRepository.isGradeExpandable,
|
||||
gradeColorTheme = preferencesRepository.gradeColorTheme
|
||||
)
|
||||
}
|
||||
analytics.logEvent("load_grade_details", "items" to it.size, "force_refresh" to forceRefresh)
|
||||
analytics.logEvent("load_grade_details", "items" to grades.size, "force_refresh" to forceRefresh)
|
||||
}) {
|
||||
Timber.i("Loading grade details result: An exception occurred")
|
||||
errorHandler.dispatch(it)
|
||||
@ -180,40 +178,21 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun createGradeItems(items: Map<String, List<Grade>>, averages: List<Triple<String, Double, String>>): List<GradeDetailsHeader> {
|
||||
val isGradeExpandable = preferencesRepository.isGradeExpandable
|
||||
val gradeColorTheme = preferencesRepository.gradeColorTheme
|
||||
|
||||
val noDescriptionString = view?.noDescriptionString.orEmpty()
|
||||
val weightString = view?.weightString.orEmpty()
|
||||
val pointsSumString = view?.pointsSumString.orEmpty()
|
||||
|
||||
return items.map { subject ->
|
||||
GradeDetailsHeader(
|
||||
subject = subject.key,
|
||||
average = formatAverage(averages.singleOrNull { subject.key == it.first }?.second),
|
||||
pointsSum = averages.singleOrNull { subject.key == it.first }?.takeIf { it.third.isNotEmpty() }?.let { pointsSumString.format(it.third) }.orEmpty(),
|
||||
number = view?.getGradeNumberString(subject.value.size).orEmpty(),
|
||||
newGrades = subject.value.filter { grade -> !grade.isRead }.size,
|
||||
isExpandable = isGradeExpandable
|
||||
).apply {
|
||||
subItems = subject.value.map { item ->
|
||||
GradeDetailsItem(
|
||||
grade = item,
|
||||
valueBgColor = item.getBackgroundColor(gradeColorTheme),
|
||||
weightString = weightString,
|
||||
noDescriptionString = noDescriptionString
|
||||
)
|
||||
}
|
||||
private fun createGradeItems(items: List<Grade>, averages: List<Triple<String, Double, String>>): List<GradeDetailsItem> {
|
||||
return items.groupBy { grade -> grade.subject }.toSortedMap().map { (subject, grades) ->
|
||||
val subItems = grades.map {
|
||||
GradeDetailsItem(it, ViewType.ITEM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatAverage(average: Double?): String {
|
||||
return view?.run {
|
||||
if (average == null || average == .0) emptyAverageString
|
||||
else averageString.format(average)
|
||||
}.orEmpty()
|
||||
listOf(GradeDetailsItem(GradeDetailsHeader(
|
||||
subject = subject,
|
||||
average = averages.singleOrNull { subject == it.first }?.second,
|
||||
pointsSum = averages.singleOrNull { subject == it.first }?.third,
|
||||
number = grades.size,
|
||||
newGrades = grades.filter { grade -> !grade.isRead }.size,
|
||||
grades = subItems
|
||||
), ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
private fun updateGrade(grade: Grade) {
|
||||
@ -221,8 +200,9 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
disposable.add(gradeRepository.updateGrade(grade)
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.subscribe({ Timber.i("Update grade result: Success") })
|
||||
{ error ->
|
||||
.subscribe({
|
||||
Timber.i("Update grade result: Success")
|
||||
}) { error ->
|
||||
Timber.i("Update grade result: An exception occurred")
|
||||
errorHandler.dispatch(error)
|
||||
})
|
||||
|
@ -1,8 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.details
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IExpandable
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
@ -10,21 +7,13 @@ interface GradeDetailsView : BaseView {
|
||||
|
||||
val isViewEmpty: Boolean
|
||||
|
||||
val emptyAverageString: String
|
||||
|
||||
val averageString: String
|
||||
|
||||
val pointsSumString: String
|
||||
|
||||
val weightString: String
|
||||
|
||||
val noDescriptionString: String
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<GradeDetailsHeader>)
|
||||
fun updateData(data: List<GradeDetailsItem>, isGradeExpandable: Boolean, gradeColorTheme: String)
|
||||
|
||||
fun updateItem(item: AbstractFlexibleItem<*>)
|
||||
fun updateItem(item: Grade, position: Int)
|
||||
|
||||
fun updateHeaderItem(item: GradeDetailsItem)
|
||||
|
||||
fun clearView()
|
||||
|
||||
@ -54,7 +43,5 @@ interface GradeDetailsView : BaseView {
|
||||
|
||||
fun enableMarkAsDoneButton(enable: Boolean)
|
||||
|
||||
fun getGradeNumberString(number: Int): String
|
||||
|
||||
fun getHeaderOfItem(item: AbstractFlexibleItem<*>): IExpandable<*, out IFlexible<*>>?
|
||||
fun getHeaderOfItem(subject: String): GradeDetailsItem
|
||||
}
|
||||
|
@ -0,0 +1,85 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.summary
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import io.github.wulkanowy.databinding.ItemGradeSummaryBinding
|
||||
import io.github.wulkanowy.databinding.ScrollableHeaderGradeSummaryBinding
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
class GradeSummaryAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private enum class ViewType(val id: Int) {
|
||||
HEADER(1),
|
||||
ITEM(2)
|
||||
}
|
||||
|
||||
var items = emptyList<GradeSummary>()
|
||||
|
||||
override fun getItemCount() = items.size + 1
|
||||
|
||||
override fun getItemViewType(position: Int) = when (position) {
|
||||
0 -> ViewType.HEADER.id
|
||||
else -> ViewType.ITEM.id
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (viewType) {
|
||||
ViewType.HEADER.id -> HeaderViewHolder(ScrollableHeaderGradeSummaryBinding.inflate(inflater, parent, false))
|
||||
ViewType.ITEM.id -> ItemViewHolder(ItemGradeSummaryBinding.inflate(inflater, parent, false))
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is HeaderViewHolder -> bindHeaderViewHolder(holder.binding)
|
||||
is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position - 1])
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindHeaderViewHolder(binding: ScrollableHeaderGradeSummaryBinding) {
|
||||
if (items.isEmpty()) return
|
||||
|
||||
with(binding) {
|
||||
gradeSummaryScrollableHeaderFinal.text = formatAverage(items.calcAverage())
|
||||
gradeSummaryScrollableHeaderCalculated.text = formatAverage(items
|
||||
.filter { value -> value.average != 0.0 }
|
||||
.map { values -> values.average }
|
||||
.reversed() // fix average precision
|
||||
.average()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindItemViewHolder(binding: ItemGradeSummaryBinding, item: GradeSummary) {
|
||||
with(binding) {
|
||||
gradeSummaryItemTitle.text = item.subject
|
||||
gradeSummaryItemPoints.text = item.pointsSum
|
||||
gradeSummaryItemAverage.text = formatAverage(item.average, "")
|
||||
gradeSummaryItemPredicted.text = "${item.predictedGrade} ${item.proposedPoints}".trim()
|
||||
gradeSummaryItemFinal.text = "${item.finalGrade} ${item.finalPoints}".trim()
|
||||
|
||||
gradeSummaryItemPointsContainer.visibility = if (item.pointsSum.isBlank()) View.GONE else View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatAverage(average: Double, defaultValue: String = "-- --"): String {
|
||||
return if (average == 0.0) defaultValue
|
||||
else String.format(Locale.FRANCE, "%.2f", average)
|
||||
}
|
||||
|
||||
private class HeaderViewHolder(val binding: ScrollableHeaderGradeSummaryBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
private class ItemViewHolder(val binding: ItemGradeSummaryBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -7,10 +7,9 @@ import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeView
|
||||
@ -23,14 +22,14 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh
|
||||
lateinit var presenter: GradeSummaryPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var gradeSummaryAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var gradeSummaryAdapter: GradeSummaryAdapter
|
||||
|
||||
companion object {
|
||||
fun newInstance() = GradeSummaryFragment()
|
||||
}
|
||||
|
||||
override val isViewEmpty
|
||||
get() = gradeSummaryAdapter.isEmpty
|
||||
get() = gradeSummaryAdapter.items.isEmpty()
|
||||
|
||||
override val predictedString
|
||||
get() = getString(R.string.grade_summary_predicted_grade)
|
||||
@ -49,10 +48,8 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
gradeSummaryAdapter.setDisplayHeadersAtStartUp(true)
|
||||
|
||||
gradeSummaryRecycler.run {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = gradeSummaryAdapter
|
||||
}
|
||||
gradeSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
@ -60,16 +57,18 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh
|
||||
gradeSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<GradeSummaryItem>, header: GradeSummaryScrollableHeader) {
|
||||
gradeSummaryAdapter.apply {
|
||||
updateDataSet(data, true)
|
||||
removeAllScrollableHeaders()
|
||||
addScrollableHeader(header)
|
||||
override fun updateData(data: List<GradeSummary>) {
|
||||
with(gradeSummaryAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearView() {
|
||||
gradeSummaryAdapter.clear()
|
||||
with(gradeSummaryAdapter) {
|
||||
items = emptyList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetView() {
|
||||
|
@ -1,64 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.summary
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_grade_summary.*
|
||||
|
||||
class GradeSummaryItem(
|
||||
val summary: GradeSummary,
|
||||
private val average: String
|
||||
) : AbstractFlexibleItem<GradeSummaryItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_grade_summary
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.run {
|
||||
gradeSummaryItemTitle.text = summary.subject
|
||||
gradeSummaryItemPoints.text = summary.pointsSum
|
||||
gradeSummaryItemAverage.text = average
|
||||
gradeSummaryItemPredicted.text = "${summary.predictedGrade} ${summary.proposedPoints}".trim()
|
||||
gradeSummaryItemFinal.text = "${summary.finalGrade} ${summary.finalPoints}".trim()
|
||||
|
||||
gradeSummaryItemPointsContainer.visibility = if (summary.pointsSum.isBlank()) GONE else VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as GradeSummaryItem
|
||||
|
||||
if (average != other.average) return false
|
||||
if (summary != other.summary) return false
|
||||
if (summary.id != other.summary.id) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = summary.hashCode()
|
||||
result = 31 * result + summary.id.hashCode()
|
||||
result = 31 * result + average.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -9,10 +9,7 @@ import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import timber.log.Timber
|
||||
import java.lang.String.format
|
||||
import java.util.Locale.FRANCE
|
||||
import javax.inject.Inject
|
||||
|
||||
class GradeSummaryPresenter @Inject constructor(
|
||||
@ -42,7 +39,7 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
.map { it.sortedBy { subject -> subject.subject } }
|
||||
.flatMap { gradesSummary ->
|
||||
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh)
|
||||
.map { averages -> createGradeSummaryItemsAndHeader(gradesSummary, averages) }
|
||||
.map { averages -> createGradeSummaryItems(gradesSummary, averages) }
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
@ -54,15 +51,15 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
enableSwipe(true)
|
||||
notifyParentDataLoaded(semesterId)
|
||||
}
|
||||
}.subscribe({ (gradeSummaryItems, gradeSummaryHeader) ->
|
||||
}.subscribe({
|
||||
Timber.i("Loading grade summary result: Success")
|
||||
view?.run {
|
||||
showEmpty(gradeSummaryItems.isEmpty())
|
||||
showContent(gradeSummaryItems.isNotEmpty())
|
||||
showEmpty(it.isEmpty())
|
||||
showContent(it.isNotEmpty())
|
||||
showErrorView(false)
|
||||
updateData(gradeSummaryItems, gradeSummaryHeader)
|
||||
updateData(it)
|
||||
}
|
||||
analytics.logEvent("load_grade_summary", "items" to gradeSummaryItems.size, "force_refresh" to forceRefresh)
|
||||
analytics.logEvent("load_grade_summary", "items" to it.size, "force_refresh" to forceRefresh)
|
||||
}) {
|
||||
Timber.i("Loading grade summary result: An exception occurred")
|
||||
errorHandler.dispatch(it)
|
||||
@ -115,20 +112,11 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
disposable.clear()
|
||||
}
|
||||
|
||||
private fun createGradeSummaryItemsAndHeader(gradesSummary: List<GradeSummary>, averages: List<Triple<String, Double, String>>): Pair<List<GradeSummaryItem>, GradeSummaryScrollableHeader> {
|
||||
return averages.filter { value -> value.second != 0.0 }
|
||||
.let { filteredAverages ->
|
||||
gradesSummary.filter { !checkEmpty(it, filteredAverages) }
|
||||
.map { gradeSummary ->
|
||||
GradeSummaryItem(
|
||||
summary = gradeSummary,
|
||||
average = formatAverage(filteredAverages.singleOrNull { gradeSummary.subject == it.first }?.second ?: .0, "")
|
||||
)
|
||||
}.let {
|
||||
it to GradeSummaryScrollableHeader(
|
||||
formatAverage(gradesSummary.calcAverage()),
|
||||
formatAverage(filteredAverages.map { values -> values.second }.average()))
|
||||
}
|
||||
private fun createGradeSummaryItems(gradesSummary: List<GradeSummary>, averages: List<Triple<String, Double, String>>): List<GradeSummary> {
|
||||
return gradesSummary
|
||||
.filter { !checkEmpty(it, averages) }
|
||||
.map { gradeSummary ->
|
||||
gradeSummary.copy(average = averages.singleOrNull { gradeSummary.subject == it.first }?.second ?: .0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,9 +125,4 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
finalGrade.isBlank() && predictedGrade.isBlank() && averages.singleOrNull { it.first == subject } == null
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatAverage(average: Double, defaultValue: String = "-- --"): String {
|
||||
return if (average == 0.0) defaultValue
|
||||
else format(FRANCE, "%.2f", average)
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.summary
|
||||
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.scrollable_header_grade_summary.*
|
||||
|
||||
class GradeSummaryScrollableHeader(private val finalAverage: String, private val calculatedAverage: String)
|
||||
: AbstractFlexibleItem<GradeSummaryScrollableHeader.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.scrollable_header_grade_summary
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?,
|
||||
position: Int, payloads: MutableList<Any>?) {
|
||||
holder?.apply {
|
||||
gradeSummaryScrollableHeaderFinal.text = finalAverage
|
||||
gradeSummaryScrollableHeaderCalculated.text = calculatedAverage
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as GradeSummaryScrollableHeader
|
||||
|
||||
if (calculatedAverage != other.calculatedAverage) return false
|
||||
if (finalAverage != other.finalAverage) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = calculatedAverage.hashCode()
|
||||
result = 31 * result + finalAverage.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View?
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.summary
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface GradeSummaryView : BaseView {
|
||||
@ -12,7 +13,7 @@ interface GradeSummaryView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<GradeSummaryItem>, header: GradeSummaryScrollableHeader)
|
||||
fun updateData(data: List<GradeSummary>)
|
||||
|
||||
fun resetView()
|
||||
|
||||
|
@ -0,0 +1,67 @@
|
||||
package io.github.wulkanowy.ui.modules.homework
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.data.db.entities.Homework
|
||||
import io.github.wulkanowy.databinding.HeaderHomeworkBinding
|
||||
import io.github.wulkanowy.databinding.ItemHomeworkBinding
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.github.wulkanowy.utils.weekDayName
|
||||
import org.threeten.bp.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomeworkAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
var items = emptyList<HomeworkItem<*>>()
|
||||
|
||||
var onClickListener: (Homework) -> Unit = {}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun getItemViewType(position: Int) = items[position].viewType.id
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (viewType) {
|
||||
HomeworkItem.ViewType.HEADER.id -> HeaderViewHolder(HeaderHomeworkBinding.inflate(inflater, parent, false))
|
||||
HomeworkItem.ViewType.ITEM.id -> ItemViewHolder(ItemHomeworkBinding.inflate(inflater, parent, false))
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is HeaderViewHolder -> bindHeaderViewHolder(holder.binding, items[position].value as LocalDate)
|
||||
is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position].value as Homework)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private fun bindHeaderViewHolder(binding: HeaderHomeworkBinding, date: LocalDate) {
|
||||
with(binding) {
|
||||
homeworkHeaderDay.text = date.weekDayName.capitalize()
|
||||
homeworkHeaderDate.text = date.toFormattedString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindItemViewHolder(binding: ItemHomeworkBinding, homework: Homework) {
|
||||
with(binding) {
|
||||
homeworkItemSubject.text = homework.subject
|
||||
homeworkItemTeacher.text = homework.teacher
|
||||
homeworkItemContent.text = homework.content
|
||||
homeworkItemCheckImage.visibility = if (homework.isDone) View.VISIBLE else View.GONE
|
||||
homeworkItemAttachmentImage.visibility = if (!homework.isDone && homework.attachments.isNotEmpty()) View.VISIBLE else View.GONE
|
||||
|
||||
root.setOnClickListener { onClickListener(homework) }
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderViewHolder(val binding: HeaderHomeworkBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class ItemViewHolder(val binding: ItemHomeworkBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -6,18 +6,15 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Homework
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.ui.modules.homework.details.HomeworkDetailsDialog
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_homework.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -27,7 +24,7 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
|
||||
lateinit var presenter: HomeworkPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var homeworkAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var homeworkAdapter: HomeworkAdapter
|
||||
|
||||
companion object {
|
||||
private const val SAVED_DATE_KEY = "CURRENT_DATE"
|
||||
@ -37,7 +34,7 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
|
||||
|
||||
override val titleStringId get() = R.string.homework_title
|
||||
|
||||
override val isViewEmpty get() = homeworkAdapter.isEmpty
|
||||
override val isViewEmpty get() = homeworkAdapter.items.isEmpty()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_homework, container, false)
|
||||
@ -50,14 +47,12 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
homeworkAdapter.setOnItemClickListener(presenter::onHomeworkItemSelected)
|
||||
homeworkAdapter.onClickListener = presenter::onHomeworkItemSelected
|
||||
|
||||
with(homeworkRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = homeworkAdapter
|
||||
addItemDecoration(FlexibleItemDecoration(context)
|
||||
.withDefaultDivider()
|
||||
.withDrawDividerOnLastItem(false))
|
||||
addItemDecoration(DividerItemDecoration(context))
|
||||
}
|
||||
|
||||
homeworkSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||
@ -70,8 +65,11 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
|
||||
homeworkNavContainer.setElevationCompat(requireContext().dpToPx(8f))
|
||||
}
|
||||
|
||||
override fun updateData(data: List<HomeworkItem>) {
|
||||
homeworkAdapter.updateDataSet(data, true)
|
||||
override fun updateData(data: List<HomeworkItem<*>>) {
|
||||
with(homeworkAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun onReloadList() {
|
||||
@ -79,7 +77,10 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
|
||||
}
|
||||
|
||||
override fun clearData() {
|
||||
homeworkAdapter.clear()
|
||||
with(homeworkAdapter) {
|
||||
items = emptyList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateNavigationWeek(date: String) {
|
||||
|
@ -1,54 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.homework
|
||||
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.ExpandableViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.github.wulkanowy.utils.weekDayName
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.header_homework.*
|
||||
import org.threeten.bp.LocalDate
|
||||
|
||||
class HomeworkHeader(private val date: LocalDate) : AbstractHeaderItem<HomeworkHeader.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.header_homework
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<*>>?, holder: HomeworkHeader.ViewHolder,
|
||||
position: Int, payloads: MutableList<Any>?
|
||||
) {
|
||||
holder.run {
|
||||
homeworkHeaderDay.text = date.weekDayName.capitalize()
|
||||
homeworkHeaderDate.text = date.toFormattedString()
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as HomeworkHeader
|
||||
|
||||
if (date != other.date) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return date.hashCode()
|
||||
}
|
||||
|
||||
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : ExpandableViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -1,53 +1,9 @@
|
||||
package io.github.wulkanowy.ui.modules.homework
|
||||
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractSectionableItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Homework
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_homework.*
|
||||
data class HomeworkItem<out T>(val value: T, val viewType: ViewType) {
|
||||
|
||||
class HomeworkItem(header: HomeworkHeader, val homework: Homework) :
|
||||
AbstractSectionableItem<HomeworkItem.ViewHolder, HomeworkHeader>(header) {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_homework
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.apply {
|
||||
homeworkItemSubject.text = homework.subject
|
||||
homeworkItemTeacher.text = homework.teacher
|
||||
homeworkItemContent.text = homework.content
|
||||
homeworkItemCheckImage.visibility = if (homework.isDone) View.VISIBLE else View.GONE
|
||||
homeworkItemAttachmentImage.visibility = if (!homework.isDone && homework.attachments.isNotEmpty()) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as HomeworkItem
|
||||
|
||||
if (homework != other.homework) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = homework.hashCode()
|
||||
result = 31 * result + homework.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) :
|
||||
FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
enum class ViewType(val id: Int) {
|
||||
HEADER(1),
|
||||
ITEM(2)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.homework
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Homework
|
||||
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
@ -18,7 +17,6 @@ import io.github.wulkanowy.utils.toFormattedString
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.ofEpochDay
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomeworkPresenter @Inject constructor(
|
||||
@ -74,11 +72,9 @@ class HomeworkPresenter @Inject constructor(
|
||||
view?.showErrorDetailsDialog(lastError)
|
||||
}
|
||||
|
||||
fun onHomeworkItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is HomeworkItem) {
|
||||
Timber.i("Select homework item ${item.homework.id}")
|
||||
view?.showTimetableDialog(item.homework)
|
||||
}
|
||||
fun onHomeworkItemSelected(homework: Homework) {
|
||||
Timber.i("Select homework item ${homework.id}")
|
||||
view?.showTimetableDialog(homework)
|
||||
}
|
||||
|
||||
private fun setBaseDateOnHolidays() {
|
||||
@ -110,8 +106,6 @@ class HomeworkPresenter @Inject constructor(
|
||||
homeworkRepository.getHomework(student, semester, currentDate, currentDate, forceRefresh)
|
||||
}
|
||||
}
|
||||
.delay(200, TimeUnit.MILLISECONDS)
|
||||
.map { it.groupBy { homework -> homework.date }.toSortedMap() }
|
||||
.map { createHomeworkItem(it) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
@ -150,12 +144,12 @@ class HomeworkPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun createHomeworkItem(items: Map<LocalDate, List<Homework>>): List<HomeworkItem> {
|
||||
return items.flatMap {
|
||||
HomeworkHeader(it.key).let { header ->
|
||||
it.value.reversed().map { item -> HomeworkItem(header, item) }
|
||||
private fun createHomeworkItem(items: List<Homework>): List<HomeworkItem<*>> {
|
||||
return items.groupBy { it.date }.toSortedMap().map { (date, exams) ->
|
||||
listOf(HomeworkItem(date, HomeworkItem.ViewType.HEADER)) + exams.reversed().map { exam ->
|
||||
HomeworkItem(exam, HomeworkItem.ViewType.ITEM)
|
||||
}
|
||||
}
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
private fun reloadView() {
|
||||
|
@ -9,7 +9,7 @@ interface HomeworkView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<HomeworkItem>)
|
||||
fun updateData(data: List<HomeworkItem<*>>)
|
||||
|
||||
fun clearData()
|
||||
|
||||
|
@ -0,0 +1,51 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.databinding.ItemLoginStudentSelectBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class LoginStudentSelectAdapter @Inject constructor() :
|
||||
RecyclerView.Adapter<LoginStudentSelectAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = emptyList<Pair<Student, Boolean>>()
|
||||
|
||||
var onClickListener: (Student, alreadySaved: Boolean) -> Unit = { _, _ -> }
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemLoginStudentSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val (student, alreadySaved) = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
loginItemName.text = "${student.studentName} ${student.className}"
|
||||
loginItemSchool.text = student.schoolName
|
||||
loginItemName.isEnabled = !alreadySaved
|
||||
loginItemSchool.isEnabled = !alreadySaved
|
||||
loginItemCheck.isEnabled = !alreadySaved
|
||||
loginItemSignedIn.visibility = if (alreadySaved) View.VISIBLE else View.GONE
|
||||
|
||||
root.setOnClickListener {
|
||||
onClickListener(student, alreadySaved)
|
||||
|
||||
with(loginItemCheck) {
|
||||
if (isEnabled) {
|
||||
isChecked = !isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemLoginStudentSelectBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -6,9 +6,7 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
@ -16,7 +14,6 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.openEmailClient
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_login_student_select.*
|
||||
import java.io.Serializable
|
||||
import javax.inject.Inject
|
||||
@ -27,7 +24,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
lateinit var presenter: LoginStudentSelectPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var loginAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var loginAdapter: LoginStudentSelectAdapter
|
||||
|
||||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
@ -48,19 +45,22 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
loginAdapter.onClickListener = presenter::onItemSelected
|
||||
loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() }
|
||||
loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } }
|
||||
loginStudentSelectContactDiscord.setOnClickListener { presenter.onDiscordClick() }
|
||||
loginStudentSelectContactEmail.setOnClickListener { presenter.onEmailClick() }
|
||||
|
||||
loginStudentSelectRecycler.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = loginAdapter
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(data: List<LoginStudentSelectItem>) {
|
||||
loginAdapter.updateDataSet(data)
|
||||
override fun updateData(data: List<Pair<Student, Boolean>>) {
|
||||
with(loginAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openMainView() {
|
||||
|
@ -1,71 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_login_student_select.*
|
||||
|
||||
class LoginStudentSelectItem(val student: Student, val alreadySaved: Boolean) :
|
||||
AbstractFlexibleItem<LoginStudentSelectItem.ItemViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_login_student_select
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ItemViewHolder {
|
||||
return ItemViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ItemViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
holder.apply {
|
||||
loginItemName.text = "${student.studentName} ${student.className}"
|
||||
loginItemSchool.text = student.schoolName
|
||||
loginItemName.isEnabled = !alreadySaved
|
||||
loginItemSchool.isEnabled = !alreadySaved
|
||||
loginItemCheck.isEnabled = !alreadySaved
|
||||
loginItemSignedIn.visibility = if (alreadySaved) VISIBLE else GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as LoginStudentSelectItem
|
||||
|
||||
if (student != other.student) return false
|
||||
if (alreadySaved != other.alreadySaved) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return student.hashCode()
|
||||
}
|
||||
|
||||
class ItemViewHolder(view: View, val adapter: FlexibleAdapter<*>) :
|
||||
FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
|
||||
override val containerView: View
|
||||
get() = itemView
|
||||
|
||||
init {
|
||||
loginItemCheck.keyListener = null
|
||||
}
|
||||
|
||||
override fun onClick(view: View?) {
|
||||
super.onClick(view)
|
||||
|
||||
if (loginItemCheck.isEnabled) {
|
||||
loginItemCheck.apply { isChecked = !isChecked }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
@ -51,13 +50,14 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
if (students.size == 1) registerStudents(students)
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is LoginStudentSelectItem && !item.alreadySaved) {
|
||||
selectedStudents.removeAll { it == item.student }
|
||||
.let { if (!it) selectedStudents.add(item.student) }
|
||||
fun onItemSelected(student: Student, alreadySaved: Boolean) {
|
||||
if (alreadySaved) return
|
||||
|
||||
view?.enableSignIn(selectedStudents.isNotEmpty())
|
||||
}
|
||||
selectedStudents
|
||||
.removeAll { it == student }
|
||||
.let { if (!it) selectedStudents.add(student) }
|
||||
|
||||
view?.enableSignIn(selectedStudents.isNotEmpty())
|
||||
}
|
||||
|
||||
private fun compareStudents(a: Student, b: Student): Boolean {
|
||||
@ -73,19 +73,17 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
disposable.add(studentRepository.getSavedStudents()
|
||||
.map { savedStudents ->
|
||||
students.map { student ->
|
||||
Pair(student, savedStudents.any { compareStudents(student, it) })
|
||||
student to savedStudents.any { compareStudents(student, it) }
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.subscribe({
|
||||
view?.updateData(it.map { studentPair ->
|
||||
LoginStudentSelectItem(studentPair.first, studentPair.second)
|
||||
})
|
||||
view?.updateData(it)
|
||||
}, {
|
||||
errorHandler.dispatch(it)
|
||||
lastError = it
|
||||
view?.updateData(students.map { student -> LoginStudentSelectItem(student, false) })
|
||||
view?.updateData(students.map { student -> student to false })
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface LoginStudentSelectView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<LoginStudentSelectItem>)
|
||||
fun updateData(data: List<Pair<Student, Boolean>>)
|
||||
|
||||
fun openMainView()
|
||||
|
||||
|
@ -7,13 +7,12 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import io.github.wulkanowy.ui.base.WidgetConfigureAdapter
|
||||
import kotlinx.android.synthetic.main.activity_widget_configure.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -21,7 +20,7 @@ class LuckyNumberWidgetConfigureActivity : BaseActivity<LuckyNumberWidgetConfigu
|
||||
LuckyNumberWidgetConfigureView {
|
||||
|
||||
@Inject
|
||||
lateinit var configureAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var configureAdapter: WidgetConfigureAdapter
|
||||
|
||||
@Inject
|
||||
override lateinit var presenter: LuckyNumberWidgetConfigurePresenter
|
||||
@ -41,10 +40,10 @@ class LuckyNumberWidgetConfigureActivity : BaseActivity<LuckyNumberWidgetConfigu
|
||||
override fun initView() {
|
||||
with(widgetConfigureRecycler) {
|
||||
adapter = configureAdapter
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
}
|
||||
|
||||
configureAdapter.setOnItemClickListener(presenter::onItemSelect)
|
||||
configureAdapter.onClickListener = presenter::onItemSelect
|
||||
}
|
||||
|
||||
override fun showThemeDialog() {
|
||||
@ -53,17 +52,20 @@ class LuckyNumberWidgetConfigureActivity : BaseActivity<LuckyNumberWidgetConfigu
|
||||
getString(R.string.widget_timetable_theme_dark)
|
||||
)
|
||||
|
||||
dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher)
|
||||
dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher)
|
||||
.setTitle(R.string.widget_timetable_theme_title)
|
||||
.setOnDismissListener { presenter.onDismissThemeView() }
|
||||
.setOnDismissListener { presenter.onDismissThemeView() }
|
||||
.setSingleChoiceItems(items, -1) { _, which ->
|
||||
presenter.onThemeSelect(which)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun updateData(data: List<LuckyNumberWidgetConfigureItem>) {
|
||||
configureAdapter.updateDataSet(data)
|
||||
override fun updateData(data: List<Pair<Student, Boolean>>) {
|
||||
with(configureAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateLuckyNumberWidget(widgetId: Int) {
|
||||
|
@ -1,60 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_account.*
|
||||
|
||||
class LuckyNumberWidgetConfigureItem(var student: Student, val isCurrent: Boolean) :
|
||||
AbstractFlexibleItem<LuckyNumberWidgetConfigureItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_account
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
val context = holder.contentView.context
|
||||
|
||||
val colorImage = if (isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
|
||||
else ColorUtils.setAlphaComponent(context.getThemeAttrColor(R.attr.colorOnSurface), 153)
|
||||
|
||||
with(holder) {
|
||||
accountItemName.text = "${student.studentName} ${student.className}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
accountItemImage.setColorFilter(colorImage)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TimetableWidgetConfigureItem
|
||||
|
||||
if (student != other.student) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = student.hashCode()
|
||||
result = 31 * result + student.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View? get() = contentView
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
@ -29,11 +28,9 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelect(item: AbstractFlexibleItem<*>) {
|
||||
if (item is LuckyNumberWidgetConfigureItem) {
|
||||
selectedStudent = item.student
|
||||
view?.showThemeDialog()
|
||||
}
|
||||
fun onItemSelect(student: Student) {
|
||||
selectedStudent = student
|
||||
view?.showThemeDialog()
|
||||
}
|
||||
|
||||
fun onThemeSelect(index: Int) {
|
||||
@ -51,7 +48,7 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
|
||||
disposable.add(studentRepository.getSavedStudents(false)
|
||||
.map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } }
|
||||
.map { (students, currentStudentId) ->
|
||||
students.map { student -> LuckyNumberWidgetConfigureItem(student, student.id == currentStudentId) }
|
||||
students.map { student -> student to (student.id == currentStudentId) }
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
@ -59,7 +56,7 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
|
||||
when {
|
||||
it.isEmpty() -> view?.openLoginView()
|
||||
it.size == 1 -> {
|
||||
selectedStudent = it.single().student
|
||||
selectedStudent = it.single().first
|
||||
view?.showThemeDialog()
|
||||
}
|
||||
else -> view?.updateData(it)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface LuckyNumberWidgetConfigureView : BaseView {
|
||||
@ -8,7 +9,7 @@ interface LuckyNumberWidgetConfigureView : BaseView {
|
||||
|
||||
fun showThemeDialog()
|
||||
|
||||
fun updateData(data: List<LuckyNumberWidgetConfigureItem>)
|
||||
fun updateData(data: List<Pair<Student, Boolean>>)
|
||||
|
||||
fun updateLuckyNumberWidget(widgetId: Int)
|
||||
|
||||
|
@ -9,7 +9,6 @@ import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
@ -115,7 +114,7 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
|
||||
AHBottomNavigationItem(R.string.more_title, R.drawable.ic_main_more, 0)
|
||||
))
|
||||
accentColor = getThemeAttrColor(R.attr.colorPrimary)
|
||||
inactiveColor = ColorUtils.setAlphaComponent(getThemeAttrColor(R.attr.colorOnSurface), 153)
|
||||
inactiveColor = getThemeAttrColor(R.attr.colorOnSurface, 153)
|
||||
defaultBackgroundColor = overlayProvider.get().compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(8f))
|
||||
titleState = ALWAYS_SHOW
|
||||
currentItem = startMenuIndex
|
||||
|
@ -14,7 +14,6 @@ import io.github.wulkanowy.ui.modules.about.license.LicenseModule
|
||||
import io.github.wulkanowy.ui.modules.about.logviewer.LogViewerFragment
|
||||
import io.github.wulkanowy.ui.modules.account.AccountDialog
|
||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceModule
|
||||
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
@ -26,7 +25,6 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.message.MessageModule
|
||||
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
|
||||
import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment
|
||||
import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceModule
|
||||
import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog
|
||||
import io.github.wulkanowy.ui.modules.more.MoreFragment
|
||||
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||
@ -52,7 +50,7 @@ abstract class MainModule {
|
||||
}
|
||||
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector(modules = [AttendanceModule::class])
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindAttendanceFragment(): AttendanceFragment
|
||||
|
||||
@PerFragment
|
||||
@ -116,7 +114,7 @@ abstract class MainModule {
|
||||
abstract fun bindAccountDialog(): AccountDialog
|
||||
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector(modules = [MobileDeviceModule::class])
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindMobileDevices(): MobileDeviceFragment
|
||||
|
||||
@PerFragment
|
||||
|
@ -1,67 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.message
|
||||
|
||||
import android.graphics.Typeface.BOLD
|
||||
import android.graphics.Typeface.NORMAL
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_message.*
|
||||
|
||||
class MessageItem(val message: Message, private val noSubjectString: String) :
|
||||
AbstractFlexibleItem<MessageItem.ViewHolder>() {
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_message
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.apply {
|
||||
val style = if (message.unread) BOLD else NORMAL
|
||||
|
||||
messageItemAuthor.run {
|
||||
text = if (message.folderId == MessageFolder.SENT.id) message.recipient else message.sender
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemSubject.run {
|
||||
text = if (message.subject.isNotBlank()) message.subject else noSubjectString
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemDate.run {
|
||||
text = message.date.toFormattedString()
|
||||
setTypeface(null, style)
|
||||
}
|
||||
with(messageItemAttachmentIcon) {
|
||||
visibility = if (message.hasAttachments) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as MessageItem
|
||||
|
||||
if (message != other.message) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return message.hashCode()
|
||||
}
|
||||
|
||||
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) :
|
||||
FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package io.github.wulkanowy.ui.modules.message.tab
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder
|
||||
import io.github.wulkanowy.databinding.ItemMessageBinding
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import javax.inject.Inject
|
||||
|
||||
class MessageTabAdapter @Inject constructor() :
|
||||
RecyclerView.Adapter<MessageTabAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = mutableListOf<Message>()
|
||||
|
||||
var onClickListener: (Message, position: Int) -> Unit = { _, _ -> }
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemMessageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
val style = if (item.unread) Typeface.BOLD else Typeface.NORMAL
|
||||
|
||||
messageItemAuthor.run {
|
||||
text = if (item.folderId == MessageFolder.SENT.id) item.recipient else item.sender
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemSubject.run {
|
||||
text = if (item.subject.isNotBlank()) item.subject else context.getString(R.string.message_no_subject)
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemDate.run {
|
||||
text = item.date.toFormattedString()
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemAttachmentIcon.visibility = if (item.hasAttachments) View.VISIBLE else View.GONE
|
||||
|
||||
root.setOnClickListener { onClickListener(item, position) }
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -7,19 +7,15 @@ import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.message.MessageItem
|
||||
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_message_tab.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -29,7 +25,7 @@ class MessageTabFragment : BaseFragment(), MessageTabView {
|
||||
lateinit var presenter: MessageTabPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var tabAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var tabAdapter: MessageTabAdapter
|
||||
|
||||
companion object {
|
||||
const val MESSAGE_TAB_FOLDER_ID = "message_tab_folder_id"
|
||||
@ -43,11 +39,8 @@ class MessageTabFragment : BaseFragment(), MessageTabView {
|
||||
}
|
||||
}
|
||||
|
||||
override val noSubjectString: String
|
||||
get() = getString(R.string.message_no_subject)
|
||||
|
||||
override val isViewEmpty
|
||||
get() = tabAdapter.isEmpty
|
||||
get() = tabAdapter.items.isEmpty()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_message_tab, container, false)
|
||||
@ -62,31 +55,30 @@ class MessageTabFragment : BaseFragment(), MessageTabView {
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
tabAdapter.setOnItemClickListener { presenter.onMessageItemSelected(it) }
|
||||
tabAdapter.onClickListener = presenter::onMessageItemSelected
|
||||
|
||||
messageTabRecycler.run {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = tabAdapter
|
||||
addItemDecoration(FlexibleItemDecoration(context)
|
||||
.withDefaultDivider()
|
||||
.withDrawDividerOnLastItem(false)
|
||||
)
|
||||
addItemDecoration(DividerItemDecoration(context))
|
||||
}
|
||||
messageTabSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<MessageItem>) {
|
||||
tabAdapter.updateDataSet(data)
|
||||
override fun updateData(data: List<Message>) {
|
||||
with(tabAdapter) {
|
||||
items = data.toMutableList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateItem(item: AbstractFlexibleItem<*>) {
|
||||
tabAdapter.updateItem(item)
|
||||
}
|
||||
|
||||
override fun clearView() {
|
||||
tabAdapter.clear()
|
||||
override fun updateItem(item: Message, position: Int) {
|
||||
with(tabAdapter) {
|
||||
items[position] = item
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
|
@ -1,13 +1,12 @@
|
||||
package io.github.wulkanowy.ui.modules.message.tab
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder
|
||||
import io.github.wulkanowy.data.repositories.message.MessageRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.message.MessageItem
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import timber.log.Timber
|
||||
@ -58,15 +57,13 @@ class MessageTabPresenter @Inject constructor(
|
||||
loadData(forceRefresh)
|
||||
}
|
||||
|
||||
fun onMessageItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item is MessageItem) {
|
||||
Timber.i("Select message ${item.message.id} item")
|
||||
view?.run {
|
||||
openMessage(item.message)
|
||||
if (item.message.unread) {
|
||||
item.message.unread = false
|
||||
updateItem(item)
|
||||
}
|
||||
fun onMessageItemSelected(message: Message, position: Int) {
|
||||
Timber.i("Select message ${message.id} item")
|
||||
view?.run {
|
||||
openMessage(message)
|
||||
if (message.unread) {
|
||||
message.unread = false
|
||||
updateItem(message, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,7 +76,6 @@ class MessageTabPresenter @Inject constructor(
|
||||
.flatMap { student ->
|
||||
semesterRepository.getCurrentSemester(student)
|
||||
.flatMap { messageRepository.getMessages(student, it, folder, forceRefresh) }
|
||||
.map { items -> items.map { MessageItem(it, view?.noSubjectString.orEmpty()) } }
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
|
@ -1,23 +1,17 @@
|
||||
package io.github.wulkanowy.ui.modules.message.tab
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.message.MessageItem
|
||||
|
||||
interface MessageTabView : BaseView {
|
||||
|
||||
val noSubjectString: String
|
||||
|
||||
val isViewEmpty: Boolean
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<MessageItem>)
|
||||
fun updateData(data: List<Message>)
|
||||
|
||||
fun updateItem(item: AbstractFlexibleItem<*>)
|
||||
|
||||
fun clearView()
|
||||
fun updateItem(item: Message, position: Int)
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
||||
|
@ -1,10 +1,38 @@
|
||||
package io.github.wulkanowy.ui.modules.mobiledevice
|
||||
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||
import io.github.wulkanowy.databinding.ItemMobileDeviceBinding
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import javax.inject.Inject
|
||||
|
||||
class MobileDeviceAdapter<T : IFlexible<*>> : FlexibleAdapter<T>(null, null, true) {
|
||||
class MobileDeviceAdapter @Inject constructor() :
|
||||
RecyclerView.Adapter<MobileDeviceAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = mutableListOf<MobileDevice>()
|
||||
|
||||
var onDeviceUnregisterListener: (device: MobileDevice, position: Int) -> Unit = { _, _ -> }
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemMobileDeviceBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val device = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
mobileDeviceItemDate.text = device.date.toFormattedString("dd.MM.yyyy HH:mm:ss")
|
||||
mobileDeviceItemName.text = device.name
|
||||
mobileDeviceItemUnregister.setOnClickListener {
|
||||
onDeviceUnregisterListener(device, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemMobileDeviceBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
|
@ -6,13 +6,13 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.helpers.UndoHelper
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.core.view.postDelayed
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog
|
||||
@ -25,7 +25,7 @@ class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledVi
|
||||
lateinit var presenter: MobileDevicePresenter
|
||||
|
||||
@Inject
|
||||
lateinit var devicesAdapter: MobileDeviceAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var devicesAdapter: MobileDeviceAdapter
|
||||
|
||||
companion object {
|
||||
fun newInstance() = MobileDeviceFragment()
|
||||
@ -35,7 +35,7 @@ class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledVi
|
||||
get() = R.string.mobile_devices_title
|
||||
|
||||
override val isViewEmpty: Boolean
|
||||
get() = devicesAdapter.isEmpty
|
||||
get() = devicesAdapter.items.isEmpty()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_mobile_device, container, false)
|
||||
@ -48,51 +48,55 @@ class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledVi
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
devicesAdapter.onDeviceUnregisterListener = presenter::onUnregisterDevice
|
||||
|
||||
with(mobileDevicesRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = devicesAdapter
|
||||
addItemDecoration(FlexibleItemDecoration(context)
|
||||
.withDefaultDivider()
|
||||
.withDrawDividerOnLastItem(false)
|
||||
)
|
||||
}
|
||||
with(devicesAdapter) {
|
||||
isPermanentDelete = false
|
||||
onDeviceUnregisterListener = presenter::onUnregisterDevice
|
||||
addItemDecoration(DividerItemDecoration(context))
|
||||
}
|
||||
|
||||
mobileDevicesSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
mobileDevicesErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
mobileDevicesErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<MobileDeviceItem>) {
|
||||
devicesAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun restoreDeleteItem() {
|
||||
devicesAdapter.restoreDeletedItems()
|
||||
}
|
||||
|
||||
override fun clearData() {
|
||||
devicesAdapter.clear()
|
||||
}
|
||||
|
||||
override fun showUndo(position: Int, device: MobileDevice) {
|
||||
val onActionListener = object : UndoHelper.OnActionListener {
|
||||
override fun onActionConfirmed(action: Int, event: Int) {
|
||||
presenter.onUnregisterConfirmed(device)
|
||||
}
|
||||
|
||||
override fun onActionCanceled(action: Int, positions: MutableList<Int>?) {
|
||||
presenter.onUnregisterCancelled()
|
||||
}
|
||||
override fun updateData(data: List<MobileDevice>) {
|
||||
with(devicesAdapter) {
|
||||
items = data.toMutableList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
UndoHelper(devicesAdapter, onActionListener)
|
||||
.withConsecutive(false)
|
||||
.withAction(UndoHelper.Action.REMOVE)
|
||||
.start(listOf(position), mobileDevicesRecycler, R.string.mobile_device_removed, R.string.all_undo, 3000)
|
||||
override fun deleteItem(device: MobileDevice, position: Int) {
|
||||
with(devicesAdapter) {
|
||||
items.removeAt(position)
|
||||
notifyItemRemoved(position)
|
||||
notifyItemRangeChanged(position, itemCount)
|
||||
}
|
||||
}
|
||||
|
||||
override fun restoreDeleteItem(device: MobileDevice, position: Int) {
|
||||
with(devicesAdapter) {
|
||||
items.add(position, device)
|
||||
notifyItemInserted(position)
|
||||
notifyItemRangeChanged(position, itemCount)
|
||||
}
|
||||
}
|
||||
|
||||
override fun showUndo(device: MobileDevice, position: Int) {
|
||||
var confirmed = true
|
||||
|
||||
Snackbar.make(mobileDevicesRecycler, getString(R.string.mobile_device_removed), 3000)
|
||||
.setAction(R.string.all_undo) {
|
||||
confirmed = false
|
||||
presenter.onUnregisterCancelled(device, position)
|
||||
}.show()
|
||||
|
||||
view?.postDelayed(3000) {
|
||||
if (confirmed) presenter.onUnregisterConfirmed(device)
|
||||
}
|
||||
}
|
||||
|
||||
override fun hideRefresh() {
|
||||
|
@ -1,53 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.mobiledevice
|
||||
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_mobile_device.*
|
||||
|
||||
class MobileDeviceItem(val device: MobileDevice) : AbstractFlexibleItem<MobileDeviceItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_mobile_device
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.apply {
|
||||
mobileDeviceItemDate.text = device.date.toFormattedString("dd.MM.yyyy HH:mm:ss")
|
||||
mobileDeviceItemName.text = device.name
|
||||
mobileDeviceItemUnregister.setOnClickListener {
|
||||
(adapter as MobileDeviceAdapter).onDeviceUnregisterListener(device, holder.flexibleAdapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as MobileDeviceItem
|
||||
|
||||
if (device.id != other.device.id) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = device.hashCode()
|
||||
result = 31 * result + device.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.mobiledevice
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
|
||||
@Module
|
||||
class MobileDeviceModule {
|
||||
|
||||
@Provides
|
||||
fun provideMobileDeviceFlexibleAdapter() = MobileDeviceAdapter<AbstractFlexibleItem<*>>()
|
||||
}
|
@ -54,7 +54,6 @@ class MobileDevicePresenter @Inject constructor(
|
||||
mobileDeviceRepository.getDevices(student, semester, forceRefresh)
|
||||
}
|
||||
}
|
||||
.map { items -> items.map { MobileDeviceItem(it) } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally {
|
||||
@ -95,14 +94,15 @@ class MobileDevicePresenter @Inject constructor(
|
||||
|
||||
fun onUnregisterDevice(device: MobileDevice, position: Int) {
|
||||
view?.run {
|
||||
showUndo(position, device)
|
||||
deleteItem(device, position)
|
||||
showUndo(device, position)
|
||||
showEmpty(isViewEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
fun onUnregisterCancelled() {
|
||||
fun onUnregisterCancelled(device: MobileDevice, position: Int) {
|
||||
view?.run {
|
||||
restoreDeleteItem()
|
||||
restoreDeleteItem(device, position)
|
||||
showEmpty(isViewEmpty)
|
||||
}
|
||||
}
|
||||
@ -116,7 +116,6 @@ class MobileDevicePresenter @Inject constructor(
|
||||
.flatMap { mobileDeviceRepository.getDevices(student, semester, it) }
|
||||
}
|
||||
}
|
||||
.map { items -> items.map { MobileDeviceItem(it) } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally {
|
||||
|
@ -9,14 +9,16 @@ interface MobileDeviceView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<MobileDeviceItem>)
|
||||
fun updateData(data: List<MobileDevice>)
|
||||
|
||||
fun restoreDeleteItem()
|
||||
fun deleteItem(device: MobileDevice, position: Int)
|
||||
|
||||
fun restoreDeleteItem(device: MobileDevice, position: Int)
|
||||
|
||||
fun showUndo(device: MobileDevice, position: Int)
|
||||
|
||||
fun hideRefresh()
|
||||
|
||||
fun clearData()
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
||||
fun enableSwipe(enable: Boolean)
|
||||
@ -29,7 +31,5 @@ interface MobileDeviceView : BaseView {
|
||||
|
||||
fun setErrorDetails(message: String)
|
||||
|
||||
fun showUndo(position: Int, device: MobileDevice)
|
||||
|
||||
fun showTokenDialog()
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package io.github.wulkanowy.ui.modules.more
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.databinding.ItemMoreBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class MoreAdapter @Inject constructor() : RecyclerView.Adapter<MoreAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = emptyList<Pair<String, Drawable?>>()
|
||||
|
||||
var onClickListener: (name: String) -> Unit = {}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemMoreBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val (title, drawable) = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
moreItemTitle.text = title
|
||||
moreItemImage.setImageDrawable(drawable)
|
||||
|
||||
root.setOnClickListener { onClickListener(title) }
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemMoreBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -5,9 +5,7 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.about.AboutFragment
|
||||
@ -21,7 +19,6 @@ import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
|
||||
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
|
||||
import io.github.wulkanowy.utils.getCompatDrawable
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_more.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -31,7 +28,7 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
|
||||
lateinit var presenter: MorePresenter
|
||||
|
||||
@Inject
|
||||
lateinit var moreAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var moreAdapter: MoreAdapter
|
||||
|
||||
companion object {
|
||||
fun newInstance() = MoreFragment()
|
||||
@ -74,10 +71,10 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
moreAdapter.setOnItemClickListener { presenter.onItemSelected(it) }
|
||||
moreAdapter.onClickListener = presenter::onItemSelected
|
||||
|
||||
moreRecycler.apply {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = moreAdapter
|
||||
}
|
||||
}
|
||||
@ -86,8 +83,11 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
|
||||
if (::presenter.isInitialized) presenter.onViewReselected()
|
||||
}
|
||||
|
||||
override fun updateData(data: List<MoreItem>) {
|
||||
moreAdapter.updateDataSet(data)
|
||||
override fun updateData(data: List<Pair<String, Drawable?>>) {
|
||||
with(moreAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openMessagesView() {
|
||||
|
@ -1,47 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.more
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_more.*
|
||||
|
||||
class MoreItem(val title: String, private val drawable: Drawable?) : AbstractFlexibleItem<MoreItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_more
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.apply {
|
||||
moreItemTitle.text = title
|
||||
moreItemImage.setImageDrawable(drawable)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as MoreItem
|
||||
|
||||
if (title != other.title) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return title.hashCode()
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.more
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
@ -21,11 +20,10 @@ class MorePresenter @Inject constructor(
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item !is MoreItem) return
|
||||
Timber.i("Select more item \"${item.title}\"")
|
||||
fun onItemSelected(title: String) {
|
||||
Timber.i("Select more item \"${title}\"")
|
||||
view?.run {
|
||||
when (item.title) {
|
||||
when (title) {
|
||||
messagesRes?.first -> openMessagesView()
|
||||
homeworkRes?.first -> openHomeworkView()
|
||||
noteRes?.first -> openNoteView()
|
||||
@ -47,15 +45,15 @@ class MorePresenter @Inject constructor(
|
||||
Timber.i("Load items for more view")
|
||||
view?.run {
|
||||
updateData(listOfNotNull(
|
||||
messagesRes?.let { MoreItem(it.first, it.second) },
|
||||
homeworkRes?.let { MoreItem(it.first, it.second) },
|
||||
noteRes?.let { MoreItem(it.first, it.second) },
|
||||
luckyNumberRes?.let { MoreItem(it.first, it.second) },
|
||||
mobileDevicesRes?.let { MoreItem(it.first, it.second) },
|
||||
schoolAndTeachersRes?.let { MoreItem(it.first, it.second) },
|
||||
settingsRes?.let { MoreItem(it.first, it.second) },
|
||||
aboutRes?.let { MoreItem(it.first, it.second) })
|
||||
)
|
||||
messagesRes,
|
||||
homeworkRes,
|
||||
noteRes,
|
||||
luckyNumberRes,
|
||||
mobileDevicesRes,
|
||||
schoolAndTeachersRes,
|
||||
settingsRes,
|
||||
aboutRes
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ interface MoreView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<MoreItem>)
|
||||
fun updateData(data: List<Pair<String, Drawable?>>)
|
||||
|
||||
fun openSettingsView()
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
package io.github.wulkanowy.ui.modules.note
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Typeface
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import io.github.wulkanowy.sdk.scrapper.notes.Note.CategoryType
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Note
|
||||
import io.github.wulkanowy.databinding.ItemNoteBinding
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import javax.inject.Inject
|
||||
|
||||
class NoteAdapter @Inject constructor() : RecyclerView.Adapter<NoteAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = mutableListOf<Note>()
|
||||
|
||||
var onClickListener: (Note, position: Int) -> Unit = { _, _ -> }
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemNoteBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
with(noteItemDate) {
|
||||
text = item.date.toFormattedString()
|
||||
setTypeface(null, if (item.isRead) Typeface.NORMAL else Typeface.BOLD)
|
||||
}
|
||||
with(noteItemType) {
|
||||
text = item.category
|
||||
setTypeface(null, if (item.isRead) Typeface.NORMAL else Typeface.BOLD)
|
||||
}
|
||||
with(noteItemPoints) {
|
||||
text = "${if (item.points > 0) "+" else ""}${item.points}"
|
||||
visibility = if (item.isPointsShow) View.VISIBLE else View.GONE
|
||||
setTextColor(when (CategoryType.getByValue(item.categoryType)) {
|
||||
CategoryType.POSITIVE -> ContextCompat.getColor(context, R.color.note_positive)
|
||||
CategoryType.NEGATIVE -> ContextCompat.getColor(context, R.color.note_negative)
|
||||
else -> context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
||||
})
|
||||
}
|
||||
noteItemTeacher.text = item.teacher
|
||||
noteItemContent.text = item.content
|
||||
|
||||
root.setOnClickListener { onClickListener(item, position) }
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemNoteBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -6,16 +6,13 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Note
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_note.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -25,7 +22,7 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView {
|
||||
lateinit var presenter: NotePresenter
|
||||
|
||||
@Inject
|
||||
lateinit var noteAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var noteAdapter: NoteAdapter
|
||||
|
||||
companion object {
|
||||
fun newInstance() = NoteFragment()
|
||||
@ -35,7 +32,7 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView {
|
||||
get() = R.string.note_title
|
||||
|
||||
override val isViewEmpty: Boolean
|
||||
get() = noteAdapter.isEmpty
|
||||
get() = noteAdapter.items.isEmpty()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_note, container, false)
|
||||
@ -47,17 +44,12 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView {
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
noteAdapter.run {
|
||||
setOnItemClickListener { presenter.onNoteItemSelected(it) }
|
||||
}
|
||||
noteAdapter.onClickListener = presenter::onNoteItemSelected
|
||||
|
||||
noteRecycler.run {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = noteAdapter
|
||||
addItemDecoration(FlexibleItemDecoration(context)
|
||||
.withDefaultDivider()
|
||||
.withDrawDividerOnLastItem(false)
|
||||
)
|
||||
addItemDecoration(DividerItemDecoration(context))
|
||||
}
|
||||
noteSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
noteErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
@ -68,16 +60,25 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView {
|
||||
(activity as? MainActivity)?.showDialogFragment(NoteDialog.newInstance(note))
|
||||
}
|
||||
|
||||
override fun updateData(data: List<NoteItem>) {
|
||||
noteAdapter.updateDataSet(data, true)
|
||||
override fun updateData(data: List<Note>) {
|
||||
with(noteAdapter) {
|
||||
items = data.toMutableList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateItem(item: AbstractFlexibleItem<*>) {
|
||||
noteAdapter.updateItem(item)
|
||||
override fun updateItem(item: Note, position: Int) {
|
||||
with(noteAdapter) {
|
||||
items[position] = item
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearData() {
|
||||
noteAdapter.clear()
|
||||
with(noteAdapter) {
|
||||
items = mutableListOf()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun showEmpty(show: Boolean) {
|
||||
|
@ -1,77 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.note
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Typeface.BOLD
|
||||
import android.graphics.Typeface.NORMAL
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.core.content.ContextCompat
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Note
|
||||
import io.github.wulkanowy.sdk.scrapper.notes.Note.CategoryType
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_note.*
|
||||
|
||||
class NoteItem(val note: Note) : AbstractFlexibleItem<NoteItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_note
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.apply {
|
||||
with(noteItemDate) {
|
||||
text = note.date.toFormattedString()
|
||||
setTypeface(null, if (note.isRead) NORMAL else BOLD)
|
||||
}
|
||||
with(noteItemType) {
|
||||
text = note.category
|
||||
setTypeface(null, if (note.isRead) NORMAL else BOLD)
|
||||
}
|
||||
with(noteItemPoints) {
|
||||
text = "${if (note.points > 0) "+" else ""}${note.points}"
|
||||
visibility = if (note.isPointsShow) VISIBLE else GONE
|
||||
setTextColor(when(CategoryType.getByValue(note.categoryType)) {
|
||||
CategoryType.POSITIVE -> ContextCompat.getColor(context, R.color.note_positive)
|
||||
CategoryType.NEGATIVE -> ContextCompat.getColor(context, R.color.note_negative)
|
||||
else -> context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
||||
})
|
||||
}
|
||||
noteItemTeacher.text = note.teacher
|
||||
noteItemContent.text = note.content
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as NoteItem
|
||||
|
||||
if (note != other.note) return false
|
||||
if (note.id != other.note.id) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = note.hashCode()
|
||||
result = 31 * result + note.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) :
|
||||
FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.note
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Note
|
||||
import io.github.wulkanowy.data.repositories.note.NoteRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
@ -53,8 +52,7 @@ class NotePresenter @Inject constructor(
|
||||
disposable.add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getCurrentSemester(it).map { semester -> semester to it } }
|
||||
.flatMap { noteRepository.getNotes(it.second, it.first, forceRefresh) }
|
||||
.map { items -> items.map { NoteItem(it) } }
|
||||
.map { items -> items.sortedByDescending { it.note.date } }
|
||||
.map { items -> items.sortedByDescending { it.date } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally {
|
||||
@ -90,16 +88,14 @@ class NotePresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onNoteItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is NoteItem) {
|
||||
Timber.i("Select note item ${item.note.id}")
|
||||
view?.run {
|
||||
showNoteDialog(item.note)
|
||||
if (!item.note.isRead) {
|
||||
item.note.isRead = true
|
||||
updateItem(item)
|
||||
updateNote(item.note)
|
||||
}
|
||||
fun onNoteItemSelected(note: Note, position: Int) {
|
||||
Timber.i("Select note item ${note.id}")
|
||||
view?.run {
|
||||
showNoteDialog(note)
|
||||
if (!note.isRead) {
|
||||
note.isRead = true
|
||||
updateItem(note, position)
|
||||
updateNote(note)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.note
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Note
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
@ -10,9 +9,9 @@ interface NoteView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<NoteItem>)
|
||||
fun updateData(data: List<Note>)
|
||||
|
||||
fun updateItem(item: AbstractFlexibleItem<*>)
|
||||
fun updateItem(item: Note, position: Int)
|
||||
|
||||
fun clearData()
|
||||
|
||||
|
@ -0,0 +1,40 @@
|
||||
package io.github.wulkanowy.ui.modules.schoolandteachers.teacher
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Teacher
|
||||
import io.github.wulkanowy.databinding.ItemTeacherBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class TeacherAdapter @Inject constructor() : RecyclerView.Adapter<TeacherAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = emptyList<Teacher>()
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemTeacherBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val teacher = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
teacherItemName.text = teacher.name
|
||||
teacherItemSubject.text = if (teacher.subject.isNotBlank()) teacher.subject else root.context.getString(R.string.teacher_no_subject)
|
||||
if (teacher.shortName.isNotBlank()) {
|
||||
teacherItemShortName.visibility = View.VISIBLE
|
||||
teacherItemShortName.text = "[${teacher.shortName}]"
|
||||
} else {
|
||||
teacherItemShortName.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemTeacherBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
@ -6,12 +6,11 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Teacher
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView
|
||||
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
|
||||
@ -25,7 +24,7 @@ class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView,
|
||||
lateinit var presenter: TeacherPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var teacherAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var teacherAdapter: TeacherAdapter
|
||||
|
||||
companion object {
|
||||
fun newInstance() = TeacherFragment()
|
||||
@ -37,7 +36,7 @@ class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView,
|
||||
override val noSubjectString get() = getString(R.string.teacher_no_subject)
|
||||
|
||||
override val isViewEmpty: Boolean
|
||||
get() = teacherAdapter.isEmpty
|
||||
get() = teacherAdapter.items.isEmpty()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_teacher, container, false)
|
||||
@ -50,28 +49,20 @@ class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView,
|
||||
|
||||
override fun initView() {
|
||||
teacherRecycler.run {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = teacherAdapter
|
||||
addItemDecoration(FlexibleItemDecoration(context)
|
||||
.withDefaultDivider()
|
||||
.withDrawDividerOnLastItem(false)
|
||||
)
|
||||
addItemDecoration(DividerItemDecoration(context))
|
||||
}
|
||||
teacherSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
teacherErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
teacherErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<TeacherItem>) {
|
||||
teacherAdapter.updateDataSet(data, true)
|
||||
}
|
||||
|
||||
override fun updateItem(item: AbstractFlexibleItem<*>) {
|
||||
teacherAdapter.updateItem(item)
|
||||
}
|
||||
|
||||
override fun clearData() {
|
||||
teacherAdapter.clear()
|
||||
override fun updateData(data: List<Teacher>) {
|
||||
with(teacherAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun showEmpty(show: Boolean) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user