forked from github/wulkanowy-mirror
Add timetable changes, attendance notifications and refactor notification deeplinks (#1547)
This commit is contained in:
parent
4401df6203
commit
f88d44f0ec
2408
app/schemas/io.github.wulkanowy.data.db.AppDatabase/43.json
Normal file
2408
app/schemas/io.github.wulkanowy.data.db.AppDatabase/43.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -102,6 +102,7 @@ import io.github.wulkanowy.data.db.migrations.Migration4
|
||||
import io.github.wulkanowy.data.db.migrations.Migration40
|
||||
import io.github.wulkanowy.data.db.migrations.Migration41
|
||||
import io.github.wulkanowy.data.db.migrations.Migration42
|
||||
import io.github.wulkanowy.data.db.migrations.Migration43
|
||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||
import io.github.wulkanowy.data.db.migrations.Migration7
|
||||
@ -151,7 +152,7 @@ import javax.inject.Singleton
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 42
|
||||
const val VERSION_SCHEMA = 43
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
@ -194,7 +195,8 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
Migration39(),
|
||||
Migration40(),
|
||||
Migration41(sharedPrefProvider),
|
||||
Migration42()
|
||||
Migration42(),
|
||||
Migration43()
|
||||
)
|
||||
|
||||
fun newInstance(
|
||||
|
@ -11,6 +11,11 @@ import javax.inject.Singleton
|
||||
@Dao
|
||||
interface AttendanceDao : BaseDao<Attendance> {
|
||||
|
||||
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Attendance>>
|
||||
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :start AND date <= :end")
|
||||
fun loadAll(
|
||||
diaryId: Int,
|
||||
studentId: Int,
|
||||
start: LocalDate,
|
||||
end: LocalDate
|
||||
): Flow<List<Attendance>>
|
||||
}
|
||||
|
@ -47,4 +47,7 @@ data class Attendance(
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
@ColumnInfo(name = "is_notified")
|
||||
var isNotified: Boolean = true
|
||||
}
|
||||
|
@ -50,4 +50,7 @@ data class Timetable(
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
@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 Migration43 : Migration(42, 43) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE Timetable ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
|
||||
database.execSQL("ALTER TABLE Attendance ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
|
||||
}
|
||||
}
|
@ -1,36 +1,19 @@
|
||||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.annotation.StringRes
|
||||
import android.content.Intent
|
||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
|
||||
sealed interface NotificationData {
|
||||
data class NotificationData(
|
||||
val intentToStart: Intent,
|
||||
val title: String,
|
||||
val content: String
|
||||
)
|
||||
|
||||
data class GroupNotificationData(
|
||||
val notificationDataList: List<NotificationData>,
|
||||
val title: String,
|
||||
val content: String,
|
||||
val intentToStart: Intent,
|
||||
val type: NotificationType
|
||||
val startMenu: MainView.Section
|
||||
val icon: Int
|
||||
val titleStringRes: Int
|
||||
val contentStringRes: Int
|
||||
}
|
||||
)
|
||||
|
||||
data class MultipleNotificationsData(
|
||||
override val type: NotificationType,
|
||||
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>,
|
||||
) : NotificationData
|
||||
|
||||
data class OneNotificationData(
|
||||
override val type: NotificationType,
|
||||
override val startMenu: MainView.Section,
|
||||
@DrawableRes override val icon: Int,
|
||||
@StringRes override val titleStringRes: Int,
|
||||
@StringRes override val contentStringRes: Int,
|
||||
|
||||
val contentValues: List<String>,
|
||||
) : NotificationData
|
||||
|
@ -14,6 +14,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.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
@ -38,6 +39,7 @@ class AttendanceRepository @Inject constructor(
|
||||
start: LocalDate,
|
||||
end: LocalDate,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = {
|
||||
@ -56,13 +58,28 @@ class AttendanceRepository @Inject constructor(
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
attendanceDb.deleteAll(old uniqueSubtract new)
|
||||
attendanceDb.insertAll(new uniqueSubtract old)
|
||||
val attendanceToAdd = (new uniqueSubtract old).map { newAttendance ->
|
||||
newAttendance.apply { if (notify) isNotified = false }
|
||||
}
|
||||
attendanceDb.insertAll(attendanceToAdd)
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||
},
|
||||
filterResult = { it.filter { item -> item.date in start..end } }
|
||||
)
|
||||
|
||||
fun getAttendanceFromDatabase(
|
||||
semester: Semester,
|
||||
start: LocalDate,
|
||||
end: LocalDate
|
||||
): Flow<List<Attendance>> {
|
||||
return attendanceDb.loadAll(semester.diaryId, semester.studentId, start, end)
|
||||
}
|
||||
|
||||
suspend fun updateTimetable(timetable: List<Attendance>) {
|
||||
return attendanceDb.updateAll(timetable)
|
||||
}
|
||||
|
||||
suspend fun excuseForAbsence(
|
||||
student: Student, semester: Semester,
|
||||
absenceList: List<Attendance>, reason: String? = null
|
||||
|
@ -47,6 +47,7 @@ class TimetableRepository @Inject constructor(
|
||||
end: LocalDate,
|
||||
forceRefresh: Boolean,
|
||||
refreshAdditional: Boolean = false,
|
||||
notify: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { (timetable, additional, headers) ->
|
||||
@ -67,7 +68,7 @@ class TimetableRepository @Inject constructor(
|
||||
timetableFull.mapToEntities(semester)
|
||||
},
|
||||
saveFetchResult = { timetableOld, timetableNew ->
|
||||
refreshTimetable(student, timetableOld.lessons, timetableNew.lessons)
|
||||
refreshTimetable(student, timetableOld.lessons, timetableNew.lessons, notify)
|
||||
refreshAdditional(timetableOld.additional, timetableNew.additional)
|
||||
refreshDayHeaders(timetableOld.headers, timetableNew.headers)
|
||||
|
||||
@ -117,13 +118,28 @@ class TimetableRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun getTimetableFromDatabase(
|
||||
semester: Semester,
|
||||
from: LocalDate,
|
||||
end: LocalDate
|
||||
): Flow<List<Timetable>> {
|
||||
return timetableDb.loadAll(semester.diaryId, semester.studentId, from, end)
|
||||
}
|
||||
|
||||
suspend fun updateTimetable(timetable: List<Timetable>) {
|
||||
return timetableDb.updateAll(timetable)
|
||||
}
|
||||
|
||||
private suspend fun refreshTimetable(
|
||||
student: Student,
|
||||
lessonsOld: List<Timetable>,
|
||||
lessonsNew: List<Timetable>,
|
||||
notify: Boolean
|
||||
) {
|
||||
val lessonsToRemove = lessonsOld uniqueSubtract lessonsNew
|
||||
val lessonsToAdd = lessonsNew uniqueSubtract lessonsOld
|
||||
val lessonsToAdd = (lessonsNew uniqueSubtract lessonsOld).map { new ->
|
||||
new.apply { if (notify) isNotified = false }
|
||||
}
|
||||
|
||||
timetableDb.deleteAll(lessonsToRemove)
|
||||
timetableDb.insertAll(lessonsToAdd)
|
||||
|
@ -15,6 +15,7 @@ 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.NewAttendanceChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewConferencesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewExamChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewGradesChannel
|
||||
@ -23,6 +24,7 @@ import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewSchoolAnnouncementsChannel
|
||||
import io.github.wulkanowy.services.sync.channels.PushChannel
|
||||
import io.github.wulkanowy.services.sync.channels.TimetableChangeChannel
|
||||
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel
|
||||
import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork
|
||||
import io.github.wulkanowy.services.sync.works.AttendanceWork
|
||||
@ -167,4 +169,12 @@ abstract class ServicesModule {
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideUpcomingLessonsChannel(channel: UpcomingLessonsChannel): Channel
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideChangeTimetableChannel(channel: TimetableChangeChannel): Channel
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideNewAttendanceChannel(channel: NewAttendanceChannel): Channel
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.services.HiltBroadcastReceiver
|
||||
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.flowWithResource
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.github.wulkanowy.utils.toLocalDateTime
|
||||
@ -41,7 +41,7 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
||||
const val NOTIFICATION_TYPE_UPCOMING = 2
|
||||
const val NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION = 3
|
||||
|
||||
const val NOTIFICATION_ID = "id"
|
||||
const val NOTIFICATION_ID = 2137
|
||||
|
||||
const val STUDENT_NAME = "student_name"
|
||||
const val STUDENT_ID = "student_id"
|
||||
@ -71,11 +71,10 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
||||
|
||||
private fun prepareNotification(context: Context, intent: Intent) {
|
||||
val type = intent.getIntExtra(LESSON_TYPE, 0)
|
||||
val notificationId = intent.getIntExtra(NOTIFICATION_ID, MainView.Section.TIMETABLE.id)
|
||||
val isPersistent = preferencesRepository.isUpcomingLessonsNotificationsPersistent
|
||||
|
||||
if (type == NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION) {
|
||||
return NotificationManagerCompat.from(context).cancel(notificationId)
|
||||
return NotificationManagerCompat.from(context).cancel(NOTIFICATION_ID)
|
||||
}
|
||||
|
||||
val studentId = intent.getIntExtra(STUDENT_ID, 0)
|
||||
@ -92,7 +91,8 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
||||
|
||||
Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: ${start.toLocalDateTime()}, student: $studentId")
|
||||
|
||||
showNotification(context, notificationId, isPersistent, studentName,
|
||||
showNotification(
|
||||
context, isPersistent, 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,
|
||||
@ -109,7 +109,6 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
||||
|
||||
private fun showNotification(
|
||||
context: Context,
|
||||
notificationId: Int,
|
||||
isPersistent: Boolean,
|
||||
studentName: String?,
|
||||
countDown: Long,
|
||||
@ -118,7 +117,7 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
||||
next: String?
|
||||
) {
|
||||
NotificationManagerCompat.from(context)
|
||||
.notify(notificationId, NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.notify(NOTIFICATION_ID, NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setContentTitle(title)
|
||||
.setContentText(next)
|
||||
.setAutoCancel(false)
|
||||
@ -138,8 +137,8 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
MainView.Section.TIMETABLE.id,
|
||||
MainActivity.getStartIntent(context, MainView.Section.TIMETABLE, true),
|
||||
NOTIFICATION_ID,
|
||||
MainActivity.getStartIntent(context, Destination.Timetable(), true),
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
|
@ -25,7 +25,6 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio
|
||||
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.DispatchersProvider
|
||||
import io.github.wulkanowy.utils.nickOrName
|
||||
import io.github.wulkanowy.utils.toTimestamp
|
||||
@ -79,7 +78,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
||||
}
|
||||
|
||||
fun cancelNotification() =
|
||||
NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id)
|
||||
NotificationManagerCompat.from(context).cancel(NOTIFICATION_ID)
|
||||
|
||||
suspend fun scheduleNotifications(lessons: List<Timetable>, student: Student) {
|
||||
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) {
|
||||
@ -156,7 +155,6 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
||||
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_UPDATE_CURRENT)
|
||||
)
|
||||
|
@ -0,0 +1,90 @@
|
||||
package io.github.wulkanowy.services.shortcuts
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ShortcutsHelper @Inject constructor(@ApplicationContext private val context: Context) {
|
||||
|
||||
private val destinations = mapOf(
|
||||
"grade" to Destination.Grade,
|
||||
"attendance" to Destination.Attendance,
|
||||
"exam" to Destination.Exam,
|
||||
"timetable" to Destination.Timetable()
|
||||
)
|
||||
|
||||
init {
|
||||
initializeShortcuts()
|
||||
}
|
||||
|
||||
fun getDestination(intent: Intent) =
|
||||
destinations[intent.getStringExtra(EXTRA_SHORTCUT_DESTINATION_ID)]
|
||||
|
||||
private fun initializeShortcuts() {
|
||||
val shortcutsInfo = listOf(
|
||||
ShortcutInfoCompat.Builder(context, "grade_shortcut")
|
||||
.setShortLabel(context.getString(R.string.grade_title))
|
||||
.setLongLabel(context.getString(R.string.grade_title))
|
||||
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_grade))
|
||||
.setIntent(MainActivity.getStartIntent(context, startNewTask = true)
|
||||
.apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "grade")
|
||||
}
|
||||
)
|
||||
.build(),
|
||||
|
||||
ShortcutInfoCompat.Builder(context, "attendance_shortcut")
|
||||
.setShortLabel(context.getString(R.string.attendance_title))
|
||||
.setLongLabel(context.getString(R.string.attendance_title))
|
||||
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_attendance))
|
||||
.setIntent(MainActivity.getStartIntent(context, startNewTask = true)
|
||||
.apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "attendance")
|
||||
}
|
||||
)
|
||||
.build(),
|
||||
|
||||
ShortcutInfoCompat.Builder(context, "exam_shortcut")
|
||||
.setShortLabel(context.getString(R.string.exam_title))
|
||||
.setLongLabel(context.getString(R.string.exam_title))
|
||||
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_exam))
|
||||
.setIntent(MainActivity.getStartIntent(context, startNewTask = true)
|
||||
.apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "exam")
|
||||
}
|
||||
)
|
||||
.build(),
|
||||
|
||||
ShortcutInfoCompat.Builder(context, "timetable_shortcut")
|
||||
.setShortLabel(context.getString(R.string.timetable_title))
|
||||
.setLongLabel(context.getString(R.string.timetable_title))
|
||||
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_timetable))
|
||||
.setIntent(MainActivity.getStartIntent(context, startNewTask = true)
|
||||
.apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "timetable")
|
||||
}
|
||||
)
|
||||
.build()
|
||||
)
|
||||
|
||||
shortcutsInfo.forEach { ShortcutManagerCompat.pushDynamicShortcut(context, it) }
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
private const val EXTRA_SHORTCUT_DESTINATION_ID = "shortcut_destination_id"
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
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 NewAttendanceChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
@ApplicationContext private val context: Context
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "new_attendance_channel"
|
||||
}
|
||||
|
||||
override fun create() {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
context.getString(R.string.channel_new_attendance),
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
)
|
||||
.apply {
|
||||
enableLights(true)
|
||||
enableVibration(true)
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
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 TimetableChangeChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
@ApplicationContext private val context: Context
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "change_timetable_channel"
|
||||
}
|
||||
|
||||
override fun create() {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
context.getString(R.string.channel_change_timetable),
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
)
|
||||
.apply {
|
||||
enableLights(true)
|
||||
enableVibration(true)
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
})
|
||||
}
|
||||
}
|
@ -4,19 +4,15 @@ import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.annotation.PluralsRes
|
||||
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.Notification
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.data.pojos.OneNotificationData
|
||||
import io.github.wulkanowy.data.repositories.NotificationRepository
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.getCompatBitmap
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.github.wulkanowy.utils.nickOrName
|
||||
@ -27,102 +23,17 @@ import kotlin.random.Random
|
||||
class AppNotificationManager @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
@ApplicationContext private val context: Context,
|
||||
private val appInfo: AppInfo,
|
||||
private val notificationRepository: NotificationRepository
|
||||
) {
|
||||
|
||||
suspend fun sendNotification(notificationData: NotificationData, student: Student) =
|
||||
when (notificationData) {
|
||||
is OneNotificationData -> sendOneNotification(notificationData, student)
|
||||
is MultipleNotificationsData -> sendMultipleNotifications(notificationData, student)
|
||||
}
|
||||
|
||||
private suspend fun sendOneNotification(
|
||||
notificationData: OneNotificationData,
|
||||
student: Student
|
||||
) {
|
||||
val content = context.getString(
|
||||
notificationData.contentStringRes,
|
||||
*notificationData.contentValues.toTypedArray()
|
||||
)
|
||||
|
||||
val title = context.getString(notificationData.titleStringRes)
|
||||
|
||||
val notification = getDefaultNotificationBuilder(notificationData)
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setStyle(
|
||||
NotificationCompat.BigTextStyle()
|
||||
.setSummaryText(student.nickOrName)
|
||||
.bigText(content)
|
||||
)
|
||||
.build()
|
||||
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), notification)
|
||||
|
||||
saveNotification(title, content, notificationData, student)
|
||||
}
|
||||
|
||||
private suspend fun sendMultipleNotifications(
|
||||
notificationData: MultipleNotificationsData,
|
||||
student: Student
|
||||
) {
|
||||
val groupType = notificationData.type.group ?: return
|
||||
val group = "${groupType}_${student.id}"
|
||||
|
||||
notificationData.sendSummaryNotification(group, student)
|
||||
|
||||
notificationData.lines.forEach { item ->
|
||||
val title = context.resources.getQuantityString(notificationData.titleStringRes, 1)
|
||||
|
||||
val notification = getDefaultNotificationBuilder(notificationData)
|
||||
.setContentTitle(title)
|
||||
.setContentText(item)
|
||||
.setStyle(
|
||||
NotificationCompat.BigTextStyle()
|
||||
.setSummaryText(student.nickOrName)
|
||||
.bigText(item)
|
||||
)
|
||||
.setGroup(group)
|
||||
.build()
|
||||
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), notification)
|
||||
|
||||
saveNotification(title, item, notificationData, student)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MultipleNotificationsData.sendSummaryNotification(group: String, student: Student) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return
|
||||
|
||||
val summaryNotification = getDefaultNotificationBuilder(this)
|
||||
.setSmallIcon(icon)
|
||||
.setContentTitle(getQuantityString(titleStringRes, lines.size))
|
||||
.setContentText(getQuantityString(contentStringRes, lines.size))
|
||||
.setStyle(
|
||||
NotificationCompat.InboxStyle()
|
||||
.setSummaryText(student.nickOrName)
|
||||
.also { builder -> lines.forEach { builder.addLine(it) } }
|
||||
)
|
||||
.setLocalOnly(true)
|
||||
.setGroup(group)
|
||||
.setGroupSummary(true)
|
||||
.build()
|
||||
|
||||
val groupId = student.id * 100 + type.ordinal
|
||||
notificationManager.notify(groupId.toInt(), summaryNotification)
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private fun getDefaultNotificationBuilder(notificationData: NotificationData): NotificationCompat.Builder {
|
||||
val pendingIntentsFlags = if (appInfo.systemVersion >= Build.VERSION_CODES.M) {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
} else {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
}
|
||||
|
||||
return NotificationCompat.Builder(context, notificationData.type.channel)
|
||||
.setLargeIcon(context.getCompatBitmap(notificationData.icon, R.color.colorPrimary))
|
||||
suspend fun sendSingleNotification(
|
||||
notificationData: NotificationData,
|
||||
notificationType: NotificationType,
|
||||
student: Student
|
||||
) {
|
||||
val notification = NotificationCompat.Builder(context, notificationType.channel)
|
||||
.setLargeIcon(context.getCompatBitmap(notificationType.icon, R.color.colorPrimary))
|
||||
.setSmallIcon(R.drawable.ic_stat_all)
|
||||
.setAutoCancel(true)
|
||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
@ -132,31 +43,122 @@ class AppNotificationManager @Inject constructor(
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
notificationData.startMenu.id,
|
||||
MainActivity.getStartIntent(context, notificationData.startMenu, true),
|
||||
pendingIntentsFlags
|
||||
Random.nextInt(),
|
||||
notificationData.intentToStart,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
.setContentTitle(notificationData.title)
|
||||
.setContentText(notificationData.content)
|
||||
.setStyle(
|
||||
NotificationCompat.BigTextStyle()
|
||||
.setSummaryText(student.nickOrName)
|
||||
.bigText(notificationData.content)
|
||||
)
|
||||
.build()
|
||||
|
||||
notificationManager.notify(Random.nextInt(), notification)
|
||||
saveNotification(notificationData, notificationType, student)
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
suspend fun sendMultipleNotifications(
|
||||
groupNotificationData: GroupNotificationData,
|
||||
student: Student
|
||||
) {
|
||||
val notificationType = groupNotificationData.type
|
||||
val groupType = notificationType.group ?: return
|
||||
val group = "${groupType}_${student.id}"
|
||||
|
||||
sendSummaryNotification(groupNotificationData, group, student)
|
||||
|
||||
groupNotificationData.notificationDataList.forEach { notificationData ->
|
||||
val notification = NotificationCompat.Builder(context, notificationType.channel)
|
||||
.setLargeIcon(context.getCompatBitmap(notificationType.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))
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
Random.nextInt(),
|
||||
notificationData.intentToStart,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
.setContentTitle(notificationData.title)
|
||||
.setContentText(notificationData.content)
|
||||
.setStyle(
|
||||
NotificationCompat.BigTextStyle()
|
||||
.setSummaryText(student.nickOrName)
|
||||
.bigText(notificationData.content)
|
||||
)
|
||||
.setGroup(group)
|
||||
.build()
|
||||
|
||||
notificationManager.notify(Random.nextInt(), notification)
|
||||
saveNotification(notificationData, groupNotificationData.type, student)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendSummaryNotification(
|
||||
groupNotificationData: GroupNotificationData,
|
||||
group: String,
|
||||
student: Student
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return
|
||||
|
||||
val summaryNotification =
|
||||
NotificationCompat.Builder(context, groupNotificationData.type.channel)
|
||||
.setContentTitle(groupNotificationData.title)
|
||||
.setContentText(groupNotificationData.content)
|
||||
.setSmallIcon(groupNotificationData.type.icon)
|
||||
.setAutoCancel(true)
|
||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setStyle(
|
||||
NotificationCompat.InboxStyle()
|
||||
.setSummaryText(student.nickOrName)
|
||||
.also { builder ->
|
||||
groupNotificationData.notificationDataList.forEach {
|
||||
builder.addLine(it.content)
|
||||
}
|
||||
}
|
||||
)
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
Random.nextInt(),
|
||||
groupNotificationData.intentToStart,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
.setLocalOnly(true)
|
||||
.setGroup(group)
|
||||
.setGroupSummary(true)
|
||||
.build()
|
||||
|
||||
val groupId = student.id * 100 + groupNotificationData.type.ordinal
|
||||
notificationManager.notify(groupId.toInt(), summaryNotification)
|
||||
}
|
||||
|
||||
private suspend fun saveNotification(
|
||||
title: String,
|
||||
content: String,
|
||||
notificationData: NotificationData,
|
||||
notificationType: NotificationType,
|
||||
student: Student
|
||||
) {
|
||||
val notificationEntity = Notification(
|
||||
studentId = student.id,
|
||||
title = title,
|
||||
content = content,
|
||||
type = notificationData.type,
|
||||
title = notificationData.title,
|
||||
content = notificationData.content,
|
||||
type = notificationType,
|
||||
date = LocalDateTime.now()
|
||||
)
|
||||
|
||||
notificationRepository.saveNotification(notificationEntity)
|
||||
}
|
||||
|
||||
private fun getQuantityString(@PluralsRes res: Int, arg: Int): String {
|
||||
return context.resources.getQuantityString(res, arg, arg)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,125 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChangeTimetableNotification @Inject constructor(
|
||||
private val appNotificationManager: AppNotificationManager,
|
||||
@ApplicationContext private val context: Context,
|
||||
) {
|
||||
|
||||
suspend fun notify(items: List<Timetable>, student: Student) {
|
||||
val currentTime = LocalDateTime.now()
|
||||
val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime }
|
||||
val notificationDataList = changedLessons.groupBy { it.date }
|
||||
.map { (date, lessons) ->
|
||||
getNotificationContents(date, lessons).map {
|
||||
NotificationData(
|
||||
title = context.getPlural(
|
||||
R.plurals.timetable_notify_new_items_title,
|
||||
1
|
||||
),
|
||||
content = it,
|
||||
intentToStart = MainActivity.getStartIntent(
|
||||
context = context,
|
||||
destination = Destination.Timetable(date),
|
||||
startNewTask = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
.flatten()
|
||||
.ifEmpty { return }
|
||||
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
notificationDataList = notificationDataList,
|
||||
title = context.getPlural(
|
||||
R.plurals.timetable_notify_new_items_title,
|
||||
changedLessons.size
|
||||
),
|
||||
content = context.getPlural(
|
||||
R.plurals.timetable_notify_new_items_group,
|
||||
changedLessons.size,
|
||||
changedLessons.size
|
||||
),
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Timetable(), true),
|
||||
type = NotificationType.CHANGE_TIMETABLE
|
||||
)
|
||||
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
|
||||
private fun getNotificationContents(date: LocalDate, lessons: List<Timetable>): List<String> {
|
||||
val formattedDate = date.toFormattedString("EEE dd.MM")
|
||||
|
||||
return if (lessons.size > 2) {
|
||||
listOf(
|
||||
context.getPlural(
|
||||
R.plurals.timetable_notify_new_items,
|
||||
lessons.size,
|
||||
formattedDate,
|
||||
lessons.size,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
lessons.map {
|
||||
buildString {
|
||||
append(
|
||||
context.getString(
|
||||
R.string.timetable_notify_lesson,
|
||||
formattedDate,
|
||||
it.number,
|
||||
it.subject
|
||||
)
|
||||
)
|
||||
if (it.roomOld.isNotBlank()) {
|
||||
appendLine()
|
||||
append(
|
||||
context.getString(
|
||||
R.string.timetable_notify_change_room,
|
||||
it.roomOld,
|
||||
it.room
|
||||
)
|
||||
)
|
||||
}
|
||||
if (it.teacherOld.isNotBlank() && it.teacher != it.teacherOld) {
|
||||
appendLine()
|
||||
append(
|
||||
context.getString(
|
||||
R.string.timetable_notify_change_teacher,
|
||||
it.teacherOld,
|
||||
it.teacher
|
||||
)
|
||||
)
|
||||
}
|
||||
if (it.subjectOld.isNotBlank()) {
|
||||
appendLine()
|
||||
append(
|
||||
context.getString(
|
||||
R.string.timetable_notify_change_subject,
|
||||
it.subjectOld,
|
||||
it.subject
|
||||
)
|
||||
)
|
||||
}
|
||||
if (it.info.isNotBlank()) {
|
||||
appendLine()
|
||||
append(it.info)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.descriptionRes
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewAttendanceNotification @Inject constructor(
|
||||
private val appNotificationManager: AppNotificationManager,
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
suspend fun notify(items: List<Attendance>, student: Student) {
|
||||
val lines = items.filterNot { it.presence || it.name == "UNKNOWN" }
|
||||
.map {
|
||||
val description = context.getString(it.descriptionRes)
|
||||
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: $description"
|
||||
}
|
||||
.ifEmpty { return }
|
||||
|
||||
val notificationDataList = lines.map {
|
||||
NotificationData(
|
||||
title = context.getPlural(R.plurals.attendance_notify_new_items_title, 1),
|
||||
content = it,
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Attendance, true)
|
||||
)
|
||||
}
|
||||
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
notificationDataList = notificationDataList,
|
||||
title = context.getPlural(
|
||||
R.plurals.attendance_notify_new_items_title,
|
||||
notificationDataList.size
|
||||
),
|
||||
content = context.getPlural(
|
||||
R.plurals.attendance_notify_new_items,
|
||||
notificationDataList.size,
|
||||
notificationDataList.size
|
||||
),
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Attendance, true),
|
||||
type = NotificationType.NEW_ATTENDANCE
|
||||
)
|
||||
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
}
|
@ -1,34 +1,52 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import android.content.Context
|
||||
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.Student
|
||||
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import java.time.LocalDateTime
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewConferenceNotification @Inject constructor(
|
||||
private val appNotificationManager: AppNotificationManager
|
||||
private val appNotificationManager: AppNotificationManager,
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
suspend fun notify(items: List<Conference>, student: Student) {
|
||||
val today = LocalDateTime.now()
|
||||
val lines = items.filter { !it.date.isBefore(today) }.map {
|
||||
"${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}"
|
||||
}.ifEmpty { return }
|
||||
val lines = items.filter { !it.date.isBefore(today) }
|
||||
.map {
|
||||
"${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}"
|
||||
}
|
||||
.ifEmpty { return }
|
||||
|
||||
val notification = MultipleNotificationsData(
|
||||
type = NotificationType.NEW_CONFERENCE,
|
||||
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 = lines
|
||||
val notificationDataList = lines.map {
|
||||
NotificationData(
|
||||
title = context.getPlural(R.plurals.conference_notify_new_item_title, 1),
|
||||
content = it,
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Conference, true)
|
||||
)
|
||||
}
|
||||
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
notificationDataList = notificationDataList,
|
||||
title = context.getPlural(R.plurals.conference_notify_new_item_title, lines.size),
|
||||
content = context.getPlural(
|
||||
R.plurals.conference_notify_new_items,
|
||||
lines.size,
|
||||
lines.size
|
||||
),
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Conference, true),
|
||||
type = NotificationType.NEW_CONFERENCE
|
||||
)
|
||||
|
||||
appNotificationManager.sendNotification(notification, student)
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,52 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import android.content.Context
|
||||
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.Student
|
||||
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewExamNotification @Inject constructor(
|
||||
private val appNotificationManager: AppNotificationManager
|
||||
private val appNotificationManager: AppNotificationManager,
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
suspend fun notify(items: List<Exam>, student: Student) {
|
||||
val today = LocalDate.now()
|
||||
val lines = items.filter { !it.date.isBefore(today) }.map {
|
||||
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.description}"
|
||||
}.ifEmpty { return }
|
||||
val lines = items.filter { !it.date.isBefore(today) }
|
||||
.map {
|
||||
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.description}"
|
||||
}
|
||||
.ifEmpty { return }
|
||||
|
||||
val notification = MultipleNotificationsData(
|
||||
type = NotificationType.NEW_EXAM,
|
||||
icon = R.drawable.ic_main_exam,
|
||||
titleStringRes = R.plurals.exam_notify_new_item_title,
|
||||
contentStringRes = R.plurals.exam_notify_new_item_content,
|
||||
summaryStringRes = R.plurals.exam_number_item,
|
||||
startMenu = MainView.Section.EXAM,
|
||||
lines = lines
|
||||
val notificationDataList = lines.map {
|
||||
NotificationData(
|
||||
title = context.getPlural(R.plurals.exam_notify_new_item_title, 1),
|
||||
content = it,
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Exam, true),
|
||||
)
|
||||
}
|
||||
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
notificationDataList = notificationDataList,
|
||||
title = context.getPlural(R.plurals.exam_notify_new_item_title, lines.size),
|
||||
content = context.getPlural(
|
||||
R.plurals.exam_notify_new_item_content,
|
||||
lines.size,
|
||||
lines.size
|
||||
),
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Exam, true),
|
||||
type = NotificationType.NEW_EXAM
|
||||
)
|
||||
|
||||
appNotificationManager.sendNotification(notification, student)
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
}
|
||||
|
@ -1,62 +1,88 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import android.content.Context
|
||||
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.Student
|
||||
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewGradeNotification @Inject constructor(
|
||||
private val appNotificationManager: AppNotificationManager
|
||||
private val appNotificationManager: AppNotificationManager,
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
suspend fun notifyDetails(items: List<Grade>, student: Student) {
|
||||
val notification = MultipleNotificationsData(
|
||||
type = NotificationType.NEW_GRADE_DETAILS,
|
||||
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}"
|
||||
}
|
||||
val notificationDataList = items.map {
|
||||
NotificationData(
|
||||
title = context.getPlural(R.plurals.grade_new_items, 1),
|
||||
content = "${it.subject}: ${it.entry}",
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Grade, true),
|
||||
)
|
||||
}
|
||||
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
notificationDataList = notificationDataList,
|
||||
title = context.getPlural(R.plurals.grade_new_items, items.size),
|
||||
content = context.getPlural(R.plurals.grade_notify_new_items, items.size, items.size),
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Grade, true),
|
||||
type = NotificationType.NEW_GRADE_DETAILS
|
||||
)
|
||||
|
||||
appNotificationManager.sendNotification(notification, student)
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
|
||||
suspend fun notifyPredicted(items: List<GradeSummary>, student: Student) {
|
||||
val notification = MultipleNotificationsData(
|
||||
type = NotificationType.NEW_GRADE_PREDICTED,
|
||||
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}"
|
||||
}
|
||||
val notificationDataList = items.map {
|
||||
NotificationData(
|
||||
title = context.getPlural(R.plurals.grade_new_items_predicted, 1),
|
||||
content = "${it.subject}: ${it.predictedGrade}",
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Grade, true),
|
||||
)
|
||||
}
|
||||
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
notificationDataList = notificationDataList,
|
||||
title = context.getPlural(R.plurals.grade_new_items_predicted, items.size),
|
||||
content = context.getPlural(
|
||||
R.plurals.grade_notify_new_items_predicted,
|
||||
items.size,
|
||||
items.size
|
||||
),
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Grade, true),
|
||||
type = NotificationType.NEW_GRADE_PREDICTED
|
||||
)
|
||||
|
||||
appNotificationManager.sendNotification(notification, student)
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
|
||||
suspend fun notifyFinal(items: List<GradeSummary>, student: Student) {
|
||||
val notification = MultipleNotificationsData(
|
||||
type = NotificationType.NEW_GRADE_FINAL,
|
||||
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}"
|
||||
}
|
||||
val notificationDataList = items.map {
|
||||
NotificationData(
|
||||
title = context.getPlural(R.plurals.grade_new_items_final, 1),
|
||||
content = "${it.subject}: ${it.finalGrade}",
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Grade, true),
|
||||
)
|
||||
}
|
||||
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
notificationDataList = notificationDataList,
|
||||
title = context.getPlural(R.plurals.grade_new_items_final, items.size),
|
||||
content = context.getPlural(
|
||||
R.plurals.grade_notify_new_items_final,
|
||||
items.size,
|
||||
items.size
|
||||
),
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Grade, true),
|
||||
type = NotificationType.NEW_GRADE_FINAL
|
||||
)
|
||||
|
||||
appNotificationManager.sendNotification(notification, student)
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,52 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import android.content.Context
|
||||
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.Student
|
||||
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewHomeworkNotification @Inject constructor(
|
||||
private val appNotificationManager: AppNotificationManager
|
||||
private val appNotificationManager: AppNotificationManager,
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
suspend fun notify(items: List<Homework>, student: Student) {
|
||||
val today = LocalDate.now()
|
||||
val lines = items.filter { !it.date.isBefore(today) }.map {
|
||||
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.content}"
|
||||
}.ifEmpty { return }
|
||||
val lines = items.filter { !it.date.isBefore(today) }
|
||||
.map {
|
||||
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.content}"
|
||||
}
|
||||
.ifEmpty { return }
|
||||
|
||||
val notification = MultipleNotificationsData(
|
||||
val notificationDataList = lines.map {
|
||||
NotificationData(
|
||||
title = context.getPlural(R.plurals.homework_notify_new_item_title, 1),
|
||||
content = it,
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Homework, true),
|
||||
)
|
||||
}
|
||||
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
title = context.getPlural(R.plurals.homework_notify_new_item_title, lines.size),
|
||||
content = context.getPlural(
|
||||
R.plurals.homework_notify_new_item_content,
|
||||
lines.size,
|
||||
lines.size
|
||||
),
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Homework, true),
|
||||
type = NotificationType.NEW_HOMEWORK,
|
||||
icon = R.drawable.ic_more_homework,
|
||||
titleStringRes = R.plurals.homework_notify_new_item_title,
|
||||
contentStringRes = R.plurals.homework_notify_new_item_content,
|
||||
summaryStringRes = R.plurals.homework_number_item,
|
||||
startMenu = MainView.Section.HOMEWORK,
|
||||
lines = lines
|
||||
notificationDataList = notificationDataList
|
||||
)
|
||||
|
||||
appNotificationManager.sendNotification(notification, student)
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,34 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import android.content.Context
|
||||
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.Student
|
||||
import io.github.wulkanowy.data.pojos.OneNotificationData
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewLuckyNumberNotification @Inject constructor(
|
||||
private val appNotificationManager: AppNotificationManager
|
||||
private val appNotificationManager: AppNotificationManager,
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
suspend fun notify(item: LuckyNumber, student: Student) {
|
||||
val notification = OneNotificationData(
|
||||
type = NotificationType.NEW_LUCKY_NUMBER,
|
||||
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())
|
||||
)
|
||||
suspend fun notify(item: LuckyNumber, student: Student) {
|
||||
val notificationData = NotificationData(
|
||||
title = context.getString(R.string.lucky_number_notify_new_item_title),
|
||||
content = context.getString(
|
||||
R.string.lucky_number_notify_new_item,
|
||||
item.luckyNumber.toString()
|
||||
),
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.LuckyNumber, true)
|
||||
)
|
||||
|
||||
appNotificationManager.sendNotification(notification, student)
|
||||
appNotificationManager.sendSingleNotification(
|
||||
notificationData = notificationData,
|
||||
notificationType = NotificationType.NEW_LUCKY_NUMBER,
|
||||
student = student
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,39 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import android.content.Context
|
||||
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.Student
|
||||
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewMessageNotification @Inject constructor(
|
||||
private val appNotificationManager: AppNotificationManager
|
||||
private val appNotificationManager: AppNotificationManager,
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
suspend fun notify(items: List<Message>, student: Student) {
|
||||
val notification = MultipleNotificationsData(
|
||||
type = NotificationType.NEW_MESSAGE,
|
||||
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}"
|
||||
}
|
||||
val notificationDataList = items.map {
|
||||
NotificationData(
|
||||
title = context.getPlural(R.plurals.message_new_items, 1),
|
||||
content = "${it.sender}: ${it.subject}",
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Message, true),
|
||||
)
|
||||
}
|
||||
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
notificationDataList = notificationDataList,
|
||||
title = context.getPlural(R.plurals.message_new_items, items.size),
|
||||
content = context.getPlural(R.plurals.message_notify_new_items, items.size, items.size),
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Message, true),
|
||||
type = NotificationType.NEW_MESSAGE
|
||||
)
|
||||
|
||||
appNotificationManager.sendNotification(notification, student)
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +1,46 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import android.content.Context
|
||||
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.Student
|
||||
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewNoteNotification @Inject constructor(
|
||||
private val appNotificationManager: AppNotificationManager
|
||||
private val appNotificationManager: AppNotificationManager,
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
suspend fun notify(items: List<Note>, student: Student) {
|
||||
val notification = MultipleNotificationsData(
|
||||
type = NotificationType.NEW_NOTE,
|
||||
icon = R.drawable.ic_stat_note,
|
||||
titleStringRes = when (NoteCategory.getByValue(items.first().categoryType)) {
|
||||
val notificationDataList = items.map {
|
||||
val titleRes = when (NoteCategory.getByValue(it.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}"
|
||||
}
|
||||
|
||||
NotificationData(
|
||||
title = context.getPlural(titleRes, 1),
|
||||
content = "${it.teacher}: ${it.category}",
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Note, true),
|
||||
)
|
||||
}
|
||||
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
notificationDataList = notificationDataList,
|
||||
intentToStart = MainActivity.getStartIntent(context, Destination.Note, true),
|
||||
title = context.getPlural(R.plurals.note_new_items, items.size),
|
||||
content = context.getPlural(R.plurals.note_notify_new_items, items.size, items.size),
|
||||
type = NotificationType.NEW_NOTE
|
||||
)
|
||||
|
||||
appNotificationManager.sendNotification(notification, student)
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,56 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import android.content.Context
|
||||
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.Student
|
||||
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewSchoolAnnouncementNotification @Inject constructor(
|
||||
private val appNotificationManager: AppNotificationManager
|
||||
private val appNotificationManager: AppNotificationManager,
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
suspend fun notify(items: List<SchoolAnnouncement>, student: Student) {
|
||||
val notification = MultipleNotificationsData(
|
||||
type = NotificationType.NEW_ANNOUNCEMENT,
|
||||
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}"
|
||||
}
|
||||
suspend fun notify(items: List<SchoolAnnouncement>, student: Student) {
|
||||
val notificationDataList = items.map {
|
||||
NotificationData(
|
||||
intentToStart = MainActivity.getStartIntent(
|
||||
context = context,
|
||||
destination = Destination.SchoolAnnouncement,
|
||||
startNewTask = true
|
||||
),
|
||||
title = context.getPlural(
|
||||
R.plurals.school_announcement_notify_new_item_title,
|
||||
1
|
||||
),
|
||||
content = "${it.subject}: ${it.content}"
|
||||
)
|
||||
}
|
||||
val groupNotificationData = GroupNotificationData(
|
||||
type = NotificationType.NEW_ANNOUNCEMENT,
|
||||
intentToStart = MainActivity.getStartIntent(
|
||||
context = context,
|
||||
destination = Destination.SchoolAnnouncement,
|
||||
startNewTask = true
|
||||
),
|
||||
title = context.getPlural(
|
||||
R.plurals.school_announcement_notify_new_item_title,
|
||||
items.size
|
||||
),
|
||||
content = context.getPlural(
|
||||
R.plurals.school_announcement_notify_new_items,
|
||||
items.size,
|
||||
items.size
|
||||
),
|
||||
notificationDataList = notificationDataList
|
||||
)
|
||||
|
||||
appNotificationManager.sendNotification(notification, student)
|
||||
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package io.github.wulkanowy.services.sync.notifications
|
||||
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewAttendanceChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewConferencesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewExamChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewGradesChannel
|
||||
@ -9,17 +11,76 @@ import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewSchoolAnnouncementsChannel
|
||||
import io.github.wulkanowy.services.sync.channels.PushChannel
|
||||
import io.github.wulkanowy.services.sync.channels.TimetableChangeChannel
|
||||
|
||||
enum class NotificationType(val group: String?, val channel: String) {
|
||||
NEW_CONFERENCE("new_conferences_group", NewConferencesChannel.CHANNEL_ID),
|
||||
NEW_EXAM("new_exam_group", NewExamChannel.CHANNEL_ID),
|
||||
NEW_GRADE_DETAILS("new_grade_details_group", NewGradesChannel.CHANNEL_ID),
|
||||
NEW_GRADE_PREDICTED("new_grade_predicted_group", NewGradesChannel.CHANNEL_ID),
|
||||
NEW_GRADE_FINAL("new_grade_final_group", NewGradesChannel.CHANNEL_ID),
|
||||
NEW_HOMEWORK("new_homework_group", NewHomeworkChannel.CHANNEL_ID),
|
||||
NEW_LUCKY_NUMBER("lucky_number_group", LuckyNumberChannel.CHANNEL_ID),
|
||||
NEW_MESSAGE("new_message_group", NewMessagesChannel.CHANNEL_ID),
|
||||
NEW_NOTE("new_notes_group", NewNotesChannel.CHANNEL_ID),
|
||||
NEW_ANNOUNCEMENT("new_school_announcements_group", NewSchoolAnnouncementsChannel.CHANNEL_ID),
|
||||
PUSH(null, PushChannel.CHANNEL_ID)
|
||||
enum class NotificationType(
|
||||
val group: String?,
|
||||
val channel: String,
|
||||
val icon: Int
|
||||
) {
|
||||
NEW_CONFERENCE(
|
||||
group = "new_conferences_group",
|
||||
channel = NewConferencesChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_more_conferences,
|
||||
),
|
||||
NEW_EXAM(
|
||||
group = "new_exam_group",
|
||||
channel = NewExamChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_main_exam
|
||||
),
|
||||
NEW_GRADE_DETAILS(
|
||||
group = "new_grade_details_group",
|
||||
channel = NewGradesChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_stat_grade,
|
||||
),
|
||||
NEW_GRADE_PREDICTED(
|
||||
group = "new_grade_predicted_group",
|
||||
channel = NewGradesChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_stat_grade,
|
||||
),
|
||||
NEW_GRADE_FINAL(
|
||||
group = "new_grade_final_group",
|
||||
channel = NewGradesChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_stat_grade,
|
||||
),
|
||||
NEW_HOMEWORK(
|
||||
group = "new_homework_group",
|
||||
channel = NewHomeworkChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_more_homework,
|
||||
),
|
||||
NEW_LUCKY_NUMBER(
|
||||
group = null,
|
||||
channel = LuckyNumberChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_stat_luckynumber,
|
||||
),
|
||||
NEW_MESSAGE(
|
||||
group = "new_message_group",
|
||||
channel = NewMessagesChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_stat_message,
|
||||
),
|
||||
NEW_NOTE(
|
||||
group = "new_notes_group",
|
||||
channel = NewNotesChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_stat_note
|
||||
),
|
||||
NEW_ANNOUNCEMENT(
|
||||
group = "new_school_announcements_group",
|
||||
channel = NewSchoolAnnouncementsChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_all_about
|
||||
),
|
||||
CHANGE_TIMETABLE(
|
||||
group = "change_timetable_group",
|
||||
channel = TimetableChangeChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_main_timetable
|
||||
),
|
||||
NEW_ATTENDANCE(
|
||||
group = "new_attendance_group",
|
||||
channel = NewAttendanceChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_main_attendance
|
||||
),
|
||||
PUSH(
|
||||
group = null,
|
||||
channel = PushChannel.CHANNEL_ID,
|
||||
icon = R.drawable.ic_stat_all
|
||||
)
|
||||
}
|
||||
|
@ -3,18 +3,40 @@ package io.github.wulkanowy.services.sync.works
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.AttendanceRepository
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification
|
||||
import io.github.wulkanowy.utils.previousOrSameSchoolDay
|
||||
import io.github.wulkanowy.utils.waitForResult
|
||||
import kotlinx.coroutines.flow.first
|
||||
import java.time.LocalDate.now
|
||||
import javax.inject.Inject
|
||||
|
||||
class AttendanceWork @Inject constructor(
|
||||
private val attendanceRepository: AttendanceRepository
|
||||
private val attendanceRepository: AttendanceRepository,
|
||||
private val newAttendanceNotification: NewAttendanceNotification,
|
||||
private val preferencesRepository: PreferencesRepository
|
||||
) : Work {
|
||||
|
||||
override suspend fun doWork(student: Student, semester: Semester) {
|
||||
attendanceRepository.getAttendance(student, semester, now().monday, now().sunday, true)
|
||||
attendanceRepository.getAttendance(
|
||||
student = student,
|
||||
semester = semester,
|
||||
start = now().previousOrSameSchoolDay,
|
||||
end = now().previousOrSameSchoolDay,
|
||||
forceRefresh = true,
|
||||
notify = preferencesRepository.isNotificationsEnable
|
||||
)
|
||||
.waitForResult()
|
||||
|
||||
attendanceRepository.getAttendanceFromDatabase(semester, now().minusDays(7), now())
|
||||
.first()
|
||||
.filterNot { it.isNotified }
|
||||
.let {
|
||||
if (it.isNotEmpty()) newAttendanceNotification.notify(it, student)
|
||||
|
||||
attendanceRepository.updateTimetable(it.onEach { attendance ->
|
||||
attendance.isNotified = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,41 @@ package io.github.wulkanowy.services.sync.works
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.TimetableRepository
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification
|
||||
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
||||
import io.github.wulkanowy.utils.waitForResult
|
||||
import kotlinx.coroutines.flow.first
|
||||
import java.time.LocalDate.now
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableWork @Inject constructor(
|
||||
private val timetableRepository: TimetableRepository
|
||||
private val timetableRepository: TimetableRepository,
|
||||
private val changeTimetableNotification: ChangeTimetableNotification,
|
||||
private val preferencesRepository: PreferencesRepository
|
||||
) : Work {
|
||||
|
||||
override suspend fun doWork(student: Student, semester: Semester) {
|
||||
timetableRepository.getTimetable(student, semester, now().monday, now().sunday, true).waitForResult()
|
||||
timetableRepository.getTimetable(
|
||||
student = student,
|
||||
semester = semester,
|
||||
start = now().nextOrSameSchoolDay,
|
||||
end = now().nextOrSameSchoolDay,
|
||||
forceRefresh = true,
|
||||
notify = preferencesRepository.isNotificationsEnable
|
||||
)
|
||||
.waitForResult()
|
||||
|
||||
timetableRepository.getTimetableFromDatabase(semester, now(), now().plusDays(7))
|
||||
.first()
|
||||
.filterNot { it.isNotified }
|
||||
.let {
|
||||
if (it.isNotEmpty()) changeTimetableNotification.notify(it, student)
|
||||
|
||||
timetableRepository.updateTimetable(it.onEach { timetable ->
|
||||
timetable.isNotified = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
132
app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
Normal file
132
app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
Normal file
@ -0,0 +1,132 @@
|
||||
package io.github.wulkanowy.ui.modules
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
|
||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardFragment
|
||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||
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.schoolandteachers.school.SchoolFragment
|
||||
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||
import java.io.Serializable
|
||||
import java.time.LocalDate
|
||||
|
||||
sealed interface Destination : Serializable {
|
||||
|
||||
val type: Type
|
||||
|
||||
val fragment: Fragment
|
||||
|
||||
enum class Type(val defaultDestination: Destination) {
|
||||
DASHBOARD(Dashboard),
|
||||
GRADE(Grade),
|
||||
ATTENDANCE(Attendance),
|
||||
EXAM(Exam),
|
||||
TIMETABLE(Timetable()),
|
||||
HOMEWORK(Homework),
|
||||
NOTE(Note),
|
||||
CONFERENCE(Conference),
|
||||
SCHOOL_ANNOUNCEMENT(SchoolAnnouncement),
|
||||
SCHOOL(School),
|
||||
LUCKY_NUMBER(More),
|
||||
MORE(More),
|
||||
MESSAGE(Message);
|
||||
}
|
||||
|
||||
object Dashboard : Destination {
|
||||
|
||||
override val type = Type.DASHBOARD
|
||||
|
||||
override val fragment get() = DashboardFragment.newInstance()
|
||||
}
|
||||
|
||||
object Grade : Destination {
|
||||
|
||||
override val type = Type.GRADE
|
||||
|
||||
override val fragment get() = GradeFragment.newInstance()
|
||||
}
|
||||
|
||||
object Attendance : Destination {
|
||||
|
||||
override val type = Type.ATTENDANCE
|
||||
|
||||
override val fragment get() = AttendanceFragment.newInstance()
|
||||
}
|
||||
|
||||
object Exam : Destination {
|
||||
|
||||
override val type = Type.EXAM
|
||||
|
||||
override val fragment get() = ExamFragment.newInstance()
|
||||
}
|
||||
|
||||
data class Timetable(val date: LocalDate? = null) : Destination {
|
||||
|
||||
override val type = Type.TIMETABLE
|
||||
|
||||
override val fragment get() = TimetableFragment.newInstance(date)
|
||||
}
|
||||
|
||||
object Homework : Destination {
|
||||
|
||||
override val type = Type.HOMEWORK
|
||||
|
||||
override val fragment get() = HomeworkFragment.newInstance()
|
||||
}
|
||||
|
||||
object Note : Destination {
|
||||
|
||||
override val type = Type.NOTE
|
||||
|
||||
override val fragment get() = NoteFragment.newInstance()
|
||||
}
|
||||
|
||||
object Conference : Destination {
|
||||
|
||||
override val type = Type.CONFERENCE
|
||||
|
||||
override val fragment get() = ConferenceFragment.newInstance()
|
||||
}
|
||||
|
||||
object SchoolAnnouncement : Destination {
|
||||
|
||||
override val type = Type.SCHOOL_ANNOUNCEMENT
|
||||
|
||||
override val fragment get() = SchoolAnnouncementFragment.newInstance()
|
||||
}
|
||||
|
||||
object School : Destination {
|
||||
|
||||
override val type = Type.SCHOOL
|
||||
|
||||
override val fragment get() = SchoolFragment.newInstance()
|
||||
}
|
||||
|
||||
object LuckyNumber : Destination {
|
||||
|
||||
override val type = Type.LUCKY_NUMBER
|
||||
|
||||
override val fragment get() = LuckyNumberFragment.newInstance()
|
||||
}
|
||||
|
||||
object More : Destination {
|
||||
|
||||
override val type = Type.MORE
|
||||
|
||||
override val fragment get() = MoreFragment.newInstance()
|
||||
}
|
||||
|
||||
object Message : Destination {
|
||||
|
||||
override val type = Type.MESSAGE
|
||||
|
||||
override val fragment get() = MessageFragment.newInstance()
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.enums.SentExcuseStatus
|
||||
import io.github.wulkanowy.databinding.ItemAttendanceBinding
|
||||
import io.github.wulkanowy.utils.description
|
||||
import io.github.wulkanowy.utils.descriptionRes
|
||||
import io.github.wulkanowy.utils.isExcusableOrNotExcused
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -36,7 +36,7 @@ class AttendanceAdapter @Inject constructor() :
|
||||
with(holder.binding) {
|
||||
attendanceItemNumber.text = item.number.toString()
|
||||
attendanceItemSubject.text = item.subject
|
||||
attendanceItemDescription.setText(item.description)
|
||||
attendanceItemDescription.setText(item.descriptionRes)
|
||||
attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE }
|
||||
attendanceItemNumber.visibility = View.GONE
|
||||
attendanceItemExcuseInfo.visibility = View.GONE
|
||||
|
@ -7,7 +7,7 @@ import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.databinding.DialogAttendanceBinding
|
||||
import io.github.wulkanowy.utils.description
|
||||
import io.github.wulkanowy.utils.descriptionRes
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
|
||||
@ -45,7 +45,7 @@ class AttendanceDialog : DialogFragment() {
|
||||
|
||||
with(binding) {
|
||||
attendanceDialogSubjectValue.text = attendance.subject
|
||||
attendanceDialogDescriptionValue.setText(attendance.description)
|
||||
attendanceDialogDescriptionValue.setText(attendance.descriptionRes)
|
||||
attendanceDialogDateValue.text = attendance.date.toFormattedString()
|
||||
attendanceDialogNumberValue.text = attendance.number.toString()
|
||||
attendanceDialogClose.setOnClickListener { dismiss() }
|
||||
|
@ -3,6 +3,8 @@ package io.github.wulkanowy.ui.modules.debug.notification
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification
|
||||
import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification
|
||||
import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification
|
||||
import io.github.wulkanowy.services.sync.notifications.NewExamNotification
|
||||
import io.github.wulkanowy.services.sync.notifications.NewGradeNotification
|
||||
@ -13,6 +15,7 @@ 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.debugAttendanceItems
|
||||
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
|
||||
@ -22,6 +25,7 @@ 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 io.github.wulkanowy.ui.modules.debug.notification.mock.debugTimetableItems
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
@ -37,6 +41,8 @@ class NotificationDebugPresenter @Inject constructor(
|
||||
private val newNoteNotification: NewNoteNotification,
|
||||
private val newSchoolAnnouncementNotification: NewSchoolAnnouncementNotification,
|
||||
private val newLuckyNumberNotification: NewLuckyNumberNotification,
|
||||
private val changeTimetableNotification: ChangeTimetableNotification,
|
||||
private val newAttendanceNotification: NewAttendanceNotification,
|
||||
) : BasePresenter<NotificationDebugView>(errorHandler, studentRepository) {
|
||||
|
||||
private val items = listOf(
|
||||
@ -64,6 +70,12 @@ class NotificationDebugPresenter @Inject constructor(
|
||||
NotificationDebugItem(R.string.note_title) { n ->
|
||||
withStudent { newNoteNotification.notify(debugNoteItems.take(n), it) }
|
||||
},
|
||||
NotificationDebugItem(R.string.attendance_title) { n ->
|
||||
withStudent { newAttendanceNotification.notify(debugAttendanceItems.take(n), it) }
|
||||
},
|
||||
NotificationDebugItem(R.string.timetable_title) { n ->
|
||||
withStudent { changeTimetableNotification.notify(debugTimetableItems.take(n), it) }
|
||||
},
|
||||
NotificationDebugItem(R.string.school_announcement_title) { n ->
|
||||
withStudent {
|
||||
newSchoolAnnouncementNotification.notify(debugSchoolAnnouncementItems.take(n), it)
|
||||
|
@ -0,0 +1,35 @@
|
||||
package io.github.wulkanowy.ui.modules.debug.notification.mock
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import java.time.LocalDate
|
||||
|
||||
val debugAttendanceItems = listOf(
|
||||
generateAttendance("Matematyka", "PRESENCE"),
|
||||
generateAttendance("Język angielski", "UNEXCUSED_LATENESS"),
|
||||
generateAttendance("Geografia", "ABSENCE_UNEXCUSED"),
|
||||
generateAttendance("Sieci komputerowe", "ABSENCE_EXCUSED"),
|
||||
generateAttendance("Systemy operacyjne", "EXCUSED_LATENESS"),
|
||||
generateAttendance("Język niemiecki", "ABSENCE_UNEXCUSED"),
|
||||
generateAttendance("Biologia", "ABSENCE_UNEXCUSED"),
|
||||
generateAttendance("Chemia", "ABSENCE_EXCUSED"),
|
||||
generateAttendance("Fizyka", "ABSENCE_UNEXCUSED"),
|
||||
generateAttendance("Matematyka", "ABSENCE_EXCUSED"),
|
||||
)
|
||||
|
||||
private fun generateAttendance(subject: String, name: String) = Attendance(
|
||||
subject = subject,
|
||||
studentId = 0,
|
||||
diaryId = 0,
|
||||
date = LocalDate.now(),
|
||||
timeId = 0,
|
||||
number = 1,
|
||||
name = name,
|
||||
presence = false,
|
||||
absence = false,
|
||||
exemption = false,
|
||||
lateness = false,
|
||||
excused = false,
|
||||
deleted = false,
|
||||
excusable = false,
|
||||
excuseStatus = ""
|
||||
)
|
@ -0,0 +1,39 @@
|
||||
package io.github.wulkanowy.ui.modules.debug.notification.mock
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import kotlin.random.Random
|
||||
|
||||
val debugTimetableItems = listOf(
|
||||
generateTimetable("Matematyka", "12", "01"),
|
||||
generateTimetable("Język angielski", "23", "12"),
|
||||
generateTimetable("Geografia", "34", "23"),
|
||||
generateTimetable("Sieci komputerowe", "45", "34"),
|
||||
generateTimetable("Systemy operacyjne", "56", "45"),
|
||||
generateTimetable("Język niemiecki", "67", "56"),
|
||||
generateTimetable("Biologia", "78", "67"),
|
||||
generateTimetable("Chemia", "89", "78"),
|
||||
generateTimetable("Fizyka", "90", "89"),
|
||||
generateTimetable("Matematyka", "01", "90"),
|
||||
)
|
||||
|
||||
private fun generateTimetable(subject: String, room: String, roomOld: String) = Timetable(
|
||||
subject = subject,
|
||||
studentId = 0,
|
||||
diaryId = 0,
|
||||
date = LocalDate.now().minusDays(Random.nextLong(0, 8)),
|
||||
number = 1,
|
||||
start = LocalDateTime.now().plusHours(1),
|
||||
end = LocalDateTime.now(),
|
||||
subjectOld = "",
|
||||
group = "",
|
||||
room = room,
|
||||
roomOld = roomOld,
|
||||
teacher = "Wtorkowska Renata",
|
||||
teacherOld = "",
|
||||
info = "",
|
||||
isStudentPlan = true,
|
||||
changes = true,
|
||||
canceled = true
|
||||
)
|
@ -66,7 +66,14 @@ class LoginStudentSelectFragment :
|
||||
}
|
||||
|
||||
override fun openMainView() {
|
||||
activity?.let { startActivity(MainActivity.getStartIntent(context = it, clear = true)) }
|
||||
activity?.let {
|
||||
startActivity(
|
||||
MainActivity.getStartIntent(
|
||||
context = it,
|
||||
startNewTask = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
|
@ -18,8 +18,8 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.toFirstResult
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import timber.log.Timber
|
||||
@ -39,6 +39,8 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
|
||||
companion object {
|
||||
|
||||
const val LUCKY_NUMBER_PENDING_INTENT_ID = 200
|
||||
|
||||
fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId"
|
||||
|
||||
fun getThemeWidgetKey(appWidgetId: Int) = "lucky_number_widget_theme_$appWidgetId"
|
||||
@ -48,18 +50,31 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
fun getWidthWidgetKey(appWidgetId: Int) = "lucky_number_widget_width_$appWidgetId"
|
||||
}
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray?) {
|
||||
override fun onUpdate(
|
||||
context: Context,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
appWidgetIds: IntArray?
|
||||
) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||
appWidgetIds?.forEach { appWidgetId ->
|
||||
val luckyNumber =
|
||||
getLuckyNumber(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)
|
||||
val appIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
LUCKY_NUMBER_PENDING_INTENT_ID,
|
||||
MainActivity.getStartIntent(context, Destination.LuckyNumber, true),
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
val luckyNumber = getLuckyNumber(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)
|
||||
val appIntent = PendingIntent.getActivity(context, MainView.Section.LUCKY_NUMBER.id,
|
||||
MainActivity.getStartIntent(context, MainView.Section.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT)
|
||||
|
||||
val remoteView = RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context)).apply {
|
||||
setTextViewText(R.id.luckyNumberWidgetNumber, luckyNumber?.luckyNumber?.toString() ?: "#")
|
||||
setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent)
|
||||
}
|
||||
val remoteView =
|
||||
RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context))
|
||||
.apply {
|
||||
setTextViewText(
|
||||
R.id.luckyNumberWidgetNumber,
|
||||
luckyNumber?.luckyNumber?.toString() ?: "#"
|
||||
)
|
||||
setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent)
|
||||
}
|
||||
|
||||
setStyles(remoteView, appWidgetId)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, remoteView)
|
||||
@ -78,7 +93,12 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAppWidgetOptionsChanged(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, newOptions: Bundle?) {
|
||||
override fun onAppWidgetOptionsChanged(
|
||||
context: Context,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
appWidgetId: Int,
|
||||
newOptions: Bundle?
|
||||
) {
|
||||
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
|
||||
|
||||
val remoteView = RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context))
|
||||
@ -88,8 +108,12 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
}
|
||||
|
||||
private fun setStyles(views: RemoteViews, appWidgetId: Int, options: Bundle? = null) {
|
||||
val width = options?.getInt(OPTION_APPWIDGET_MIN_WIDTH) ?: sharedPref.getLong(getWidthWidgetKey(appWidgetId), 74).toInt()
|
||||
val height = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: sharedPref.getLong(getHeightWidgetKey(appWidgetId), 74).toInt()
|
||||
val width = options?.getInt(OPTION_APPWIDGET_MIN_WIDTH) ?: sharedPref.getLong(
|
||||
getWidthWidgetKey(appWidgetId), 74
|
||||
).toInt()
|
||||
val height = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: sharedPref.getLong(
|
||||
getHeightWidgetKey(appWidgetId), 74
|
||||
).toInt()
|
||||
|
||||
with(sharedPref) {
|
||||
putLong(getWidthWidgetKey(appWidgetId), width.toLong())
|
||||
@ -112,7 +136,11 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun RemoteViews.setVisibility(imageTop: Boolean, imageLeft: Boolean, title: Boolean = false) {
|
||||
private fun RemoteViews.setVisibility(
|
||||
imageTop: Boolean,
|
||||
imageLeft: Boolean,
|
||||
title: Boolean = false
|
||||
) {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, if (imageTop) VISIBLE else GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, if (imageLeft) VISIBLE else GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, if (title) VISIBLE else GONE)
|
||||
@ -152,7 +180,8 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
|
||||
private fun getCorrectLayoutId(appWidgetId: Int, context: Context): Int {
|
||||
val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
|
||||
val isSystemDarkMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||
val isSystemDarkMode =
|
||||
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||
|
||||
return if (savedTheme == 1L || (savedTheme == 2L && isSystemDarkMode)) {
|
||||
R.layout.widget_luckynumber_dark
|
||||
|
@ -5,16 +5,10 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION_CODES.P
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
@ -29,20 +23,10 @@ import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.ActivityMainBinding
|
||||
import io.github.wulkanowy.services.shortcuts.ShortcutsHelper
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
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.dashboard.DashboardFragment
|
||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||
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
|
||||
import io.github.wulkanowy.utils.InAppReviewHelper
|
||||
@ -75,6 +59,9 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
@Inject
|
||||
lateinit var shortcutsHelper: ShortcutsHelper
|
||||
|
||||
private var accountMenu: MenuItem? = null
|
||||
|
||||
private val overlayProvider by lazy { ElevationOverlayProvider(this) }
|
||||
@ -83,15 +70,19 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
FragNavController(supportFragmentManager, R.id.main_fragment_container)
|
||||
|
||||
companion object {
|
||||
const val EXTRA_START_MENU = "extraStartMenu"
|
||||
|
||||
private const val EXTRA_START_DESTINATION = "start_destination"
|
||||
|
||||
fun getStartIntent(
|
||||
context: Context,
|
||||
startMenu: MainView.Section? = null,
|
||||
clear: Boolean = false
|
||||
destination: Destination? = null,
|
||||
startNewTask: Boolean = false
|
||||
) = Intent(context, MainActivity::class.java).apply {
|
||||
if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
|
||||
startMenu?.let { putExtra(EXTRA_START_MENU, it.id) }
|
||||
putExtra(EXTRA_START_DESTINATION, destination)
|
||||
|
||||
if (startNewTask) {
|
||||
flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,42 +97,21 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
|
||||
override val currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString
|
||||
|
||||
override var startMenuIndex = 0
|
||||
|
||||
override var startMenuMoreIndex = -1
|
||||
|
||||
private val moreMenuFragments = mapOf<Int, Fragment>(
|
||||
MainView.Section.MESSAGE.id to MessageFragment.newInstance(),
|
||||
MainView.Section.EXAM.id to ExamFragment.newInstance(),
|
||||
MainView.Section.HOMEWORK.id to HomeworkFragment.newInstance(),
|
||||
MainView.Section.NOTE.id to NoteFragment.newInstance(),
|
||||
MainView.Section.CONFERENCE.id to ConferenceFragment.newInstance(),
|
||||
MainView.Section.SCHOOL_ANNOUNCEMENT.id to SchoolAnnouncementFragment.newInstance(),
|
||||
MainView.Section.LUCKY_NUMBER.id to LuckyNumberFragment.newInstance(),
|
||||
)
|
||||
private var savedInstanceState: Bundle? = null
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityMainBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
setSupportActionBar(binding.mainToolbar)
|
||||
this.savedInstanceState = savedInstanceState
|
||||
messageContainer = binding.mainMessageContainer
|
||||
updateHelper.messageContainer = binding.mainFragmentContainer
|
||||
|
||||
val section = MainView.Section.values()
|
||||
.singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) }
|
||||
|
||||
presenter.onAttachView(this, section)
|
||||
|
||||
with(navController) {
|
||||
initialize(startMenuIndex, savedInstanceState)
|
||||
pushFragment(moreMenuFragments[startMenuMoreIndex])
|
||||
}
|
||||
|
||||
if (appInfo.systemVersion >= Build.VERSION_CODES.N_MR1) {
|
||||
initShortcuts()
|
||||
}
|
||||
val destination = intent.getSerializableExtra(EXTRA_START_DESTINATION) as Destination?
|
||||
?: shortcutsHelper.getDestination(intent)
|
||||
|
||||
presenter.onAttachView(this, destination)
|
||||
updateHelper.checkAndInstallUpdates(this)
|
||||
}
|
||||
|
||||
@ -157,54 +127,6 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
updateHelper.onActivityResult(requestCode, resultCode)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N_MR1)
|
||||
fun initShortcuts() {
|
||||
val shortcutsList = mutableListOf<ShortcutInfo>()
|
||||
|
||||
listOf(
|
||||
Triple(
|
||||
getString(R.string.grade_title),
|
||||
R.drawable.ic_shortcut_grade,
|
||||
MainView.Section.GRADE
|
||||
),
|
||||
Triple(
|
||||
getString(R.string.attendance_title),
|
||||
R.drawable.ic_shortcut_attendance,
|
||||
MainView.Section.ATTENDANCE
|
||||
),
|
||||
Triple(
|
||||
getString(R.string.exam_title),
|
||||
R.drawable.ic_shortcut_exam,
|
||||
MainView.Section.EXAM
|
||||
),
|
||||
Triple(
|
||||
getString(R.string.timetable_title),
|
||||
R.drawable.ic_shortcut_timetable,
|
||||
MainView.Section.TIMETABLE
|
||||
)
|
||||
).forEach { (title, icon, enum) ->
|
||||
shortcutsList.add(
|
||||
ShortcutInfo.Builder(applicationContext, title)
|
||||
.setShortLabel(title)
|
||||
.setLongLabel(title)
|
||||
.setIcon(Icon.createWithResource(applicationContext, icon))
|
||||
.setIntents(
|
||||
arrayOf(
|
||||
Intent(applicationContext, MainActivity::class.java)
|
||||
.setAction(Intent.ACTION_VIEW),
|
||||
Intent(applicationContext, MainActivity::class.java)
|
||||
.putExtra(EXTRA_START_MENU, enum.id)
|
||||
.setAction(Intent.ACTION_VIEW)
|
||||
.addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)
|
||||
)
|
||||
)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
getSystemService<ShortcutManager>()?.dynamicShortcuts = shortcutsList
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
menuInflater.inflate(R.menu.action_menu_main, menu)
|
||||
accountMenu = menu?.findItem(R.id.mainMenuAccount)
|
||||
@ -213,15 +135,38 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
return true
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
override fun initView() {
|
||||
override fun initView(startMenuIndex: Int, rootDestinations: List<Destination>) {
|
||||
initializeToolbar()
|
||||
initializeBottomNavigation(startMenuIndex)
|
||||
initializeNavController(startMenuIndex, rootDestinations)
|
||||
}
|
||||
|
||||
private fun initializeNavController(startMenuIndex: Int, rootDestinations: List<Destination>) {
|
||||
with(navController) {
|
||||
setOnViewChangeListener { destinationView ->
|
||||
presenter.onViewChange(destinationView)
|
||||
analytics.setCurrentScreen(
|
||||
this@MainActivity,
|
||||
destinationView::class.java.simpleName
|
||||
)
|
||||
}
|
||||
fragmentHideStrategy = HIDE
|
||||
rootFragments = rootDestinations.map { it.fragment }
|
||||
|
||||
initialize(startMenuIndex, savedInstanceState)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeToolbar() {
|
||||
with(binding.mainToolbar) {
|
||||
stateListAnimator = null
|
||||
setBackgroundColor(
|
||||
overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(4f))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeBottomNavigation(startMenuIndex: Int) {
|
||||
with(binding.mainBottomNav) {
|
||||
with(menu) {
|
||||
add(Menu.NONE, 0, Menu.NONE, R.string.dashboard_title)
|
||||
@ -239,36 +184,6 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
setOnItemSelectedListener { presenter.onTabSelected(it.itemId, false) }
|
||||
setOnItemReselectedListener { presenter.onTabSelected(it.itemId, true) }
|
||||
}
|
||||
|
||||
with(navController) {
|
||||
setOnViewChangeListener { section, name ->
|
||||
if (section == MainView.Section.ACCOUNT || section == MainView.Section.STUDENT_INFO) {
|
||||
binding.mainBottomNav.isVisible = false
|
||||
|
||||
if (appInfo.systemVersion >= P) {
|
||||
window.navigationBarColor = getThemeAttrColor(R.attr.colorSurface)
|
||||
}
|
||||
} else {
|
||||
binding.mainBottomNav.isVisible = true
|
||||
|
||||
if (appInfo.systemVersion >= P) {
|
||||
window.navigationBarColor =
|
||||
getThemeAttrColor(android.R.attr.navigationBarColor)
|
||||
}
|
||||
}
|
||||
|
||||
analytics.setCurrentScreen(this@MainActivity, name)
|
||||
presenter.onViewChange(section)
|
||||
}
|
||||
fragmentHideStrategy = HIDE
|
||||
rootFragments = listOf(
|
||||
DashboardFragment.newInstance(),
|
||||
GradeFragment.newInstance(),
|
||||
AttendanceFragment.newInstance(),
|
||||
TimetableFragment.newInstance(),
|
||||
MoreFragment.newInstance()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreferenceStartFragment(
|
||||
@ -317,6 +232,22 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
ViewCompat.setElevation(binding.mainToolbar, if (show) dpToPx(4f) else 0f)
|
||||
}
|
||||
|
||||
override fun showBottomNavigation(show: Boolean) {
|
||||
binding.mainBottomNav.isVisible = show
|
||||
|
||||
if (appInfo.systemVersion >= P) {
|
||||
window.navigationBarColor = if (show) {
|
||||
getThemeAttrColor(android.R.attr.navigationBarColor)
|
||||
} else {
|
||||
getThemeAttrColor(R.attr.colorSurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun openMoreDestination(destination: Destination) {
|
||||
pushView(destination.fragment)
|
||||
}
|
||||
|
||||
override fun notifyMenuViewReselected() {
|
||||
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected()
|
||||
}
|
||||
@ -373,6 +304,6 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
navController.onSaveInstanceState(outState)
|
||||
intent.removeExtra(EXTRA_START_MENU)
|
||||
intent.removeExtra(EXTRA_START_DESTINATION)
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,15 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.main.MainView.Section.GRADE
|
||||
import io.github.wulkanowy.ui.modules.main.MainView.Section.MESSAGE
|
||||
import io.github.wulkanowy.ui.modules.main.MainView.Section.SCHOOL
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.account.AccountView
|
||||
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsView
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeView
|
||||
import io.github.wulkanowy.ui.modules.message.MessageView
|
||||
import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolView
|
||||
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
import io.github.wulkanowy.utils.flowWithResource
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@ -27,19 +32,40 @@ class MainPresenter @Inject constructor(
|
||||
|
||||
private var studentsWitSemesters: List<StudentWithSemesters>? = null
|
||||
|
||||
fun onAttachView(view: MainView, initMenu: MainView.Section?) {
|
||||
super.onAttachView(view)
|
||||
view.apply {
|
||||
getProperViewIndexes(initMenu).let { (main, more) ->
|
||||
startMenuIndex = main
|
||||
startMenuMoreIndex = more
|
||||
private val rootDestinationTypeList = listOf(
|
||||
Destination.Type.DASHBOARD,
|
||||
Destination.Type.GRADE,
|
||||
Destination.Type.ATTENDANCE,
|
||||
Destination.Type.TIMETABLE,
|
||||
Destination.Type.MORE
|
||||
)
|
||||
|
||||
private val Destination?.startMenuIndex
|
||||
get() = when {
|
||||
this == null -> prefRepository.startMenuIndex
|
||||
type in rootDestinationTypeList -> {
|
||||
rootDestinationTypeList.indexOf(type)
|
||||
}
|
||||
initView()
|
||||
Timber.i("Main view was initialized with $startMenuIndex menu index and $startMenuMoreIndex more index")
|
||||
else -> 4
|
||||
}
|
||||
|
||||
fun onAttachView(view: MainView, initDestination: Destination?) {
|
||||
super.onAttachView(view)
|
||||
|
||||
val startMenuIndex = initDestination.startMenuIndex
|
||||
val destinations = rootDestinationTypeList.map {
|
||||
if (it == initDestination?.type) initDestination else it.defaultDestination
|
||||
}
|
||||
|
||||
view.initView(startMenuIndex, destinations)
|
||||
if (initDestination != null && startMenuIndex == 4) {
|
||||
view.openMoreDestination(initDestination)
|
||||
}
|
||||
|
||||
syncManager.startPeriodicSyncWorker()
|
||||
analytics.logEvent("app_open", "destination" to initMenu?.name)
|
||||
|
||||
analytics.logEvent("app_open", "destination" to initDestination.toString())
|
||||
Timber.i("Main view was initialized with $initDestination")
|
||||
}
|
||||
|
||||
fun onActionMenuCreated() {
|
||||
@ -64,9 +90,10 @@ class MainPresenter @Inject constructor(
|
||||
}.launch("avatar")
|
||||
}
|
||||
|
||||
fun onViewChange(section: MainView.Section?) {
|
||||
fun onViewChange(destinationView: BaseView) {
|
||||
view?.apply {
|
||||
showActionBarElevation(section != GRADE && section != MESSAGE && section != SCHOOL)
|
||||
showBottomNavigation(destinationView !is AccountView && destinationView !is StudentInfoView && destinationView !is AccountDetailsView)
|
||||
showActionBarElevation(destinationView !is GradeView && destinationView !is MessageView && destinationView !is SchoolView)
|
||||
currentViewTitle?.let { setViewTitle(it) }
|
||||
currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) }
|
||||
currentStackSize?.let {
|
||||
@ -134,10 +161,4 @@ class MainPresenter @Inject constructor(
|
||||
|
||||
view?.showStudentAvatar(currentStudent)
|
||||
}
|
||||
|
||||
private fun getProperViewIndexes(initMenu: MainView.Section?) = when (initMenu?.id) {
|
||||
in 0..3 -> initMenu!!.id to -1
|
||||
in 4..100 -> 4 to initMenu!!.id
|
||||
else -> prefRepository.startMenuIndex to -1
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,10 @@ package io.github.wulkanowy.ui.modules.main
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
|
||||
interface MainView : BaseView {
|
||||
|
||||
var startMenuIndex: Int
|
||||
|
||||
var startMenuMoreIndex: Int
|
||||
|
||||
val isRootView: Boolean
|
||||
|
||||
val currentViewTitle: String?
|
||||
@ -18,7 +15,7 @@ interface MainView : BaseView {
|
||||
|
||||
val currentStackSize: Int?
|
||||
|
||||
fun initView()
|
||||
fun initView(startMenuIndex: Int, rootDestinations: List<Destination>)
|
||||
|
||||
fun switchMenuView(position: Int)
|
||||
|
||||
@ -28,6 +25,8 @@ interface MainView : BaseView {
|
||||
|
||||
fun showActionBarElevation(show: Boolean)
|
||||
|
||||
fun showBottomNavigation(show: Boolean)
|
||||
|
||||
fun notifyMenuViewReselected()
|
||||
|
||||
fun notifyMenuViewChanged()
|
||||
@ -42,6 +41,8 @@ interface MainView : BaseView {
|
||||
|
||||
fun showInAppReview()
|
||||
|
||||
fun openMoreDestination(destination: Destination)
|
||||
|
||||
interface MainChildView {
|
||||
|
||||
fun onFragmentReselected()
|
||||
@ -57,25 +58,4 @@ interface MainView : BaseView {
|
||||
get() = ""
|
||||
set(_) {}
|
||||
}
|
||||
|
||||
enum class Section {
|
||||
DASHBOARD,
|
||||
GRADE,
|
||||
ATTENDANCE,
|
||||
TIMETABLE,
|
||||
MORE,
|
||||
MESSAGE,
|
||||
EXAM,
|
||||
HOMEWORK,
|
||||
NOTE,
|
||||
CONFERENCE,
|
||||
SCHOOL_ANNOUNCEMENT,
|
||||
SCHOOL,
|
||||
LUCKY_NUMBER,
|
||||
ACCOUNT,
|
||||
STUDENT_INFO,
|
||||
SETTINGS;
|
||||
|
||||
val id get() = ordinal
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Notification
|
||||
import io.github.wulkanowy.databinding.ItemNotificationsCenterBinding
|
||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||
@ -28,26 +27,12 @@ class NotificationsCenterAdapter @Inject constructor() :
|
||||
notificationsCenterItemTitle.text = item.title
|
||||
notificationsCenterItemContent.text = item.content
|
||||
notificationsCenterItemDate.text = item.date.toFormattedString("HH:mm, d MMM")
|
||||
notificationsCenterItemIcon.setImageResource(item.type.toDrawableResId())
|
||||
notificationsCenterItemIcon.setImageResource(item.type.icon)
|
||||
|
||||
root.setOnClickListener { onItemClickListener(item.type) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun NotificationType.toDrawableResId() = when (this) {
|
||||
NotificationType.NEW_CONFERENCE -> R.drawable.ic_more_conferences
|
||||
NotificationType.NEW_EXAM -> R.drawable.ic_main_exam
|
||||
NotificationType.NEW_GRADE_DETAILS -> R.drawable.ic_stat_grade
|
||||
NotificationType.NEW_GRADE_PREDICTED -> R.drawable.ic_stat_grade
|
||||
NotificationType.NEW_GRADE_FINAL -> R.drawable.ic_stat_grade
|
||||
NotificationType.NEW_HOMEWORK -> R.drawable.ic_more_homework
|
||||
NotificationType.NEW_LUCKY_NUMBER -> R.drawable.ic_stat_luckynumber
|
||||
NotificationType.NEW_MESSAGE -> R.drawable.ic_stat_message
|
||||
NotificationType.NEW_NOTE -> R.drawable.ic_stat_note
|
||||
NotificationType.NEW_ANNOUNCEMENT -> R.drawable.ic_all_about
|
||||
NotificationType.PUSH -> R.drawable.ic_stat_all
|
||||
}
|
||||
|
||||
class ViewHolder(val binding: ItemNotificationsCenterBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
@ -59,4 +44,4 @@ class NotificationsCenterAdapter @Inject constructor() :
|
||||
override fun areItemsTheSame(oldItem: Notification, newItem: Notification) =
|
||||
oldItem.id == newItem.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import io.github.wulkanowy.data.db.entities.Notification
|
||||
import io.github.wulkanowy.databinding.FragmentNotificationsCenterBinding
|
||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
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
|
||||
@ -21,6 +22,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
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 javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@ -104,5 +106,7 @@ class NotificationsCenterFragment :
|
||||
NotificationType.NEW_NOTE -> NoteFragment.newInstance()
|
||||
NotificationType.NEW_ANNOUNCEMENT -> SchoolAnnouncementFragment.newInstance()
|
||||
NotificationType.PUSH -> null
|
||||
NotificationType.CHANGE_TIMETABLE -> TimetableFragment.newInstance()
|
||||
NotificationType.NEW_ATTENDANCE -> AttendanceFragment.newInstance()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import timber.log.Timber
|
||||
|
||||
class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView {
|
||||
class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView, SettingsView {
|
||||
|
||||
companion object {
|
||||
|
||||
@ -19,4 +19,16 @@ class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView {
|
||||
setPreferencesFromResource(R.xml.scheme_preferences, rootKey)
|
||||
Timber.i("Settings view was initialized")
|
||||
}
|
||||
|
||||
override fun showError(text: String, error: Throwable) {}
|
||||
|
||||
override fun showMessage(text: String) {}
|
||||
|
||||
override fun showExpiredDialog() {}
|
||||
|
||||
override fun openClearLoginView() {}
|
||||
|
||||
override fun showErrorDetailsDialog(error: Throwable) {}
|
||||
|
||||
override fun showChangePasswordSnackbar(redirectUrl: String) {}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.settings
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface SettingsView : BaseView
|
@ -24,8 +24,8 @@ import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.services.HiltBroadcastReceiver
|
||||
import io.github.wulkanowy.services.widgets.TimetableWidgetService
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
import io.github.wulkanowy.utils.capitalise
|
||||
import io.github.wulkanowy.utils.createNameInitialsDrawable
|
||||
@ -60,6 +60,8 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TIMETABLE_PENDING_INTENT_ID = 201
|
||||
|
||||
private const val EXTRA_TOGGLED_WIDGET_ID = "extraToggledWidget"
|
||||
|
||||
private const val EXTRA_BUTTON_TYPE = "extraButtonType"
|
||||
@ -174,8 +176,8 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
|
||||
)
|
||||
val appIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
MainView.Section.TIMETABLE.id,
|
||||
MainActivity.getStartIntent(context, MainView.Section.TIMETABLE, true),
|
||||
TIMETABLE_PENDING_INTENT_ID,
|
||||
MainActivity.getStartIntent(context, Destination.Timetable(), true),
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
|
@ -29,7 +29,7 @@ private fun calculatePercentage(presence: Double, absence: Double): Double {
|
||||
return if ((presence + absence) == 0.0) 0.0 else (presence / (presence + absence)) * 100
|
||||
}
|
||||
|
||||
inline val Attendance.description
|
||||
inline val Attendance.descriptionRes
|
||||
get() = when (AttendanceCategory.getCategoryByName(name)) {
|
||||
AttendanceCategory.PRESENCE -> R.string.attendance_present
|
||||
AttendanceCategory.ABSENCE_UNEXCUSED -> R.string.attendance_absence_unexcused
|
||||
|
@ -18,6 +18,7 @@ import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.graphics.applyCanvas
|
||||
@ -57,6 +58,9 @@ fun Context.getCompatDrawable(@DrawableRes drawableRes: Int, @ColorRes colorRes:
|
||||
fun Context.getCompatBitmap(@DrawableRes drawableRes: Int, @ColorRes colorRes: Int) =
|
||||
getCompatDrawable(drawableRes, colorRes)?.toBitmap()
|
||||
|
||||
fun Context.getPlural(@PluralsRes pluralRes: Int, quantity: Int, vararg arguments: Any) =
|
||||
resources.getQuantityString(pluralRes, quantity, *arguments)
|
||||
|
||||
fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -> Unit = {}) {
|
||||
Intent.parseUri(uri, 0).let {
|
||||
try {
|
||||
|
@ -2,16 +2,19 @@ package io.github.wulkanowy.utils
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.ncapdevi.fragnav.FragNavController
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
inline fun FragNavController.setOnViewChangeListener(crossinline listener: (section: MainView.Section?, name: String?) -> Unit) {
|
||||
inline fun FragNavController.setOnViewChangeListener(crossinline listener: (view: BaseView) -> Unit) {
|
||||
transactionListener = object : FragNavController.TransactionListener {
|
||||
override fun onFragmentTransaction(fragment: Fragment?, transactionType: FragNavController.TransactionType) {
|
||||
listener(fragment?.toSection(), fragment?.let { it::class.java.simpleName })
|
||||
override fun onFragmentTransaction(
|
||||
fragment: Fragment?,
|
||||
transactionType: FragNavController.TransactionType
|
||||
) {
|
||||
fragment?.let { listener(it as BaseView) }
|
||||
}
|
||||
|
||||
override fun onTabTransaction(fragment: Fragment?, index: Int) {
|
||||
listener(fragment?.toSection(), fragment?.let { it::class.java.simpleName })
|
||||
fragment?.let { listener(it as BaseView) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
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.dashboard.DashboardFragment
|
||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
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
|
||||
|
||||
fun Fragment.toSection(): MainView.Section? {
|
||||
return when (this) {
|
||||
is GradeFragment -> MainView.Section.GRADE
|
||||
is AttendanceFragment -> MainView.Section.ATTENDANCE
|
||||
is ExamFragment -> MainView.Section.EXAM
|
||||
is TimetableFragment -> MainView.Section.TIMETABLE
|
||||
is MoreFragment -> MainView.Section.MORE
|
||||
is MessageFragment -> MainView.Section.MESSAGE
|
||||
is HomeworkFragment -> MainView.Section.HOMEWORK
|
||||
is NoteFragment -> MainView.Section.NOTE
|
||||
is LuckyNumberFragment -> MainView.Section.LUCKY_NUMBER
|
||||
is SettingsFragment -> MainView.Section.SETTINGS
|
||||
is SchoolAndTeachersFragment -> MainView.Section.SCHOOL
|
||||
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
|
||||
is DashboardFragment -> MainView.Section.DASHBOARD
|
||||
else -> null
|
||||
}
|
||||
}
|
@ -163,6 +163,26 @@
|
||||
<string name="timetable_now">Now: %s</string>
|
||||
<string name="timetable_next">Next: %s</string>
|
||||
<string name="timetable_later">Later: %s</string>
|
||||
<string name="timetable_notify_lesson">%1$s lesson %2$d - %3$s</string>
|
||||
<string name="timetable_notify_change_room">Change of room from %1$s to %2$s</string>
|
||||
<string name="timetable_notify_change_teacher">Change of teacher from %1$s to %2$s</string>
|
||||
<string name="timetable_notify_change_subject">Change of subject from %1$s to %2$s</string>
|
||||
<plurals name="timetable_notify_new_items_title">
|
||||
<item quantity="one">Timetable change</item>
|
||||
<item quantity="other">Timetable changes</item>
|
||||
</plurals>
|
||||
<plurals name="timetable_notify_new_items">
|
||||
<item quantity="one">%1$s - %2$d change in timetable</item>
|
||||
<item quantity="other">%1$s - %2$d changes in timetable</item>
|
||||
</plurals>
|
||||
<plurals name="timetable_notify_new_items_group">
|
||||
<item quantity="one">%1$d change in timetable</item>
|
||||
<item quantity="other">%1$d changes in timetable</item>
|
||||
</plurals>
|
||||
<plurals name="timetable_number_item">
|
||||
<item quantity="one">%d change</item>
|
||||
<item quantity="other">%d changes</item>
|
||||
</plurals>
|
||||
|
||||
|
||||
<!--Completed lessons-->
|
||||
@ -200,6 +220,18 @@
|
||||
<string name="attendance_excuse_title">Excuse</string>
|
||||
<string name="attendance_excuse_reason" translatable="false">z powodu</string>
|
||||
<string name="attendance_excuse_formula" translatable="false">Dzień dobry,\nProszę o usprawiedliwienie mojego dziecka w dniu %s z lekcji %s%s%s.\n\nPozdrawiam.</string>
|
||||
<plurals name="attendance_notify_new_items_title">
|
||||
<item quantity="one">New attendance</item>
|
||||
<item quantity="other">New attendance</item>
|
||||
</plurals>
|
||||
<plurals name="attendance_notify_new_items">
|
||||
<item quantity="one">%1$d new attendance</item>
|
||||
<item quantity="other">%1$d attendance</item>
|
||||
</plurals>
|
||||
<plurals name="attendance_number_item">
|
||||
<item quantity="one">%d attendance</item>
|
||||
<item quantity="other">%d attendance</item>
|
||||
</plurals>
|
||||
|
||||
|
||||
<!--Attendance summary-->
|
||||
@ -706,6 +738,8 @@
|
||||
<string name="channel_push">Push notifications</string>
|
||||
<string name="channel_upcoming_lessons">Upcoming lessons</string>
|
||||
<string name="channel_debug">Debug</string>
|
||||
<string name="channel_change_timetable">Timetable change</string>
|
||||
<string name="channel_new_attendance">New attendance</string>
|
||||
|
||||
|
||||
<!--Colors-->
|
||||
|
@ -42,20 +42,12 @@ class MainPresenterTest {
|
||||
MockKAnnotations.init(this)
|
||||
clearMocks(mainView)
|
||||
|
||||
every { mainView.startMenuIndex = any() } just Runs
|
||||
every { mainView.startMenuMoreIndex = any() } just Runs
|
||||
every { mainView.startMenuIndex } returns 1
|
||||
every { mainView.startMenuMoreIndex } returns 1
|
||||
every { mainView.initView() } just Runs
|
||||
presenter = MainPresenter(errorHandler, studentRepository, prefRepository, syncManager, analytics)
|
||||
every { mainView.initView(any(), any()) } just Runs
|
||||
presenter =
|
||||
MainPresenter(errorHandler, studentRepository, prefRepository, syncManager, analytics)
|
||||
presenter.onAttachView(mainView, null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun initMenuTest() {
|
||||
verify { mainView.initView() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onTabSelectedTest() {
|
||||
every { mainView.notifyMenuViewChanged() } just Runs
|
||||
|
Loading…
x
Reference in New Issue
Block a user