forked from github/wulkanowy-mirror
Add notifications center (#1524)
This commit is contained in:
parent
dc90549b9d
commit
d69118b085
2316
app/schemas/io.github.wulkanowy.data.db.AppDatabase/40.json
Normal file
2316
app/schemas/io.github.wulkanowy.data.db.AppDatabase/40.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -45,6 +45,7 @@
|
|||||||
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
|
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.splash.SplashActivity"
|
android:name=".ui.modules.splash.SplashActivity"
|
||||||
|
android:exported="true"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/WulkanowyTheme.SplashScreen"
|
android:theme="@style/WulkanowyTheme.SplashScreen"
|
||||||
tools:ignore="LockedOrientationActivity">
|
tools:ignore="LockedOrientationActivity">
|
||||||
@ -74,6 +75,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
|
android:exported="true"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@ -83,6 +85,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity"
|
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
|
android:exported="true"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@ -95,11 +98,20 @@
|
|||||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||||
<service
|
<service
|
||||||
android:name=".services.piggyback.VulcanNotificationListenerService"
|
android:name=".services.piggyback.VulcanNotificationListenerService"
|
||||||
|
android:exported="true"
|
||||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.service.notification.NotificationListenerService" />
|
<action android:name="android.service.notification.NotificationListenerService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
<service
|
||||||
|
android:name=".services.messaging.AppMessagingService"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
|
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
|
||||||
@ -114,6 +126,7 @@
|
|||||||
</receiver>
|
</receiver>
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetProvider"
|
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/lucky_number_title">
|
android:label="@string/lucky_number_title">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
@ -8,8 +8,8 @@ import androidx.preference.PreferenceManager
|
|||||||
import com.chuckerteam.chucker.api.ChuckerCollector
|
import com.chuckerteam.chucker.api.ChuckerCollector
|
||||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||||
import com.chuckerteam.chucker.api.RetentionManager
|
import com.chuckerteam.chucker.api.RetentionManager
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
@ -202,4 +202,8 @@ internal class RepositoryModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideSchoolAnnouncementDao(database: AppDatabase) = database.schoolAnnouncementDao
|
fun provideSchoolAnnouncementDao(database: AppDatabase) = database.schoolAnnouncementDao
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideNotificationDao(database: AppDatabase) = database.notificationDao
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import io.github.wulkanowy.data.db.dao.AttendanceDao
|
|||||||
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
||||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||||
import io.github.wulkanowy.data.db.dao.ConferenceDao
|
import io.github.wulkanowy.data.db.dao.ConferenceDao
|
||||||
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
|
||||||
import io.github.wulkanowy.data.db.dao.ExamDao
|
import io.github.wulkanowy.data.db.dao.ExamDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeDao
|
import io.github.wulkanowy.data.db.dao.GradeDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
|
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
|
||||||
@ -23,8 +22,10 @@ import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
|||||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||||
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
||||||
import io.github.wulkanowy.data.db.dao.NoteDao
|
import io.github.wulkanowy.data.db.dao.NoteDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.NotificationDao
|
||||||
import io.github.wulkanowy.data.db.dao.RecipientDao
|
import io.github.wulkanowy.data.db.dao.RecipientDao
|
||||||
import io.github.wulkanowy.data.db.dao.ReportingUnitDao
|
import io.github.wulkanowy.data.db.dao.ReportingUnitDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
||||||
import io.github.wulkanowy.data.db.dao.SchoolDao
|
import io.github.wulkanowy.data.db.dao.SchoolDao
|
||||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||||
@ -38,7 +39,6 @@ import io.github.wulkanowy.data.db.entities.Attendance
|
|||||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||||
import io.github.wulkanowy.data.db.entities.Conference
|
import io.github.wulkanowy.data.db.entities.Conference
|
||||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
import io.github.wulkanowy.data.db.entities.Exam
|
||||||
import io.github.wulkanowy.data.db.entities.Grade
|
import io.github.wulkanowy.data.db.entities.Grade
|
||||||
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
|
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
|
||||||
@ -51,9 +51,11 @@ import io.github.wulkanowy.data.db.entities.Message
|
|||||||
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||||
import io.github.wulkanowy.data.db.entities.Note
|
import io.github.wulkanowy.data.db.entities.Note
|
||||||
|
import io.github.wulkanowy.data.db.entities.Notification
|
||||||
import io.github.wulkanowy.data.db.entities.Recipient
|
import io.github.wulkanowy.data.db.entities.Recipient
|
||||||
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||||
import io.github.wulkanowy.data.db.entities.School
|
import io.github.wulkanowy.data.db.entities.School
|
||||||
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.db.entities.StudentInfo
|
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||||
@ -95,6 +97,7 @@ import io.github.wulkanowy.data.db.migrations.Migration37
|
|||||||
import io.github.wulkanowy.data.db.migrations.Migration38
|
import io.github.wulkanowy.data.db.migrations.Migration38
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration39
|
import io.github.wulkanowy.data.db.migrations.Migration39
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||||
|
import io.github.wulkanowy.data.db.migrations.Migration40
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration7
|
import io.github.wulkanowy.data.db.migrations.Migration7
|
||||||
@ -134,6 +137,7 @@ import javax.inject.Singleton
|
|||||||
StudentInfo::class,
|
StudentInfo::class,
|
||||||
TimetableHeader::class,
|
TimetableHeader::class,
|
||||||
SchoolAnnouncement::class,
|
SchoolAnnouncement::class,
|
||||||
|
Notification::class
|
||||||
],
|
],
|
||||||
version = AppDatabase.VERSION_SCHEMA,
|
version = AppDatabase.VERSION_SCHEMA,
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
@ -142,7 +146,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 39
|
const val VERSION_SCHEMA = 40
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||||
Migration2(),
|
Migration2(),
|
||||||
@ -183,6 +187,7 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
Migration37(),
|
Migration37(),
|
||||||
Migration38(),
|
Migration38(),
|
||||||
Migration39(),
|
Migration39(),
|
||||||
|
Migration40()
|
||||||
)
|
)
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
@ -252,4 +257,6 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
abstract val timetableHeaderDao: TimetableHeaderDao
|
abstract val timetableHeaderDao: TimetableHeaderDao
|
||||||
|
|
||||||
abstract val schoolAnnouncementDao: SchoolAnnouncementDao
|
abstract val schoolAnnouncementDao: SchoolAnnouncementDao
|
||||||
|
|
||||||
|
abstract val notificationDao: NotificationDao
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import io.github.wulkanowy.data.db.entities.Notification
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Dao
|
||||||
|
interface NotificationDao : BaseDao<Notification> {
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Notifications WHERE student_id = :studentId OR student_id = -1")
|
||||||
|
fun loadAll(studentId: Long): Flow<List<Notification>>
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Entity(tableName = "Notifications")
|
||||||
|
data class Notification(
|
||||||
|
|
||||||
|
@ColumnInfo(name = "student_id")
|
||||||
|
val studentId: Long,
|
||||||
|
|
||||||
|
val title: String,
|
||||||
|
|
||||||
|
val content: String,
|
||||||
|
|
||||||
|
val type: NotificationType,
|
||||||
|
|
||||||
|
val date: LocalDateTime,
|
||||||
|
|
||||||
|
val data: String? = null
|
||||||
|
) {
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration40 : Migration(39, 40) {
|
||||||
|
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS `Notifications` (
|
||||||
|
`student_id` INTEGER NOT NULL,
|
||||||
|
`title` TEXT NOT NULL,
|
||||||
|
`content` TEXT NOT NULL,
|
||||||
|
`type` TEXT NOT NULL,
|
||||||
|
`date` INTEGER NOT NULL,
|
||||||
|
`data` TEXT,
|
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ import androidx.annotation.StringRes
|
|||||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
|
|
||||||
sealed interface Notification {
|
sealed interface NotificationData {
|
||||||
val type: NotificationType
|
val type: NotificationType
|
||||||
val startMenu: MainView.Section
|
val startMenu: MainView.Section
|
||||||
val icon: Int
|
val icon: Int
|
||||||
@ -14,7 +14,7 @@ sealed interface Notification {
|
|||||||
val contentStringRes: Int
|
val contentStringRes: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
data class MultipleNotifications(
|
data class MultipleNotificationsData(
|
||||||
override val type: NotificationType,
|
override val type: NotificationType,
|
||||||
override val startMenu: MainView.Section,
|
override val startMenu: MainView.Section,
|
||||||
@DrawableRes override val icon: Int,
|
@DrawableRes override val icon: Int,
|
||||||
@ -23,9 +23,9 @@ data class MultipleNotifications(
|
|||||||
|
|
||||||
@PluralsRes val summaryStringRes: Int,
|
@PluralsRes val summaryStringRes: Int,
|
||||||
val lines: List<String>,
|
val lines: List<String>,
|
||||||
) : Notification
|
) : NotificationData
|
||||||
|
|
||||||
data class OneNotification(
|
data class OneNotificationData(
|
||||||
override val type: NotificationType,
|
override val type: NotificationType,
|
||||||
override val startMenu: MainView.Section,
|
override val startMenu: MainView.Section,
|
||||||
@DrawableRes override val icon: Int,
|
@DrawableRes override val icon: Int,
|
||||||
@ -33,4 +33,4 @@ data class OneNotification(
|
|||||||
@StringRes override val contentStringRes: Int,
|
@StringRes override val contentStringRes: Int,
|
||||||
|
|
||||||
val contentValues: List<String>,
|
val contentValues: List<String>,
|
||||||
) : Notification
|
) : NotificationData
|
@ -0,0 +1,15 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.dao.NotificationDao
|
||||||
|
import io.github.wulkanowy.data.db.entities.Notification
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class NotificationRepository @Inject constructor(private val notificationDao: NotificationDao) {
|
||||||
|
|
||||||
|
fun getNotifications(studentId: Long) = notificationDao.loadAll(studentId)
|
||||||
|
|
||||||
|
suspend fun saveNotification(notification: Notification) =
|
||||||
|
notificationDao.insertAll(listOf(notification))
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
package io.github.wulkanowy.services.sync.notifications
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
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.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
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import javax.inject.Inject
|
||||||
|
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}"
|
||||||
|
val groupId = student.id * 100 + notificationData.type.ordinal
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return
|
||||||
|
|
||||||
|
val summaryNotification = getDefaultNotificationBuilder(notificationData)
|
||||||
|
.setSmallIcon(notificationData.icon)
|
||||||
|
.setGroup(group)
|
||||||
|
.setStyle(NotificationCompat.InboxStyle().setSummaryText(student.nickOrName))
|
||||||
|
.setGroupSummary(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
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))
|
||||||
|
.setSmallIcon(R.drawable.ic_stat_all)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
|
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||||
|
.setContentIntent(
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
notificationData.startMenu.id,
|
||||||
|
MainActivity.getStartIntent(context, notificationData.startMenu, true),
|
||||||
|
pendingIntentsFlags
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun saveNotification(
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
notificationData: NotificationData,
|
||||||
|
student: Student
|
||||||
|
) {
|
||||||
|
val notificationEntity = Notification(
|
||||||
|
studentId = student.id,
|
||||||
|
title = title,
|
||||||
|
content = content,
|
||||||
|
type = notificationData.type,
|
||||||
|
date = LocalDateTime.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
notificationRepository.saveNotification(notificationEntity)
|
||||||
|
}
|
||||||
|
}
|
@ -1,102 +0,0 @@
|
|||||||
package io.github.wulkanowy.services.sync.notifications
|
|
||||||
|
|
||||||
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 io.github.wulkanowy.R
|
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
import io.github.wulkanowy.data.pojos.MultipleNotifications
|
|
||||||
import io.github.wulkanowy.data.pojos.Notification
|
|
||||||
import io.github.wulkanowy.data.pojos.OneNotification
|
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
|
||||||
import io.github.wulkanowy.utils.getCompatBitmap
|
|
||||||
import io.github.wulkanowy.utils.getCompatColor
|
|
||||||
import io.github.wulkanowy.utils.nickOrName
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
abstract class BaseNotification(
|
|
||||||
private val context: Context,
|
|
||||||
private val notificationManager: NotificationManagerCompat,
|
|
||||||
) {
|
|
||||||
|
|
||||||
protected fun sendNotification(notification: Notification, student: Student) =
|
|
||||||
when (notification) {
|
|
||||||
is OneNotification -> sendOneNotification(notification, student)
|
|
||||||
is MultipleNotifications -> sendMultipleNotifications(notification, student)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendOneNotification(notification: OneNotification, student: Student?) {
|
|
||||||
notificationManager.notify(
|
|
||||||
Random.nextInt(Int.MAX_VALUE),
|
|
||||||
getNotificationBuilder(notification).apply {
|
|
||||||
val content = context.getString(
|
|
||||||
notification.contentStringRes,
|
|
||||||
*notification.contentValues.toTypedArray()
|
|
||||||
)
|
|
||||||
setContentTitle(context.getString(notification.titleStringRes))
|
|
||||||
setContentText(content)
|
|
||||||
setStyle(
|
|
||||||
NotificationCompat.BigTextStyle()
|
|
||||||
.setSummaryText(student?.nickOrName)
|
|
||||||
.bigText(content)
|
|
||||||
)
|
|
||||||
}.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendMultipleNotifications(notification: MultipleNotifications, student: Student) {
|
|
||||||
val group = notification.type.group + student.id
|
|
||||||
val groupId = student.id * 100 + notification.type.ordinal
|
|
||||||
|
|
||||||
notification.lines.forEach { item ->
|
|
||||||
notificationManager.notify(
|
|
||||||
Random.nextInt(Int.MAX_VALUE),
|
|
||||||
getNotificationBuilder(notification).apply {
|
|
||||||
setContentTitle(getQuantityString(notification.titleStringRes, 1))
|
|
||||||
setContentText(item)
|
|
||||||
setStyle(
|
|
||||||
NotificationCompat.BigTextStyle()
|
|
||||||
.setSummaryText(student.nickOrName)
|
|
||||||
.bigText(item)
|
|
||||||
)
|
|
||||||
setGroup(group)
|
|
||||||
}.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return
|
|
||||||
|
|
||||||
notificationManager.notify(
|
|
||||||
groupId.toInt(),
|
|
||||||
getNotificationBuilder(notification).apply {
|
|
||||||
setSmallIcon(notification.icon)
|
|
||||||
setGroup(group)
|
|
||||||
setStyle(NotificationCompat.InboxStyle().setSummaryText(student.nickOrName))
|
|
||||||
setGroupSummary(true)
|
|
||||||
}.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getNotificationBuilder(notification: Notification) = NotificationCompat
|
|
||||||
.Builder(context, notification.type.channel)
|
|
||||||
.setLargeIcon(context.getCompatBitmap(notification.icon, R.color.colorPrimary))
|
|
||||||
.setSmallIcon(R.drawable.ic_stat_all)
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
||||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
|
||||||
.setContentIntent(
|
|
||||||
PendingIntent.getActivity(
|
|
||||||
context, notification.startMenu.id,
|
|
||||||
MainActivity.getStartIntent(context, notification.startMenu, true),
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun getQuantityString(@PluralsRes id: Int, value: Int): String {
|
|
||||||
return context.resources.getQuantityString(id, value, value)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +1,25 @@
|
|||||||
package io.github.wulkanowy.services.sync.notifications
|
package io.github.wulkanowy.services.sync.notifications
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Conference
|
import io.github.wulkanowy.data.db.entities.Conference
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.pojos.MultipleNotifications
|
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewConferenceNotification @Inject constructor(
|
class NewConferenceNotification @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
private val appNotificationManager: AppNotificationManager
|
||||||
notificationManager: NotificationManagerCompat,
|
) {
|
||||||
) : BaseNotification(context, notificationManager) {
|
|
||||||
|
|
||||||
fun notify(items: List<Conference>, student: Student) {
|
suspend fun notify(items: List<Conference>, student: Student) {
|
||||||
val today = LocalDateTime.now()
|
val today = LocalDateTime.now()
|
||||||
val lines = items.filter { !it.date.isBefore(today) }.map {
|
val lines = items.filter { !it.date.isBefore(today) }.map {
|
||||||
"${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}"
|
"${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}"
|
||||||
}.ifEmpty { return }
|
}.ifEmpty { return }
|
||||||
|
|
||||||
val notification = MultipleNotifications(
|
val notification = MultipleNotificationsData(
|
||||||
type = NotificationType.NEW_CONFERENCE,
|
type = NotificationType.NEW_CONFERENCE,
|
||||||
icon = R.drawable.ic_more_conferences,
|
icon = R.drawable.ic_more_conferences,
|
||||||
titleStringRes = R.plurals.conference_notify_new_item_title,
|
titleStringRes = R.plurals.conference_notify_new_item_title,
|
||||||
@ -33,6 +29,6 @@ class NewConferenceNotification @Inject constructor(
|
|||||||
lines = lines
|
lines = lines
|
||||||
)
|
)
|
||||||
|
|
||||||
sendNotification(notification, student)
|
appNotificationManager.sendNotification(notification, student)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,25 @@
|
|||||||
package io.github.wulkanowy.services.sync.notifications
|
package io.github.wulkanowy.services.sync.notifications
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
import io.github.wulkanowy.data.db.entities.Exam
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.pojos.MultipleNotifications
|
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewExamNotification @Inject constructor(
|
class NewExamNotification @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
private val appNotificationManager: AppNotificationManager
|
||||||
notificationManager: NotificationManagerCompat,
|
) {
|
||||||
) : BaseNotification(context, notificationManager) {
|
|
||||||
|
|
||||||
fun notify(items: List<Exam>, student: Student) {
|
suspend fun notify(items: List<Exam>, student: Student) {
|
||||||
val today = LocalDate.now()
|
val today = LocalDate.now()
|
||||||
val lines = items.filter { !it.date.isBefore(today) }.map {
|
val lines = items.filter { !it.date.isBefore(today) }.map {
|
||||||
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.description}"
|
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.description}"
|
||||||
}.ifEmpty { return }
|
}.ifEmpty { return }
|
||||||
|
|
||||||
val notification = MultipleNotifications(
|
val notification = MultipleNotificationsData(
|
||||||
type = NotificationType.NEW_EXAM,
|
type = NotificationType.NEW_EXAM,
|
||||||
icon = R.drawable.ic_main_exam,
|
icon = R.drawable.ic_main_exam,
|
||||||
titleStringRes = R.plurals.exam_notify_new_item_title,
|
titleStringRes = R.plurals.exam_notify_new_item_title,
|
||||||
@ -33,6 +29,6 @@ class NewExamNotification @Inject constructor(
|
|||||||
lines = lines
|
lines = lines
|
||||||
)
|
)
|
||||||
|
|
||||||
sendNotification(notification, student)
|
appNotificationManager.sendNotification(notification, student)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,19 @@
|
|||||||
package io.github.wulkanowy.services.sync.notifications
|
package io.github.wulkanowy.services.sync.notifications
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Grade
|
import io.github.wulkanowy.data.db.entities.Grade
|
||||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.pojos.MultipleNotifications
|
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewGradeNotification @Inject constructor(
|
class NewGradeNotification @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
private val appNotificationManager: AppNotificationManager
|
||||||
notificationManager: NotificationManagerCompat,
|
) {
|
||||||
) : BaseNotification(context, notificationManager) {
|
|
||||||
|
|
||||||
fun notifyDetails(items: List<Grade>, student: Student) {
|
suspend fun notifyDetails(items: List<Grade>, student: Student) {
|
||||||
val notification = MultipleNotifications(
|
val notification = MultipleNotificationsData(
|
||||||
type = NotificationType.NEW_GRADE_DETAILS,
|
type = NotificationType.NEW_GRADE_DETAILS,
|
||||||
icon = R.drawable.ic_stat_grade,
|
icon = R.drawable.ic_stat_grade,
|
||||||
titleStringRes = R.plurals.grade_new_items,
|
titleStringRes = R.plurals.grade_new_items,
|
||||||
@ -29,11 +25,11 @@ class NewGradeNotification @Inject constructor(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
sendNotification(notification, student)
|
appNotificationManager.sendNotification(notification, student)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyPredicted(items: List<GradeSummary>, student: Student) {
|
suspend fun notifyPredicted(items: List<GradeSummary>, student: Student) {
|
||||||
val notification = MultipleNotifications(
|
val notification = MultipleNotificationsData(
|
||||||
type = NotificationType.NEW_GRADE_PREDICTED,
|
type = NotificationType.NEW_GRADE_PREDICTED,
|
||||||
icon = R.drawable.ic_stat_grade,
|
icon = R.drawable.ic_stat_grade,
|
||||||
titleStringRes = R.plurals.grade_new_items_predicted,
|
titleStringRes = R.plurals.grade_new_items_predicted,
|
||||||
@ -45,11 +41,11 @@ class NewGradeNotification @Inject constructor(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
sendNotification(notification, student)
|
appNotificationManager.sendNotification(notification, student)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyFinal(items: List<GradeSummary>, student: Student) {
|
suspend fun notifyFinal(items: List<GradeSummary>, student: Student) {
|
||||||
val notification = MultipleNotifications(
|
val notification = MultipleNotificationsData(
|
||||||
type = NotificationType.NEW_GRADE_FINAL,
|
type = NotificationType.NEW_GRADE_FINAL,
|
||||||
icon = R.drawable.ic_stat_grade,
|
icon = R.drawable.ic_stat_grade,
|
||||||
titleStringRes = R.plurals.grade_new_items_final,
|
titleStringRes = R.plurals.grade_new_items_final,
|
||||||
@ -61,6 +57,6 @@ class NewGradeNotification @Inject constructor(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
sendNotification(notification, student)
|
appNotificationManager.sendNotification(notification, student)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,25 @@
|
|||||||
package io.github.wulkanowy.services.sync.notifications
|
package io.github.wulkanowy.services.sync.notifications
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Homework
|
import io.github.wulkanowy.data.db.entities.Homework
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.pojos.MultipleNotifications
|
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewHomeworkNotification @Inject constructor(
|
class NewHomeworkNotification @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
private val appNotificationManager: AppNotificationManager
|
||||||
notificationManager: NotificationManagerCompat,
|
) {
|
||||||
) : BaseNotification(context, notificationManager) {
|
|
||||||
|
|
||||||
fun notify(items: List<Homework>, student: Student) {
|
suspend fun notify(items: List<Homework>, student: Student) {
|
||||||
val today = LocalDate.now()
|
val today = LocalDate.now()
|
||||||
val lines = items.filter { !it.date.isBefore(today) }.map {
|
val lines = items.filter { !it.date.isBefore(today) }.map {
|
||||||
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.content}"
|
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.content}"
|
||||||
}.ifEmpty { return }
|
}.ifEmpty { return }
|
||||||
|
|
||||||
val notification = MultipleNotifications(
|
val notification = MultipleNotificationsData(
|
||||||
type = NotificationType.NEW_HOMEWORK,
|
type = NotificationType.NEW_HOMEWORK,
|
||||||
icon = R.drawable.ic_more_homework,
|
icon = R.drawable.ic_more_homework,
|
||||||
titleStringRes = R.plurals.homework_notify_new_item_title,
|
titleStringRes = R.plurals.homework_notify_new_item_title,
|
||||||
@ -33,6 +29,6 @@ class NewHomeworkNotification @Inject constructor(
|
|||||||
lines = lines
|
lines = lines
|
||||||
)
|
)
|
||||||
|
|
||||||
sendNotification(notification, student)
|
appNotificationManager.sendNotification(notification, student)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,26 @@
|
|||||||
package io.github.wulkanowy.services.sync.notifications
|
package io.github.wulkanowy.services.sync.notifications
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.pojos.OneNotification
|
import io.github.wulkanowy.data.pojos.OneNotificationData
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewLuckyNumberNotification @Inject constructor(
|
class NewLuckyNumberNotification @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
private val appNotificationManager: AppNotificationManager
|
||||||
notificationManager: NotificationManagerCompat,
|
) {
|
||||||
) : BaseNotification(context, notificationManager) {
|
|
||||||
|
|
||||||
fun notify(item: LuckyNumber, student: Student) {
|
suspend fun notify(item: LuckyNumber, student: Student) {
|
||||||
val notification = OneNotification(
|
val notification = OneNotificationData(
|
||||||
type = NotificationType.NEW_LUCKY_NUMBER,
|
type = NotificationType.NEW_LUCKY_NUMBER,
|
||||||
icon = R.drawable.ic_stat_luckynumber,
|
icon = R.drawable.ic_stat_luckynumber,
|
||||||
titleStringRes = R.string.lucky_number_notify_new_item_title,
|
titleStringRes = R.string.lucky_number_notify_new_item_title,
|
||||||
contentStringRes = R.string.lucky_number_notify_new_item,
|
contentStringRes = R.string.lucky_number_notify_new_item,
|
||||||
startMenu = MainView.Section.LUCKY_NUMBER,
|
startMenu = MainView.Section.LUCKY_NUMBER,
|
||||||
contentValues = listOf(item.luckyNumber.toString())
|
contentValues = listOf(item.luckyNumber.toString())
|
||||||
)
|
)
|
||||||
|
|
||||||
sendNotification(notification, student)
|
appNotificationManager.sendNotification(notification, student)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,18 @@
|
|||||||
package io.github.wulkanowy.services.sync.notifications
|
package io.github.wulkanowy.services.sync.notifications
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.pojos.MultipleNotifications
|
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewMessageNotification @Inject constructor(
|
class NewMessageNotification @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
private val appNotificationManager: AppNotificationManager
|
||||||
notificationManager: NotificationManagerCompat,
|
) {
|
||||||
) : BaseNotification(context, notificationManager) {
|
|
||||||
|
|
||||||
fun notify(items: List<Message>, student: Student) {
|
suspend fun notify(items: List<Message>, student: Student) {
|
||||||
val notification = MultipleNotifications(
|
val notification = MultipleNotificationsData(
|
||||||
type = NotificationType.NEW_MESSAGE,
|
type = NotificationType.NEW_MESSAGE,
|
||||||
icon = R.drawable.ic_stat_message,
|
icon = R.drawable.ic_stat_message,
|
||||||
titleStringRes = R.plurals.message_new_items,
|
titleStringRes = R.plurals.message_new_items,
|
||||||
@ -28,6 +24,6 @@ class NewMessageNotification @Inject constructor(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
sendNotification(notification, student)
|
appNotificationManager.sendNotification(notification, student)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,19 @@
|
|||||||
package io.github.wulkanowy.services.sync.notifications
|
package io.github.wulkanowy.services.sync.notifications
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Note
|
import io.github.wulkanowy.data.db.entities.Note
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.pojos.MultipleNotifications
|
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||||
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
|
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewNoteNotification @Inject constructor(
|
class NewNoteNotification @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
private val appNotificationManager: AppNotificationManager
|
||||||
notificationManager: NotificationManagerCompat,
|
) {
|
||||||
) : BaseNotification(context, notificationManager) {
|
|
||||||
|
|
||||||
fun notify(items: List<Note>, student: Student) {
|
suspend fun notify(items: List<Note>, student: Student) {
|
||||||
val notification = MultipleNotifications(
|
val notification = MultipleNotificationsData(
|
||||||
type = NotificationType.NEW_NOTE,
|
type = NotificationType.NEW_NOTE,
|
||||||
icon = R.drawable.ic_stat_note,
|
icon = R.drawable.ic_stat_note,
|
||||||
titleStringRes = when (NoteCategory.getByValue(items.first().categoryType)) {
|
titleStringRes = when (NoteCategory.getByValue(items.first().categoryType)) {
|
||||||
@ -41,6 +37,6 @@ class NewNoteNotification @Inject constructor(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
sendNotification(notification, student)
|
appNotificationManager.sendNotification(notification, student)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,29 @@
|
|||||||
package io.github.wulkanowy.services.sync.notifications
|
package io.github.wulkanowy.services.sync.notifications
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.pojos.MultipleNotifications
|
import io.github.wulkanowy.data.pojos.MultipleNotificationsData
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewSchoolAnnouncementNotification @Inject constructor(
|
class NewSchoolAnnouncementNotification @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
private val appNotificationManager: AppNotificationManager
|
||||||
notificationManager: NotificationManagerCompat,
|
) {
|
||||||
) : BaseNotification(context, notificationManager) {
|
|
||||||
|
|
||||||
fun notify(items: List<SchoolAnnouncement>, student: Student) {
|
suspend fun notify(items: List<SchoolAnnouncement>, student: Student) {
|
||||||
val notification = MultipleNotifications(
|
val notification = MultipleNotificationsData(
|
||||||
type = NotificationType.NEW_ANNOUNCEMENT,
|
type = NotificationType.NEW_ANNOUNCEMENT,
|
||||||
icon = R.drawable.ic_all_about,
|
icon = R.drawable.ic_all_about,
|
||||||
titleStringRes = R.plurals.school_announcement_notify_new_item_title,
|
titleStringRes = R.plurals.school_announcement_notify_new_item_title,
|
||||||
contentStringRes = R.plurals.school_announcement_notify_new_items,
|
contentStringRes = R.plurals.school_announcement_notify_new_items,
|
||||||
summaryStringRes = R.plurals.school_announcement_number_item,
|
summaryStringRes = R.plurals.school_announcement_number_item,
|
||||||
startMenu = MainView.Section.SCHOOL_ANNOUNCEMENT,
|
startMenu = MainView.Section.SCHOOL_ANNOUNCEMENT,
|
||||||
lines = items.map {
|
lines = items.map {
|
||||||
"${it.subject}: ${it.content}"
|
"${it.subject}: ${it.content}"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
sendNotification(notification, student)
|
appNotificationManager.sendNotification(notification, student)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,9 @@ import io.github.wulkanowy.services.sync.channels.NewHomeworkChannel
|
|||||||
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
|
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
|
||||||
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
||||||
import io.github.wulkanowy.services.sync.channels.NewSchoolAnnouncementsChannel
|
import io.github.wulkanowy.services.sync.channels.NewSchoolAnnouncementsChannel
|
||||||
|
import io.github.wulkanowy.services.sync.channels.PushChannel
|
||||||
|
|
||||||
enum class NotificationType(val group: String, val channel: String) {
|
enum class NotificationType(val group: String?, val channel: String) {
|
||||||
NEW_CONFERENCE("new_conferences_group", NewConferencesChannel.CHANNEL_ID),
|
NEW_CONFERENCE("new_conferences_group", NewConferencesChannel.CHANNEL_ID),
|
||||||
NEW_EXAM("new_exam_group", NewExamChannel.CHANNEL_ID),
|
NEW_EXAM("new_exam_group", NewExamChannel.CHANNEL_ID),
|
||||||
NEW_GRADE_DETAILS("new_grade_details_group", NewGradesChannel.CHANNEL_ID),
|
NEW_GRADE_DETAILS("new_grade_details_group", NewGradesChannel.CHANNEL_ID),
|
||||||
@ -20,4 +21,5 @@ enum class NotificationType(val group: String, val channel: String) {
|
|||||||
NEW_MESSAGE("new_message_group", NewMessagesChannel.CHANNEL_ID),
|
NEW_MESSAGE("new_message_group", NewMessagesChannel.CHANNEL_ID),
|
||||||
NEW_NOTE("new_notes_group", NewNotesChannel.CHANNEL_ID),
|
NEW_NOTE("new_notes_group", NewNotesChannel.CHANNEL_ID),
|
||||||
NEW_ANNOUNCEMENT("new_school_announcements_group", NewSchoolAnnouncementsChannel.CHANNEL_ID),
|
NEW_ANNOUNCEMENT("new_school_announcements_group", NewSchoolAnnouncementsChannel.CHANNEL_ID),
|
||||||
|
PUSH(null, PushChannel.CHANNEL_ID)
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,12 @@ import io.github.wulkanowy.utils.waitForResult
|
|||||||
import java.time.LocalDate.now
|
import java.time.LocalDate.now
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work {
|
class AttendanceWork @Inject constructor(
|
||||||
|
private val attendanceRepository: AttendanceRepository
|
||||||
|
) : Work {
|
||||||
|
|
||||||
override suspend fun doWork(student: Student, semester: Semester) {
|
override suspend fun doWork(student: Student, semester: Semester) {
|
||||||
attendanceRepository.getAttendance(student, semester, now().monday, now().sunday, true).waitForResult()
|
attendanceRepository.getAttendance(student, semester, now().monday, now().sunday, true)
|
||||||
|
.waitForResult()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
|||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.notificationscenter.NotificationsCenterFragment
|
||||||
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
|
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
|
||||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||||
import io.github.wulkanowy.utils.capitalise
|
import io.github.wulkanowy.utils.capitalise
|
||||||
@ -120,6 +121,7 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
|||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.dashboard_menu_tiles -> presenter.onDashboardTileSettingsSelected()
|
R.id.dashboard_menu_tiles -> presenter.onDashboardTileSettingsSelected()
|
||||||
|
R.id.dashboard_menu_notifaction_list -> presenter.onNotificationsCenterSelected()
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,6 +184,10 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
|||||||
if (::presenter.isInitialized) presenter.onViewReselected()
|
if (::presenter.isInitialized) presenter.onViewReselected()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openNotificationsCenterView() {
|
||||||
|
(requireActivity() as MainActivity).pushView(NotificationsCenterFragment.newInstance())
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
dashboardAdapter.clearTimers()
|
dashboardAdapter.clearTimers()
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
|
@ -209,6 +209,11 @@ class DashboardPresenter @Inject constructor(
|
|||||||
view?.showErrorDetailsDialog(lastError)
|
view?.showErrorDetailsDialog(lastError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onNotificationsCenterSelected(): Boolean {
|
||||||
|
view?.openNotificationsCenterView()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
fun onDashboardTileSettingsSelected(): Boolean {
|
fun onDashboardTileSettingsSelected(): Boolean {
|
||||||
view?.showDashboardTileSettings(preferencesRepository.selectedDashboardTiles.toList())
|
view?.showDashboardTileSettings(preferencesRepository.selectedDashboardTiles.toList())
|
||||||
return true
|
return true
|
||||||
|
@ -23,4 +23,6 @@ interface DashboardView : BaseView {
|
|||||||
fun resetView()
|
fun resetView()
|
||||||
|
|
||||||
fun popViewToRoot()
|
fun popViewToRoot()
|
||||||
|
|
||||||
|
fun openNotificationsCenterView()
|
||||||
}
|
}
|
@ -87,7 +87,7 @@ class NotificationDebugPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun withStudent(block: (Student) -> Unit) {
|
private fun withStudent(block: suspend (Student) -> Unit) {
|
||||||
launch {
|
launch {
|
||||||
block(studentRepository.getCurrentStudent(false))
|
block(studentRepository.getCurrentStudent(false))
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.notificationscenter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
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
|
||||||
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class NotificationsCenterAdapter @Inject constructor() :
|
||||||
|
ListAdapter<Notification, NotificationsCenterAdapter.ViewHolder>(DiffUtilCallback()) {
|
||||||
|
|
||||||
|
var onItemClickListener: (NotificationType) -> Unit = {}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
|
||||||
|
ItemNotificationsCenterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val item = getItem(position)
|
||||||
|
|
||||||
|
with(holder.binding) {
|
||||||
|
notificationsCenterItemTitle.text = item.title
|
||||||
|
notificationsCenterItemContent.text = item.content
|
||||||
|
notificationsCenterItemDate.text = item.date.toFormattedString("HH:mm, d MMM")
|
||||||
|
notificationsCenterItemIcon.setImageResource(item.type.toDrawableResId())
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
private class DiffUtilCallback : DiffUtil.ItemCallback<Notification>() {
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: Notification, newItem: Notification) =
|
||||||
|
oldItem == newItem
|
||||||
|
|
||||||
|
override fun areItemsTheSame(oldItem: Notification, newItem: Notification) =
|
||||||
|
oldItem.id == newItem.id
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.notificationscenter
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
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.conference.ConferenceFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
|
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 javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class NotificationsCenterFragment :
|
||||||
|
BaseFragment<FragmentNotificationsCenterBinding>(R.layout.fragment_notifications_center),
|
||||||
|
NotificationsCenterView, MainView.TitledView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: NotificationsCenterPresenter
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var notificationsCenterAdapter: NotificationsCenterAdapter
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun newInstance() = NotificationsCenterFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val titleStringId: Int
|
||||||
|
get() = R.string.notifications_center_title
|
||||||
|
|
||||||
|
override val isViewEmpty: Boolean
|
||||||
|
get() = notificationsCenterAdapter.itemCount == 0
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
binding = FragmentNotificationsCenterBinding.bind(view)
|
||||||
|
presenter.onAttachView(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
notificationsCenterAdapter.onItemClickListener = { notificationType ->
|
||||||
|
notificationType.toDestinationFragment()
|
||||||
|
?.let { (requireActivity() as MainActivity).pushView(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
with(binding.notificationsCenterRecycler) {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = notificationsCenterAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateData(data: List<Notification>) {
|
||||||
|
notificationsCenterAdapter.submitList(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showEmpty(show: Boolean) {
|
||||||
|
binding.notificationsCenterEmpty.isVisible = show
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showProgress(show: Boolean) {
|
||||||
|
binding.notificationsCenterProgress.isVisible = show
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showContent(show: Boolean) {
|
||||||
|
binding.notificationsCenterRecycler.isVisible = show
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showErrorView(show: Boolean) {
|
||||||
|
binding.notificationCenterError.isVisible = show
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setErrorDetails(message: String) {
|
||||||
|
binding.notificationCenterErrorMessage.text = message
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
presenter.onDetachView()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun NotificationType.toDestinationFragment(): Fragment? = when (this) {
|
||||||
|
NotificationType.NEW_CONFERENCE -> ConferenceFragment.newInstance()
|
||||||
|
NotificationType.NEW_EXAM -> ExamFragment.newInstance()
|
||||||
|
NotificationType.NEW_GRADE_DETAILS -> GradeFragment.newInstance()
|
||||||
|
NotificationType.NEW_GRADE_PREDICTED -> GradeFragment.newInstance()
|
||||||
|
NotificationType.NEW_GRADE_FINAL -> GradeFragment.newInstance()
|
||||||
|
NotificationType.NEW_HOMEWORK -> HomeworkFragment.newInstance()
|
||||||
|
NotificationType.NEW_LUCKY_NUMBER -> LuckyNumberFragment.newInstance()
|
||||||
|
NotificationType.NEW_MESSAGE -> MessageFragment.newInstance()
|
||||||
|
NotificationType.NEW_NOTE -> NoteFragment.newInstance()
|
||||||
|
NotificationType.NEW_ANNOUNCEMENT -> SchoolAnnouncementFragment.newInstance()
|
||||||
|
NotificationType.PUSH -> null
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.notificationscenter
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.repositories.NotificationRepository
|
||||||
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.emitAll
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class NotificationsCenterPresenter @Inject constructor(
|
||||||
|
private val notificationRepository: NotificationRepository,
|
||||||
|
errorHandler: ErrorHandler,
|
||||||
|
studentRepository: StudentRepository
|
||||||
|
) : BasePresenter<NotificationsCenterView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
|
override fun onAttachView(view: NotificationsCenterView) {
|
||||||
|
super.onAttachView(view)
|
||||||
|
view.initView()
|
||||||
|
Timber.i("Notifications centre view was initialized")
|
||||||
|
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRetry() {
|
||||||
|
view?.run {
|
||||||
|
showErrorView(false)
|
||||||
|
showProgress(true)
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDetailsClick() {
|
||||||
|
view?.showErrorDetailsDialog(lastError)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadData() {
|
||||||
|
Timber.i("Loading notifications data started")
|
||||||
|
|
||||||
|
flow {
|
||||||
|
val studentId = studentRepository.getCurrentStudent(false).id
|
||||||
|
emitAll(notificationRepository.getNotifications(studentId))
|
||||||
|
}
|
||||||
|
.map { notificationList -> notificationList.sortedByDescending { it.date } }
|
||||||
|
.catch { Timber.i("Loading notifications result: An exception occurred") }
|
||||||
|
.onEach {
|
||||||
|
Timber.i("Loading notifications result: Success")
|
||||||
|
|
||||||
|
if (it.isEmpty()) {
|
||||||
|
view?.run {
|
||||||
|
showContent(false)
|
||||||
|
showProgress(false)
|
||||||
|
showEmpty(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view?.run {
|
||||||
|
showContent(true)
|
||||||
|
showProgress(false)
|
||||||
|
showEmpty(false)
|
||||||
|
updateData(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.launch()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||||
|
view?.run {
|
||||||
|
if (isViewEmpty) {
|
||||||
|
lastError = error
|
||||||
|
setErrorDetails(message)
|
||||||
|
showErrorView(true)
|
||||||
|
showEmpty(false)
|
||||||
|
} else showError(message, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.notificationscenter
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.Notification
|
||||||
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
|
interface NotificationsCenterView : BaseView {
|
||||||
|
|
||||||
|
val isViewEmpty: Boolean
|
||||||
|
|
||||||
|
fun initView()
|
||||||
|
|
||||||
|
fun updateData(data: List<Notification>)
|
||||||
|
|
||||||
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
|
fun showEmpty(show: Boolean)
|
||||||
|
|
||||||
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
|
fun showErrorView(show: Boolean)
|
||||||
|
|
||||||
|
fun setErrorDetails(message: String)
|
||||||
|
}
|
@ -184,6 +184,7 @@ class NotificationsFragment : PreferenceFragmentCompat(),
|
|||||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||||
setNotificationPiggybackPreferenceChecked(false)
|
setNotificationPiggybackPreferenceChecked(false)
|
||||||
}
|
}
|
||||||
|
.setOnDismissListener { setNotificationPiggybackPreferenceChecked(false) }
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
106
app/src/main/res/layout/fragment_notifications_center.xml
Normal file
106
app/src/main/res/layout/fragment_notifications_center.xml
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/notifications_center_recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:itemCount="4"
|
||||||
|
tools:listitem="@layout/item_notifications_center"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||||
|
android:id="@+id/notifications_center_progress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:visibility="gone" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/notifications_center_empty"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:ignore="UseCompoundDrawables">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
app:srcCompat="@drawable/ic_settings_notifications"
|
||||||
|
app:tint="?colorOnBackground"
|
||||||
|
tools:ignore="contentDescription" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/all_no_data"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/notification_center_error"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="invisible"
|
||||||
|
tools:ignore="UseCompoundDrawables"
|
||||||
|
tools:visibility="invisible">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
app:srcCompat="@drawable/ic_error"
|
||||||
|
app:tint="?colorOnBackground"
|
||||||
|
tools:ignore="contentDescription" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notification_center_error_message"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:text="@string/error_unknown"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/notification_center_error_details"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:text="@string/all_details" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/notification_center_error_retry"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/all_retry" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
70
app/src/main/res/layout/item_notifications_center.xml
Normal file
70
app/src/main/res/layout/item_notifications_center.xml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:cardElevation="4dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notifications_center_item_date"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/notifications_center_item_icon"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="@tools:sample/date/ddmmyy" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notifications_center_item_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/notifications_center_item_icon"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/notifications_center_item_date"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notifications_center_item_content"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="5"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/notifications_center_item_icon"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/notifications_center_item_title"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/notifications_center_item_icon"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:tint="?colorPrimary"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
@ -1,10 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/dashboard_menu_notifaction_list"
|
||||||
|
android:icon="@drawable/ic_settings_notifications"
|
||||||
|
android:orderInCategory="1"
|
||||||
|
android:title="@string/notifications_center_title"
|
||||||
|
app:iconTint="@color/material_on_surface_emphasis_medium"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/dashboard_menu_tiles"
|
android:id="@+id/dashboard_menu_tiles"
|
||||||
android:icon="@drawable/ic_more_settings"
|
android:icon="@drawable/ic_more_settings"
|
||||||
android:orderInCategory="1"
|
android:orderInCategory="2"
|
||||||
android:title="@string/pref_dashboard_appearance_tiles_title"
|
android:title="@string/pref_dashboard_appearance_tiles_title"
|
||||||
app:iconTint="@color/material_on_surface_emphasis_medium"
|
app:iconTint="@color/material_on_surface_emphasis_medium"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/mainMenuAccount"
|
android:id="@+id/mainMenuAccount"
|
||||||
android:orderInCategory="2"
|
android:orderInCategory="10"
|
||||||
app:showAsAction="always"
|
app:showAsAction="always"
|
||||||
tools:ignore="MenuTitle" />
|
tools:ignore="MenuTitle" />
|
||||||
</menu>
|
</menu>
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
<string name="account_details_title">Account details</string>
|
<string name="account_details_title">Account details</string>
|
||||||
<string name="student_info_title">Student info</string>
|
<string name="student_info_title">Student info</string>
|
||||||
<string name="dashboard_title">Dashboard</string>
|
<string name="dashboard_title">Dashboard</string>
|
||||||
|
<string name="notifications_center_title">Notifications center</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Subtitles-->
|
<!--Subtitles-->
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package io.github.wulkanowy.services.messaging
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import com.google.firebase.messaging.FirebaseMessagingService
|
||||||
|
import com.google.firebase.messaging.RemoteMessage
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import io.github.wulkanowy.data.db.entities.Notification
|
||||||
|
import io.github.wulkanowy.data.repositories.NotificationRepository
|
||||||
|
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@SuppressLint("MissingFirebaseInstanceTokenRefresh")
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class AppMessagingService : FirebaseMessagingService() {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var notificationRepository: NotificationRepository
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
|
||||||
|
private val serviceScope = CoroutineScope(Dispatchers.Main + job)
|
||||||
|
|
||||||
|
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||||
|
val remoteMessageData = remoteMessage.data
|
||||||
|
val title = remoteMessageData["title"] ?: return
|
||||||
|
val content = remoteMessageData["content"] ?: return
|
||||||
|
val customData = remoteMessageData["custom_data"]
|
||||||
|
|
||||||
|
val notification = Notification(
|
||||||
|
title = title,
|
||||||
|
content = content,
|
||||||
|
data = customData,
|
||||||
|
date = LocalDateTime.now(),
|
||||||
|
type = NotificationType.PUSH,
|
||||||
|
studentId = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
serviceScope.launch {
|
||||||
|
try {
|
||||||
|
notificationRepository.saveNotification(notification)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
job.cancel()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user