[Notifications] Add filtering notifications to show during sync.

This commit is contained in:
Kuba Szczodrzyński 2020-02-22 00:07:22 +01:00
parent af8bda9e92
commit a4493ec964
7 changed files with 195 additions and 55 deletions

View File

@ -29,8 +29,8 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
val grades by lazy { ProfileConfigGrades(this) }
val ui by lazy { ProfileConfigUI(this) }
val sync by lazy { ProfileConfigSync(this) }
/*
val sync by lazy { ConfigSync(this) }
val timetable by lazy { ConfigTimetable(this) }
val grades by lazy { ConfigGrades(this) }*/
@ -56,4 +56,4 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
db.configDao().add(ConfigEntry(profileId, key, value))
}
}
}
}

View File

@ -0,0 +1,15 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-21.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
class ProfileConfigSync(private val config: ProfileConfig) {
private var mNotificationFilter: List<Int>? = null
var notificationFilter: List<Int>
get() { mNotificationFilter = mNotificationFilter ?: config.values.get("notificationFilter", listOf()); return mNotificationFilter ?: listOf() }
set(value) { config.set("notificationFilter", value); mNotificationFilter = value }
}

View File

@ -44,6 +44,17 @@ class SzkolnyTask(val app: App, val syncingProfiles: List<Profile>) : IApiTask(-
}
d(TAG, "Created ${notificationList.count()} notifications.")
// filter notifications
notificationList
.mapNotNull { it.profileId }
.distinct()
.map { app.config.getFor(it).sync.notificationFilter }
.forEach { filter ->
filter.forEach { type ->
notificationList.removeAll { it.type == type }
}
}
// update the database
app.db.metadataDao().setAllNotified(true)
if (notificationList.isNotEmpty())

View File

@ -145,25 +145,31 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
event.topic
)*/
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT
val notification = Notification(
id = Notification.buildId(event.profileId, type, event.id),
title = app.getNotificationTitle(type),
text = message,
type = type,
profileId = profile?.id,
profileName = profile?.name,
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
addedDate = metadata.addedDate
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
val notificationFilter = app.config.getFor(event.profileId).sync.notificationFilter
if (!notificationFilter.contains(type)) {
val notification = Notification(
id = Notification.buildId(event.profileId, type, event.id),
title = app.getNotificationTitle(type),
text = message,
type = type,
profileId = profile?.id,
profileName = profile?.name,
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
addedDate = metadata.addedDate
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
notificationList += notification
}
events += event
metadataList += metadata
notificationList += notification
}
app.db.eventDao().addAll(events)
app.db.metadataDao().addAllReplace(metadataList)
app.db.notificationDao().addAll(notificationList)
PostNotifications(app, notificationList)
if (notificationList.isNotEmpty()) {
app.db.notificationDao().addAll(notificationList)
PostNotifications(app, notificationList)
}
}
private fun unsharedEvent(teamCode: String, eventId: Long, message: String) {
@ -172,19 +178,26 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
val profile = profiles.firstOrNull { it.id == team.profileId }
val notification = Notification(
id = Notification.buildId(profile?.id ?: 0, Notification.TYPE_REMOVED_SHARED_EVENT, eventId),
title = app.getNotificationTitle(Notification.TYPE_REMOVED_SHARED_EVENT),
text = message,
type = Notification.TYPE_REMOVED_SHARED_EVENT,
profileId = profile?.id,
profileName = profile?.name,
viewId = MainActivity.DRAWER_ITEM_AGENDA
)
notificationList += notification
val notificationFilter = app.config.getFor(team.profileId).sync.notificationFilter
if (!notificationFilter.contains(Notification.TYPE_REMOVED_SHARED_EVENT)) {
val notification = Notification(
id = Notification.buildId(profile?.id
?: 0, Notification.TYPE_REMOVED_SHARED_EVENT, eventId),
title = app.getNotificationTitle(Notification.TYPE_REMOVED_SHARED_EVENT),
text = message,
type = Notification.TYPE_REMOVED_SHARED_EVENT,
profileId = profile?.id,
profileName = profile?.name,
viewId = MainActivity.DRAWER_ITEM_AGENDA
)
notificationList += notification
}
app.db.eventDao().remove(team.profileId, eventId)
}
app.db.notificationDao().addAll(notificationList)
PostNotifications(app, notificationList)
if (notificationList.isNotEmpty()) {
app.db.notificationDao().addAll(notificationList)
PostNotifications(app, notificationList)
}
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-21.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.sync
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.onClick
import kotlin.coroutines.CoroutineContext
// TODO refactor dialog to allow configuring other profiles
// than the selected one in UI
class NotificationFilterDialog(
val activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "NotificationFilterDialog"
private val notificationTypes = listOf(
Notification.TYPE_TIMETABLE_LESSON_CHANGE to R.string.notification_type_timetable_lesson_change,
Notification.TYPE_NEW_GRADE to R.string.notification_type_new_grade,
Notification.TYPE_NEW_EVENT to R.string.notification_type_new_event,
Notification.TYPE_NEW_HOMEWORK to R.string.notification_type_new_homework,
Notification.TYPE_NEW_MESSAGE to R.string.notification_type_new_message,
Notification.TYPE_LUCKY_NUMBER to R.string.notification_type_lucky_number,
Notification.TYPE_NEW_NOTICE to R.string.notification_type_notice,
Notification.TYPE_NEW_ATTENDANCE to R.string.notification_type_attendance,
Notification.TYPE_NEW_ANNOUNCEMENT to R.string.notification_type_new_announcement,
Notification.TYPE_NEW_SHARED_EVENT to R.string.notification_type_new_shared_event,
Notification.TYPE_NEW_SHARED_HOMEWORK to R.string.notification_type_new_shared_homework,
Notification.TYPE_REMOVED_SHARED_EVENT to R.string.notification_type_removed_shared_event
)
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val notificationFilter = mutableListOf<Int>()
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
notificationFilter.clear()
notificationFilter += app.config.forProfile().sync.notificationFilter
val items = notificationTypes.map { app.getString(it.second) }.toTypedArray()
val checkedItems = notificationTypes.map { !notificationFilter.contains(it.first) }.toBooleanArray()
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.dialog_notification_filter_title)
//.setMessage(R.string.dialog_notification_filter_text)
.setMultiChoiceItems(items, checkedItems) { _, which, isChecked ->
val type = notificationTypes[which].first
notificationFilter.remove(type)
if (!isChecked)
notificationFilter += type
}
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick {
if (notificationFilter.isEmpty()) {
app.config.forProfile().sync.notificationFilter = notificationFilter
dialog.dismiss()
return@onClick
}
// warn user when he tries to disable some notifications
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.are_you_sure)
.setMessage(R.string.notification_filter_warning)
.setPositiveButton(R.string.ok) { _, _ ->
app.config.forProfile().sync.notificationFilter = notificationFilter
dialog.dismiss()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
}}
}

