forked from github/wulkanowy-mirror
Add new notifications (#1243)
This commit is contained in:
@ -86,6 +86,7 @@ import io.github.wulkanowy.data.db.migrations.Migration32
|
||||
import io.github.wulkanowy.data.db.migrations.Migration33
|
||||
import io.github.wulkanowy.data.db.migrations.Migration34
|
||||
import io.github.wulkanowy.data.db.migrations.Migration35
|
||||
import io.github.wulkanowy.data.db.migrations.Migration36
|
||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||
@ -132,7 +133,7 @@ import javax.inject.Singleton
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 35
|
||||
const val VERSION_SCHEMA = 36
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
@ -168,7 +169,8 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
Migration32(),
|
||||
Migration33(),
|
||||
Migration34(),
|
||||
Migration35(appInfo)
|
||||
Migration35(appInfo),
|
||||
Migration36()
|
||||
)
|
||||
|
||||
fun newInstance(
|
||||
|
@ -36,4 +36,7 @@ data class Exam(
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
@ColumnInfo(name = "is_notified")
|
||||
var isNotified: Boolean = true
|
||||
}
|
||||
|
@ -37,4 +37,7 @@ data class Homework(
|
||||
|
||||
@ColumnInfo(name = "is_done")
|
||||
var isDone: Boolean = false
|
||||
|
||||
@ColumnInfo(name = "is_notified")
|
||||
var isNotified: Boolean = true
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration36 : Migration(35, 36) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
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")
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.ExamDao
|
||||
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.mappers.mapToEntities
|
||||
@ -12,6 +13,8 @@ import io.github.wulkanowy.utils.init
|
||||
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
|
||||
@ -28,20 +31,54 @@ class ExamRepository @Inject constructor(
|
||||
|
||||
private val cacheKey = "exam"
|
||||
|
||||
fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
|
||||
fun getExams(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
start: LocalDate,
|
||||
end: LocalDate,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
|
||||
query = { examDb.loadAll(semester.diaryId, semester.studentId, start.startExamsDay, start.endExamsDay) },
|
||||
shouldFetch = {
|
||||
it.isEmpty() || forceRefresh
|
||||
|| refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end))
|
||||
},
|
||||
query = {
|
||||
examDb.loadAll(
|
||||
diaryId = semester.diaryId,
|
||||
studentId = semester.studentId,
|
||||
from = start.startExamsDay,
|
||||
end = start.endExamsDay
|
||||
)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getExams(start.startExamsDay, start.endExamsDay, semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
val examsToSave = (new uniqueSubtract old).onEach {
|
||||
if (notify) it.isNotified = false
|
||||
}
|
||||
|
||||
examDb.deleteAll(old uniqueSubtract new)
|
||||
examDb.insertAll(new uniqueSubtract old)
|
||||
examDb.insertAll(examsToSave)
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||
},
|
||||
filterResult = { it.filter { item -> item.date in start..end } }
|
||||
)
|
||||
|
||||
fun getNotNotifiedExam(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)
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ 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
|
||||
@ -29,18 +30,39 @@ class HomeworkRepository @Inject constructor(
|
||||
|
||||
private val cacheKey = "homework"
|
||||
|
||||
fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
|
||||
fun getHomework(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
start: LocalDate,
|
||||
end: LocalDate,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
|
||||
query = { homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday) },
|
||||
shouldFetch = {
|
||||
it.isEmpty() || forceRefresh ||
|
||||
refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end))
|
||||
},
|
||||
query = {
|
||||
homeworkDb.loadAll(
|
||||
semesterId = semester.semesterId,
|
||||
studentId = semester.studentId,
|
||||
from = start.monday,
|
||||
end = end.sunday
|
||||
)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getHomework(start.monday, end.sunday)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
val homeWorkToSave = (new uniqueSubtract old).onEach {
|
||||
if (notify) it.isNotified = false
|
||||
}
|
||||
|
||||
homeworkDb.deleteAll(old uniqueSubtract new)
|
||||
homeworkDb.insertAll(new uniqueSubtract old)
|
||||
homeworkDb.insertAll(homeWorkToSave)
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||
}
|
||||
@ -51,4 +73,15 @@ class HomeworkRepository @Inject constructor(
|
||||
isDone = !isDone
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fun getNotNotifiedHomework(
|
||||
semester: Semester,
|
||||
start: LocalDate,
|
||||
end: LocalDate
|
||||
) = homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday)
|
||||
.map {
|
||||
it.filter { homework -> !homework.isNotified }
|
||||
}
|
||||
|
||||
suspend fun updateHomework(homework: List<Homework>) = homeworkDb.updateAll(homework)
|
||||
}
|
@ -15,7 +15,9 @@ import dagger.multibindings.IntoSet
|
||||
import io.github.wulkanowy.services.sync.channels.Channel
|
||||
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
||||
import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewExamChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewGradesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewHomeworkChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.PushChannel
|
||||
@ -115,6 +117,14 @@ abstract class ServicesModule {
|
||||
@IntoSet
|
||||
abstract fun provideLuckyNumberChannel(channel: LuckyNumberChannel): Channel
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideNewExamChannel(channel: NewExamChannel): Channel
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideNewHomeworkChannel(channel: NewHomeworkChannel): Channel
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideNewGradesChannel(channel: NewGradesChannel): Channel
|
||||
|
@ -0,0 +1,32 @@
|
||||
package io.github.wulkanowy.services.sync.channels
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import javax.inject.Inject
|
||||
|
||||
@TargetApi(26)
|
||||
class NewExamChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
@ApplicationContext private val context: Context
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "new_exam_channel"
|
||||
}
|
||||
|
||||
override fun create() {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_exam), NotificationManager.IMPORTANCE_HIGH)
|
||||
.apply {
|
||||
enableLights(true)
|
||||
enableVibration(true)
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package io.github.wulkanowy.services.sync.channels
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import javax.inject.Inject
|
||||
|
||||
@TargetApi(26)
|
||||
class NewHomeworkChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
@ApplicationContext private val context: Context
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "new_homework_channel"
|
||||
}
|
||||
|
||||
override fun create() {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_homework), NotificationManager.IMPORTANCE_HIGH)
|
||||
.apply {
|
||||
enableLights(true)
|
||||
enableVibration(true)
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
})
|
||||
}
|
||||
}
|
@ -1,15 +1,85 @@
|
||||
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.getCompatColor
|
||||
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(private val examRepository: ExamRepository) : Work {
|
||||
class ExamWork @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val examRepository: ExamRepository,
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val preferencesRepository: PreferencesRepository
|
||||
) : Work {
|
||||
|
||||
override suspend fun doWork(student: Student, semester: Semester) {
|
||||
examRepository.getExams(student, semester, now(), now(), true).waitForResult()
|
||||
examRepository.getExams(
|
||||
student = student,
|
||||
semester = semester,
|
||||
start = now(),
|
||||
end = now(),
|
||||
forceRefresh = true,
|
||||
notify = preferencesRepository.isNotificationsEnable
|
||||
).waitForResult()
|
||||
|
||||
examRepository.getNotNotifiedExam(semester, now()).first().let {
|
||||
if (it.isNotEmpty()) 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_main_exam)
|
||||
.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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,90 @@
|
||||
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.getCompatColor
|
||||
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(private val homeworkRepository: HomeworkRepository) : Work {
|
||||
class HomeworkWork @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val homeworkRepository: HomeworkRepository,
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val preferencesRepository: PreferencesRepository
|
||||
) : Work {
|
||||
|
||||
override suspend fun doWork(student: Student, semester: Semester) {
|
||||
homeworkRepository.getHomework(student, semester, now().monday, now().sunday, true).waitForResult()
|
||||
homeworkRepository.getHomework(
|
||||
student = student,
|
||||
semester = semester,
|
||||
start = now().monday,
|
||||
end = now().sunday,
|
||||
forceRefresh = true,
|
||||
notify = preferencesRepository.isNotificationsEnable
|
||||
).waitForResult()
|
||||
|
||||
homeworkRepository.getNotNotifiedHomework(semester, now().monday, now().sunday).first()
|
||||
.let {
|
||||
if (it.isNotEmpty()) 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_more_homework)
|
||||
.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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user