1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 05:39:10 -05:00

Add notifications debug screen (#1370)

This commit is contained in:
Mikołaj Pich 2021-06-21 10:29:04 +02:00 committed by GitHub
parent 64feae9f1c
commit 27e1a07eec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 1330 additions and 557 deletions

View File

@ -0,0 +1,35 @@
package io.github.wulkanowy.data.pojos
import androidx.annotation.DrawableRes
import androidx.annotation.PluralsRes
import androidx.annotation.StringRes
import io.github.wulkanowy.ui.modules.main.MainView
sealed interface Notification {
val channelId: String
val startMenu: MainView.Section
val icon: Int
val titleStringRes: Int
val contentStringRes: Int
}
data class MultipleNotifications(
override val channelId: String,
override val startMenu: MainView.Section,
@DrawableRes override val icon: Int,
@PluralsRes override val titleStringRes: Int,
@PluralsRes override val contentStringRes: Int,
@PluralsRes val summaryStringRes: Int,
val lines: List<String>,
) : Notification
data class OneNotification(
override val channelId: String,
override val startMenu: MainView.Section,
@DrawableRes override val icon: Int,
@StringRes override val titleStringRes: Int,
@StringRes override val contentStringRes: Int,
val contentValues: List<String>,
) : Notification

View File

@ -12,7 +12,6 @@ import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@ -60,13 +59,9 @@ class ConferenceRepository @Inject constructor(
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)
fun getNotNotifiedConference(semester: Semester): Flow<List<Conference>> {
return conferenceDb.loadAll(
diaryId = semester.diaryId,
studentId = semester.studentId
).map {
it.filter { conference -> !conference.isNotified }
}
fun getConferenceFromDatabase(semester: Semester): Flow<List<Conference>> {
return conferenceDb.loadAll(semester.diaryId, semester.studentId)
}
suspend fun updateConference(conference: List<Conference>) = conferenceDb.updateAll(conference)

View File

@ -14,7 +14,6 @@ import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.startExamsDay
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
@ -69,15 +68,13 @@ class ExamRepository @Inject constructor(
filterResult = { it.filter { item -> item.date in start..end } }
)
fun getNotNotifiedExam(semester: Semester, start: LocalDate): Flow<List<Exam>> {
fun getExamsFromDatabase(semester: Semester, start: LocalDate): Flow<List<Exam>> {
return examDb.loadAll(
diaryId = semester.diaryId,
studentId = semester.studentId,
from = start.startExamsDay,
end = start.endExamsDay
).map {
it.filter { exam -> !exam.isNotified }
}
)
}
suspend fun updateExam(exam: List<Exam>) = examDb.updateAll(exam)

View File

@ -104,22 +104,16 @@ class GradeRepository @Inject constructor(
}
}
fun getNotNotifiedGrades(semester: Semester): Flow<List<Grade>> {
return gradeDb.loadAll(semester.semesterId, semester.studentId).map {
it.filter { grade -> !grade.isNotified }
}
fun getGradesFromDatabase(semester: Semester): Flow<List<Grade>> {
return gradeDb.loadAll(semester.semesterId, semester.studentId)
}
fun getNotNotifiedPredictedGrades(semester: Semester): Flow<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map {
it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified }
}
fun getGradesPredictedFromDatabase(semester: Semester): Flow<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
}
fun getNotNotifiedFinalGrades(semester: Semester): Flow<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map {
it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified }
}
fun getGradesFinalFromDatabase(semester: Semester): Flow<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
}
suspend fun updateGrade(grade: Grade) {

View File

@ -13,7 +13,6 @@ import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
@ -31,12 +30,9 @@ class HomeworkRepository @Inject constructor(
private val cacheKey = "homework"
fun getHomework(
student: Student,
semester: Semester,
start: LocalDate,
end: LocalDate,
forceRefresh: Boolean,
notify: Boolean = false
student: Student, semester: Semester,
start: LocalDate, end: LocalDate,
forceRefresh: Boolean, notify: Boolean = false
) = networkBoundResource(
mutex = saveFetchResultMutex,
shouldFetch = {
@ -74,14 +70,8 @@ class HomeworkRepository @Inject constructor(
}))
}
fun getNotNotifiedHomework(
semester: Semester,
start: LocalDate,
end: LocalDate
) = homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday)
.map {
it.filter { homework -> !homework.isNotified }
}
fun getHomeworkFromDatabase(semester: Semester, start: LocalDate, end: LocalDate) =
homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday)
suspend fun updateHomework(homework: List<Homework>) = homeworkDb.updateAll(homework)
}
}

View File

@ -77,8 +77,8 @@ class MessageRepository @Inject constructor(
}
)
fun getNotNotifiedMessages(student: Student): Flow<List<Message>> {
return messagesDb.loadAll(student.id.toInt(), RECEIVED.id).map { it.filter { message -> !message.isNotified && message.unread } }
fun getMessagesFromDatabase(student: Student): Flow<List<Message>> {
return messagesDb.loadAll(student.id.toInt(), RECEIVED.id)
}
suspend fun updateMessages(messages: List<Message>) {

View File

@ -50,8 +50,8 @@ class NoteRepository @Inject constructor(
}
)
fun getNotNotifiedNotes(student: Student): Flow<List<Note>> {
return noteDb.loadAll(student.studentId).map { it.filter { note -> !note.isNotified } }
fun getNotesFromDatabase(student: Student): Flow<List<Note>> {
return noteDb.loadAll(student.studentId)
}
suspend fun updateNote(note: Note) {

View File

@ -57,12 +57,8 @@ class SchoolAnnouncementRepository @Inject constructor(
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
)
fun getNotNotifiedSchoolAnnouncement(semester: Semester): Flow<List<SchoolAnnouncement>> {
return schoolAnnouncementDb.loadAll(
studentId = semester.studentId
).map {
it.filter { schoolAnnouncement -> !schoolAnnouncement.isNotified }
}
fun getSchoolAnnouncementFromDatabase(student: Student): Flow<List<SchoolAnnouncement>> {
return schoolAnnouncementDb.loadAll(student.studentId)
}
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) = schoolAnnouncementDb.updateAll(schoolAnnouncement)

View File

@ -0,0 +1,70 @@
package io.github.wulkanowy.services.sync.notifications
import android.app.PendingIntent
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import io.github.wulkanowy.R
import io.github.wulkanowy.data.pojos.MultipleNotifications
import io.github.wulkanowy.data.pojos.Notification
import io.github.wulkanowy.data.pojos.OneNotification
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import kotlin.random.Random
abstract class BaseNotification(
private val context: Context,
private val notificationManager: NotificationManagerCompat,
) {
protected fun sendNotification(notification: Notification) {
notificationManager.notify(
Random.nextInt(Int.MAX_VALUE),
NotificationCompat.Builder(context, notification.channelId)
.setLargeIcon(context.getCompatBitmap(notification.icon, R.color.colorPrimary))
.setSmallIcon(R.drawable.ic_stat_all)
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(
context, notification.startMenu.id,
MainActivity.getStartIntent(context, notification.startMenu, true),
PendingIntent.FLAG_UPDATE_CURRENT
)
)
.apply {
when (notification) {
is OneNotification -> buildForOneNotification(notification)
is MultipleNotifications -> buildForMultipleNotification(notification)
}
}
.build()
)
}
private fun NotificationCompat.Builder.buildForOneNotification(n: OneNotification) {
val content = context.getString(n.contentStringRes, *n.contentValues.toTypedArray())
setContentTitle(context.getString(n.titleStringRes))
setContentText(content)
setStyle(NotificationCompat.BigTextStyle().run {
bigText(content)
this
})
}
private fun NotificationCompat.Builder.buildForMultipleNotification(n: MultipleNotifications) {
val lines = n.lines.size
setContentTitle(context.resources.getQuantityString(n.titleStringRes, lines, lines))
setContentText(context.resources.getQuantityString(n.contentStringRes, lines, lines))
setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(
context.resources.getQuantityString(n.summaryStringRes, n.lines.size, n.lines.size)
)
n.lines.forEach(::addLine)
this
})
}
}

View File

@ -0,0 +1,33 @@
package io.github.wulkanowy.services.sync.notifications
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.data.pojos.MultipleNotifications
import io.github.wulkanowy.services.sync.channels.NewConferencesChannel
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
class NewConferenceNotification @Inject constructor(
@ApplicationContext private val context: Context,
notificationManager: NotificationManagerCompat,
) : BaseNotification(context, notificationManager) {
fun notify(items: List<Conference>) {
val notification = MultipleNotifications(
channelId = NewConferencesChannel.CHANNEL_ID,
icon = R.drawable.ic_more_conferences,
titleStringRes = R.plurals.conference_notify_new_item_title,
contentStringRes = R.plurals.conference_notify_new_items,
summaryStringRes = R.plurals.conference_number_item,
startMenu = MainView.Section.CONFERENCE,
lines = items.map {
"${it.title}: ${it.subject}"
}
)
sendNotification(notification)
}
}

