Skip to content

Commit 57121c7

Browse files
authored
Make notifications non experimental (#1575)
1 parent c8e8a72 commit 57121c7

File tree

8 files changed

+80
-16
lines changed

8 files changed

+80
-16
lines changed

androidApp/src/main/java/dev/johnoreilly/confetti/ConfettiApplication.kt

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.google.firebase.crashlytics.setCustomKeys
1010
import com.google.firebase.ktx.Firebase
1111
import dev.johnoreilly.confetti.di.appModule
1212
import dev.johnoreilly.confetti.di.initKoin
13+
import dev.johnoreilly.confetti.work.SessionNotificationSender
1314
import dev.johnoreilly.confetti.work.SessionNotificationWorker
1415
import dev.johnoreilly.confetti.work.setupDailyRefresh
1516
import kotlinx.coroutines.launch
@@ -33,12 +34,12 @@ class ConfettiApplication : Application() {
3334

3435
if (isFirebaseInstalled) {
3536
if (!BuildConfig.DEBUG) {
36-
Firebase.crashlytics.setCrashlyticsCollectionEnabled(true)
37+
Firebase.crashlytics.isCrashlyticsCollectionEnabled = true
3738
Firebase.crashlytics.setCustomKeys {
3839
key("appName", "androidApp")
3940
}
4041
} else {
41-
Firebase.crashlytics.setCrashlyticsCollectionEnabled(false)
42+
Firebase.crashlytics.isCrashlyticsCollectionEnabled = false
4243
}
4344
}
4445

@@ -54,13 +55,7 @@ class ConfettiApplication : Application() {
5455
setupDailyRefresh(workManager)
5556

5657
ProcessLifecycleOwner.get().lifecycleScope.launch {
57-
get<AppSettings>().experimentalFeaturesEnabledFlow.collect { isEnabled ->
58-
if (isEnabled) {
59-
SessionNotificationWorker.startPeriodicWorkRequest(workManager)
60-
} else {
61-
SessionNotificationWorker.cancelWorkRequest(workManager)
62-
}
63-
}
58+
get<SessionNotificationSender>().updateSchedule()
6459
}
6560
}
6661
}

shared/src/androidMain/kotlin/dev/johnoreilly/confetti/work/SessionNotificationSender.kt

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@ import android.content.Context
55
import android.os.Build
66
import android.util.Log
77
import androidx.core.app.NotificationManagerCompat
8+
import androidx.work.WorkManager
89
import com.apollographql.cache.normalized.FetchPolicy
10+
import dev.johnoreilly.confetti.AppSettings
911
import dev.johnoreilly.confetti.ConfettiRepository
1012
import dev.johnoreilly.confetti.auth.Authentication
1113
import dev.johnoreilly.confetti.notifications.SessionNotificationBuilder
1214
import dev.johnoreilly.confetti.notifications.SummaryNotificationBuilder
1315
import dev.johnoreilly.confetti.utils.DateService
1416
import dev.johnoreilly.confetti.work.NotificationSender.Selector
17+
import kotlinx.coroutines.CoroutineScope
1518
import kotlinx.coroutines.flow.first
19+
import kotlinx.coroutines.launch
1620
import kotlin.random.Random
1721

1822
class SessionNotificationSender(
@@ -21,7 +25,10 @@ class SessionNotificationSender(
2125
private val dateService: DateService,
2226
private val notificationManager: NotificationManagerCompat,
2327
private val authentication: Authentication,
24-
): NotificationSender {
28+
private val appSettings: AppSettings,
29+
private val coroutineScope: CoroutineScope,
30+
private val workManager: WorkManager,
31+
) : NotificationSender {
2532
private val sessionNotificationBuilder = SessionNotificationBuilder(context)
2633
private val summaryNotificationBuilder = SummaryNotificationBuilder(context)
2734

@@ -85,13 +92,19 @@ class SessionNotificationSender(
8592

8693
// If there are multiple notifications, we create a summary to group them.
8794
if (upcomingSessions.count() > 1) {
88-
sendNotification(SUMMARY_ID, summaryNotificationBuilder.createSummaryNotification(upcomingSessions, SUMMARY_ID).build())
95+
sendNotification(
96+
SUMMARY_ID,
97+
summaryNotificationBuilder.createSummaryNotification(upcomingSessions, SUMMARY_ID).build()
98+
)
8999
}
90100

91101
// We reverse the sessions to show early sessions first.
92102
for (session in upcomingSessions.reversed()) {
93103
val notificationId = Random.nextInt(Integer.MAX_VALUE / 2, Integer.MAX_VALUE)
94-
sendNotification(notificationId, sessionNotificationBuilder.createNotification(session, conferenceId, notificationId).build())
104+
sendNotification(
105+
notificationId,
106+
sessionNotificationBuilder.createNotification(session, conferenceId, notificationId).build()
107+
)
95108
}
96109
}
97110

@@ -110,6 +123,18 @@ class SessionNotificationSender(
110123
}
111124
}
112125

126+
override suspend fun updateSchedule() {
127+
updateSchedule(appSettings.notificationsEnabledFlow.first())
128+
}
129+
130+
override fun updateSchedule(enabled: Boolean) {
131+
if (enabled) {
132+
SessionNotificationWorker.startPeriodicWorkRequest(workManager)
133+
} else {
134+
SessionNotificationWorker.cancelWorkRequest(workManager)
135+
}
136+
}
137+
113138
companion object {
114139
internal val CHANNEL_ID = "SessionNotification"
115140
internal val GROUP = "dev.johnoreilly.confetti.SESSIONS_ALERT"

shared/src/commonMain/composeResources/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<string name="dynamic_color_no">No</string>
2727
<string name="dismiss_dialog_button_text">OK</string>
2828
<string name="use_experimental_features">Use Experimental Features</string>
29+
<string name="enable_notifications">Enable Notifications</string>
2930
<string name="settings_boolean_true">Yes</string>
3031
<string name="settings_boolean_false">No</string>
3132
<string name="developerSettings">Developer Settings\n</string>

shared/src/commonMain/kotlin/dev/johnoreilly/confetti/AppSettings.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ class AppSettings(val settings: FlowSettings) {
2424
)
2525
}
2626

27-
val notificationsActiveFlow: Flow<Boolean>
28-
get() = experimentalFeaturesEnabledFlow
27+
val notificationsEnabledFlow: Flow<Boolean> = settings
28+
.getBooleanFlow(NOTIFICATIONS_ENABLED, false)
2929

3030
val experimentalFeaturesEnabledFlow = settings
3131
.getBooleanFlow(EXPERIMENTAL_FEATURES_ENABLED, false)
@@ -37,6 +37,10 @@ class AppSettings(val settings: FlowSettings) {
3737
settings.putBoolean(EXPERIMENTAL_FEATURES_ENABLED, value)
3838
}
3939

40+
suspend fun setNotificationsEnabled(value: Boolean) {
41+
settings.putBoolean(NOTIFICATIONS_ENABLED, value)
42+
}
43+
4044
suspend fun getConference(): String {
4145
return settings.getStringFlow(CONFERENCE_SETTING, CONFERENCE_NOT_SET).first()
4246
}
@@ -70,6 +74,7 @@ class AppSettings(val settings: FlowSettings) {
7074
companion object {
7175
const val DEVELOPER_MODE = "developer_mode"
7276
const val EXPERIMENTAL_FEATURES_ENABLED = "experimental_features_enabled"
77+
const val NOTIFICATIONS_ENABLED = "notifications_enabled"
7378
const val ENABLED_LANGUAGES_SETTING = "enabled_languages_2"
7479
const val CONFERENCE_SETTING = "conference"
7580
const val CONFERENCE_THEME_COLOR_SETTING = "conferenceThemeColor"

shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SessionsComponent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ class SessionsSimpleComponent(
253253
isRefreshing,
254254
searchQuery,
255255
selectedSessionId,
256-
appSettings.notificationsActiveFlow,
256+
appSettings.notificationsEnabledFlow,
257257
::uiStates
258258
)
259259
}

shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SettingsComponent.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
@file:OptIn(ExperimentalSettingsApi::class, ExperimentalCoroutinesApi::class)
2+
13
package dev.johnoreilly.confetti.decompose
24

35
import com.arkivanov.decompose.ComponentContext
6+
import com.russhwolf.settings.ExperimentalSettingsApi
47
import dev.johnoreilly.confetti.AppSettings
58
import dev.johnoreilly.confetti.auth.Authentication
69
import dev.johnoreilly.confetti.work.NotificationSender
10+
import kotlinx.coroutines.ExperimentalCoroutinesApi
711
import kotlinx.coroutines.flow.SharingStarted
812
import kotlinx.coroutines.flow.StateFlow
913
import kotlinx.coroutines.flow.combine
@@ -23,6 +27,7 @@ data class DeveloperSettings(
2327
data class UserEditableSettings(
2428
val darkThemeConfig: DarkThemeConfig,
2529
val useExperimentalFeatures: Boolean,
30+
val notificationsEnabled: Boolean,
2631
)
2732

2833
enum class ThemeBrand {
@@ -40,6 +45,7 @@ interface SettingsComponent {
4045

4146
fun updateDarkThemeConfig(darkThemeConfig: DarkThemeConfig)
4247
fun updateUseExperimentalFeatures(value: Boolean)
48+
fun updateNotificationsEnabled(value: Boolean)
4349
fun enableDeveloperMode()
4450
fun sendNotifications()
4551
val supportsNotifications: Boolean
@@ -73,10 +79,12 @@ class DefaultSettingsComponent(
7379
combine(
7480
settings.getStringFlow(darkThemeConfigKey, DarkThemeConfig.FOLLOW_SYSTEM.toString()),
7581
appSettings.experimentalFeaturesEnabledFlow,
76-
) { darkThemeConfig, useExperimentalFeatures ->
82+
appSettings.notificationsEnabledFlow,
83+
) { darkThemeConfig, useExperimentalFeatures, useNotifications ->
7784
UserEditableSettings(
7885
useExperimentalFeatures = useExperimentalFeatures,
7986
darkThemeConfig = DarkThemeConfig.valueOf(darkThemeConfig),
87+
notificationsEnabled = useNotifications
8088
)
8189
}.stateIn(
8290
scope = coroutineScope,
@@ -96,6 +104,12 @@ class DefaultSettingsComponent(
96104
}
97105
}
98106

107+
override fun updateNotificationsEnabled(value: Boolean) {
108+
coroutineScope.launch {
109+
appSettings.setNotificationsEnabled(value)
110+
notificationSender?.updateSchedule(value)
111+
}
112+
}
99113

100114
override fun enableDeveloperMode() {
101115
coroutineScope.launch {

shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/settings/SettingsUI.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import confetti.shared.generated.resources.dark_mode_config_light
4646
import confetti.shared.generated.resources.dark_mode_config_system_default
4747
import confetti.shared.generated.resources.dark_mode_preference
4848
import confetti.shared.generated.resources.developerSettings
49+
import confetti.shared.generated.resources.enable_notifications
4950
import confetti.shared.generated.resources.settings_boolean_false
5051
import confetti.shared.generated.resources.settings_boolean_true
5152
import confetti.shared.generated.resources.settings_title
@@ -72,6 +73,7 @@ fun SettingsUI(
7273
onEnableDeveloperMode = component::enableDeveloperMode,
7374
onSendNotifications = component::sendNotifications,
7475
supportsNotifications = component.supportsNotifications,
76+
onNotificationsEnabled = component::updateNotificationsEnabled,
7577
popBack = popBack
7678
)
7779
}
@@ -85,6 +87,7 @@ fun SettingsUI(
8587
onEnableDeveloperMode: () -> Unit,
8688
onSendNotifications: () -> Unit,
8789
supportsNotifications: Boolean,
90+
onNotificationsEnabled: (value: Boolean) -> Unit,
8891
popBack: () -> Unit
8992
) {
9093
val scrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
@@ -131,6 +134,8 @@ fun SettingsUI(
131134
settings = userEditableSettings,
132135
onChangeDarkThemeConfig = onChangeDarkThemeConfig,
133136
onChangeUseExperimentalFeatures = onChangeUseExperimentalFeatures,
137+
onChangeNotificationsEnabled = onNotificationsEnabled,
138+
supportsNotifications = supportsNotifications,
134139
)
135140
}
136141
}
@@ -210,10 +215,19 @@ fun SettingsUI(
210215
@Composable
211216
private fun SettingsPanel(
212217
settings: UserEditableSettings?,
218+
supportsNotifications: Boolean,
213219
onChangeUseExperimentalFeatures: (value: Boolean) -> Unit,
214220
onChangeDarkThemeConfig: (darkThemeConfig: DarkThemeConfig) -> Unit,
221+
onChangeNotificationsEnabled: (value: Boolean) -> Unit,
215222
) {
216223
if (settings != null) {
224+
BooleanSettings(
225+
title = stringResource(Res.string.enable_notifications),
226+
value = settings.notificationsEnabled,
227+
onValueChange = { value -> onChangeNotificationsEnabled(value) },
228+
enabled = supportsNotifications
229+
)
230+
217231
BooleanSettings(
218232
title = stringResource(Res.string.use_experimental_features),
219233
value = settings.useExperimentalFeatures,
@@ -246,18 +260,21 @@ private fun BooleanSettings(
246260
title: String,
247261
value: Boolean,
248262
onValueChange: (Boolean) -> Unit,
263+
enabled: Boolean = true,
249264
) {
250265
SettingsDialogSectionTitle(text = title)
251266
Column(Modifier.selectableGroup()) {
252267
SettingsDialogThemeChooserRow(
253268
text = stringResource(Res.string.settings_boolean_true),
254269
selected = value,
255270
onClick = { onValueChange(true) },
271+
enabled = enabled,
256272
)
257273
SettingsDialogThemeChooserRow(
258274
text = stringResource(Res.string.settings_boolean_false),
259275
selected = !value,
260276
onClick = { onValueChange(false) },
277+
enabled = enabled,
261278
)
262279
}
263280
}
@@ -276,6 +293,7 @@ fun SettingsDialogThemeChooserRow(
276293
text: String,
277294
selected: Boolean,
278295
onClick: () -> Unit,
296+
enabled: Boolean = true,
279297
) {
280298
Row(
281299
Modifier
@@ -284,13 +302,15 @@ fun SettingsDialogThemeChooserRow(
284302
selected = selected,
285303
role = Role.RadioButton,
286304
onClick = onClick,
305+
enabled = enabled,
287306
)
288307
.padding(12.dp),
289308
verticalAlignment = Alignment.CenterVertically,
290309
) {
291310
RadioButton(
292311
selected = selected,
293312
onClick = null,
313+
enabled = enabled,
294314
)
295315
Spacer(Modifier.width(8.dp))
296316
Text(text)

shared/src/commonMain/kotlin/dev/johnoreilly/confetti/work/NotificationSender.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,8 @@ interface NotificationSender {
3737
}
3838

3939
suspend fun sendNotification(selector: Selector = Today())
40+
41+
suspend fun updateSchedule() {}
42+
43+
fun updateSchedule(enabled: Boolean) {}
4044
}

0 commit comments

Comments
 (0)