diff --git a/app/build.gradle b/app/build.gradle index 17cc6d958..7c311dfc2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt index 96ec7cb84..3fab98563 100644 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt @@ -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") diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt deleted file mode 100644 index d67aa2a7f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.github.wulkanowy.data.pojos - -class AppCreator(val displayName: String, val githubUsername: String) diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt new file mode 100644 index 000000000..e792bde46 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt @@ -0,0 +1,3 @@ +package io.github.wulkanowy.data.pojos + +class Contributor(val displayName: String, val githubUsername: String) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt index fa752ed29..6a0b2d32e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt @@ -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> { - return Single.fromCallable> { + fun getAppCreators(): Single> { + return Single.fromCallable> { Gson().fromJson( assets.open("contributors.json").bufferedReader().use { it.readText() }, - Array::class.java + Array::class.java ).toList() } } diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt index 4f5683850..db5ff59b3 100644 --- a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt +++ b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt @@ -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>(null, null, true) - @Singleton @Provides fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context) diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseExpandableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseExpandableAdapter.kt new file mode 100644 index 000000000..eee4625c9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseExpandableAdapter.kt @@ -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 : RecyclerView.Adapter() { + + 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) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/WidgetConfigureAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/WidgetConfigureAdapter.kt new file mode 100644 index 000000000..cefe6ed75 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/WidgetConfigureAdapter.kt @@ -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() { + + var items = emptyList>() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutAdapter.kt new file mode 100644 index 000000000..35dec3b4f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutAdapter.kt @@ -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() { + + private enum class ViewType(val id: Int) { + ITEM_HEADER(1), + ITEM_ELEMENT(2) + } + + var items = emptyList>() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt index 5a32ac837..b2893c1e1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt @@ -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> + 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) { + override fun updateData(data: List>) { with(aboutAdapter) { - removeAllScrollableHeaders() - addScrollableHeader(header) - updateDataSet(items) + items = data + notifyDataSetChanged() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutItem.kt deleted file mode 100644 index 29f1cd8c8..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_about - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>) = ViewHolder(view, adapter) - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { - 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>) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt index 7e740b32b..27237ea6f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -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 + )) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutScrollableHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutScrollableHeader.kt deleted file mode 100644 index 07bb41249..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutScrollableHeader.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.scrollable_header_about - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>) = ViewHolder(view, adapter) - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { - 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>) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt index 4bc0c3fe0..79b700ea3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt @@ -23,7 +23,7 @@ interface AboutView : BaseView { fun initView() - fun updateData(header: AboutScrollableHeader, items: List) + fun updateData(data: List>) fun openLogViewer() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorAdapter.kt new file mode 100644 index 000000000..215cd27db --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorAdapter.kt @@ -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() { + + var items = emptyList() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorFragment.kt index c181c3d38..2544836cb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorFragment.kt @@ -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> + 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) { - creatorsAdapter.updateDataSet(data) + override fun updateData(data: List) { + with(creatorsAdapter) { + items = data + notifyDataSetChanged() + } } override fun openUserGithubPage(username: String) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorItem.kt deleted file mode 100644 index 844b5bd8d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_contributor - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>) = ViewHolder(view, adapter) - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { - 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>) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt index 721b25007..416a59ce5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt @@ -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) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorView.kt index 18ec3a8ec..8007e4e3f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorView.kt @@ -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) + fun updateData(data: List) fun openUserGithubPage(username: String) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt new file mode 100644 index 000000000..07025c09f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt @@ -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() { + + var items = emptyList() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt index 2681680b1..d64c6225c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt @@ -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> + lateinit var licenseAdapter: LicenseAdapter @Inject lateinit var libs: Lazy @@ -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) { - licenseAdapter.updateDataSet(data) + override fun updateData(data: List) { + with(licenseAdapter) { + items = data + notifyDataSetChanged() + } } override fun openLicense(licenseHtml: String) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseItem.kt deleted file mode 100644 index 8dcb89224..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_license - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>) = ViewHolder(view, adapter) - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { - 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>) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt index dc48b098b..d0f6d69e0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt @@ -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) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt index 3939d3e80..0680dbb73 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt @@ -9,7 +9,7 @@ interface LicenseView : BaseView { fun initView() - fun updateData(data: List) + fun updateData(data: List) fun openLicense(licenseHtml: String) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt new file mode 100644 index 000000000..7df0ca378 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt @@ -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() { + + var items = emptyList() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt index cfff31c98..dc8cce928 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt @@ -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> + 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) { - accountAdapter.updateDataSet(data) + override fun updateData(data: List) { + with(accountAdapter) { + items = data + notifyDataSetChanged() + } } override fun showError(text: String, error: Throwable) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt deleted file mode 100644 index d3a3ee6a3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_account - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>) = ViewHolder(view, adapter) - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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 - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt index e9b4b81ee..3416a043f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt @@ -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( })) } } - diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt index ede5023ba..abb9e1d27 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt @@ -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) + fun updateData(data: List) fun dismissView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt index 75f998404..a63d5045a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt @@ -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> : FlexibleAdapter(null, null, true) { +class AttendanceAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() 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) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt index 9969b1c78..31b0a3c29 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -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> + 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) { - attendanceAdapter.updateDataSet(data, true) + override fun updateData(data: List) { + 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() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt deleted file mode 100644 index 7355aec2e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_attendance - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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 - } - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceModule.kt deleted file mode 100644 index eb35fea1b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceModule.kt +++ /dev/null @@ -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>() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index 7fc044744..3a1fb0ceb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -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 { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt index 03e95053f..484070a2e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -18,7 +18,7 @@ interface AttendanceView : BaseView { fun initView() - fun updateData(data: List) + fun updateData(data: List) fun updateNavigationDay(date: String) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryAdapter.kt new file mode 100644 index 000000000..a6d4cf220 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryAdapter.kt @@ -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() { + + private enum class ViewType(val id: Int) { + HEADER(1), + ITEM(2) + } + + var items = emptyList() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt index 8e30ea8b7..4ec9cceda 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt @@ -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> + lateinit var attendanceSummaryAdapter: AttendanceSummaryAdapter private lateinit var subjectsAdapter: ArrayAdapter @@ -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, header: AttendanceSummaryScrollableHeader) { + override fun updateDataSet(data: List) { 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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt deleted file mode 100644 index 265d6ce44..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_attendance_summary - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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 - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt index 8fc5b6e4a..72dfc327e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -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): 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): List { - 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)}%" - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryScrollableHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryScrollableHeader.kt deleted file mode 100644 index c258f71d2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryScrollableHeader.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.scrollable_header_attendance_summary - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, position: Int, payloads: MutableList?) { - 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>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt index 8bd5332d6..dd4053c72 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt @@ -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, header: AttendanceSummaryScrollableHeader) + fun updateDataSet(data: List) fun updateSubjects(data: ArrayList) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamAdapter.kt new file mode 100644 index 000000000..85061997c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamAdapter.kt @@ -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() { + + var items = emptyList>() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt index b880f4650..cc395f626 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt @@ -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> + 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) { - examAdapter.updateDataSet(data, true) + override fun updateData(data: List>) { + 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() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamHeader.kt deleted file mode 100644 index 0a5b862c3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamHeader.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.header_exam - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder, - position: Int, payloads: MutableList?) { - 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>?) : ExpandableViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt index 8971b4df3..579e37203 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt @@ -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(val value: T, val viewType: ViewType) { -class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem(header) { - - override fun getLayoutRes() = R.layout.item_exam - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt index aac9bc4bb..495602fc5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt @@ -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>): List { - return items.flatMap { - ExamHeader(it.key).let { header -> - it.value.reversed().map { item -> ExamItem(header, item) } + private fun createExamItems(items: List): List> { + 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() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt index 5f4a74306..00429bae6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt @@ -9,7 +9,7 @@ interface ExamView : BaseView { fun initView() - fun updateData(data: List) + fun updateData(data: List>) fun updateNavigationWeek(date: String) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt new file mode 100644 index 000000000..22367d02c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt @@ -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() { + + private var headers = mutableListOf() + + private var items = mutableListOf() + + private var expandedPosition = RecyclerView.NO_POSITION + + private var isExpandable = false + + var onClickListener: (Grade, position: Int) -> Unit = { _, _ -> } + + var colorTheme = "" + + fun setDataItems(data: List, 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) { + 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, private val new: List) : + 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] + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt index 9505d354f..e0cfca2f1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt @@ -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> + 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) { - gradeDetailsAdapter.updateDataSet(data, true) + override fun updateData(data: List, 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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt deleted file mode 100644 index 4a34a1457..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt +++ /dev/null @@ -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() { - - init { - isExpanded = !isExpandable - } - - override fun getLayoutRes() = R.layout.header_grade_details - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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>?) : - 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) } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt deleted file mode 100644 index 39a911e62..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt +++ /dev/null @@ -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() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt index 1e47eca5d..f1adbdea2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt @@ -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() { - - override fun getLayoutRes() = R.layout.item_grade_details - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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 +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index a9e5b2b7d..83501182d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -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>, averages: List>): List { - 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, averages: List>): List { + 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) }) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt index e2977bcbe..e71fcc3c8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt @@ -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) + fun updateData(data: List, 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 } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt new file mode 100644 index 000000000..30c4ccc23 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt @@ -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() { + + private enum class ViewType(val id: Int) { + HEADER(1), + ITEM(2) + } + + var items = emptyList() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt index 05fde5227..3addfb6ed 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt @@ -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> + 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, header: GradeSummaryScrollableHeader) { - gradeSummaryAdapter.apply { - updateDataSet(data, true) - removeAllScrollableHeaders() - addScrollableHeader(header) + override fun updateData(data: List) { + with(gradeSummaryAdapter) { + items = data + notifyDataSetChanged() } } override fun clearView() { - gradeSummaryAdapter.clear() + with(gradeSummaryAdapter) { + items = emptyList() + notifyDataSetChanged() + } } override fun resetView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt deleted file mode 100644 index 95c32d176..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_grade_summary - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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 - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index c12f2a516..53c69db70 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -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, averages: List>): Pair, 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, averages: List>): List { + 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) - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryScrollableHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryScrollableHeader.kt deleted file mode 100644 index f1c535c71..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryScrollableHeader.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.scrollable_header_grade_summary - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, - position: Int, payloads: MutableList?) { - 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>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt index cf3184873..974d91415 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt @@ -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, header: GradeSummaryScrollableHeader) + fun updateData(data: List) fun resetView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkAdapter.kt new file mode 100644 index 000000000..a87ad18e8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkAdapter.kt @@ -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() { + + var items = emptyList>() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt index a011a015c..ba0bf1bef 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt @@ -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> + 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) { - homeworkAdapter.updateDataSet(data, true) + override fun updateData(data: List>) { + 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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt deleted file mode 100644 index 490237883..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.header_homework - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder( - adapter: FlexibleAdapter>?, holder: HomeworkHeader.ViewHolder, - position: Int, payloads: MutableList? - ) { - 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>?) : ExpandableViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt index 3c2dd7baf..7e0039583 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt @@ -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(val value: T, val viewType: ViewType) { -class HomeworkItem(header: HomeworkHeader, val homework: Homework) : - AbstractSectionableItem(header) { - - override fun getLayoutRes() = R.layout.item_homework - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt index aae18df32..d39efde14 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt @@ -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>): List { - return items.flatMap { - HomeworkHeader(it.key).let { header -> - it.value.reversed().map { item -> HomeworkItem(header, item) } + private fun createHomeworkItem(items: List): List> { + 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() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt index 1d241df46..2a678cd4c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt @@ -9,7 +9,7 @@ interface HomeworkView : BaseView { fun initView() - fun updateData(data: List) + fun updateData(data: List>) fun clearData() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt new file mode 100644 index 000000000..7383a5ecb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt @@ -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() { + + var items = emptyList>() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt index 9860af22f..48a3cc614 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt @@ -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> + 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) { - loginAdapter.updateDataSet(data) + override fun updateData(data: List>) { + with(loginAdapter) { + items = data + notifyDataSetChanged() + } } override fun openMainView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt deleted file mode 100644 index 06be61387..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_login_student_select - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ItemViewHolder { - return ItemViewHolder(view, adapter) - } - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ItemViewHolder, position: Int, payloads: MutableList) { - 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 } - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt index 82de5a887..b2d0aed91 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt @@ -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 }) }) ) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt index d80b059db..89431dfc6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt @@ -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) + fun updateData(data: List>) fun openMainView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt index e8ce3bcfb..84f4e06e1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt @@ -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> + lateinit var configureAdapter: WidgetConfigureAdapter @Inject override lateinit var presenter: LuckyNumberWidgetConfigurePresenter @@ -41,10 +40,10 @@ class LuckyNumberWidgetConfigureActivity : BaseActivity presenter.onThemeSelect(which) } .show() } - override fun updateData(data: List) { - configureAdapter.updateDataSet(data) + override fun updateData(data: List>) { + with(configureAdapter) { + items = data + notifyDataSetChanged() + } } override fun updateLuckyNumberWidget(widgetId: Int) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureItem.kt deleted file mode 100644 index e260b7fcb..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_account - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>) = ViewHolder(view, adapter) - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { - 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 - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt index 6e4716bfa..1bb7447a2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt index fa4c0cc61..c8c348ed3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt @@ -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) + fun updateData(data: List>) fun updateLuckyNumberWidget(widgetId: Int) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index 864ad4239..e44b47a7b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -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(), 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 diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt index 1610d0298..03f6ac382 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt @@ -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 diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt deleted file mode 100644 index 7e52233c9..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt +++ /dev/null @@ -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() { - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun getLayoutRes() = R.layout.item_message - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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 - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt new file mode 100644 index 000000000..93097fd20 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt @@ -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() { + + var items = mutableListOf() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt index ce0fc7805..c39aa3e28 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -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> + 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) { - tabAdapter.updateDataSet(data) + override fun updateData(data: List) { + 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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index cbdb89b2e..37b45d03d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt index 92115ed33..94ece8ec2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt @@ -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) + fun updateData(data: List) - fun updateItem(item: AbstractFlexibleItem<*>) - - fun clearView() + fun updateItem(item: Message, position: Int) fun showProgress(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceAdapter.kt index 27c72ce69..4bc3097d6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceAdapter.kt @@ -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> : FlexibleAdapter(null, null, true) { +class MobileDeviceAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = mutableListOf() 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) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt index d47574f60..08ec26755 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt @@ -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> + 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) { - 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?) { - presenter.onUnregisterCancelled() - } + override fun updateData(data: List) { + 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() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceItem.kt deleted file mode 100644 index 436c2d0e2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_mobile_device - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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 - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceModule.kt deleted file mode 100644 index 59bbaa9f8..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceModule.kt +++ /dev/null @@ -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>() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt index a6a83f8a9..d8c99b221 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt @@ -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 { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt index 869b59bb1..ec2d3f87f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt @@ -9,14 +9,16 @@ interface MobileDeviceView : BaseView { fun initView() - fun updateData(data: List) + fun updateData(data: List) - 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() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt new file mode 100644 index 000000000..70587b0cf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt @@ -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() { + + var items = emptyList>() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt index ef9c36fab..25cda2b2a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt @@ -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> + 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) { - moreAdapter.updateDataSet(data) + override fun updateData(data: List>) { + with(moreAdapter) { + items = data + notifyDataSetChanged() + } } override fun openMessagesView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt deleted file mode 100644 index 85b604e77..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_more - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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 - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt index 096f89e9b..593645c19 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt @@ -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 + )) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt index 41008176d..922afdfd5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt @@ -23,7 +23,7 @@ interface MoreView : BaseView { fun initView() - fun updateData(data: List) + fun updateData(data: List>) fun openSettingsView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteAdapter.kt new file mode 100644 index 000000000..2ffcad949 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteAdapter.kt @@ -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() { + + var items = mutableListOf() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt index e5335e459..b01dc4939 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt @@ -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> + 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) { - noteAdapter.updateDataSet(data, true) + override fun updateData(data: List) { + 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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt deleted file mode 100644 index 53fe6fa91..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt +++ /dev/null @@ -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() { - - override fun getLayoutRes() = R.layout.item_note - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - 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 - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt index 7acf37a4c..00df71b96 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt @@ -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) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt index a9c9f4f2f..a7cbab8f4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt @@ -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) + fun updateData(data: List) - fun updateItem(item: AbstractFlexibleItem<*>) + fun updateItem(item: Note, position: Int) fun clearData() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherAdapter.kt new file mode 100644 index 000000000..8deeae05b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherAdapter.kt @@ -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() { + + var items = emptyList() + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt index b6bb9c101..7d1003263 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt @@ -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> + 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) { - teacherAdapter.updateDataSet(data, true) - } - - override fun updateItem(item: AbstractFlexibleItem<*>) { - teacherAdapter.updateItem(item) - } - - override fun clearData() { - teacherAdapter.clear() + override fun updateData(data: List) { + with(teacherAdapter) { + items = data + notifyDataSetChanged() + } } override fun showEmpty(show: Boolean) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherItem.kt deleted file mode 100644 index 368317744..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherItem.kt +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.ui.modules.schoolandteachers.teacher - -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.Teacher -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_teacher.* - -class TeacherItem(val teacher: Teacher, private val noSubjectText: String) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_teacher - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): TeacherItem.ViewHolder { - return TeacherItem.ViewHolder(view, adapter) - } - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: TeacherItem.ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - teacherItemName.text = teacher.name - teacherItemSubject.text = if (teacher.subject.isNotBlank()) teacher.subject else noSubjectText - if (teacher.shortName.isNotBlank()) { - teacherItemShortName.visibility = VISIBLE - teacherItemShortName.text = "[${teacher.shortName}]" - } else { - teacherItemShortName.visibility = GONE - } - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as TeacherItem - - if (teacher != other.teacher) return false - if (teacher.id != other.teacher.id) return false - return true - } - - override fun hashCode(): Int { - var result = teacher.hashCode() - result = 31 * result + teacher.id.toInt() - return result - } - - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt index c5b6cfd69..e888308fd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt @@ -58,7 +58,6 @@ class TeacherPresenter @Inject constructor( } } .map { it.filter { teacher -> teacher.name.isNotBlank() } } - .map { items -> items.map { TeacherItem(it, view?.noSubjectString.orEmpty()) } } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt index b16be8ff9..c655bfad8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.schoolandteachers.teacher -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Teacher import io.github.wulkanowy.ui.base.BaseView import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView @@ -12,14 +12,10 @@ interface TeacherView : BaseView, SchoolAndTeachersChildView { fun initView() - fun updateData(data: List) - - fun updateItem(item: AbstractFlexibleItem<*>) + fun updateData(data: List) fun hideRefresh() - fun clearData() - fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt new file mode 100644 index 000000000..b4b2671e0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt @@ -0,0 +1,199 @@ +package io.github.wulkanowy.ui.modules.timetable + +import android.graphics.Paint +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.databinding.ItemTimetableBinding +import io.github.wulkanowy.databinding.ItemTimetableSmallBinding +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class TimetableAdapter @Inject constructor() : RecyclerView.Adapter() { + + private enum class ViewType(val id: Int) { + ITEM_NORMAL(1), + ITEM_SMALL(2) + } + + var items = emptyList() + + var onClickListener: (Timetable) -> Unit = {} + + var showWholeClassPlan: String = "no" + + override fun getItemCount() = items.size + + override fun getItemViewType(position: Int) = when { + !items[position].isStudentPlan && showWholeClassPlan == "small" -> ViewType.ITEM_SMALL.id + else -> ViewType.ITEM_NORMAL.id + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + ViewType.ITEM_NORMAL.id -> ItemViewHolder(ItemTimetableBinding.inflate(inflater, parent, false)) + ViewType.ITEM_SMALL.id -> SmallItemViewHolder(ItemTimetableSmallBinding.inflate(inflater, parent, false)) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val lesson = items[position] + + when (holder) { + is ItemViewHolder -> bindNormalView(holder.binding, lesson) + is SmallItemViewHolder -> bindSmallView(holder.binding, lesson) + } + } + + private fun bindSmallView(binding: ItemTimetableSmallBinding, lesson: Timetable) { + with(binding) { + timetableSmallItemNumber.text = lesson.number.toString() + timetableSmallItemSubject.text = lesson.subject + timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm") + timetableSmallItemRoom.text = lesson.room + timetableSmallItemTeacher.text = lesson.teacher + + bindSubjectStyle(timetableSmallItemSubject, lesson) + bindSmallDescription(binding, lesson) + bindSmallColors(binding, lesson) + + root.setOnClickListener { onClickListener(lesson) } + } + } + + private fun bindNormalView(binding: ItemTimetableBinding, lesson: Timetable) { + with(binding) { + timetableItemNumber.text = lesson.number.toString() + timetableItemSubject.text = lesson.subject + timetableItemRoom.text = lesson.room + timetableItemTeacher.text = lesson.teacher + timetableItemTimeStart.text = lesson.start.toFormattedString("HH:mm") + timetableItemTimeFinish.text = lesson.end.toFormattedString("HH:mm") + + bindSubjectStyle(timetableItemSubject, lesson) + bindNormalDescription(binding, lesson) + bindNormalColors(binding, lesson) + + root.setOnClickListener { onClickListener(lesson) } + } + } + + private fun bindSubjectStyle(subjectView: TextView, lesson: Timetable) { + subjectView.paintFlags = if (lesson.canceled) subjectView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG + else subjectView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() + } + + private fun bindSmallDescription(binding: ItemTimetableSmallBinding, lesson: Timetable) { + with(binding) { + if (lesson.info.isNotBlank() && !lesson.changes) { + timetableSmallItemDescription.visibility = android.view.View.VISIBLE + timetableSmallItemDescription.text = lesson.info + + timetableSmallItemRoom.visibility = android.view.View.GONE + timetableSmallItemTeacher.visibility = android.view.View.GONE + + timetableSmallItemDescription.setTextColor(root.context.getThemeAttrColor( + if (lesson.canceled) R.attr.colorPrimary + else R.attr.colorTimetableChange + )) + } else { + timetableSmallItemDescription.visibility = android.view.View.GONE + timetableSmallItemRoom.visibility = android.view.View.VISIBLE + timetableSmallItemTeacher.visibility = android.view.View.VISIBLE + } + } + } + + private fun bindNormalDescription(binding: ItemTimetableBinding, lesson: Timetable) { + with(binding) { + if (lesson.info.isNotBlank() && !lesson.changes) { + timetableItemDescription.visibility = android.view.View.VISIBLE + timetableItemDescription.text = lesson.info + + timetableItemRoom.visibility = android.view.View.GONE + timetableItemTeacher.visibility = android.view.View.GONE + + timetableItemDescription.setTextColor(root.context.getThemeAttrColor( + if (lesson.canceled) R.attr.colorPrimary + else R.attr.colorTimetableChange + )) + } else { + timetableItemDescription.visibility = android.view.View.GONE + timetableItemRoom.visibility = android.view.View.VISIBLE + timetableItemTeacher.visibility = android.view.View.VISIBLE + } + } + } + + private fun bindSmallColors(binding: ItemTimetableSmallBinding, lesson: Timetable) { + with(binding) { + if (lesson.canceled) { + updateNumberAndSubjectCanceledColor(timetableSmallItemNumber, timetableSmallItemSubject) + } else { + updateNumberColor(timetableSmallItemNumber, lesson) + updateSubjectColor(timetableSmallItemSubject, lesson) + updateRoomColor(timetableSmallItemRoom, lesson) + updateTeacherColor(timetableSmallItemTeacher, lesson) + } + } + } + + private fun bindNormalColors(binding: ItemTimetableBinding, lesson: Timetable) { + with(binding) { + if (lesson.canceled) { + updateNumberAndSubjectCanceledColor(timetableItemNumber, timetableItemSubject) + } else { + updateNumberColor(timetableItemNumber, lesson) + updateSubjectColor(timetableItemSubject, lesson) + updateRoomColor(timetableItemRoom, lesson) + updateTeacherColor(timetableItemTeacher, lesson) + } + } + } + + private fun updateNumberAndSubjectCanceledColor(numberView: TextView, subjectView: TextView) { + numberView.setTextColor(numberView.context.getThemeAttrColor(R.attr.colorPrimary)) + subjectView.setTextColor(subjectView.context.getThemeAttrColor(R.attr.colorPrimary)) + } + + private fun updateNumberColor(numberView: TextView, lesson: Timetable) { + numberView.setTextColor(numberView.context.getThemeAttrColor( + if (lesson.changes || lesson.info.isNotBlank()) R.attr.colorTimetableChange + else android.R.attr.textColorPrimary + )) + } + + private fun updateSubjectColor(subjectView: TextView, lesson: Timetable) { + subjectView.setTextColor(subjectView.context.getThemeAttrColor( + if (lesson.subjectOld.isNotBlank() && lesson.subjectOld != lesson.subject) R.attr.colorTimetableChange + else android.R.attr.textColorPrimary + )) + } + + private fun updateRoomColor(roomView: TextView, lesson: Timetable) { + roomView.setTextColor(roomView.context.getThemeAttrColor( + if (lesson.roomOld.isNotBlank() && lesson.roomOld != lesson.room) R.attr.colorTimetableChange + else android.R.attr.textColorSecondary + )) + } + + private fun updateTeacherColor(teacherTextView: TextView, lesson: Timetable) { + teacherTextView.setTextColor(teacherTextView.context.getThemeAttrColor( + if (lesson.teacherOld.isNotBlank() && lesson.teacherOld != lesson.teacher) R.attr.colorTimetableChange + else android.R.attr.textColorSecondary + )) + } + + private class ItemViewHolder(val binding: ItemTimetableBinding) : + RecyclerView.ViewHolder(binding.root) + + private class SmallItemViewHolder(val binding: ItemTimetableSmallBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt index ef6057dfa..2c15b9b3f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -9,20 +9,17 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager import com.wdullaer.materialdatetimepicker.date.DatePickerDialog -import eu.davidea.flexibleadapter.FlexibleAdapter -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.Timetable 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.timetable.completed.CompletedLessonsFragment import io.github.wulkanowy.utils.SchooldaysRangeLimiter import io.github.wulkanowy.utils.dpToPx -import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_timetable.* import org.threeten.bp.LocalDate import javax.inject.Inject @@ -34,7 +31,7 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, lateinit var presenter: TimetablePresenter @Inject - lateinit var timetableAdapter: FlexibleAdapter> + lateinit var timetableAdapter: TimetableAdapter companion object { private const val SAVED_DATE_KEY = "CURRENT_DATE" @@ -44,7 +41,7 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, override val titleStringId get() = R.string.timetable_title - override val isViewEmpty get() = timetableAdapter.isEmpty + override val isViewEmpty get() = timetableAdapter.items.isEmpty() override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize @@ -64,15 +61,12 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, } override fun initView() { - timetableAdapter.setOnItemClickListener(presenter::onTimetableItemSelected) + timetableAdapter.onClickListener = presenter::onTimetableItemSelected with(timetableRecycler) { - layoutManager = SmoothScrollLinearLayoutManager(context) + layoutManager = LinearLayoutManager(context) adapter = timetableAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider() - .withDrawDividerOnLastItem(false) - ) + addItemDecoration(DividerItemDecoration(context)) } timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh) @@ -95,12 +89,19 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, else false } - override fun updateData(data: List) { - timetableAdapter.updateDataSet(data, true) + override fun updateData(data: List, showWholeClassPlanType: String) { + with(timetableAdapter) { + items = data + showWholeClassPlan = showWholeClassPlanType + notifyDataSetChanged() + } } override fun clearData() { - timetableAdapter.clear() + with(timetableAdapter) { + items = emptyList() + notifyDataSetChanged() + } } override fun updateNavigationDay(date: String) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt deleted file mode 100644 index 5b35b85d1..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt +++ /dev/null @@ -1,197 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import android.annotation.SuppressLint -import android.graphics.Paint -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.widget.TextView -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.Timetable -import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_timetable.* -import kotlinx.android.synthetic.main.item_timetable_small.* - -class TimetableItem(val lesson: Timetable, private val showWholeClassPlan: String) : - AbstractFlexibleItem() { - - override fun getLayoutRes() = when { - showWholeClassPlan == "small" && !lesson.isStudentPlan -> R.layout.item_timetable_small - else -> R.layout.item_timetable - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - when (itemViewType) { - R.layout.item_timetable_small -> bindSmallView(holder) - R.layout.item_timetable -> bindNormalView(holder) - } - } - - private fun bindSmallView(holder: ViewHolder) { - with(holder) { - timetableSmallItemNumber.text = lesson.number.toString() - timetableSmallItemSubject.text = lesson.subject - timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm") - timetableSmallItemRoom.text = lesson.room - timetableSmallItemTeacher.text = lesson.teacher - - updateSubjectStyle(timetableSmallItemSubject) - updateSmallDescription(this) - updateSmallColors(this) - } - } - - private fun bindNormalView(holder: ViewHolder) { - with(holder) { - timetableItemNumber.text = lesson.number.toString() - timetableItemSubject.text = lesson.subject - timetableItemRoom.text = lesson.room - timetableItemTeacher.text = lesson.teacher - timetableItemTimeStart.text = lesson.start.toFormattedString("HH:mm") - timetableItemTimeFinish.text = lesson.end.toFormattedString("HH:mm") - - updateSubjectStyle(timetableItemSubject) - updateNormalDescription(this) - updateNormalColors(this) - } - } - - private fun updateSubjectStyle(subjectView: TextView) { - subjectView.paintFlags = if (lesson.canceled) subjectView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG - else subjectView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() - } - - private fun updateSmallDescription(holder: ViewHolder) { - with(holder) { - if (lesson.info.isNotBlank() && !lesson.changes) { - timetableSmallItemDescription.visibility = VISIBLE - timetableSmallItemDescription.text = lesson.info - - timetableSmallItemRoom.visibility = GONE - timetableSmallItemTeacher.visibility = GONE - - timetableSmallItemDescription.setTextColor(holder.view.context.getThemeAttrColor( - if (lesson.canceled) R.attr.colorPrimary - else R.attr.colorTimetableChange - )) - } else { - timetableSmallItemDescription.visibility = GONE - timetableSmallItemRoom.visibility = VISIBLE - timetableSmallItemTeacher.visibility = VISIBLE - } - } - } - - private fun updateNormalDescription(holder: ViewHolder) { - with(holder) { - if (lesson.info.isNotBlank() && !lesson.changes) { - timetableItemDescription.visibility = VISIBLE - timetableItemDescription.text = lesson.info - - timetableItemRoom.visibility = GONE - timetableItemTeacher.visibility = GONE - - timetableItemDescription.setTextColor(holder.view.context.getThemeAttrColor( - if (lesson.canceled) R.attr.colorPrimary - else R.attr.colorTimetableChange - )) - } else { - timetableItemDescription.visibility = GONE - timetableItemRoom.visibility = VISIBLE - timetableItemTeacher.visibility = VISIBLE - } - } - } - - private fun updateSmallColors(holder: ViewHolder) { - with(holder) { - if (lesson.canceled) { - updateNumberAndSubjectCanceledColor(timetableSmallItemNumber, timetableSmallItemSubject) - } else { - updateNumberColor(timetableSmallItemNumber) - updateSubjectColor(timetableSmallItemSubject) - updateRoomColor(timetableSmallItemRoom) - updateTeacherColor(timetableSmallItemTeacher) - } - } - } - - private fun updateNormalColors(holder: ViewHolder) { - with(holder) { - if (lesson.canceled) { - updateNumberAndSubjectCanceledColor(timetableItemNumber, timetableItemSubject) - } else { - updateNumberColor(timetableItemNumber) - updateSubjectColor(timetableItemSubject) - updateRoomColor(timetableItemRoom) - updateTeacherColor(timetableItemTeacher) - } - } - } - - private fun updateNumberAndSubjectCanceledColor(numberView: TextView, subjectView: TextView) { - numberView.setTextColor(numberView.context.getThemeAttrColor(R.attr.colorPrimary)) - subjectView.setTextColor(subjectView.context.getThemeAttrColor(R.attr.colorPrimary)) - } - - private fun updateNumberColor(numberView: TextView) { - numberView.setTextColor(numberView.context.getThemeAttrColor( - if (lesson.changes || lesson.info.isNotBlank()) R.attr.colorTimetableChange - else android.R.attr.textColorPrimary - )) - } - - private fun updateSubjectColor(subjectView: TextView) { - subjectView.setTextColor(subjectView.context.getThemeAttrColor( - if (lesson.subjectOld.isNotBlank() && lesson.subjectOld != lesson.subject) R.attr.colorTimetableChange - else android.R.attr.textColorPrimary - )) - } - - private fun updateRoomColor(roomView: TextView) { - roomView.setTextColor(roomView.context.getThemeAttrColor( - if (lesson.roomOld.isNotBlank() && lesson.roomOld != lesson.room) R.attr.colorTimetableChange - else android.R.attr.textColorSecondary - )) - } - - private fun updateTeacherColor(teacherTextView: TextView) { - teacherTextView.setTextColor(teacherTextView.context.getThemeAttrColor( - if (lesson.teacherOld.isNotBlank() && lesson.teacherOld != lesson.teacher) R.attr.colorTimetableChange - else android.R.attr.textColorSecondary - )) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as TimetableItem - - if (lesson != other.lesson) return false - return true - } - - override fun hashCode(): Int { - var result = lesson.hashCode() - result = 31 * result + lesson.id.toInt() - return result - } - - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : - FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index 2e9d0a0b3..03d79081d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -1,7 +1,6 @@ package io.github.wulkanowy.ui.modules.timetable import android.annotation.SuppressLint -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository @@ -22,7 +21,6 @@ import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.of import org.threeten.bp.LocalDate.ofEpochDay import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS import javax.inject.Inject class TimetablePresenter @Inject constructor( @@ -102,11 +100,9 @@ class TimetablePresenter @Inject constructor( } } - fun onTimetableItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is TimetableItem) { - Timber.i("Select timetable item ${item.lesson.id}") - view?.showTimetableDialog(item.lesson) - } + fun onTimetableItemSelected(lesson: Timetable) { + Timber.i("Select timetable item ${lesson.id}") + view?.showTimetableDialog(lesson) } fun onCompletedLessonsSwitchSelected(): Boolean { @@ -139,9 +135,8 @@ class TimetablePresenter @Inject constructor( timetableRepository.getTimetable(student, semester, currentDate, currentDate, forceRefresh) } } - .delay(200, MILLISECONDS) - .map { createTimetableItems(it) } - .map { items -> items.sortedWith(compareBy({ it.lesson.number }, { !it.lesson.isStudentPlan })) } + .map { items -> items.filter { if (prefRepository.showWholeClassPlan == "no") it.isStudentPlan else true } } + .map { items -> items.sortedWith(compareBy({ it.number }, { !it.isStudentPlan })) } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { @@ -154,7 +149,7 @@ class TimetablePresenter @Inject constructor( .subscribe({ Timber.i("Loading timetable result: Success") view?.apply { - updateData(it) + updateData(it, prefRepository.showWholeClassPlan) showEmpty(it.isEmpty()) showErrorView(false) showContent(it.isNotEmpty()) @@ -178,12 +173,6 @@ class TimetablePresenter @Inject constructor( } } - private fun createTimetableItems(items: List): List { - return items - .filter { if (prefRepository.showWholeClassPlan == "no") it.isStudentPlan else true } - .map { TimetableItem(it, prefRepository.showWholeClassPlan) } - } - private fun reloadView() { Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}") view?.apply { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt index f730a2712..8399498c6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt @@ -12,7 +12,7 @@ interface TimetableView : BaseView { fun initView() - fun updateData(data: List) + fun updateData(data: List, showWholeClassPlanType: String) fun updateNavigationDay(date: String) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt deleted file mode 100644 index fd6dc8a66..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -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.CompletedLesson -import io.github.wulkanowy.utils.getThemeAttrColor -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_completed_lesson.* - -class CompletedLessonItem(val completedLesson: CompletedLesson) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_completed_lesson - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): CompletedLessonItem.ViewHolder { - return CompletedLessonItem.ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: CompletedLessonItem.ViewHolder?, position: Int, payloads: MutableList?) { - holder?.apply { - completedLessonItemNumber.text = completedLesson.number.toString() - completedLessonItemNumber.setTextColor(holder.contentView.context.getThemeAttrColor( - if (completedLesson.substitution.isNotEmpty()) R.attr.colorTimetableChange - else android.R.attr.textColorPrimary - )) - completedLessonItemSubject.text = completedLesson.subject - completedLessonItemTopic.text = completedLesson.topic - completedLessonItemAlert.visibility = if (completedLesson.substitution.isNotEmpty()) VISIBLE else GONE - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as CompletedLessonItem - - if (completedLesson != other.completedLesson) return false - - return true - } - - override fun hashCode(): Int { - return completedLesson.hashCode() - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsAdapter.kt new file mode 100644 index 000000000..3399a8a23 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsAdapter.kt @@ -0,0 +1,45 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +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.CompletedLesson +import io.github.wulkanowy.databinding.ItemCompletedLessonBinding +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +class CompletedLessonsAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + var onClickListener: (CompletedLesson) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemCompletedLessonBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + completedLessonItemNumber.text = item.number.toString() + completedLessonItemNumber.setTextColor(root.context.getThemeAttrColor( + if (item.substitution.isNotEmpty()) R.attr.colorTimetableChange + else android.R.attr.textColorPrimary + )) + completedLessonItemSubject.text = item.subject + completedLessonItemTopic.text = item.topic + completedLessonItemAlert.visibility = if (item.substitution.isNotEmpty()) View.VISIBLE else View.GONE + + root.setOnClickListener { onClickListener(item) } + } + } + + class ItemViewHolder(val binding: ItemCompletedLessonBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt index 60c16b2dc..544343a42 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt @@ -7,19 +7,17 @@ import android.view.View.GONE import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager import com.wdullaer.materialdatetimepicker.date.DatePickerDialog -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.data.db.entities.CompletedLesson 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.SchooldaysRangeLimiter import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.getCompatDrawable -import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_timetable_completed.* import org.threeten.bp.LocalDate import javax.inject.Inject @@ -30,7 +28,7 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView. lateinit var presenter: CompletedLessonsPresenter @Inject - lateinit var completedLessonsAdapter: FlexibleAdapter> + lateinit var completedLessonsAdapter: CompletedLessonsAdapter companion object { private const val SAVED_DATE_KEY = "CURRENT_DATE" @@ -40,7 +38,7 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView. override val titleStringId get() = R.string.completed_lessons_title - override val isViewEmpty get() = completedLessonsAdapter.isEmpty + override val isViewEmpty get() = completedLessonsAdapter.items.isEmpty() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_timetable_completed, container, false) @@ -53,11 +51,12 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView. } override fun initView() { - completedLessonsAdapter.setOnItemClickListener(presenter::onCompletedLessonsItemSelected) + completedLessonsAdapter.onClickListener = presenter::onCompletedLessonsItemSelected with(completedLessonsRecycler) { - layoutManager = SmoothScrollLinearLayoutManager(context) + layoutManager = LinearLayoutManager(context) adapter = completedLessonsAdapter + addItemDecoration(DividerItemDecoration(context)) } completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) @@ -71,12 +70,18 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView. completedLessonsNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } - override fun updateData(data: List) { - completedLessonsAdapter.updateDataSet(data, true) + override fun updateData(data: List) { + with(completedLessonsAdapter) { + items = data + notifyDataSetChanged() + } } override fun clearData() { - completedLessonsAdapter.clear() + with(completedLessonsAdapter) { + items = emptyList() + notifyDataSetChanged() + } } override fun updateNavigationDay(date: String) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt index 001fed97b..355411eb5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -1,7 +1,7 @@ package io.github.wulkanowy.ui.modules.timetable.completed import android.annotation.SuppressLint -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository @@ -18,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 import javax.inject.Inject class CompletedLessonsPresenter @Inject constructor( @@ -87,11 +86,9 @@ class CompletedLessonsPresenter @Inject constructor( view?.showErrorDetailsDialog(lastError) } - fun onCompletedLessonsItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is CompletedLessonItem) { - Timber.i("Select completed lessons item ${item.completedLesson.id}") - view?.showCompletedLessonDialog(item.completedLesson) - } + fun onCompletedLessonsItemSelected(completedLesson: CompletedLesson) { + Timber.i("Select completed lessons item ${completedLesson.id}") + view?.showCompletedLessonDialog(completedLesson) } private fun setBaseDateOnHolidays() { @@ -119,9 +116,7 @@ class CompletedLessonsPresenter @Inject constructor( completedLessonsRepository.getCompletedLessons(student, semester, currentDate, currentDate, forceRefresh) } } - .delay(200, TimeUnit.MILLISECONDS) - .map { items -> items.map { CompletedLessonItem(it) } } - .map { items -> items.sortedBy { it.completedLesson.number } } + .map { items -> items.sortedBy { it.number } } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt index a6a327e2d..170e19694 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt @@ -10,7 +10,7 @@ interface CompletedLessonsView : BaseView { fun initView() - fun updateData(data: List) + fun updateData(data: List) fun clearData() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt index 7636637f4..9208cd9e6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt @@ -8,14 +8,13 @@ import android.os.Bundle 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.BaseActivity +import io.github.wulkanowy.ui.base.WidgetConfigureAdapter import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER -import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.activity_widget_configure.* import javax.inject.Inject @@ -23,7 +22,7 @@ class TimetableWidgetConfigureActivity : BaseActivity> + lateinit var configureAdapter: WidgetConfigureAdapter @Inject override lateinit var presenter: TimetableWidgetConfigurePresenter @@ -43,10 +42,10 @@ class TimetableWidgetConfigureActivity : BaseActivity) { - configureAdapter.updateDataSet(data) + override fun updateData(data: List>) { + with(configureAdapter) { + items = data + notifyDataSetChanged() + } } override fun updateTimetableWidget(widgetId: Int) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt deleted file mode 100644 index a3338c684..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetablewidget - -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.utils.getThemeAttrColor -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_account.* - -class TimetableWidgetConfigureItem(val student: Student, private val isCurrent: Boolean) : - AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_account - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>) = ViewHolder(view, adapter) - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { - 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 - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt index e16851083..57dde824c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.timetablewidget -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 @@ -32,13 +31,11 @@ class TimetableWidgetConfigurePresenter @Inject constructor( loadData() } - fun onItemSelect(item: AbstractFlexibleItem<*>) { - if (item is TimetableWidgetConfigureItem) { - selectedStudent = item.student + fun onItemSelect(student: Student) { + selectedStudent = student - if (isFromProvider) registerStudent(selectedStudent) - else view?.showThemeDialog() - } + if (isFromProvider) registerStudent(selectedStudent) + else view?.showThemeDialog() } fun onThemeSelect(index: Int) { @@ -48,7 +45,7 @@ class TimetableWidgetConfigurePresenter @Inject constructor( registerStudent(selectedStudent) } - fun onDismissThemeView(){ + fun onDismissThemeView() { view?.finishView() } @@ -56,7 +53,7 @@ class TimetableWidgetConfigurePresenter @Inject constructor( disposable.add(studentRepository.getSavedStudents(false) .map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } } .map { (students, currentStudentId) -> - students.map { student -> TimetableWidgetConfigureItem(student, student.id == currentStudentId) } + students.map { student -> student to (student.id == currentStudentId) } } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) @@ -64,7 +61,7 @@ class TimetableWidgetConfigurePresenter @Inject constructor( when { it.isEmpty() -> view?.openLoginView() it.size == 1 && !isFromProvider -> { - selectedStudent = it.single().student + selectedStudent = it.single().first view?.showThemeDialog() } else -> view?.updateData(it) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt index 7cac892d9..056225ab5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt @@ -1,12 +1,13 @@ package io.github.wulkanowy.ui.modules.timetablewidget +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.ui.base.BaseView interface TimetableWidgetConfigureView : BaseView { fun initView() - fun updateData(data: List) + fun updateData(data: List>) fun updateTimetableWidget(widgetId: Int) diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/DividerItemDecoration.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/DividerItemDecoration.kt new file mode 100644 index 000000000..b0b6999eb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/DividerItemDecoration.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.ui.widgets + +import android.content.Context +import android.graphics.Canvas +import android.view.View +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.RecyclerView + +class DividerItemDecoration(context: Context) : DividerItemDecoration(context, VERTICAL) { + + override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) { + canvas.save() + val dividerLeft = parent.paddingLeft + val dividerRight = parent.width - parent.paddingRight + val childCount = parent.childCount + + for (i in 0..childCount - 2) { + val child: View = parent.getChildAt(i) + val params = child.layoutParams as RecyclerView.LayoutParams + val dividerTop: Int = child.bottom + params.bottomMargin + val dividerBottom = dividerTop + drawable!!.intrinsicHeight + drawable?.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom) + drawable?.draw(canvas) + } + canvas.restore() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt index 582b1c835..489e7e6fb 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -9,6 +9,8 @@ import androidx.annotation.ColorInt import androidx.annotation.ColorRes import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat +import androidx.core.graphics.ColorUtils +import io.github.wulkanowy.R @ColorInt fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int { @@ -20,6 +22,11 @@ fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int { } } +@ColorInt +fun Context.getThemeAttrColor(@AttrRes colorAttr: Int, alpha: Int): Int { + return ColorUtils.setAlphaComponent(getThemeAttrColor(colorAttr), alpha) +} + @ColorInt fun Context.getCompatColor(@ColorRes colorRes: Int) = ContextCompat.getColor(this, colorRes) diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt deleted file mode 100644 index bd6867a38..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.utils - -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem - -inline fun FlexibleAdapter<*>.setOnItemClickListener(crossinline listener: (item: AbstractFlexibleItem<*>) -> Unit) { - addListener(FlexibleAdapter.OnItemClickListener { _, position -> - listener(getItem(position) as AbstractFlexibleItem<*>) - true - }) -} \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_account.xml b/app/src/main/res/layout/dialog_account.xml index f2ee0bca7..6e975c80d 100644 --- a/app/src/main/res/layout/dialog_account.xml +++ b/app/src/main/res/layout/dialog_account.xml @@ -2,7 +2,7 @@ diff --git a/app/src/main/res/layout/header_exam.xml b/app/src/main/res/layout/header_exam.xml index 3a2e38c59..43f089f73 100644 --- a/app/src/main/res/layout/header_exam.xml +++ b/app/src/main/res/layout/header_exam.xml @@ -10,7 +10,7 @@ android:paddingTop="10dp" android:paddingRight="20dp" android:paddingBottom="10dp" - tools:context=".ui.modules.exam.ExamHeader"> + tools:context=".ui.modules.exam.ExamAdapter"> diff --git a/app/src/main/res/layout/header_grade_details.xml b/app/src/main/res/layout/header_grade_details.xml index 75e01745f..f2ba9a8c9 100644 --- a/app/src/main/res/layout/header_grade_details.xml +++ b/app/src/main/res/layout/header_grade_details.xml @@ -1,81 +1,93 @@ - + android:background="?android:windowBackground"> - + android:background="?selectableItemBackground" + tools:context=".ui.modules.grade.details.GradeDetailsAdapter"> - + - + - + - - + + + + + + + diff --git a/app/src/main/res/layout/header_homework.xml b/app/src/main/res/layout/header_homework.xml index 207fcdb40..e1c6608f4 100644 --- a/app/src/main/res/layout/header_homework.xml +++ b/app/src/main/res/layout/header_homework.xml @@ -10,7 +10,7 @@ android:paddingTop="10dp" android:paddingRight="20dp" android:paddingBottom="10dp" - tools:context=".ui.modules.homework.HomeworkHeader"> + tools:context=".ui.modules.homework.HomeworkAdapter"> diff --git a/app/src/main/res/layout/item_about.xml b/app/src/main/res/layout/item_about.xml index b5dc3758c..f988c47ba 100644 --- a/app/src/main/res/layout/item_about.xml +++ b/app/src/main/res/layout/item_about.xml @@ -4,17 +4,16 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="72dp" - android:background="?selectableItemBackground"> + android:background="?selectableItemBackground" + tools:context=".ui.modules.about.AboutAdapter"> @@ -24,9 +23,7 @@ android:layout_width="wrap_content" android:layout_height="32dp" android:layout_marginEnd="16dp" - android:layout_marginRight="16dp" android:layout_toEndOf="@id/aboutItemImage" - android:layout_toRightOf="@id/aboutItemImage" android:ellipsize="end" android:gravity="bottom" android:maxLines="1" @@ -39,13 +36,11 @@ android:layout_height="20dp" android:layout_below="@id/aboutItemTitle" android:layout_marginEnd="16dp" - android:layout_marginRight="16dp" android:layout_toEndOf="@id/aboutItemImage" - android:layout_toRightOf="@id/aboutItemImage" android:ellipsize="end" android:gravity="bottom" android:maxLines="1" android:textColor="?android:textColorSecondary" android:textSize="14sp" tools:text="@tools:sample/lorem" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/item_account.xml b/app/src/main/res/layout/item_account.xml index 309be8121..614e4d2df 100644 --- a/app/src/main/res/layout/item_account.xml +++ b/app/src/main/res/layout/item_account.xml @@ -1,5 +1,5 @@ - + tools:context=".ui.modules.account.AccountAdapter"> - + diff --git a/app/src/main/res/layout/item_attendance.xml b/app/src/main/res/layout/item_attendance.xml index 56bcab440..6917c6ce0 100644 --- a/app/src/main/res/layout/item_attendance.xml +++ b/app/src/main/res/layout/item_attendance.xml @@ -11,7 +11,7 @@ android:paddingEnd="12dp" android:paddingRight="12dp" android:paddingBottom="7dp" - tools:context=".ui.modules.attendance.AttendanceItem"> + tools:context=".ui.modules.attendance.AttendanceAdapter"> + tools:text="5" + tools:visibility="visible" /> + android:orientation="vertical" + tools:context=".ui.modules.attendance.summary.AttendanceSummaryAdapter"> @@ -46,7 +46,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" - android:layout_marginLeft="20dp" android:layout_weight="1" android:text="@string/attendance_present" android:textSize="14sp" /> @@ -57,9 +56,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="25dp" - android:layout_marginRight="25dp" android:gravity="end" android:textSize="12sp" tools:text="50" /> @@ -77,7 +74,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" - android:layout_marginLeft="20dp" android:layout_weight="1" android:text="@string/attendance_absence_unexcused" android:textSize="14sp" /> @@ -88,9 +84,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="25dp" - android:layout_marginRight="25dp" android:gravity="end" android:textSize="12sp" tools:text="0" /> @@ -108,7 +102,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" - android:layout_marginLeft="20dp" android:layout_weight="1" android:text="@string/attendance_absence_excused" android:textSize="14sp" /> @@ -119,9 +112,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="25dp" - android:layout_marginRight="25dp" android:gravity="end" android:textSize="12sp" tools:text="25" /> @@ -139,7 +130,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" - android:layout_marginLeft="20dp" android:layout_weight="1" android:text="@string/attendance_absence_school" android:textSize="14sp" /> @@ -150,9 +140,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="25dp" - android:layout_marginRight="25dp" android:gravity="end" android:textSize="12sp" tools:text="0" /> @@ -170,7 +158,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" - android:layout_marginLeft="20dp" android:layout_weight="1" android:text="@string/attendance_exemption" android:textSize="14sp" /> @@ -181,9 +168,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="25dp" - android:layout_marginRight="25dp" android:gravity="end" android:textSize="12sp" tools:text="6" /> @@ -201,7 +186,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" - android:layout_marginLeft="20dp" android:layout_weight="1" android:text="@string/attendance_unexcused_lateness" android:textSize="14sp" /> @@ -212,9 +196,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="25dp" - android:layout_marginRight="25dp" android:gravity="end" android:textSize="12sp" tools:text="0" /> @@ -232,7 +214,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" - android:layout_marginLeft="20dp" android:layout_weight="1" android:text="@string/attendance_excused_lateness" android:textSize="14sp" /> @@ -243,9 +224,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="25dp" - android:layout_marginRight="25dp" android:gravity="end" android:textSize="12sp" tools:text="0" /> diff --git a/app/src/main/res/layout/item_completed_lesson.xml b/app/src/main/res/layout/item_completed_lesson.xml index 8b49ba765..b9beec804 100644 --- a/app/src/main/res/layout/item_completed_lesson.xml +++ b/app/src/main/res/layout/item_completed_lesson.xml @@ -3,16 +3,12 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/ic_all_divider" - android:foreground="?selectableItemBackground" + android:background="?selectableItemBackground" android:paddingStart="8dp" - android:paddingLeft="8dp" android:paddingTop="6dp" android:paddingEnd="12dp" - android:paddingRight="12dp" android:paddingBottom="6dp" - tools:context=".ui.modules.timetable.completed.CompletedLessonItem" - tools:ignore="UnusedAttribute"> + tools:context=".ui.modules.timetable.completed.CompletedLessonsAdapter"> + android:minHeight="56dp" + android:orientation="vertical" + tools:context=".ui.modules.about.contributor.ContributorAdapter"> + android:layout_marginBottom="8dp" + android:contentDescription="@string/contributor_avatar_description" /> + tools:text="@tools:sample/lorem" /> diff --git a/app/src/main/res/layout/item_exam.xml b/app/src/main/res/layout/item_exam.xml index 459a7ecb3..bd1d47312 100644 --- a/app/src/main/res/layout/item_exam.xml +++ b/app/src/main/res/layout/item_exam.xml @@ -4,14 +4,13 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?selectableItemBackground" - tools:context=".ui.modules.exam.ExamItem"> + tools:context=".ui.modules.exam.ExamAdapter"> @@ -22,7 +21,6 @@ android:layout_height="wrap_content" android:layout_below="@id/examItemSubject" android:layout_alignStart="@id/examItemSubject" - android:layout_alignLeft="@id/examItemSubject" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:textSize="13sp" @@ -34,15 +32,11 @@ android:layout_height="wrap_content" android:layout_below="@id/examItemSubject" android:layout_alignParentEnd="true" - android:layout_alignParentRight="true" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginTop="5dp" android:layout_marginEnd="20dp" - android:layout_marginRight="20dp" android:layout_marginBottom="10dp" android:layout_toEndOf="@id/examItemType" - android:layout_toRightOf="@id/examItemType" android:gravity="end" android:textSize="13sp" tools:text="@tools:sample/lorem" /> diff --git a/app/src/main/res/layout/item_grade_details.xml b/app/src/main/res/layout/item_grade_details.xml index d6d99d9b8..ccc968c08 100644 --- a/app/src/main/res/layout/item_grade_details.xml +++ b/app/src/main/res/layout/item_grade_details.xml @@ -1,82 +1,82 @@ - + android:background="?android:windowBackground"> - - - + android:background="?selectableItemBackground" + android:paddingStart="12dp" + android:paddingTop="7dp" + android:paddingEnd="12dp" + android:paddingBottom="7dp" + tools:context=".ui.modules.grade.details.GradeDetailsAdapter"> - + - + - - + + + + + + + + diff --git a/app/src/main/res/layout/item_grade_summary.xml b/app/src/main/res/layout/item_grade_summary.xml index a3a49ab62..85a78571e 100644 --- a/app/src/main/res/layout/item_grade_summary.xml +++ b/app/src/main/res/layout/item_grade_summary.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - tools:context=".ui.modules.grade.summary.GradeSummaryItem"> + tools:context=".ui.modules.grade.summary.GradeSummaryAdapter"> @@ -48,7 +47,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" - android:layout_marginLeft="20dp" android:layout_weight="1" android:text="@string/grade_summary_points" android:textSize="14sp" /> @@ -59,9 +57,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="25dp" - android:layout_marginRight="25dp" android:gravity="end" android:textSize="12sp" tools:text="123/150" /> @@ -79,7 +75,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" - android:layout_marginLeft="20dp" android:layout_weight="1" android:text="@string/grade_summary_predicted_grade" android:textSize="14sp" /> @@ -90,9 +85,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="25dp" - android:layout_marginRight="25dp" android:gravity="end" android:textSize="12sp" tools:text="5" /> @@ -110,7 +103,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" - android:layout_marginLeft="20dp" android:layout_weight="1" android:text="@string/grade_summary_final_grade" android:textSize="14sp" /> @@ -121,9 +113,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="25dp" - android:layout_marginRight="25dp" android:gravity="end" android:textSize="12sp" tools:text="5" /> diff --git a/app/src/main/res/layout/item_homework.xml b/app/src/main/res/layout/item_homework.xml index 62566e9c2..89c3c6885 100644 --- a/app/src/main/res/layout/item_homework.xml +++ b/app/src/main/res/layout/item_homework.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?selectableItemBackground" - tools:context=".ui.modules.homework.HomeworkItem"> + tools:context=".ui.modules.homework.HomeworkAdapter"> + android:paddingRight="16dp" + tools:context=".ui.modules.about.license.LicenseAdapter"> + tools:context=".ui.modules.login.studentselect.LoginStudentSelectAdapter"> diff --git a/app/src/main/res/layout/item_message.xml b/app/src/main/res/layout/item_message.xml index 4379c2fc2..111de88c9 100644 --- a/app/src/main/res/layout/item_message.xml +++ b/app/src/main/res/layout/item_message.xml @@ -9,14 +9,13 @@ android:paddingTop="10dp" android:paddingRight="16dp" android:paddingBottom="10dp" - tools:context=".ui.modules.message.MessageItem"> + tools:context=".ui.modules.message.tab.MessageTabAdapter"> + tools:context=".ui.modules.mobiledevice.MobileDeviceAdapter"> + tools:context=".ui.modules.more.MoreAdapter"> diff --git a/app/src/main/res/layout/item_note.xml b/app/src/main/res/layout/item_note.xml index 3a56f9de7..660f32e00 100644 --- a/app/src/main/res/layout/item_note.xml +++ b/app/src/main/res/layout/item_note.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?selectableItemBackground" - tools:context=".ui.modules.note.NoteItem"> + tools:context=".ui.modules.note.NoteAdapter"> + tools:context=".ui.modules.schoolandteachers.teacher.TeacherAdapter"> + tools:context=".ui.modules.timetable.TimetableAdapter"> @@ -86,35 +80,30 @@ android:id="@+id/timetableItemTeacher" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignBottom="@+id/timetableItemNumber" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginEnd="16dp" - android:layout_marginRight="16dp" - android:layout_toEndOf="@id/timetableItemRoom" - android:layout_toRightOf="@id/timetableItemRoom" - android:maxLines="1" android:ellipsize="end" + android:maxLines="1" android:textColor="?android:textColorSecondary" android:textSize="13sp" + app:layout_constraintBottom_toBottomOf="@+id/timetableItemNumber" + app:layout_constraintStart_toEndOf="@id/timetableItemRoom" tools:text="Agata Kowalska - Błaszczyk" tools:visibility="gone" /> - + diff --git a/app/src/main/res/layout/item_timetable_small.xml b/app/src/main/res/layout/item_timetable_small.xml index 7a5ab033f..98a213ec3 100644 --- a/app/src/main/res/layout/item_timetable_small.xml +++ b/app/src/main/res/layout/item_timetable_small.xml @@ -3,14 +3,13 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?selectableItemBackground" - tools:context=".ui.modules.timetable.TimetableItem"> + tools:context=".ui.modules.timetable.TimetableAdapter"> @@ -37,9 +34,7 @@ android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_toEndOf="@+id/timetableSmallItemTimeStart" - android:layout_toRightOf="@+id/timetableSmallItemTimeStart" android:ellipsize="end" android:maxLines="1" android:textColor="?android:textColorPrimary" @@ -51,10 +46,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginTop="1dp" android:layout_toEndOf="@+id/timetableSmallItemSubject" - android:layout_toRightOf="@+id/timetableSmallItemSubject" android:maxLines="1" android:textColor="?android:textColorSecondary" android:textSize="13sp" @@ -65,10 +58,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginTop="1dp" android:layout_toEndOf="@id/timetableSmallItemRoom" - android:layout_toRightOf="@id/timetableSmallItemRoom" android:ellipsize="end" android:maxLines="1" android:textColor="?android:textColorSecondary" @@ -80,12 +71,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" - android:layout_marginLeft="10dp" android:layout_marginTop="1dp" android:layout_marginEnd="16dp" - android:layout_marginRight="16dp" android:layout_toEndOf="@id/timetableSmallItemTeacher" - android:layout_toRightOf="@id/timetableSmallItemTeacher" android:ellipsize="end" android:singleLine="true" android:textColor="?android:textColorSecondary" diff --git a/app/src/main/res/layout/scrollable_header_about.xml b/app/src/main/res/layout/scrollable_header_about.xml index 9b7bb7324..72186bf79 100644 --- a/app/src/main/res/layout/scrollable_header_about.xml +++ b/app/src/main/res/layout/scrollable_header_about.xml @@ -4,14 +4,14 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="104dp" - android:orientation="vertical"> + android:orientation="vertical" + tools:context=".ui.modules.about.AboutAdapter"> + android:orientation="vertical" + tools:context=".ui.modules.attendance.summary.AttendanceSummaryAdapter"> + android:orientation="horizontal" + tools:context=".ui.modules.grade.summary.GradeSummaryAdapter"> - \ No newline at end of file + diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt index 258dc54fe..121391dee 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt @@ -53,7 +53,7 @@ class LoginStudentSelectPresenterTest { fun onSelectedStudentTest() { doReturn(Single.just(listOf(1L))).`when`(studentRepository).saveStudents(listOf(testStudent)) doReturn(Completable.complete()).`when`(studentRepository).switchStudent(testStudent) - presenter.onItemSelected(LoginStudentSelectItem(testStudent, false)) + presenter.onItemSelected(testStudent, false) presenter.onSignIn() verify(loginStudentSelectView).showContent(false) verify(loginStudentSelectView).showProgress(true) @@ -64,7 +64,7 @@ class LoginStudentSelectPresenterTest { fun onSelectedStudentErrorTest() { doReturn(Single.error(testException)).`when`(studentRepository).saveStudents(listOf(testStudent)) doReturn(Completable.complete()).`when`(studentRepository).logoutStudent(testStudent) - presenter.onItemSelected(LoginStudentSelectItem(testStudent, false)) + presenter.onItemSelected(testStudent, false) presenter.onSignIn() verify(loginStudentSelectView).showContent(false) verify(loginStudentSelectView).showProgress(true)