View File

@ -0,0 +1,33 @@
package io.github.wulkanowy.services.sync.notifications
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.pojos.MultipleNotifications
import io.github.wulkanowy.services.sync.channels.NewExamChannel
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
class NewExamNotification @Inject constructor(
@ApplicationContext private val context: Context,
notificationManager: NotificationManagerCompat,
) : BaseNotification(context, notificationManager) {
fun notify(items: List<Exam>) {
val notification = MultipleNotifications(
channelId = NewExamChannel.CHANNEL_ID,
icon = R.drawable.ic_main_exam,
titleStringRes = R.plurals.exam_notify_new_item_title,
contentStringRes = R.plurals.grade_notify_new_items, // TODO add missing string
summaryStringRes = R.plurals.exam_number_item,
startMenu = MainView.Section.EXAM,
lines = items.map {
"${it.subject}: ${it.description}"
}
)
sendNotification(notification)
}
}

View File

@ -0,0 +1,66 @@
package io.github.wulkanowy.services.sync.notifications
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.pojos.MultipleNotifications
import io.github.wulkanowy.services.sync.channels.NewGradesChannel
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
class NewGradeNotification @Inject constructor(
@ApplicationContext private val context: Context,
notificationManager: NotificationManagerCompat,
) : BaseNotification(context, notificationManager) {
fun notifyDetails(items: List<Grade>) {
val notification = MultipleNotifications(
channelId = NewGradesChannel.CHANNEL_ID,
icon = R.drawable.ic_stat_grade,
titleStringRes = R.plurals.grade_new_items,
contentStringRes = R.plurals.grade_notify_new_items,
summaryStringRes = R.plurals.grade_number_item,
startMenu = MainView.Section.GRADE,
lines = items.map {
"${it.subject}: ${it.entry}"
}
)
sendNotification(notification)
}
fun notifyPredicted(items: List<GradeSummary>) {
val notification = MultipleNotifications(
channelId = NewGradesChannel.CHANNEL_ID,
icon = R.drawable.ic_stat_grade,
titleStringRes = R.plurals.grade_new_items_predicted,
contentStringRes = R.plurals.grade_notify_new_items_predicted,
summaryStringRes = R.plurals.grade_number_item,
startMenu = MainView.Section.GRADE,
lines = items.map {
"${it.subject}: ${it.predictedGrade}"
}
)
sendNotification(notification)
}
fun notifyFinal(items: List<GradeSummary>) {
val notification = MultipleNotifications(
channelId = NewGradesChannel.CHANNEL_ID,
icon = R.drawable.ic_stat_grade,
titleStringRes = R.plurals.grade_new_items_final,
contentStringRes = R.plurals.grade_notify_new_items_final,
summaryStringRes = R.plurals.grade_number_item,
startMenu = MainView.Section.GRADE,
lines = items.map {
"${it.subject}: ${it.finalGrade}"
}
)
sendNotification(notification)
}
}

View File

@ -0,0 +1,33 @@
package io.github.wulkanowy.services.sync.notifications
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.pojos.MultipleNotifications
import io.github.wulkanowy.services.sync.channels.NewHomeworkChannel
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
class NewHomeworkNotification @Inject constructor(
@ApplicationContext private val context: Context,
notificationManager: NotificationManagerCompat,
) : BaseNotification(context, notificationManager) {
fun notify(items: List<Homework>) {
val notification = MultipleNotifications(
channelId = NewHomeworkChannel.CHANNEL_ID,
icon = R.drawable.ic_more_homework,
titleStringRes = R.plurals.homework_notify_new_item_title,
contentStringRes = R.plurals.homework_notify_new_item_title, // todo: you received %d new homework
summaryStringRes = R.plurals.homework_number_item,
startMenu = MainView.Section.HOMEWORK,
lines = items.map {
"${it.subject}: ${it.content}"
}
)
sendNotification(notification)
}
}

View File

@ -0,0 +1,30 @@
package io.github.wulkanowy.services.sync.notifications
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.pojos.OneNotification
import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
class NewLuckyNumberNotification @Inject constructor(
@ApplicationContext private val context: Context,
notificationManager: NotificationManagerCompat,
) : BaseNotification(context, notificationManager) {
fun notify(item: LuckyNumber) {
val notification = OneNotification(
channelId = LuckyNumberChannel.CHANNEL_ID,
icon = R.drawable.ic_stat_luckynumber,
titleStringRes = R.string.lucky_number_notify_new_item_title,
contentStringRes = R.string.lucky_number_notify_new_item,
startMenu = MainView.Section.LUCKY_NUMBER,
contentValues = listOf(item.luckyNumber.toString())
)
sendNotification(notification)
}
}

View File

@ -0,0 +1,33 @@
package io.github.wulkanowy.services.sync.notifications
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.pojos.MultipleNotifications
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
class NewMessageNotification @Inject constructor(
@ApplicationContext private val context: Context,
notificationManager: NotificationManagerCompat,
) : BaseNotification(context, notificationManager) {
fun notify(items: List<Message>) {
val notification = MultipleNotifications(
channelId = NewMessagesChannel.CHANNEL_ID,
icon = R.drawable.ic_stat_message,
titleStringRes = R.plurals.message_new_items,
contentStringRes = R.plurals.message_notify_new_items,
summaryStringRes = R.plurals.message_number_item,
startMenu = MainView.Section.MESSAGE,
lines = items.map {
"${it.sender}: ${it.subject}"
}
)
sendNotification(notification)
}
}

View File

@ -0,0 +1,46 @@
package io.github.wulkanowy.services.sync.notifications
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.pojos.MultipleNotifications
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
class NewNoteNotification @Inject constructor(
@ApplicationContext private val context: Context,
notificationManager: NotificationManagerCompat,
) : BaseNotification(context, notificationManager) {
fun notify(items: List<Note>) {
val notification = MultipleNotifications(
channelId = NewMessagesChannel.CHANNEL_ID,
icon = R.drawable.ic_stat_note,
titleStringRes = when (NoteCategory.getByValue(items.first().categoryType)) {
NoteCategory.POSITIVE -> R.plurals.praise_new_items
NoteCategory.NEUTRAL -> R.plurals.neutral_note_new_items
else -> R.plurals.note_new_items
},
contentStringRes = when (NoteCategory.getByValue(items.first().categoryType)) {
NoteCategory.POSITIVE -> R.plurals.praise_notify_new_items
NoteCategory.NEUTRAL -> R.plurals.neutral_note_notify_new_items
else -> R.plurals.note_notify_new_items
},
summaryStringRes = when (NoteCategory.getByValue(items.first().categoryType)) {
NoteCategory.POSITIVE -> R.plurals.praise_number_item
NoteCategory.NEUTRAL -> R.plurals.neutral_note_number_item
else -> R.plurals.note_number_item
},
startMenu = MainView.Section.NOTE,
lines = items.map {
"${it.teacher}: ${it.category}"
}
)
sendNotification(notification)
}
}

View File

@ -0,0 +1,33 @@
package io.github.wulkanowy.services.sync.notifications
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
import io.github.wulkanowy.data.pojos.MultipleNotifications
import io.github.wulkanowy.services.sync.channels.NewSchoolAnnouncementsChannel
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
class NewSchoolAnnouncementNotification @Inject constructor(
@ApplicationContext private val context: Context,
notificationManager: NotificationManagerCompat,
) : BaseNotification(context, notificationManager) {
fun notify(items: List<SchoolAnnouncement>) {
val notification = MultipleNotifications(
channelId = NewSchoolAnnouncementsChannel.CHANNEL_ID,
icon = R.drawable.ic_all_about,
titleStringRes = R.plurals.school_announcement_notify_new_item_title,
contentStringRes = R.plurals.school_announcement_notify_new_items,
summaryStringRes = R.plurals.school_announcement_number_item,
startMenu = MainView.Section.SCHOOL_ANNOUNCEMENT,
lines = items.map {
"${it.subject}: ${it.content}"
}
)
sendNotification(notification)
}
}

View File

