forked from github/wulkanowy-mirror
Add school announcements (#1323)
This commit is contained in:
parent
075cfb20b1
commit
4e80441167
2248
app/schemas/io.github.wulkanowy.data.db.AppDatabase/38.json
Normal file
2248
app/schemas/io.github.wulkanowy.data.db.AppDatabase/38.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -185,4 +185,8 @@ internal class RepositoryModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideTimetableHeaderDao(database: AppDatabase) = database.timetableHeaderDao
|
fun provideTimetableHeaderDao(database: AppDatabase) = database.timetableHeaderDao
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideSchoolAnnouncementDao(database: AppDatabase) = database.schoolAnnouncementDao
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import io.github.wulkanowy.data.db.dao.AttendanceDao
|
|||||||
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
||||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||||
import io.github.wulkanowy.data.db.dao.ConferenceDao
|
import io.github.wulkanowy.data.db.dao.ConferenceDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
||||||
import io.github.wulkanowy.data.db.dao.ExamDao
|
import io.github.wulkanowy.data.db.dao.ExamDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeDao
|
import io.github.wulkanowy.data.db.dao.GradeDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
|
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
|
||||||
@ -37,6 +38,7 @@ import io.github.wulkanowy.data.db.entities.Attendance
|
|||||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||||
import io.github.wulkanowy.data.db.entities.Conference
|
import io.github.wulkanowy.data.db.entities.Conference
|
||||||
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
import io.github.wulkanowy.data.db.entities.Exam
|
||||||
import io.github.wulkanowy.data.db.entities.Grade
|
import io.github.wulkanowy.data.db.entities.Grade
|
||||||
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
|
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
|
||||||
@ -90,6 +92,7 @@ import io.github.wulkanowy.data.db.migrations.Migration34
|
|||||||
import io.github.wulkanowy.data.db.migrations.Migration35
|
import io.github.wulkanowy.data.db.migrations.Migration35
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration36
|
import io.github.wulkanowy.data.db.migrations.Migration36
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration37
|
import io.github.wulkanowy.data.db.migrations.Migration37
|
||||||
|
import io.github.wulkanowy.data.db.migrations.Migration38
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||||
@ -129,6 +132,7 @@ import javax.inject.Singleton
|
|||||||
TimetableAdditional::class,
|
TimetableAdditional::class,
|
||||||
StudentInfo::class,
|
StudentInfo::class,
|
||||||
TimetableHeader::class,
|
TimetableHeader::class,
|
||||||
|
SchoolAnnouncement::class,
|
||||||
],
|
],
|
||||||
version = AppDatabase.VERSION_SCHEMA,
|
version = AppDatabase.VERSION_SCHEMA,
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
@ -137,7 +141,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 37
|
const val VERSION_SCHEMA = 38
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||||
Migration2(),
|
Migration2(),
|
||||||
@ -176,6 +180,7 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
Migration35(appInfo),
|
Migration35(appInfo),
|
||||||
Migration36(),
|
Migration36(),
|
||||||
Migration37(),
|
Migration37(),
|
||||||
|
Migration38(),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
@ -243,4 +248,6 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
abstract val studentInfoDao: StudentInfoDao
|
abstract val studentInfoDao: StudentInfoDao
|
||||||
|
|
||||||
abstract val timetableHeaderDao: TimetableHeaderDao
|
abstract val timetableHeaderDao: TimetableHeaderDao
|
||||||
|
|
||||||
|
abstract val schoolAnnouncementDao: SchoolAnnouncementDao
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
@Singleton
|
||||||
|
interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> {
|
||||||
|
|
||||||
|
@Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId")
|
||||||
|
fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import java.io.Serializable
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
@Entity(tableName = "SchoolAnnouncements")
|
||||||
|
data class SchoolAnnouncement(
|
||||||
|
|
||||||
|
@ColumnInfo(name = "student_id")
|
||||||
|
val studentId: Int,
|
||||||
|
|
||||||
|
val date: LocalDate,
|
||||||
|
|
||||||
|
val subject: String,
|
||||||
|
|
||||||
|
val content: String
|
||||||
|
) : Serializable {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
@ -9,4 +9,4 @@ class Migration36 : Migration(35, 36) {
|
|||||||
database.execSQL("ALTER TABLE Exams ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
|
database.execSQL("ALTER TABLE Exams ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
|
||||||
database.execSQL("ALTER TABLE Homework ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
|
database.execSQL("ALTER TABLE Homework ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration38 : Migration(37, 38) {
|
||||||
|
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("""
|
||||||
|
CREATE TABLE IF NOT EXISTS `SchoolAnnouncements` (
|
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`student_id` INTEGER NOT NULL,
|
||||||
|
`date` INTEGER NOT NULL,
|
||||||
|
`subject` TEXT NOT NULL,
|
||||||
|
`content` TEXT NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package io.github.wulkanowy.data.mappers
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformation
|
||||||
|
|
||||||
|
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||||
|
SchoolAnnouncement(
|
||||||
|
studentId = student.studentId,
|
||||||
|
date = it.date,
|
||||||
|
subject = it.subject,
|
||||||
|
content = it.content,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.networkBoundResource
|
||||||
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class SchoolAnnouncementRepository @Inject constructor(
|
||||||
|
private val schoolAnnouncementDb: SchoolAnnouncementDao,
|
||||||
|
private val sdk: Sdk,
|
||||||
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val saveFetchResultMutex = Mutex()
|
||||||
|
|
||||||
|
private val cacheKey = "school_announcement"
|
||||||
|
|
||||||
|
fun getSchoolAnnouncements(student: Student, forceRefresh: Boolean) = networkBoundResource(
|
||||||
|
mutex = saveFetchResultMutex,
|
||||||
|
shouldFetch = {
|
||||||
|
it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(
|
||||||
|
getRefreshKey(cacheKey, student)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
query = { schoolAnnouncementDb.loadAll(student.studentId) },
|
||||||
|
fetch = { sdk.init(student).getDirectorInformation().mapToEntities(student) },
|
||||||
|
saveFetchResult = { old, new ->
|
||||||
|
schoolAnnouncementDb.deleteAll(old uniqueSubtract new)
|
||||||
|
schoolAnnouncementDb.insertAll(new uniqueSubtract old)
|
||||||
|
|
||||||
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
@ -33,6 +33,7 @@ import io.github.wulkanowy.services.sync.works.LuckyNumberWork
|
|||||||
import io.github.wulkanowy.services.sync.works.MessageWork
|
import io.github.wulkanowy.services.sync.works.MessageWork
|
||||||
import io.github.wulkanowy.services.sync.works.NoteWork
|
import io.github.wulkanowy.services.sync.works.NoteWork
|
||||||
import io.github.wulkanowy.services.sync.works.RecipientWork
|
import io.github.wulkanowy.services.sync.works.RecipientWork
|
||||||
|
import io.github.wulkanowy.services.sync.works.SchoolAnnouncementWork
|
||||||
import io.github.wulkanowy.services.sync.works.TeacherWork
|
import io.github.wulkanowy.services.sync.works.TeacherWork
|
||||||
import io.github.wulkanowy.services.sync.works.TimetableWork
|
import io.github.wulkanowy.services.sync.works.TimetableWork
|
||||||
import io.github.wulkanowy.services.sync.works.Work
|
import io.github.wulkanowy.services.sync.works.Work
|
||||||
@ -46,15 +47,18 @@ abstract class ServicesModule {
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun provideWorkManager(@ApplicationContext context: Context) = WorkManager.getInstance(context)
|
fun provideWorkManager(@ApplicationContext context: Context) =
|
||||||
|
WorkManager.getInstance(context)
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideNotificationManager(@ApplicationContext context: Context) = NotificationManagerCompat.from(context)
|
fun provideNotificationManager(@ApplicationContext context: Context) =
|
||||||
|
NotificationManagerCompat.from(context)
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideAlarmManager(@ApplicationContext context: Context): AlarmManager = context.getSystemService()!!
|
fun provideAlarmManager(@ApplicationContext context: Context): AlarmManager =
|
||||||
|
context.getSystemService()!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@ -109,6 +113,10 @@ abstract class ServicesModule {
|
|||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun provideGradeStatistics(work: GradeStatisticsWork): Work
|
abstract fun provideGradeStatistics(work: GradeStatisticsWork): Work
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
abstract fun provideSchoolAnnouncementWork(work: SchoolAnnouncementWork): Work
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun provideDebugChannel(channel: DebugChannel): Channel
|
abstract fun provideDebugChannel(channel: DebugChannel): Channel
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package io.github.wulkanowy.services.sync.works
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
|
||||||
|
import io.github.wulkanowy.utils.waitForResult
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SchoolAnnouncementWork @Inject constructor(
|
||||||
|
private val schoolAnnouncementRepository: SchoolAnnouncementRepository,
|
||||||
|
) : Work {
|
||||||
|
|
||||||
|
override suspend fun doWork(student: Student, semester: Semester) {
|
||||||
|
schoolAnnouncementRepository.getSchoolAnnouncements(student, true).waitForResult()
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import io.github.wulkanowy.R
|
|||||||
import io.github.wulkanowy.databinding.FragmentMoreBinding
|
import io.github.wulkanowy.databinding.FragmentMoreBinding
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
|
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
|
||||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||||
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
@ -56,6 +57,9 @@ class MoreFragment : BaseFragment<FragmentMoreBinding>(R.layout.fragment_more),
|
|||||||
override val conferencesRes: Pair<String, Drawable?>?
|
override val conferencesRes: Pair<String, Drawable?>?
|
||||||
get() = context?.run { getString(R.string.conferences_title) to getCompatDrawable(R.drawable.ic_more_conferences) }
|
get() = context?.run { getString(R.string.conferences_title) to getCompatDrawable(R.drawable.ic_more_conferences) }
|
||||||
|
|
||||||
|
override val schoolAnnouncementRes: Pair<String, Drawable?>?
|
||||||
|
get() = context?.run { getString(R.string.school_announcement_title) to getCompatDrawable(R.drawable.ic_all_about) }
|
||||||
|
|
||||||
override val schoolAndTeachersRes: Pair<String, Drawable?>?
|
override val schoolAndTeachersRes: Pair<String, Drawable?>?
|
||||||
get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) }
|
get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) }
|
||||||
|
|
||||||
@ -108,6 +112,10 @@ class MoreFragment : BaseFragment<FragmentMoreBinding>(R.layout.fragment_more),
|
|||||||
(activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance())
|
(activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openSchoolAnnouncementView() {
|
||||||
|
(activity as? MainActivity)?.pushView(SchoolAnnouncementFragment.newInstance())
|
||||||
|
}
|
||||||
|
|
||||||
override fun openConferencesView() {
|
override fun openConferencesView() {
|
||||||
(activity as? MainActivity)?.pushView(ConferenceFragment.newInstance())
|
(activity as? MainActivity)?.pushView(ConferenceFragment.newInstance())
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ class MorePresenter @Inject constructor(
|
|||||||
luckyNumberRes?.first -> openLuckyNumberView()
|
luckyNumberRes?.first -> openLuckyNumberView()
|
||||||
mobileDevicesRes?.first -> openMobileDevicesView()
|
mobileDevicesRes?.first -> openMobileDevicesView()
|
||||||
conferencesRes?.first -> openConferencesView()
|
conferencesRes?.first -> openConferencesView()
|
||||||
|
schoolAnnouncementRes?.first -> openSchoolAnnouncementView()
|
||||||
schoolAndTeachersRes?.first -> openSchoolAndTeachersView()
|
schoolAndTeachersRes?.first -> openSchoolAndTeachersView()
|
||||||
settingsRes?.first -> openSettingsView()
|
settingsRes?.first -> openSettingsView()
|
||||||
}
|
}
|
||||||
@ -49,6 +50,7 @@ class MorePresenter @Inject constructor(
|
|||||||
luckyNumberRes,
|
luckyNumberRes,
|
||||||
mobileDevicesRes,
|
mobileDevicesRes,
|
||||||
conferencesRes,
|
conferencesRes,
|
||||||
|
schoolAnnouncementRes,
|
||||||
schoolAndTeachersRes,
|
schoolAndTeachersRes,
|
||||||
settingsRes
|
settingsRes
|
||||||
))
|
))
|
||||||
|
@ -17,6 +17,8 @@ interface MoreView : BaseView {
|
|||||||
|
|
||||||
val conferencesRes: Pair<String, Drawable?>?
|
val conferencesRes: Pair<String, Drawable?>?
|
||||||
|
|
||||||
|
val schoolAnnouncementRes: Pair<String, Drawable?>?
|
||||||
|
|
||||||
val schoolAndTeachersRes: Pair<String, Drawable?>?
|
val schoolAndTeachersRes: Pair<String, Drawable?>?
|
||||||
|
|
||||||
val settingsRes: Pair<String, Drawable?>?
|
val settingsRes: Pair<String, Drawable?>?
|
||||||
@ -39,6 +41,8 @@ interface MoreView : BaseView {
|
|||||||
|
|
||||||
fun openMobileDevicesView()
|
fun openMobileDevicesView()
|
||||||
|
|
||||||
|
fun openSchoolAnnouncementView()
|
||||||
|
|
||||||
fun openConferencesView()
|
fun openConferencesView()
|
||||||
|
|
||||||
fun openSchoolAndTeachersView()
|
fun openSchoolAndTeachersView()
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.schoolannouncement
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
|
import io.github.wulkanowy.databinding.ItemSchoolAnnouncementBinding
|
||||||
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SchoolAnnouncementAdapter @Inject constructor() :
|
||||||
|
RecyclerView.Adapter<SchoolAnnouncementAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
var items = emptyList<SchoolAnnouncement>()
|
||||||
|
|
||||||
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
|
||||||
|
ItemSchoolAnnouncementBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val item = items[position]
|
||||||
|
|
||||||
|
with(holder.binding) {
|
||||||
|
schoolAnnouncementItemDate.text = item.date.toFormattedString()
|
||||||
|
schoolAnnouncementItemType.text = item.subject
|
||||||
|
schoolAnnouncementItemContent.text = HtmlCompat.fromHtml(
|
||||||
|
item.content, HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(val binding: ItemSchoolAnnouncementBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.schoolannouncement
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
|
import io.github.wulkanowy.databinding.FragmentSchoolAnnouncementBinding
|
||||||
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class SchoolAnnouncementFragment :
|
||||||
|
BaseFragment<FragmentSchoolAnnouncementBinding>(R.layout.fragment_school_announcement),
|
||||||
|
SchoolAnnouncementView, MainView.TitledView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: SchoolAnnouncementPresenter
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var schoolAnnouncementAdapter: SchoolAnnouncementAdapter
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance() = SchoolAnnouncementFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val titleStringId: Int
|
||||||
|
get() = R.string.school_announcement_title
|
||||||
|
|
||||||
|
override val isViewEmpty: Boolean
|
||||||
|
get() = schoolAnnouncementAdapter.items.isEmpty()
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
binding = FragmentSchoolAnnouncementBinding.bind(view)
|
||||||
|
presenter.onAttachView(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
with(binding.directorInformationRecycler) {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = schoolAnnouncementAdapter
|
||||||
|
addItemDecoration(DividerItemDecoration(context))
|
||||||
|
}
|
||||||
|
with(binding) {
|
||||||
|
directorInformationSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
directorInformationSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
directorInformationSwipe.setProgressBackgroundColorSchemeColor(
|
||||||
|
requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)
|
||||||
|
)
|
||||||
|
directorInformationErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
|
directorInformationErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateData(data: List<SchoolAnnouncement>) {
|
||||||
|
with(schoolAnnouncementAdapter) {
|
||||||
|
items = data
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearData() {
|
||||||
|
with(schoolAnnouncementAdapter) {
|
||||||
|
items = listOf()
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showEmpty(show: Boolean) {
|
||||||
|
binding.directorInformationEmpty.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showErrorView(show: Boolean) {
|
||||||
|
binding.directorInformationError.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setErrorDetails(message: String) {
|
||||||
|
binding.directorInformationErrorMessage.text = message
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showProgress(show: Boolean) {
|
||||||
|
binding.directorInformationProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun enableSwipe(enable: Boolean) {
|
||||||
|
binding.directorInformationSwipe.isEnabled = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showContent(show: Boolean) {
|
||||||
|
binding.directorInformationRecycler.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showRefresh(show: Boolean) {
|
||||||
|
binding.directorInformationSwipe.isRefreshing = show
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
presenter.onDetachView()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.schoolannouncement
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.Status
|
||||||
|
import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
|
||||||
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
|
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||||
|
import io.github.wulkanowy.utils.afterLoading
|
||||||
|
import io.github.wulkanowy.utils.flowWithResourceIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SchoolAnnouncementPresenter @Inject constructor(
|
||||||
|
errorHandler: ErrorHandler,
|
||||||
|
studentRepository: StudentRepository,
|
||||||
|
private val analytics: AnalyticsHelper,
|
||||||
|
private val schoolAnnouncementRepository: SchoolAnnouncementRepository,
|
||||||
|
) : BasePresenter<SchoolAnnouncementView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
|
override fun onAttachView(view: SchoolAnnouncementView) {
|
||||||
|
super.onAttachView(view)
|
||||||
|
view.initView()
|
||||||
|
Timber.i("School announcement view was initialized")
|
||||||
|
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSwipeRefresh() {
|
||||||
|
Timber.i("Force refreshing the School announcement")
|
||||||
|
loadData(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRetry() {
|
||||||
|
view?.run {
|
||||||
|
showErrorView(false)
|
||||||
|
showProgress(true)
|
||||||
|
}
|
||||||
|
loadData(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDetailsClick() {
|
||||||
|
view?.showErrorDetailsDialog(lastError)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadData(forceRefresh: Boolean = false) {
|
||||||
|
Timber.i("Loading School announcement data started")
|
||||||
|
|
||||||
|
flowWithResourceIn {
|
||||||
|
val student = studentRepository.getCurrentStudent()
|
||||||
|
schoolAnnouncementRepository.getSchoolAnnouncements(student, forceRefresh)
|
||||||
|
}.onEach {
|
||||||
|
when (it.status) {
|
||||||
|
Status.LOADING -> {
|
||||||
|
if (!it.data.isNullOrEmpty()) {
|
||||||
|
view?.run {
|
||||||
|
enableSwipe(true)
|
||||||
|
showRefresh(true)
|
||||||
|
showProgress(false)
|
||||||
|
showContent(true)
|
||||||
|
updateData(it.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Status.SUCCESS -> {
|
||||||
|
Timber.i("Loading School announcement result: Success")
|
||||||
|
view?.apply {
|
||||||
|
updateData(it.data!!.sortedByDescending { item -> item.date })
|
||||||
|
showEmpty(it.data.isEmpty())
|
||||||
|
showErrorView(false)
|
||||||
|
showContent(it.data.isNotEmpty())
|
||||||
|
}
|
||||||
|
analytics.logEvent(
|
||||||
|
"load_school_announcement",
|
||||||
|
"items" to it.data!!.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Status.ERROR -> {
|
||||||
|
Timber.i("Loading School announcement result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it.error!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.afterLoading {
|
||||||
|
view?.run {
|
||||||
|
showRefresh(false)
|
||||||
|
showProgress(false)
|
||||||
|
enableSwipe(true)
|
||||||
|
}
|
||||||
|
}.launch()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||||
|
view?.run {
|
||||||
|
if (isViewEmpty) {
|
||||||
|
lastError = error
|
||||||
|
setErrorDetails(message)
|
||||||
|
showErrorView(true)
|
||||||
|
showEmpty(false)
|
||||||
|
} else showError(message, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.schoolannouncement
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
|
interface SchoolAnnouncementView : BaseView {
|
||||||
|
|
||||||
|
val isViewEmpty: Boolean
|
||||||
|
|
||||||
|
fun initView()
|
||||||
|
|
||||||
|
fun updateData(data: List<SchoolAnnouncement>)
|
||||||
|
|
||||||
|
fun clearData()
|
||||||
|
|
||||||
|
fun showEmpty(show: Boolean)
|
||||||
|
|
||||||
|
fun showErrorView(show: Boolean)
|
||||||
|
|
||||||
|
fun setErrorDetails(message: String)
|
||||||
|
|
||||||
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
|
fun enableSwipe(enable: Boolean)
|
||||||
|
|
||||||
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
|
fun showRefresh(show: Boolean)
|
||||||
|
}
|
106
app/src/main/res/layout/fragment_school_announcement.xml
Normal file
106
app/src/main/res/layout/fragment_school_announcement.xml
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.modules.schoolannouncement.SchoolAnnouncementFragment">
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||||
|
android:id="@+id/directorInformationProgress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminate="true"
|
||||||
|
tools:visibility="invisible" />
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/directorInformationSwipe"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/directorInformationRecycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:listitem="@layout/item_school_announcement" />
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/directorInformationEmpty"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:ignore="UseCompoundDrawables">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/school_announcement_no_items"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="100dp"
|
||||||
|
android:minHeight="100dp"
|
||||||
|
app:srcCompat="@drawable/ic_all_about"
|
||||||
|
app:tint="?android:attr/textColorPrimary"
|
||||||
|
tools:ignore="contentDescription" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/directorInformationError"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="invisible"
|
||||||
|
tools:ignore="UseCompoundDrawables"
|
||||||
|
tools:visibility="invisible">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
app:srcCompat="@drawable/ic_error"
|
||||||
|
app:tint="?colorOnBackground"
|
||||||
|
tools:ignore="contentDescription" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/directorInformationErrorMessage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:text="@string/error_unknown"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/directorInformationErrorDetails"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:text="@string/all_details" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/directorInformationErrorRetry"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/all_retry" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
54
app/src/main/res/layout/item_school_announcement.xml
Normal file
54
app/src/main/res/layout/item_school_announcement.xml
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/note_subitem_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
tools:context=".ui.modules.schoolannouncement.SchoolAnnouncementAdapter">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/schoolAnnouncementItemDate"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="15dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="@tools:sample/date/ddmmyy" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/schoolAnnouncementItemType"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginBottom="5dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/schoolAnnouncementItemDate"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/schoolAnnouncementItemDate"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/schoolAnnouncementItemDate"
|
||||||
|
app:layout_goneMarginEnd="0dp"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/schoolAnnouncementItemContent"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginBottom="15dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/schoolAnnouncementItemType"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/schoolAnnouncementItemDate"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/schoolAnnouncementItemType"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -355,6 +355,11 @@
|
|||||||
<string name="conference_no_items">No info about conferences</string>
|
<string name="conference_no_items">No info about conferences</string>
|
||||||
|
|
||||||
|
|
||||||
|
<!--Director information-->
|
||||||
|
<string name="school_announcement_title">School announcements</string>
|
||||||
|
<string name="school_announcement_no_items">No school announcements</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Account-->
|
<!--Account-->
|
||||||
<string name="account_add_new">Add account</string>
|
<string name="account_add_new">Add account</string>
|
||||||
<string name="account_logout">Logout</string>
|
<string name="account_logout">Logout</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user