View File

@ -54,6 +54,7 @@ import pl.szczodrzynski.edziennik.sync.UpdateWorker;
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.sync.NotificationFilterDialog;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.edziennik.utils.models.Date;
@ -217,6 +218,24 @@ public class SettingsNewFragment extends MaterialAboutFragment {
})
);*/
items.add(
new MaterialAboutActionItem(
getString(R.string.settings_profile_notifications_text),
getString(R.string.settings_profile_notifications_subtext),
new IconicsDrawable(activity)
.icon(CommunityMaterial.Icon.cmd_filter_outline)
.size(IconicsSize.dp(iconSizeDp))
.color(IconicsColor.colorInt(iconColor))
)
.setOnClickAction(() -> {
new NotificationFilterDialog(activity, null, null);
})
);
items.add(getMoreItem(() -> addCardItems(CARD_PROFILE, getProfileCard(true))));
}
else {
items.add(
new MaterialAboutSwitchItem(
getString(R.string.settings_profile_sync_text),
@ -226,35 +245,14 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.size(IconicsSize.dp(iconSizeDp))
.color(IconicsColor.colorInt(iconColor))
)
.setChecked(app.getProfile().getSyncEnabled())
.setOnChangeAction(((isChecked, tag) -> {
app.getProfile().setSyncEnabled(isChecked);
app.profileSave();
return true;
}))
.setChecked(app.getProfile().getSyncEnabled())
.setOnChangeAction(((isChecked, tag) -> {
app.getProfile().setSyncEnabled(isChecked);
app.profileSave();
return true;
}))
);
items.add(getMoreItem(() -> addCardItems(CARD_PROFILE, getProfileCard(true))));
}
else {
/*items.add(
new MaterialAboutSwitchItem(
getString(R.string.settings_profile_notify_text),
getString(R.string.settings_profile_notify_subtext),
new IconicsDrawable(activity)
.icon(CommunityMaterial.Icon.cmd_bell_ring)
.size(IconicsSize.dp(iconSizeDp))
.color(IconicsColor.colorInt(iconColor))
)
.setChecked(app.getProfile().getSyncNotifications())
.setOnChangeAction(((isChecked, tag) -> {
app.getProfile().setSyncNotifications(isChecked);
app.profileSave();
return true;
}))
);*/
items.add(
new MaterialAboutActionItem(
getString(R.string.settings_profile_remove_text),

View File

@ -567,7 +567,7 @@
<string name="notification_type_new_homework">Nowe zadanie domowe</string>
<string name="notification_type_new_message">Nowa wiadomość</string>
<string name="notification_type_new_shared_event">Udostępniono wydarzenie</string>
<string name="notification_type_notice">Ocena ucznia</string>
<string name="notification_type_notice">Wpis zachowania</string>
<string name="notification_type_server_message">Wiadomość z serwera</string>
<string name="notification_type_timetable_change">Zmiana planu zajęć</string>
<string name="notification_type_timetable_lesson_change">Zmiana lekcji</string>
@ -1181,5 +1181,10 @@
<string name="notification_user_action_required_title">Wymagane działanie w aplikacji</string>
<string name="notification_user_action_required_text">Problem, który uniemożliwia synchronizację musi być rozwiązany przez użytkownika. Kliknij, aby uzyskać więcej informacji.</string>
<string name="notification_user_action_required_captcha_librus">Librus: wymagane rozwiązanie zadania Captcha. Kliknij, aby kontynuować logowanie do dziennika.</string>
<string name="settings_profile_notifications_text">Filtruj powiadomienia</string>
<string name="settings_profile_notifications_subtext">Wyłącz określone rodzaje powiadomień</string>
<string name="dialog_notification_filter_title">Pokazuj wybrane powiadomienia</string>
<string name="dialog_notification_filter_text"><![CDATA[Zaznacz, które powiadomienia mają być pokazywane w systemie oraz w aplikacji.]]></string>
<string name="notification_filter_warning">Czy na pewno chcesz zastosować te ustawienia?\n\nNie będziesz widział informacji o niektórych danych, przez co możesz przeoczyć ważne komunikaty, wiadomości lub oceny.\n\nUstawienia zostaną zastosowane dla aktualnie otwartego profilu.</string>
<string name="dialog_day_lessons_info">%s - %s (%s lekcji - %s godzin %s minut)</string>
</resources>