@ -1,31 +1,18 @@
package io.github.wulkanowy.services.sync.works
import android.app.PendingIntent
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.ConferenceRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewConferencesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject
import kotlin.random.Random
class ConferenceWork @Inject constructor(
@ApplicationContext private val context: Context,
private val conferenceRepository: ConferenceRepository,
private val notificationManager: NotificationManagerCompat,
private val preferencesRepository: PreferencesRepository
private val preferencesRepository: PreferencesRepository,
private val newConferenceNotification: NewConferenceNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester) {
@ -36,44 +23,13 @@ class ConferenceWork @Inject constructor(
notify = preferencesRepository.isNotificationsEnable
).waitForResult()
conferenceRepository.getNotNotifiedConference(semester).first().let {
if (it.isNotEmpty()) notify(it)
conferenceRepository.getConferenceFromDatabase(semester).first()
.filter { !it.isNotified }.let {
if (it.isNotEmpty()) newConferenceNotification.notify(it)
conferenceRepository.updateConference(it.onEach { conference -> conference.isNotified = true })
}
}
private fun notify(conference: List<Conference>) {
notificationManager.notify(
Random.nextInt(Int.MAX_VALUE),
NotificationCompat.Builder(context, NewConferencesChannel.CHANNEL_ID)
.setContentTitle(context.resources.getQuantityString(R.plurals.conference_notify_new_item_title, conference.size, conference.size))
.setContentText(context.resources.getQuantityString(R.plurals.conference_notify_new_items, conference.size, conference.size))
.setSmallIcon(R.drawable.ic_stat_all)
.setLargeIcon(context.getCompatBitmap(R.drawable.ic_more_conferences, R.color.colorPrimary))
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(
context, MainView.Section.CONFERENCE.id,
MainActivity.getStartIntent(context, MainView.Section.CONFERENCE, true),
PendingIntent.FLAG_UPDATE_CURRENT
)
)
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(
context.resources.getQuantityString(
R.plurals.conference_number_item,
conference.size,
conference.size
)
)
conference.forEach { addLine("${it.title}: ${it.subject}") }
this
conferenceRepository.updateConference(it.onEach { conference ->
conference.isNotified = true
})
.build()
)
}
}
}
}

View File

@ -1,32 +1,19 @@
package io.github.wulkanowy.services.sync.works
import android.app.PendingIntent
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.ExamRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewExamChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.services.sync.notifications.NewExamNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import java.time.LocalDate.now
import javax.inject.Inject
import kotlin.random.Random
class ExamWork @Inject constructor(
@ApplicationContext private val context: Context,
private val examRepository: ExamRepository,
private val notificationManager: NotificationManagerCompat,
private val preferencesRepository: PreferencesRepository
private val preferencesRepository: PreferencesRepository,
private val newExamNotification: NewExamNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester) {
@ -39,49 +26,11 @@ class ExamWork @Inject constructor(
notify = preferencesRepository.isNotificationsEnable
).waitForResult()
examRepository.getNotNotifiedExam(semester, now()).first().let {
if (it.isNotEmpty()) notify(it)
examRepository.getExamsFromDatabase(semester, now()).first()
.filter { !it.isNotified }.let {
if (it.isNotEmpty()) newExamNotification.notify(it)
examRepository.updateExam(it.onEach { exam -> exam.isNotified = true })
}
}
private fun notify(exam: List<Exam>) {
notificationManager.notify(
Random.nextInt(Int.MAX_VALUE),
NotificationCompat.Builder(context, NewExamChannel.CHANNEL_ID)
.setContentTitle(
context.resources.getQuantityString(
R.plurals.exam_notify_new_item_title, exam.size, exam.size
)
)
.setSmallIcon(R.drawable.ic_stat_all)
.setLargeIcon(
context.getCompatBitmap(R.drawable.ic_main_exam, R.color.colorPrimary)
)
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(
context, MainView.Section.MESSAGE.id,
MainActivity.getStartIntent(context, MainView.Section.EXAM, true),
PendingIntent.FLAG_UPDATE_CURRENT
)
)
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(
context.resources.getQuantityString(
R.plurals.exam_number_item,
exam.size,
exam.size
)
)
exam.forEach { addLine("${it.subject}: ${it.description}") }
this
})
.build()
)
examRepository.updateExam(it.onEach { exam -> exam.isNotified = true })
}
}
}

View File

@ -1,111 +1,51 @@
package io.github.wulkanowy.services.sync.works
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.DEFAULT_ALL
import androidx.core.app.NotificationCompat.PRIORITY_HIGH
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewGradesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.services.sync.notifications.NewGradeNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject
import kotlin.random.Random
class GradeWork @Inject constructor(
@ApplicationContext private val context: Context,
private val notificationManager: NotificationManagerCompat,
private val gradeRepository: GradeRepository,
private val preferencesRepository: PreferencesRepository
private val preferencesRepository: PreferencesRepository,
private val newGradeNotification: NewGradeNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester) {
gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable).waitForResult()
gradeRepository.getGrades(
student = student,
semester = semester,
forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable
).waitForResult()
gradeRepository.getNotNotifiedGrades(semester).first().let {
if (it.isNotEmpty()) notifyDetails(it)
gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true })
}
gradeRepository.getGradesFromDatabase(semester).first()
.filter { !it.isNotified }.let {
if (it.isNotEmpty()) newGradeNotification.notifyDetails(it)
gradeRepository.getNotNotifiedPredictedGrades(semester).first().let {
if (it.isNotEmpty()) notifyPredicted(it)
gradeRepository.updateGradesSummary(it.onEach { grade -> grade.isPredictedGradeNotified = true })
}
gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true })
}
gradeRepository.getNotNotifiedFinalGrades(semester).first().let {
if (it.isNotEmpty()) notifyFinal(it)
gradeRepository.updateGradesSummary(it.onEach { grade -> grade.isFinalGradeNotified = true })
}
}
gradeRepository.getGradesPredictedFromDatabase(semester).first()
.filter { !it.isPredictedGradeNotified }.let {
if (it.isNotEmpty()) newGradeNotification.notifyPredicted(it)
private fun notifyDetails(grades: List<Grade>) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), getNotificationBuilder()
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, grades.size, grades.size))
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size))
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size))
grades.forEach { addLine("${it.subject}: ${it.entry}") }
this
})
.build()
)
}
gradeRepository.updateGradesSummary(it.onEach { grade ->
grade.isPredictedGradeNotified = true
})
}
private fun notifyPredicted(gradesSummary: List<GradeSummary>) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), getNotificationBuilder()
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items_predicted, gradesSummary.size, gradesSummary.size))
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items_predicted, gradesSummary.size, gradesSummary.size))
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, gradesSummary.size, gradesSummary.size))
gradesSummary.forEach { addLine("${it.subject}: ${it.predictedGrade}") }
this
})
.build()
)
}
gradeRepository.getGradesFinalFromDatabase(semester).first()
.filter { !it.isFinalGradeNotified }.let {
if (it.isNotEmpty()) newGradeNotification.notifyFinal(it)
private fun notifyFinal(gradesSummary: List<GradeSummary>) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), getNotificationBuilder()
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items_final, gradesSummary.size, gradesSummary.size))
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items_final, gradesSummary.size, gradesSummary.size))
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, gradesSummary.size, gradesSummary.size))
gradesSummary.forEach { addLine("${it.subject}: ${it.finalGrade}") }
this
})
.build()
)
}
private fun getNotificationBuilder(): NotificationCompat.Builder {
return NotificationCompat.Builder(context, NewGradesChannel.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_all)
.setLargeIcon(
context.getCompatBitmap(R.drawable.ic_stat_grade, R.color.colorPrimary)
)
.setAutoCancel(true)
.setPriority(PRIORITY_HIGH)
.setDefaults(DEFAULT_ALL)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(
context, MainView.Section.GRADE.id,
MainActivity.getStartIntent(context, MainView.Section.GRADE, true),
FLAG_UPDATE_CURRENT
)
)
gradeRepository.updateGradesSummary(it.onEach { grade ->
grade.isFinalGradeNotified = true
})
}
}
}

View File

