forked from github/wulkanowy-mirror
Add notification about upcoming lesson (#578)
This commit is contained in:
parent
115da64167
commit
29226dd93e
@ -8,12 +8,15 @@ import androidx.test.filters.SdkSuppress
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
|
||||
import io.github.wulkanowy.data.repositories.getStudent
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.mockk
|
||||
import io.reactivex.Single
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@ -34,11 +37,17 @@ class TimetableRepositoryTest {
|
||||
.strategy(TestInternetObservingStrategy())
|
||||
.build()
|
||||
|
||||
@MockK
|
||||
private lateinit var studentMock: Student
|
||||
|
||||
private val student = getStudent()
|
||||
|
||||
@MockK
|
||||
private lateinit var semesterMock: Semester
|
||||
|
||||
@MockK
|
||||
private lateinit var timetableNotificationSchedulerHelper: TimetableNotificationSchedulerHelper
|
||||
|
||||
private lateinit var timetableRemote: TimetableRemote
|
||||
|
||||
private lateinit var timetableLocal: TimetableLocal
|
||||
@ -52,10 +61,17 @@ class TimetableRepositoryTest {
|
||||
timetableLocal = TimetableLocal(testDb.timetableDao)
|
||||
timetableRemote = TimetableRemote(mockSdk)
|
||||
|
||||
every { timetableNotificationSchedulerHelper.scheduleNotifications(any(), any()) } returns mockk()
|
||||
every { timetableNotificationSchedulerHelper.cancelScheduled(any(), any()) } returns mockk()
|
||||
|
||||
every { studentMock.studentId } returns 1
|
||||
every { studentMock.studentName } returns "Jan Kowalski"
|
||||
|
||||
every { semesterMock.studentId } returns 1
|
||||
every { semesterMock.diaryId } returns 2
|
||||
every { semesterMock.schoolYear } returns 2019
|
||||
every { semesterMock.semesterId } returns 1
|
||||
|
||||
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
|
||||
}
|
||||
|
||||
@ -80,7 +96,7 @@ class TimetableRepositoryTest {
|
||||
createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F")
|
||||
))
|
||||
|
||||
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote)
|
||||
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote, timetableNotificationSchedulerHelper)
|
||||
.getTimetable(student, semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true)
|
||||
.blockingGet()
|
||||
|
||||
@ -126,7 +142,7 @@ class TimetableRepositoryTest {
|
||||
createTimetableRemote(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true)
|
||||
))
|
||||
|
||||
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote)
|
||||
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote, timetableNotificationSchedulerHelper)
|
||||
.getTimetable(student, semesterMock, LocalDate.of(2019, 12, 23), LocalDate.of(2019, 12, 25), true)
|
||||
.blockingGet()
|
||||
|
||||
|
@ -92,6 +92,8 @@
|
||||
android:resource="@xml/provider_widget_lucky_number" />
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".services.alarm.TimetableNotificationReceiver" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="${applicationId}.workmanager-init"
|
||||
|
@ -1,9 +1,11 @@
|
||||
package io.github.wulkanowy.data
|
||||
|
||||
import android.app.AlarmManager
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.Resources
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy
|
||||
|
@ -55,6 +55,10 @@ class PreferencesRepository @Inject constructor(
|
||||
val isNotificationsEnable: Boolean
|
||||
get() = getBoolean(R.string.pref_key_notifications_enable, R.bool.pref_default_notifications_enable)
|
||||
|
||||
val isUpcomingLessonsNotificationsEnableKey = context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
|
||||
val isUpcomingLessonsNotificationsEnable: Boolean
|
||||
get() = getBoolean(isUpcomingLessonsNotificationsEnableKey, R.bool.pref_default_notification_upcoming_lessons_enable)
|
||||
|
||||
val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
|
||||
val isDebugNotificationEnable: Boolean
|
||||
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
|
||||
|
@ -5,6 +5,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||
import io.github.wulkanowy.utils.friday
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
@ -18,7 +19,8 @@ import javax.inject.Singleton
|
||||
class TimetableRepository @Inject constructor(
|
||||
private val settings: InternetObservingSettings,
|
||||
private val local: TimetableLocal,
|
||||
private val remote: TimetableRemote
|
||||
private val remote: TimetableRemote,
|
||||
private val schedulerHelper: TimetableNotificationSchedulerHelper
|
||||
) {
|
||||
|
||||
fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single<List<Timetable>> {
|
||||
@ -31,8 +33,8 @@ class TimetableRepository @Inject constructor(
|
||||
local.getTimetable(semester, monday, friday)
|
||||
.toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteTimetable(old.uniqueSubtract(new))
|
||||
local.saveTimetable(new.uniqueSubtract(old).map { item ->
|
||||
local.deleteTimetable(old.uniqueSubtract(new).also { schedulerHelper.cancelScheduled(it) })
|
||||
local.saveTimetable(new.uniqueSubtract(old).also { schedulerHelper.scheduleNotifications(it, student) }.map { item ->
|
||||
item.also { new ->
|
||||
old.singleOrNull { new.start == it.start }?.let { old ->
|
||||
return@map new.copy(
|
||||
@ -45,7 +47,7 @@ class TimetableRepository @Inject constructor(
|
||||
}
|
||||
}.flatMap {
|
||||
local.getTimetable(semester, monday, friday).toSingle(emptyList())
|
||||
}).map { list -> list.filter { it.date in start..end } }
|
||||
}).map { list -> list.filter { it.date in start..end }.also { schedulerHelper.scheduleNotifications(it, student) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import io.github.wulkanowy.di.scopes.PerActivity
|
||||
import io.github.wulkanowy.ui.base.ErrorDialog
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginModule
|
||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity
|
||||
@ -48,4 +49,7 @@ internal abstract class BindingModule {
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLuckyNumberWidgetProvider(): LuckyNumberWidgetProvider
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindTimetableNotificationReceiver(): TimetableNotificationReceiver
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package io.github.wulkanowy.services
|
||||
|
||||
import android.app.AlarmManager
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.work.WorkManager
|
||||
import com.squareup.inject.assisted.dagger2.AssistedModule
|
||||
import dagger.Binds
|
||||
@ -15,6 +17,7 @@ import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewGradesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel
|
||||
import io.github.wulkanowy.services.sync.channels.PushChannel
|
||||
import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork
|
||||
import io.github.wulkanowy.services.sync.works.AttendanceWork
|
||||
@ -46,6 +49,10 @@ abstract class ServicesModule {
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideNotificationManager(context: Context) = NotificationManagerCompat.from(context)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAlarmManager(context: Context): AlarmManager = context.getSystemService()!!
|
||||
}
|
||||
|
||||
@ContributesAndroidInjector
|
||||
@ -126,4 +133,8 @@ abstract class ServicesModule {
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun providePushChannel(channel: PushChannel): Channel
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideUpcomingLessonsChannel(channel: UpcomingLessonsChannel): Channel
|
||||
}
|
||||
|
@ -0,0 +1,117 @@
|
||||
package io.github.wulkanowy.services.alarm
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION_CODES.N
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import dagger.android.AndroidInjection
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.github.wulkanowy.utils.toLocalDateTime
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableNotificationReceiver : BroadcastReceiver() {
|
||||
|
||||
@Inject
|
||||
lateinit var studentRepository: StudentRepository
|
||||
|
||||
@Inject
|
||||
lateinit var schedulers: SchedulersProvider
|
||||
|
||||
companion object {
|
||||
const val NOTIFICATION_TYPE_CURRENT = 1
|
||||
const val NOTIFICATION_TYPE_UPCOMING = 2
|
||||
const val NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION = 3
|
||||
|
||||
const val NOTIFICATION_ID = "id"
|
||||
|
||||
const val STUDENT_NAME = "student_name"
|
||||
const val STUDENT_ID = "student_id"
|
||||
const val LESSON_TYPE = "type"
|
||||
const val LESSON_TITLE = "title"
|
||||
const val LESSON_ROOM = "room"
|
||||
const val LESSON_NEXT_TITLE = "next_title"
|
||||
const val LESSON_NEXT_ROOM = "next_room"
|
||||
const val LESSON_START = "start_timestamp"
|
||||
const val LESSON_END = "end_timestamp"
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
Timber.d("Receiving intent... ${intent.toUri(0)}")
|
||||
AndroidInjection.inject(this, context)
|
||||
|
||||
studentRepository.getCurrentStudent(false)
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.subscribe({
|
||||
val studentId = intent.getIntExtra(STUDENT_ID, 0)
|
||||
if (it.studentId == studentId) prepareNotification(context, intent)
|
||||
else Timber.d("Notification studentId($studentId) differs from current(${it.studentId})")
|
||||
}, { Timber.e(it) })
|
||||
}
|
||||
|
||||
private fun prepareNotification(context: Context, intent: Intent) {
|
||||
val type = intent.getIntExtra(LESSON_TYPE, 0)
|
||||
val notificationId = intent.getIntExtra(NOTIFICATION_ID, MainView.Section.TIMETABLE.id)
|
||||
|
||||
if (type == NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION) {
|
||||
return NotificationManagerCompat.from(context).cancel(notificationId)
|
||||
}
|
||||
|
||||
val studentId = intent.getIntExtra(STUDENT_ID, 0)
|
||||
val studentName = intent.getStringExtra(STUDENT_NAME)
|
||||
|
||||
val subject = intent.getStringExtra(LESSON_TITLE)
|
||||
val room = intent.getStringExtra(LESSON_ROOM)
|
||||
|
||||
val start = intent.getLongExtra(LESSON_START, 0)
|
||||
val end = intent.getLongExtra(LESSON_END, 0)
|
||||
|
||||
val nextSubject = intent.getStringExtra(LESSON_NEXT_TITLE)
|
||||
val nextRoom = intent.getStringExtra(LESSON_NEXT_ROOM)
|
||||
|
||||
Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: ${start.toLocalDateTime()}, student: $studentId")
|
||||
|
||||
showNotification(context, notificationId, studentName,
|
||||
if (type == NOTIFICATION_TYPE_CURRENT) end else start, end - start,
|
||||
context.getString(if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next, "($room) $subject".removePrefix("()")),
|
||||
nextSubject?.let { context.getString(R.string.timetable_later, "($nextRoom) $nextSubject".removePrefix("()")) }
|
||||
)
|
||||
}
|
||||
|
||||
private fun showNotification(context: Context, notificationId: Int, studentName: String?, countDown: Long, timeout: Long, title: String, next: String?) {
|
||||
NotificationManagerCompat.from(context).notify(notificationId, NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setContentTitle(title)
|
||||
.setContentText(next)
|
||||
.setAutoCancel(false)
|
||||
.setOngoing(true)
|
||||
.setWhen(countDown)
|
||||
.apply {
|
||||
if (Build.VERSION.SDK_INT >= N) setUsesChronometer(true)
|
||||
}
|
||||
.setTimeoutAfter(timeout)
|
||||
.setSmallIcon(R.drawable.ic_stat_timetable)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setStyle(NotificationCompat.InboxStyle().also {
|
||||
it.setSummaryText(studentName)
|
||||
it.addLine(next)
|
||||
})
|
||||
.setContentIntent(PendingIntent.getActivity(context, MainView.Section.TIMETABLE.id,
|
||||
MainActivity.getStartIntent(context, MainView.Section.TIMETABLE, true), FLAG_UPDATE_CURRENT))
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package io.github.wulkanowy.services.alarm
|
||||
|
||||
import android.app.AlarmManager
|
||||
import android.app.AlarmManager.RTC_WAKEUP
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_CANCEL_CURRENT
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.app.AlarmManagerCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_END
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_NEXT_ROOM
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_NEXT_TITLE
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_ROOM
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_START
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_TITLE
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_TYPE
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_ID
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_TYPE_CURRENT
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_TYPE_UPCOMING
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_ID
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.toTimestamp
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import org.threeten.bp.LocalDateTime.now
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableNotificationSchedulerHelper @Inject constructor(
|
||||
private val context: Context,
|
||||
private val alarmManager: AlarmManager,
|
||||
private val preferencesRepository: PreferencesRepository
|
||||
) {
|
||||
|
||||
private fun getRequestCode(time: LocalDateTime, studentId: Int) = (time.toTimestamp() * studentId).toInt()
|
||||
|
||||
private fun getUpcomingLessonTime(index: Int, day: List<Timetable>, lesson: Timetable): LocalDateTime {
|
||||
return day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
|
||||
}
|
||||
|
||||
fun cancelScheduled(lessons: List<Timetable>, studentId: Int = 1) {
|
||||
lessons.sortedBy { it.start }.forEachIndexed { index, lesson ->
|
||||
val upcomingTime = getUpcomingLessonTime(index, lessons, lesson)
|
||||
cancelScheduledTo(upcomingTime..lesson.start, getRequestCode(upcomingTime, studentId))
|
||||
cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId))
|
||||
|
||||
Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId")
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelScheduledTo(range: ClosedRange<LocalDateTime>, requestCode: Int) {
|
||||
if (now() in range) cancelNotification()
|
||||
alarmManager.cancel(PendingIntent.getBroadcast(context, requestCode, Intent(), FLAG_CANCEL_CURRENT))
|
||||
}
|
||||
|
||||
fun cancelNotification() = NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id)
|
||||
|
||||
fun scheduleNotifications(lessons: List<Timetable>, student: Student) {
|
||||
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) return cancelScheduled(lessons, student.studentId)
|
||||
|
||||
lessons.groupBy { it.date }
|
||||
.map { it.value.sortedBy { lesson -> lesson.start } }
|
||||
.map { it.filter { lesson -> !lesson.canceled && lesson.isStudentPlan } }
|
||||
.map { day ->
|
||||
day.forEachIndexed { index, lesson ->
|
||||
val intent = createIntent(student, lesson, day.getOrNull(index + 1))
|
||||
|
||||
if (lesson.start > now()) {
|
||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, day, lesson))
|
||||
}
|
||||
|
||||
if (lesson.end > now()) {
|
||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start)
|
||||
if (day.lastIndex == index) {
|
||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createIntent(student: Student, lesson: Timetable, nextLesson: Timetable?): Intent {
|
||||
return Intent(context, TimetableNotificationReceiver::class.java).apply {
|
||||
putExtra(STUDENT_ID, student.studentId)
|
||||
putExtra(STUDENT_NAME, student.studentName)
|
||||
putExtra(LESSON_ROOM, lesson.room)
|
||||
putExtra(LESSON_START, lesson.start.toTimestamp())
|
||||
putExtra(LESSON_END, lesson.end.toTimestamp())
|
||||
putExtra(LESSON_TITLE, lesson.subject)
|
||||
putExtra(LESSON_NEXT_TITLE, nextLesson?.subject)
|
||||
putExtra(LESSON_NEXT_ROOM, nextLesson?.room)
|
||||
}
|
||||
}
|
||||
|
||||
private fun scheduleBroadcast(intent: Intent, studentId: Int, notificationType: Int, time: LocalDateTime) {
|
||||
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, RTC_WAKEUP, time.toTimestamp(),
|
||||
PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also {
|
||||
it.putExtra(NOTIFICATION_ID, MainView.Section.TIMETABLE.id)
|
||||
it.putExtra(LESSON_TYPE, notificationType)
|
||||
}, FLAG_CANCEL_CURRENT)
|
||||
)
|
||||
Timber.d("TimetableNotification scheduled: type: $notificationType, subject: ${intent.getStringExtra(LESSON_TITLE)}, start: $time, student: $studentId")
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package io.github.wulkanowy.services.sync.channels
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Notification.VISIBILITY_PUBLIC
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager.IMPORTANCE_DEFAULT
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import io.github.wulkanowy.R
|
||||
import javax.inject.Inject
|
||||
|
||||
@TargetApi(26)
|
||||
class UpcomingLessonsChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val context: Context
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "lesson_channel"
|
||||
}
|
||||
|
||||
override fun create() {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_upcoming_lessons), IMPORTANCE_DEFAULT).apply {
|
||||
lockscreenVisibility = VISIBILITY_PUBLIC
|
||||
setShowBadge(false)
|
||||
enableVibration(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import androidx.work.WorkInfo
|
||||
import com.chuckerteam.chucker.api.ChuckerCollector
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
@ -20,6 +21,7 @@ class SettingsPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val timetableNotificationHelper: TimetableNotificationSchedulerHelper,
|
||||
private val analytics: FirebaseAnalyticsHelper,
|
||||
private val syncManager: SyncManager,
|
||||
private val chuckerCollector: ChuckerCollector,
|
||||
@ -36,17 +38,17 @@ class SettingsPresenter @Inject constructor(
|
||||
fun onSharedPreferenceChanged(key: String) {
|
||||
Timber.i("Change settings $key")
|
||||
|
||||
with(preferencesRepository) {
|
||||
preferencesRepository.apply {
|
||||
when (key) {
|
||||
serviceEnableKey -> with(syncManager) { if (isServiceEnabled) startPeriodicSyncWorker() else stopSyncWorker() }
|
||||
servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startPeriodicSyncWorker(true)
|
||||
isDebugNotificationEnableKey -> chuckerCollector.showNotification = isDebugNotificationEnable
|
||||
appThemeKey -> view?.recreateView()
|
||||
isUpcomingLessonsNotificationsEnableKey -> if (!isUpcomingLessonsNotificationsEnable) timetableNotificationHelper.cancelNotification()
|
||||
appLanguageKey -> view?.run {
|
||||
updateLanguage(if (appLanguage == "system") appInfo.systemLanguage else appLanguage)
|
||||
recreateView()
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
analytics.logEvent("setting_changed", "name" to key)
|
||||
|
@ -4,9 +4,13 @@ import org.threeten.bp.DayOfWeek.FRIDAY
|
||||
import org.threeten.bp.DayOfWeek.MONDAY
|
||||
import org.threeten.bp.DayOfWeek.SATURDAY
|
||||
import org.threeten.bp.DayOfWeek.SUNDAY
|
||||
import org.threeten.bp.Instant.ofEpochMilli
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import org.threeten.bp.LocalDateTime.ofInstant
|
||||
import org.threeten.bp.Month
|
||||
import org.threeten.bp.ZoneId
|
||||
import org.threeten.bp.ZoneOffset
|
||||
import org.threeten.bp.format.DateTimeFormatter.ofPattern
|
||||
import org.threeten.bp.format.TextStyle.FULL_STANDALONE
|
||||
import org.threeten.bp.temporal.TemporalAdjusters.firstInMonth
|
||||
@ -18,6 +22,10 @@ private const val DATE_PATTERN = "dd.MM.yyyy"
|
||||
|
||||
fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate = LocalDate.parse(this, ofPattern(format))
|
||||
|
||||
fun LocalDateTime.toTimestamp() = atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toInstant().toEpochMilli()
|
||||
|
||||
fun Long.toLocalDateTime() = ofInstant(ofEpochMilli(this), ZoneId.systemDefault())
|
||||
|
||||
fun LocalDate.toFormattedString(format: String = DATE_PATTERN): String = format(ofPattern(format))
|
||||
|
||||
fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = format(ofPattern(format))
|
||||
|
BIN
app/src/main/res/drawable-hdpi/ic_stat_timetable.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_stat_timetable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 312 B |
BIN
app/src/main/res/drawable-mdpi/ic_stat_timetable.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_stat_timetable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 275 B |
BIN
app/src/main/res/drawable-xhdpi/ic_stat_timetable.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_stat_timetable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 358 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_stat_timetable.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_stat_timetable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 459 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_stat_timetable.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_stat_timetable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 659 B |
@ -121,6 +121,9 @@
|
||||
<string name="timetable_time">Godziny</string>
|
||||
<string name="timetable_changes">Zmiany</string>
|
||||
<string name="timetable_no_items">Brak lekcji w tym dniu</string>
|
||||
<string name="timetable_now">Teraz: %s</string>
|
||||
<string name="timetable_next">Za chwilę: %s</string>
|
||||
<string name="timetable_later">Później: %s</string>
|
||||
<!--Completed lessons-->
|
||||
<string name="completed_lessons_title">Lekcje zrealizowane</string>
|
||||
<string name="completed_lessons_button">Zobacz lekcje zrealizowane</string>
|
||||
@ -319,6 +322,7 @@
|
||||
<string name="pref_view_app_language">Język aplikacji</string>
|
||||
<string name="pref_notify_header">Powiadomienia</string>
|
||||
<string name="pref_notify_switch">Pokazuj powiadomienia</string>
|
||||
<string name="pref_notify_upcoming_lessons_switch">Pokazuj powiadomienia o następnych lekcjach</string>
|
||||
<string name="pref_notify_debug_switch">Pokazuj powiadomienia debugowania</string>
|
||||
<string name="pref_services_header">Synchronizacja</string>
|
||||
<string name="pref_services_switch">Automatyczna aktualizacja</string>
|
||||
@ -344,6 +348,7 @@
|
||||
<string name="channel_new_message">Nowe wiadomości</string>
|
||||
<string name="channel_new_notes">Nowe uwagi</string>
|
||||
<string name="channel_push">Powiadomienia push</string>
|
||||
<string name="channel_upcoming_lessons">Nadchodzące lekcje</string>
|
||||
<string name="channel_debug">Debugowanie</string>
|
||||
<!--Colors-->
|
||||
<string name="all_black">Czarny</string>
|
||||
|
@ -13,6 +13,7 @@
|
||||
<string name="pref_default_services_interval">60</string>
|
||||
<bool name="pref_default_services_wifi_only">false</bool>
|
||||
<bool name="pref_default_notifications_enable">true</bool>
|
||||
<bool name="pref_default_notification_upcoming_lessons_enable">false</bool>
|
||||
<bool name="pref_default_notification_debug">false</bool>
|
||||
<string name="pref_default_grade_modifier_plus">0.33</string>
|
||||
<string name="pref_default_grade_modifier_minus">0.33</string>
|
||||
|
@ -14,6 +14,7 @@
|
||||
<string name="pref_key_services_wifi_only">services_disable_wifi_only</string>
|
||||
<string name="pref_key_services_force_sync">services_force_sync</string>
|
||||
<string name="pref_key_notifications_enable">notifications_enable</string>
|
||||
<string name="pref_key_notifications_upcoming_lessons_enable">notifications_upcoming_lessons_enable</string>
|
||||
<string name="pref_key_notification_debug">notification_debug</string>
|
||||
<string name="pref_key_grade_modifier_plus">grade_modifier_plus</string>
|
||||
<string name="pref_key_grade_modifier_minus">grade_modifier_minus</string>
|
||||
|
@ -130,6 +130,9 @@
|
||||
<string name="timetable_time">Hours</string>
|
||||
<string name="timetable_changes">Changes</string>
|
||||
<string name="timetable_no_items">No lessons this day</string>
|
||||
<string name="timetable_now">Now: %s</string>
|
||||
<string name="timetable_next">Next: %s</string>
|
||||
<string name="timetable_later">Later: %s</string>
|
||||
|
||||
|
||||
<!--Completed lessons-->
|
||||
@ -356,6 +359,7 @@
|
||||
|
||||
<string name="pref_notify_header">Notifications</string>
|
||||
<string name="pref_notify_switch">Show notifications</string>
|
||||
<string name="pref_notify_upcoming_lessons_switch">Show upcoming lesson notifications</string>
|
||||
<string name="pref_notify_debug_switch">Show debug notifications</string>
|
||||
|
||||
<string name="pref_services_header">Synchronization</string>
|
||||
@ -386,6 +390,7 @@
|
||||
<string name="channel_new_message">New messages</string>
|
||||
<string name="channel_new_notes">New notes</string>
|
||||
<string name="channel_push">Push notifications</string>
|
||||
<string name="channel_upcoming_lessons">Upcoming lessons</string>
|
||||
<string name="channel_debug">Debug</string>
|
||||
|
||||
|
||||
|
@ -96,6 +96,11 @@
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="@string/pref_key_notifications_enable"
|
||||
app:title="@string/pref_notify_switch" />
|
||||
<SwitchPreferenceCompat
|
||||
app:defaultValue="@bool/pref_default_notification_upcoming_lessons_enable"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="@string/pref_key_notifications_upcoming_lessons_enable"
|
||||
app:title="@string/pref_notify_upcoming_lessons_switch" />
|
||||
<SwitchPreferenceCompat
|
||||
app:defaultValue="@bool/pref_default_notification_debug"
|
||||
app:iconSpaceReserved="false"
|
||||
|
Loading…
x
Reference in New Issue
Block a user