@ -1,34 +1,21 @@
package io.github.wulkanowy.services.sync.works
import android.app.PendingIntent
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.HomeworkRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewHomeworkChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.services.sync.notifications.NewHomeworkNotification
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import java.time.LocalDate.now
import javax.inject.Inject
import kotlin.random.Random
class HomeworkWork @Inject constructor(
@ApplicationContext private val context: Context,
private val homeworkRepository: HomeworkRepository,
private val notificationManager: NotificationManagerCompat,
private val preferencesRepository: PreferencesRepository
private val preferencesRepository: PreferencesRepository,
private val newHomeworkNotification: NewHomeworkNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester) {
@ -41,50 +28,13 @@ class HomeworkWork @Inject constructor(
notify = preferencesRepository.isNotificationsEnable
).waitForResult()
homeworkRepository.getNotNotifiedHomework(semester, now().monday, now().sunday).first()
.let {
if (it.isNotEmpty()) notify(it)
homeworkRepository.getHomeworkFromDatabase(semester, now().monday, now().sunday).first()
.filter { !it.isNotified }.let {
if (it.isNotEmpty()) newHomeworkNotification.notify(it)
homeworkRepository.updateHomework(it.onEach { homework ->
homework.isNotified = true
})
}
}
private fun notify(homework: List<Homework>) {
notificationManager.notify(
Random.nextInt(Int.MAX_VALUE),
NotificationCompat.Builder(context, NewHomeworkChannel.CHANNEL_ID)
.setContentTitle(
context.resources.getQuantityString(
R.plurals.homework_notify_new_item_title, homework.size, homework.size
)
)
.setSmallIcon(R.drawable.ic_stat_all)
.setLargeIcon(
context.getCompatBitmap(R.drawable.ic_more_homework, R.color.colorPrimary)
)
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(
context, MainView.Section.MESSAGE.id,
MainActivity.getStartIntent(context, MainView.Section.HOMEWORK, true),
PendingIntent.FLAG_UPDATE_CURRENT
)
)
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(
context.resources.getQuantityString(
R.plurals.homework_number_item, homework.size, homework.size
)
)
homework.forEach { addLine("${it.subject}: ${it.content}") }
this
})
.build()
)
}
}

View File

@ -1,59 +1,29 @@
package io.github.wulkanowy.services.sync.works
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.DEFAULT_ALL
import androidx.core.app.NotificationCompat.PRIORITY_HIGH
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.services.sync.notifications.NewLuckyNumberNotification
import io.github.wulkanowy.utils.waitForResult
import javax.inject.Inject
import kotlin.random.Random
class LuckyNumberWork @Inject constructor(
@ApplicationContext private val context: Context,
private val notificationManager: NotificationManagerCompat,
private val luckyNumberRepository: LuckyNumberRepository,
private val preferencesRepository: PreferencesRepository
private val preferencesRepository: PreferencesRepository,
private val newLuckyNumberNotification: NewLuckyNumberNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester) {
luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable).waitForResult()
luckyNumberRepository.getLuckyNumber(
student = student,
forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable
).waitForResult()
luckyNumberRepository.getNotNotifiedLuckyNumber(student)?.let {
notify(it)
newLuckyNumberNotification.notify(it)
luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true })
}
}
private fun notify(luckyNumber: LuckyNumber) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, LuckyNumberChannel.CHANNEL_ID)
.setContentTitle(context.getString(R.string.lucky_number_notify_new_item_title))
.setContentText(context.getString(R.string.lucky_number_notify_new_item, luckyNumber.luckyNumber))
.setSmallIcon(R.drawable.ic_stat_all)
.setLargeIcon(
context.getCompatBitmap(R.drawable.ic_stat_luckynumber, R.color.colorPrimary)
)
.setAutoCancel(true)
.setDefaults(DEFAULT_ALL)
.setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MainView.Section.MESSAGE.id,
MainActivity.getStartIntent(context, MainView.Section.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT))
.build())
}
}

View File

@ -1,67 +1,34 @@
package io.github.wulkanowy.services.sync.works
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.DEFAULT_ALL
import androidx.core.app.NotificationCompat.PRIORITY_HIGH
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject
import kotlin.random.Random
class MessageWork @Inject constructor(
@ApplicationContext private val context: Context,
private val notificationManager: NotificationManagerCompat,
private val messageRepository: MessageRepository,
private val preferencesRepository: PreferencesRepository
private val preferencesRepository: PreferencesRepository,
private val newMessageNotification: NewMessageNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester) {
messageRepository.getMessages(student, semester, RECEIVED, true, preferencesRepository.isNotificationsEnable).waitForResult()
messageRepository.getMessages(
student = student,
semester = semester,
folder = RECEIVED,
forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable
).waitForResult()
messageRepository.getNotNotifiedMessages(student).first().let {
if (it.isNotEmpty()) notify(it)
messageRepository.updateMessages(it.onEach { message -> message.isNotified = true })
}
}
private fun notify(messages: List<Message>) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewMessagesChannel.CHANNEL_ID)
.setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, messages.size, messages.size))
.setContentText(context.resources.getQuantityString(R.plurals.message_notify_new_items, messages.size, messages.size))
.setSmallIcon(R.drawable.ic_stat_all)
.setLargeIcon(
context.getCompatBitmap(R.drawable.ic_stat_message, R.color.colorPrimary)
)
.setAutoCancel(true)
.setDefaults(DEFAULT_ALL)
.setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MainView.Section.MESSAGE.id,
MainActivity.getStartIntent(context, MainView.Section.MESSAGE, true), FLAG_UPDATE_CURRENT)
)
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size))
messages.forEach { addLine("${it.sender}: ${it.subject}") }
this
})
.build())
messageRepository.getMessagesFromDatabase(student).first()
.filter { !it.isNotified && it.unread }.let {
if (it.isNotEmpty()) newMessageNotification.notify(it)
messageRepository.updateMessages(it.onEach { message -> message.isNotified = true })
}
}
}

View File

@ -1,87 +1,34 @@
package io.github.wulkanowy.services.sync.works
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.DEFAULT_ALL
import androidx.core.app.NotificationCompat.PRIORITY_HIGH
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.NoteRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory.NEUTRAL
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory.POSITIVE
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.services.sync.notifications.NewNoteNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject
import kotlin.random.Random
class NoteWork @Inject constructor(
@ApplicationContext private val context: Context,
private val notificationManager: NotificationManagerCompat,
private val noteRepository: NoteRepository,
private val preferencesRepository: PreferencesRepository
private val preferencesRepository: PreferencesRepository,
private val newNoteNotification: NewNoteNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester) {
noteRepository.getNotes(student, semester, true, preferencesRepository.isNotificationsEnable).waitForResult()
noteRepository.getNotes(
student = student,
semester = semester,
forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable
).waitForResult()
noteRepository.getNotNotifiedNotes(student).first().let {
if (it.isNotEmpty()) notify(it)
noteRepository.updateNotes(it.onEach { note -> note.isNotified = true })
}
}
noteRepository.getNotesFromDatabase(student).first()
.filter { !it.isNotified }.let {
if (it.isNotEmpty()) newNoteNotification.notify(it)
private fun notify(notes: List<Note>) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewNotesChannel.CHANNEL_ID)
.setContentTitle(
when (NoteCategory.getByValue(notes.first().categoryType)) {
POSITIVE -> context.resources.getQuantityString(R.plurals.praise_new_items, notes.size, notes.size)
NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_new_items, notes.size, notes.size)
else -> context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size)
}
)
.setContentText(
when (NoteCategory.getByValue(notes.first().categoryType)) {
POSITIVE -> context.resources.getQuantityString(R.plurals.praise_notify_new_items, notes.size, notes.size)
NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_notify_new_items, notes.size, notes.size)
else -> context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size)
}
)
.setSmallIcon(R.drawable.ic_stat_all)
.setLargeIcon(
context.getCompatBitmap(R.drawable.ic_stat_note, R.color.colorPrimary)
)
.setAutoCancel(true)
.setDefaults(DEFAULT_ALL)
.setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MainView.Section.NOTE.id,
MainActivity.getStartIntent(context, MainView.Section.NOTE, true), FLAG_UPDATE_CURRENT))
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(
when (NoteCategory.getByValue(notes.first().categoryType)) {
POSITIVE -> context.resources.getQuantityString(R.plurals.praise_number_item, notes.size, notes.size)
NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_number_item, notes.size, notes.size)
else -> context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size)
}
)
notes.forEach { addLine("${it.teacher}: ${it.category}") }
this
})
.build())
noteRepository.updateNotes(it.onEach { note -> note.isNotified = true })
}
}
}

View File

@ -1,83 +1,35 @@
package io.github.wulkanowy.services.sync.works
import android.app.PendingIntent
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
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.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewSchoolAnnouncementsChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject
import kotlin.random.Random
class SchoolAnnouncementWork @Inject constructor(
@ApplicationContext private val context: Context,
private val schoolAnnouncementRepository: SchoolAnnouncementRepository,
private val notificationManager: NotificationManagerCompat,
private val preferencesRepository: PreferencesRepository
private val preferencesRepository: PreferencesRepository,
private val newSchoolAnnouncementNotification: NewSchoolAnnouncementNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester) {
schoolAnnouncementRepository.getSchoolAnnouncements(
student,
true,
student = student,
forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable
).waitForResult()
schoolAnnouncementRepository.getNotNotifiedSchoolAnnouncement(semester).first().let {
if (it.isNotEmpty()) notify(it)
schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student).first()
.filter { !it.isNotified }.let {
if (it.isNotEmpty()) newSchoolAnnouncementNotification.notify(it)
schoolAnnouncementRepository.updateSchoolAnnouncement(it.onEach { schoolAnnouncement -> schoolAnnouncement.isNotified = true })
}
}
private fun notify(schoolAnnouncement: List<SchoolAnnouncement>) {
notificationManager.notify(
Random.nextInt(Int.MAX_VALUE),
NotificationCompat.Builder(context, NewSchoolAnnouncementsChannel.CHANNEL_ID)
.setContentTitle(context.resources.getQuantityString(
R.plurals.school_announcement_notify_new_item_title,
schoolAnnouncement.size,
schoolAnnouncement.size
))
.setContentText(context.resources.getQuantityString(R.plurals.school_announcement_notify_new_items, schoolAnnouncement.size, schoolAnnouncement.size))
.setSmallIcon(R.drawable.ic_stat_all)
.setLargeIcon(context.getCompatBitmap(R.drawable.ic_all_about, R.color.colorPrimary))
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(
context, MainView.Section.SCHOOL_ANNOUNCEMENT.id,
MainActivity.getStartIntent(context, MainView.Section.SCHOOL_ANNOUNCEMENT, true),
PendingIntent.FLAG_UPDATE_CURRENT
)
)
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(
context.resources.getQuantityString(
R.plurals.school_announcement_number_item,
schoolAnnouncement.size,
schoolAnnouncement.size
)
)
schoolAnnouncement.forEach { addLine("${it.subject}: ${it.content}") }
this
schoolAnnouncementRepository.updateSchoolAnnouncement(it.onEach { schoolAnnouncement ->
schoolAnnouncement.isNotified = true
})
.build()
)
}
}
}

View File

@ -10,7 +10,7 @@ import io.github.wulkanowy.databinding.FragmentAboutBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
import io.github.wulkanowy.ui.modules.about.logviewer.LogViewerFragment
import io.github.wulkanowy.ui.modules.debug.DebugFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo
@ -37,34 +37,60 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
override val versionRes: Triple<String, String, Drawable?>?
get() = context?.run {
val buildTimestamp = appInfo.buildTimestamp.toLocalDateTime().toFormattedString("yyyy-MM-dd")
val versionSignature = "${appInfo.versionName}-${appInfo.buildFlavor} (${appInfo.versionCode}), $buildTimestamp"
Triple(getString(R.string.about_version), versionSignature, getCompatDrawable(R.drawable.ic_all_about))
val buildTimestamp =
appInfo.buildTimestamp.toLocalDateTime().toFormattedString("yyyy-MM-dd")
val versionSignature =
"${appInfo.versionName}-${appInfo.buildFlavor} (${appInfo.versionCode}), $buildTimestamp"
Triple(
getString(R.string.about_version),
versionSignature,
getCompatDrawable(R.drawable.ic_all_about)
)
}
override val creatorsRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_contributor), getString(R.string.about_contributor_summary), getCompatDrawable(R.drawable.ic_about_creator))
Triple(
getString(R.string.about_contributor),
getString(R.string.about_contributor_summary),
getCompatDrawable(R.drawable.ic_about_creator)
)
}
override val feedbackRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_feedback), getString(R.string.about_feedback_summary), getCompatDrawable(R.drawable.ic_about_feedback))
Triple(
getString(R.string.about_feedback),
getString(R.string.about_feedback_summary),
getCompatDrawable(R.drawable.ic_about_feedback)
)
}
override val faqRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_faq), getString(R.string.about_faq_summary), getCompatDrawable(R.drawable.ic_about_faq))
Triple(
getString(R.string.about_faq),
getString(R.string.about_faq_summary),
getCompatDrawable(R.drawable.ic_about_faq)
)
}
override val discordRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_discord), getString(R.string.about_discord_summary), getCompatDrawable(R.drawable.ic_about_discord))
Triple(
getString(R.string.about_discord),
getString(R.string.about_discord_summary),
getCompatDrawable(R.drawable.ic_about_discord)
)
}
override val facebookRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_facebook), getString(R.string.about_facebook_summary), getCompatDrawable(R.drawable.ic_about_facebook))
Triple(
getString(R.string.about_facebook),
getString(R.string.about_facebook_summary),
getCompatDrawable(R.drawable.ic_about_facebook)
)
}
override val homepageRes: Triple<String, String, Drawable?>?
@ -78,12 +104,20 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
override val licensesRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_licenses), getString(R.string.about_licenses_summary), getCompatDrawable(R.drawable.ic_about_licenses))
Triple(
getString(R.string.about_licenses),
getString(R.string.about_licenses_summary),
getCompatDrawable(R.drawable.ic_about_licenses)
)
}
override val privacyRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_privacy), getString(R.string.about_privacy_summary), getCompatDrawable(R.drawable.ic_about_privacy))
Triple(
getString(R.string.about_privacy),
getString(R.string.about_privacy_summary),
getCompatDrawable(R.drawable.ic_about_privacy)
)
}
override val titleStringId get() = R.string.about_title
@ -118,8 +152,8 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
context?.openAppInMarket(::showMessage)
}
override fun openLogViewer() {
(activity as? MainActivity)?.pushView(LogViewerFragment.newInstance())
override fun openDebugScreen() {
(activity as? MainActivity)?.pushView(DebugFragment.newInstance())
}
override fun openDiscordInvite() {
@ -155,7 +189,10 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
}
override fun openFaqPage() {
context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania", ::showMessage)
context?.openInternetBrowser(
"https://wulkanowy.github.io/czesto-zadawane-pytania",
::showMessage
)
}
override fun openLicenses() {
@ -167,7 +204,10 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
}
override fun openPrivacyPolicy() {
context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage)
context?.openInternetBrowser(
"https://wulkanowy.github.io/polityka-prywatnosci.html",
::showMessage
)
}
override fun onDestroyView() {

View File

@ -26,10 +26,10 @@ class AboutPresenter @Inject constructor(
view?.run {
when (name) {
versionRes?.first -> {
Timber.i("Opening log viewer")
if (appInfo.isDebug) openLogViewer()
Timber.i("Opening debug screen")
if (appInfo.isDebug) openDebugScreen()
else openAppInMarket()
analytics.logEvent("about_open", "name" to "log_viewer")
analytics.logEvent("about_open", "name" to "debug_screen")
}
feedbackRes?.first -> {
Timber.i("Opening email client")

View File

@ -29,7 +29,7 @@ interface AboutView : BaseView {
fun openAppInMarket()
fun openLogViewer()
fun openDebugScreen()
fun openDiscordInvite()

View File

@ -0,0 +1,32 @@
package io.github.wulkanowy.ui.modules.debug
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.databinding.ItemDebugBinding
class DebugAdapter : RecyclerView.Adapter<DebugAdapter.ItemViewHolder>() {
var items = emptyList<DebugItem>()
var onItemClickListener: (DebugItem) -> Unit = {}
override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
ItemDebugBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val item = items[position]
with(holder.binding) {
debugItemName.setText(item.title)
root.setOnClickListener {
onItemClickListener(item)
}
}
}
class ItemViewHolder(val binding: ItemDebugBinding) : RecyclerView.ViewHolder(binding.root)
}

View File

@ -0,0 +1,65 @@
package io.github.wulkanowy.ui.modules.debug
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.databinding.FragmentDebugBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.debug.logviewer.LogViewerFragment
import io.github.wulkanowy.ui.modules.debug.notification.NotificationDebugFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
@AndroidEntryPoint
class DebugFragment : BaseFragment<FragmentDebugBinding>(R.layout.fragment_debug), DebugView,
MainView.TitledView {
@Inject
lateinit var presenter: DebugPresenter
private val debugAdapter = DebugAdapter()
override val titleStringId: Int
get() = R.string.debug_title
companion object {
fun newInstance() = DebugFragment()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentDebugBinding.bind(view)
presenter.onAttachView(this)
}
override fun initView() {
debugAdapter.onItemClickListener = presenter::onItemSelect
with(binding.debugRecycler) {
layoutManager = LinearLayoutManager(context)
adapter = debugAdapter
}
}
override fun setItems(itemList: List<DebugItem>) {
with(debugAdapter) {
items = itemList
notifyDataSetChanged()
}
}
override fun openLogViewer() {
(activity as? MainActivity)?.pushView(LogViewerFragment.newInstance())
}
override fun openNotificationsDebug() {
(activity as? MainActivity)?.pushView(NotificationDebugFragment.newInstance())
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
}

View File

@ -0,0 +1,7 @@
package io.github.wulkanowy.ui.modules.debug
import androidx.annotation.StringRes
data class DebugItem(
@StringRes val title: Int,
)

View File

@ -0,0 +1,37 @@
package io.github.wulkanowy.ui.modules.debug
import io.github.wulkanowy.R
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import timber.log.Timber
import javax.inject.Inject
class DebugPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
) : BasePresenter<DebugView>(errorHandler, studentRepository) {
val items = listOf(
DebugItem(R.string.logviewer_title),
DebugItem(R.string.notification_debug_title),
)
override fun onAttachView(view: DebugView) {
super.onAttachView(view)
Timber.i("Debug view was initialized")
with(view) {
initView()
setItems(items)
}
}
fun onItemSelect(item: DebugItem) {
when (item.title) {
R.string.logviewer_title -> view?.openLogViewer()
R.string.notification_debug_title -> view?.openNotificationsDebug()
else -> Timber.d("Unknown debug item: $item")
}
}
}

View File

@ -0,0 +1,14 @@
package io.github.wulkanowy.ui.modules.debug
import io.github.wulkanowy.ui.base.BaseView
interface DebugView : BaseView {
fun initView()
fun setItems(itemList: List<DebugItem>)
fun openLogViewer()
fun openNotificationsDebug()
}

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.modules.about.logviewer
package io.github.wulkanowy.ui.modules.debug.logviewer
import android.view.ViewGroup
import android.widget.TextView

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.modules.about.logviewer
package io.github.wulkanowy.ui.modules.debug.logviewer
import android.content.Intent
import android.content.Intent.EXTRA_EMAIL

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.modules.about.logviewer
package io.github.wulkanowy.ui.modules.debug.logviewer
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.LoggerRepository

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.modules.about.logviewer
package io.github.wulkanowy.ui.modules.debug.logviewer
import io.github.wulkanowy.ui.base.BaseView
import java.io.File

View File

@ -0,0 +1,31 @@
package io.github.wulkanowy.ui.modules.debug.notification
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.databinding.ItemDebugNotificationsBinding
class NotificationDebugAdapter : RecyclerView.Adapter<NotificationDebugAdapter.ItemViewHolder>() {
var items = emptyList<NotificationDebugItem>()
override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
ItemDebugNotificationsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val item = items[position]
with(holder.binding) {
title.setText(item.title)
button1.setOnClickListener { item.onClickCallback(1) }
button2.setOnClickListener { item.onClickCallback(3) }
button3.setOnClickListener { item.onClickCallback(10) }
}
}
class ItemViewHolder(val binding: ItemDebugNotificationsBinding) :
RecyclerView.ViewHolder(binding.root)
}

View File

@ -0,0 +1,54 @@
package io.github.wulkanowy.ui.modules.debug.notification
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.databinding.FragmentDebugNotificationsBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
@AndroidEntryPoint
class NotificationDebugFragment :
BaseFragment<FragmentDebugNotificationsBinding>(R.layout.fragment_debug_notifications),
NotificationDebugView, MainView.TitledView {
@Inject
lateinit var presenter: NotificationDebugPresenter
private val notificationDebugAdapter = NotificationDebugAdapter()
override val titleStringId: Int
get() = R.string.notification_debug_title
companion object {
fun newInstance() = NotificationDebugFragment()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentDebugNotificationsBinding.bind(view)
presenter.onAttachView(this)
}
override fun initView() {
with(binding.recyclerView) {
adapter = notificationDebugAdapter
layoutManager = LinearLayoutManager(requireContext())
}
}
override fun setItems(notificationDebugs: List<NotificationDebugItem>) {
with(notificationDebugAdapter) {
items = notificationDebugs
notifyDataSetChanged()
}
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
}

View File

@ -0,0 +1,8 @@
package io.github.wulkanowy.ui.modules.debug.notification
import androidx.annotation.StringRes
data class NotificationDebugItem(
@StringRes val title: Int,
val onClickCallback: (numberOfNotifications: Int) -> Unit,
)

View File

@ -0,0 +1,83 @@
package io.github.wulkanowy.ui.modules.debug.notification
import io.github.wulkanowy.R
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification
import io.github.wulkanowy.services.sync.notifications.NewExamNotification
import io.github.wulkanowy.services.sync.notifications.NewGradeNotification
import io.github.wulkanowy.services.sync.notifications.NewHomeworkNotification
import io.github.wulkanowy.services.sync.notifications.NewLuckyNumberNotification
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
import io.github.wulkanowy.services.sync.notifications.NewNoteNotification
import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugConferenceItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugExamItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeDetailsItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeSummaryItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugHomeworkItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugLuckyNumber
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugMessageItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugNoteItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugSchoolAnnouncementItems
import timber.log.Timber
import javax.inject.Inject
class NotificationDebugPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val newGradeNotification: NewGradeNotification,
private val newHomeworkNotification: NewHomeworkNotification,
private val newConferenceNotification: NewConferenceNotification,
private val newExamNotification: NewExamNotification,
private val newMessageNotification: NewMessageNotification,
private val newNoteNotification: NewNoteNotification,
private val newSchoolAnnouncementNotification: NewSchoolAnnouncementNotification,
private val newLuckyNumberNotification: NewLuckyNumberNotification,
) : BasePresenter<NotificationDebugView>(errorHandler, studentRepository) {
private val items = listOf(
NotificationDebugItem(R.string.grade_title) {
newGradeNotification.notifyDetails(debugGradeDetailsItems.take(it))
},
NotificationDebugItem(R.string.grade_summary_predicted_grade) {
newGradeNotification.notifyPredicted(debugGradeSummaryItems.take(it))
},
NotificationDebugItem(R.string.grade_summary_final_grade) {
newGradeNotification.notifyFinal(debugGradeSummaryItems.take(it))
},
NotificationDebugItem(R.string.homework_title) {
newHomeworkNotification.notify(debugHomeworkItems.take(it))
},
NotificationDebugItem(R.string.conferences_title) {
newConferenceNotification.notify(debugConferenceItems.take(it))
},
NotificationDebugItem(R.string.exam_title) {
newExamNotification.notify(debugExamItems.take(it))
},
NotificationDebugItem(R.string.message_title) {
newMessageNotification.notify(debugMessageItems.take(it))
},
NotificationDebugItem(R.string.note_title) {
newNoteNotification.notify(debugNoteItems.take(it))
},
NotificationDebugItem(R.string.school_announcement_title) {
newSchoolAnnouncementNotification.notify(debugSchoolAnnouncementItems.take(it))
},
NotificationDebugItem(R.string.lucky_number_title) {
repeat(it) {
newLuckyNumberNotification.notify(debugLuckyNumber)
}
},
)
override fun onAttachView(view: NotificationDebugView) {
super.onAttachView(view)
Timber.i("Notification debug view was initialized")
with(view) {
initView()
setItems(items)
}
}
}

View File

@ -0,0 +1,10 @@
package io.github.wulkanowy.ui.modules.debug.notification
import io.github.wulkanowy.ui.base.BaseView
interface NotificationDebugView : BaseView {
fun initView()
fun setItems(notificationDebugs: List<NotificationDebugItem>)
}

View File

@ -0,0 +1,58 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Conference
import java.time.LocalDateTime
val debugConferenceItems = listOf(
generateConference(
title = "Spotkanie z rodzicami/opiekunami",
subject = "Podsumowanie I semestru - średnia klasy, oceny, frekwencja, zachowanie"
),
generateConference(
title = "ZSW",
subject = "Pierwsze - organizacyjne zebranie z rodzicami klas pierwszych"
),
generateConference(
title = "Spotkanie z rodzicami w sprawie bójki",
subject = "Pierwsze - i miejmy nadzieję ostatnie - zebranie w takiej sprawie"
),
generateConference(
title = "Spotkanie z rodzicami w sprawie kolejnej bójki",
subject = "Kolejne - ale miejmy jeszcze nadzieję, że ostatnie - zebranie w takiej sprawie"
),
generateConference(
title = "Spotkanie z rodzicami w sprawie jeszcze jednej bójki",
subject = "Proszę państwa, proszę uspokoić swoje dzieci"
),
generateConference(
title = "Spotkanie w sprawie wydalenia części uczniów",
subject = "Proszę państwa, to jest krok ostateczny, którego nikt nie chciał się podjąć, ale ktoś musi"
),
generateConference(
title = "Spotkanie organizacyjne w drugim semestrze",
subject = "Prezentacja na temat projektu 'Spokojnej szkoły'"
),
generateConference(
title = "Spotkanie z pierwszakami",
subject = "Mamy sobie do pogadania"
),
generateConference(
title = "Spotkanie z rodzicami szóstoklaistów",
subject = "Musimy przygotować dzieci do ważnej uroczystości"
),
generateConference(
title = "Spotkanie podsumowujące pracę w ciągu ostatniego roku szkolnego",
subject = "Proszę państwa, zapraszam serdecznie na spotkanie"
),
)
private fun generateConference(title: String, subject: String) = Conference(
title = title,
subject = subject,
studentId = 0,
diaryId = 0,
agenda = "",
conferenceId = 0,
date = LocalDateTime.now(),
presentOnConference = "",
)

View File

@ -0,0 +1,30 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Exam
import java.time.LocalDate
val debugExamItems = listOf(
generateExam("Matematyka", "Figury na płaszczyźnie"),
generateExam("Język angielski", "czasowniki nieregularne 1 część"),
generateExam("Geografia", "Opolszczyzna - mapa"),
generateExam("Sieci komputerowe", "Zaciskanie erjotek"),
generateExam("Systemy operacyjne", "Instalacja ubuntu 16.04"),
generateExam("Język niemiecki", "oral exam"),
generateExam("Biologia", "Budowa koniczyny"),
generateExam("Chemia", "synteza płynnego zaliczenia"),
generateExam("Fizyka", "telekineza"),
generateExam("Matematyka", "Liczby zespolone i pochodne piątego rzędu"),
)
private fun generateExam(subject: String, description: String) = Exam(
subject = subject,
description = description,
studentId = 0,
diaryId = 0,
date = LocalDate.now(),
entryDate = LocalDate.now(),
group = "",
type = "",
teacher = "",
teacherSymbol = ""
)

View File

@ -0,0 +1,35 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Grade
import java.time.LocalDate
val debugGradeDetailsItems = listOf(
generateGrade("Matematyka", "+"),
generateGrade("Matematyka", "2="),
generateGrade("Fizyka", "-"),
generateGrade("Geografia", "4+"),
generateGrade("Sieci komputerowe", "1"),
generateGrade("Systemy operacyjne", "3+"),
generateGrade("Język polski", "2-"),
generateGrade("Język angielski", "4+"),
generateGrade("Religia", "6"),
generateGrade("Język niemiecki", "1!"),
generateGrade("Wychowanie fizyczne", "5"),
)
private fun generateGrade(subject: String, entry: String) = Grade(
subject = subject,
entry = entry,
semesterId = 0,
studentId = 0,
value = 0.0,
modifier = 0.0,
comment = "",
color = "",
gradeSymbol = "",
description = "",
weight = "",
weightValue = 0.0,
date = LocalDate.now(),
teacher = ""
)

View File

@ -0,0 +1,30 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.GradeSummary
val debugGradeSummaryItems = listOf(
generateSummary("Matematyka", "2-", "2"),
generateSummary("Fizyka", "1", "2"),
generateSummary("Geografia", "4+", "5"),
generateSummary("Sieci komputerowe", "2", "5"),
generateSummary("Systemy operacyjne", "3", "4"),
generateSummary("Język polski", "1", "3"),
generateSummary("Język angielski", "4", "3"),
generateSummary("Religia", "5", "6"),
generateSummary("Język niemiecki", "2", "2"),
generateSummary("Wychowanie fizyczne", "5", "5"),
generateSummary("Biologia", "4", "4"),
)
private fun generateSummary(subject: String, predicted: String, final: String) = GradeSummary(
semesterId = 0,
studentId = 0,
position = 0,
subject = subject,
predictedGrade = predicted,
finalGrade = final,
proposedPoints = "",
finalPoints = "",
pointsSum = "",
average = .0
)

View File

@ -0,0 +1,30 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Homework
import java.time.LocalDate
val debugHomeworkItems = listOf(
generateHomework("Chemia", "Test diagnozujący i Rozdział I do 30.10"),
generateHomework("Etyka", "Notatka własna do zajęć o ks. Jerzym Popiełuszko"),
generateHomework("Język angielski", "Zadania egzaminacyjne"),
generateHomework("Metodologia programowania", "Wszystkie instrukcje"),
generateHomework("Język polski", "Notatka własna na temat Wokulskiego z lektury Lalka"),
generateHomework("Systemy operacyjne", "Sprawozdanie z wykonania ćwiczenia nr 21.137"),
generateHomework("Matematyka", "Zadania od strony 1 do 128"),
generateHomework("Język niemiecki", "Opis swoich wakacji - dialog z kolegą"),
generateHomework("Język angielski", "Opis swoich wakacji - dialog z kolegą"),
generateHomework("Wychowanie fizyczne", "Notatka na temat skoku w dald"),
generateHomework("Biologia", "Notatka na temat grzechotnika"),
)
private fun generateHomework(subject: String, content: String) = Homework(
subject = subject,
content = content,
semesterId = 0,
studentId = 0,
date = LocalDate.now(),
entryDate = LocalDate.now(),
teacher = "",
teacherSymbol = "",
attachments = listOf(),
)

View File

@ -0,0 +1,12 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.LuckyNumber
import java.time.LocalDate
import kotlin.random.Random
val debugLuckyNumber
get() = LuckyNumber(
studentId = 0,
date = LocalDate.now(),
luckyNumber = Random.nextInt(1, 128),
)

View File

@ -0,0 +1,32 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Message
import java.time.LocalDateTime
val debugMessageItems = listOf(
generateMessage("Kowalski Jan", "Tytuł"),
generateMessage("Nazwisko Imię", "Tytuł wiadomości"),
generateMessage("Malinowski Kazimierz", "Nakrętki"),
generateMessage("Jastębowszki Orest", "Prośba do uczniów o pomoc przy projekcie"),
generateMessage("Metylowy Oranż", "Pozew o plagiat"),
generateMessage("VULCAN", "Uwaga na nieautoryzowane aplikacje"),
generateMessage("Mama", "Zacznij się w końcu uczyć do matury!!!11"),
generateMessage("Tata", "Kupisz mi coś w sklepie?"),
generateMessage("Wychowawca", "Upomnienie od wychowawcy za nieobecności"),
generateMessage("Kochanowska Joanna", "Poprawa rozprawki - termin"),
)
private fun generateMessage(sender: String, subject: String) = Message(
sender = sender,
subject = subject,
studentId = 0,
realId = 0,
messageId = 0,
senderId = 0,
recipient = "",
date = LocalDateTime.now(),
folderId = 0,
unread = true,
removed = false,
hasAttachments = false
)

View File

@ -0,0 +1,29 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
import java.time.LocalDate
val debugNoteItems = listOf(
generateNote("Aleksadra Krajewska", "Przeszkadzanie na lekcjach", NoteCategory.NEGATIVE),
generateNote("Zofia Czerwińska", "Udział w konkursie szkolnym", NoteCategory.POSITIVE),
generateNote("Stanisław Krupa", "Kultura języka", NoteCategory.NEUTRAL),
generateNote("Karolina Kowalska", "Wypełnianie obowiązków ucznia", NoteCategory.NEUTRAL),
generateNote("Joanna Krupa", "Umycie tablicy cifem", NoteCategory.POSITIVE),
generateNote("Duchowicz Maksymilian", "Reprezentowanie szkoły", NoteCategory.POSITIVE),
generateNote("Michał Mazur", "Przeszkadzanie na lekcji", NoteCategory.NEGATIVE),
generateNote("Karolina Kowalska", "Wypełnianie obowiązków ucznia", NoteCategory.NEGATIVE),
generateNote("Aleksandra Krajewska", "Wysadzenie klasy w powietrze", NoteCategory.NEGATIVE),
)
private fun generateNote(teacher: String, category: String, type: NoteCategory) = Note(
teacher = teacher,
category = category,
categoryType = type.id,
studentId = 0,
date = LocalDate.now(),
teacherSymbol = "",
isPointsShow = false,
points = 0,
content = ""
)

View File

@ -0,0 +1,24 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
import java.time.LocalDate
val debugSchoolAnnouncementItems = listOf(
generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych\n03.05.2021 - poniedziałek"),
generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do noszenia maseczek"),
generateAnnouncement("Święto szkoły", "W najbliższych dniach obchodzimy święto szkoły, podczas którego..."),
generateAnnouncement("Rocznica odzyskania przez szkołę sztandaru", "Juz niedługo, bo za tydzień, a dokładnie za 8 dni..."),
generateAnnouncement("Ogłoszenie w sprawie otwarcia stołówki", "Wszyscy uczniowie zainteresowani obiadami w szkole..."),
generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie pilnym"),
generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych\n21.06.2021 - poniedziałek"),
generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do zdjęcia maseczek"),
generateAnnouncement("Święto państwowe", "W najbliższych dniach obchodzimy święto państwowe, podczas którego..."),
generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie wolnym"),
)
private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement(
subject = subject,
content = content,
studentId = 0,
date = LocalDate.now()
)

View File

@ -32,6 +32,7 @@ import io.github.wulkanowy.databinding.ActivityMainBinding
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
@ -39,6 +40,7 @@ import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo
@ -107,7 +109,9 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
MainView.Section.MESSAGE.id to MessageFragment.newInstance(),
MainView.Section.HOMEWORK.id to HomeworkFragment.newInstance(),
MainView.Section.NOTE.id to NoteFragment.newInstance(),
MainView.Section.LUCKY_NUMBER.id to LuckyNumberFragment.newInstance()
MainView.Section.LUCKY_NUMBER.id to LuckyNumberFragment.newInstance(),
MainView.Section.SCHOOL_ANNOUNCEMENT.id to SchoolAnnouncementFragment.newInstance(),
MainView.Section.CONFERENCE.id to ConferenceFragment.newInstance(),
)
@SuppressLint("NewApi")

View File

@ -5,6 +5,7 @@ import io.github.wulkanowy.ui.modules.about.AboutFragment
import io.github.wulkanowy.ui.modules.account.AccountFragment
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
@ -14,6 +15,7 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
@ -35,6 +37,8 @@ fun Fragment.toSection(): MainView.Section? {
is AccountFragment -> MainView.Section.ACCOUNT
is AccountDetailsFragment -> MainView.Section.ACCOUNT
is StudentInfoFragment -> MainView.Section.STUDENT_INFO
is ConferenceFragment -> MainView.Section.CONFERENCE
is SchoolAnnouncementFragment -> MainView.Section.SCHOOL_ANNOUNCEMENT
else -> null
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/debugRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_debug" />
</FrameLayout>

View File

@ -0,0 +1,6 @@
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_debug_notifications" />

View File

@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.modules.about.logviewer.LogViewerFragment">
tools:context=".ui.modules.debug.logviewer.LogViewerFragment">
<HorizontalScrollView
android:layout_width="match_parent"

View File

@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/moreRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/item_more" />
</FrameLayout>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="vertical"
android:padding="16dp"
tools:context=".ui.modules.debug.DebugAdapter">
<TextView
android:id="@+id/debugItemName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="bottom"
android:singleLine="true"
android:textSize="16sp"
tools:text="@tools:sample/lorem/random" />
</LinearLayout>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="10dp"
tools:ignore="HardcodedText">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:text="@string/grade_title"
android:textSize="26sp"
app:layout_constraintTop_toTopOf="parent"
tools:text="Oceny" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:text="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:text="3"
app:layout_constraintStart_toEndOf="@id/button_1"
app:layout_constraintTop_toTopOf="@id/button_1" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:text="10"
app:layout_constraintStart_toEndOf="@id/button_2"
app:layout_constraintTop_toTopOf="@id/button_2" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -324,7 +324,7 @@
<string name="lucky_number_header">Dnešní šťastné číslo je</string>
<string name="lucky_number_empty">Žádné informace o šťastném čísle</string>
<string name="lucky_number_notify_new_item_title">Šťastné číslo pro dnešek</string>
<string name="lucky_number_notify_new_item">Dnes je šťastným číslem: %d</string>
<string name="lucky_number_notify_new_item">Dnes je šťastným číslem: %s</string>
<string name="lucky_number_history_button">Zobrazit historii</string>
<!--Lucky number history-->
<string name="lucky_number_history_title">Historie šťastných čísel</string>

View File

@ -278,7 +278,7 @@
<string name="lucky_number_header">Die heutige Glücksnummer ist </string>
<string name="lucky_number_empty">Keine Information über die Glücksnummer.</string>
<string name="lucky_number_notify_new_item_title">Glücksnummer für heute</string>
<string name="lucky_number_notify_new_item">Die heutige Glücksnummer ist: %d</string>
<string name="lucky_number_notify_new_item">Die heutige Glücksnummer ist: %s</string>
<string name="lucky_number_history_button">Verlauf anzeigen</string>
<!--Lucky number history-->
<string name="lucky_number_history_title">Glücksnummerverlauf</string>

View File

@ -324,7 +324,7 @@
<string name="lucky_number_header">Today\'s lucky number is</string>
<string name="lucky_number_empty">No info about the lucky number</string>
<string name="lucky_number_notify_new_item_title">Lucky number for today</string>
<string name="lucky_number_notify_new_item">Today\'s lucky number is: %d</string>
<string name="lucky_number_notify_new_item">Today\'s lucky number is: %s</string>
<string name="lucky_number_history_button">Show history</string>
<!--Lucky number history-->
<string name="lucky_number_history_title">Lucky number history</string>

View File

@ -324,7 +324,7 @@
<string name="lucky_number_header">Dzisiejszym szczęśliwym numerkiem jest</string>
<string name="lucky_number_empty">Brak informacji o szczęśliwym numerku</string>
<string name="lucky_number_notify_new_item_title">Szczęśliwy numerek na dzisiaj</string>
<string name="lucky_number_notify_new_item">Dziś szczęśliwym numerkiem jest: %d</string>
<string name="lucky_number_notify_new_item">Dziś szczęśliwym numerkiem jest: %s</string>
<string name="lucky_number_history_button">Pokaż historię</string>
<!--Lucky number history-->
<string name="lucky_number_history_title">Historia numerków</string>

View File

@ -324,7 +324,7 @@
<string name="lucky_number_header">Сегодняшний счастливый номер это</string>
<string name="lucky_number_empty">Нет данных о счастливом номере</string>
<string name="lucky_number_notify_new_item_title">Сегодняшний счастливый номер</string>
<string name="lucky_number_notify_new_item">Сегодняшний счастливый номер это: %d</string>
<string name="lucky_number_notify_new_item">Сегодняшний счастливый номер это: %s</string>
<string name="lucky_number_history_button">Показать историю</string>
<!--Lucky number history-->
<string name="lucky_number_history_title">История удачных чисел</string>

View File

@ -324,7 +324,7 @@
<string name="lucky_number_header">Dnešné šťastné číslo je</string>
<string name="lucky_number_empty">Žiadne informácie o šťastnom čísle</string>
<string name="lucky_number_notify_new_item_title">Šťastné číslo pre dnešok</string>
<string name="lucky_number_notify_new_item">Dnes je šťastným číslom: %d</string>
<string name="lucky_number_notify_new_item">Dnes je šťastným číslom: %s</string>
<string name="lucky_number_history_button">Zobraziť históriu</string>
<!--Lucky number history-->
<string name="lucky_number_history_title">História šťastných čísel</string>

View File

@ -324,7 +324,7 @@
<string name="lucky_number_header">Сьогоднішній щасливий номер</string>
<string name="lucky_number_empty">Брак інформації о щасливому номері</string>
<string name="lucky_number_notify_new_item_title">Сьогоднішній щасливий номер</string>
<string name="lucky_number_notify_new_item">Сьогоднішнім щасливим номером є %d</string>
<string name="lucky_number_notify_new_item">Сьогоднішнім щасливим номером є %s</string>
<string name="lucky_number_history_button">Показати історію</string>
<!--Lucky number history-->
<string name="lucky_number_history_title">Історія щасливих чисел</string>

View File

@ -40,7 +40,7 @@
<item>https://vulcan.net.pl/?login</item>
<item>https://vulcan.net.pl/?login</item>
<item>https://vulcan.net.pl/?login</item>
<item>http://fakelog.cf/?email</item>
<item>http://fakelog.tk/?email</item>
</string-array>
<string-array name="hosts_symbols">
<item>Default</item>

View File

@ -11,6 +11,8 @@
<string name="more_title">More</string>
<string name="about_title">About</string>
<string name="logviewer_title">Log viewer</string>
<string name="debug_title">Debug</string>
<string name="notification_debug_title">Notification debug</string>
<string name="contributors_title">Contributors</string>
<string name="license_title">Licenses</string>
<string name="message_title">Messages</string>
@ -310,7 +312,7 @@
<string name="lucky_number_header">Today\'s lucky number is</string>
<string name="lucky_number_empty">No info about the lucky number</string>
<string name="lucky_number_notify_new_item_title">Lucky number for today</string>
<string name="lucky_number_notify_new_item">Today\'s lucky number is: %d</string>
<string name="lucky_number_notify_new_item">Today\'s lucky number is: %s</string>
<string name="lucky_number_history_button">Show history</string>
<!--Lucky number history-->