1616
1717package com.ichi2.anki.reviewreminders
1818
19- import android.Manifest
2019import android.content.Context
2120import android.content.Intent
22- import android.os.Build
2321import android.os.Bundle
22+ import android.view.Menu
23+ import android.view.MenuInflater
24+ import android.view.MenuItem
2425import android.view.View
25- import androidx.activity.result.contract.ActivityResultContracts
26- import androidx.annotation.RequiresApi
2726import androidx.appcompat.app.AppCompatActivity
2827import androidx.core.os.BundleCompat
28+ import androidx.core.view.MenuProvider
2929import androidx.core.view.isVisible
3030import androidx.fragment.app.Fragment
31+ import androidx.fragment.app.activityViewModels
32+ import androidx.fragment.app.commit
3133import androidx.fragment.app.setFragmentResult
3234import androidx.fragment.app.setFragmentResultListener
35+ import androidx.lifecycle.Lifecycle
36+ import androidx.lifecycle.flowWithLifecycle
37+ import androidx.lifecycle.lifecycleScope
3338import androidx.recyclerview.widget.DividerItemDecoration
3439import androidx.recyclerview.widget.LinearLayoutManager
3540import com.google.android.material.snackbar.Snackbar
@@ -44,16 +49,14 @@ import com.ichi2.anki.launchCatchingTask
4449import com.ichi2.anki.libanki.DeckId
4550import com.ichi2.anki.model.SelectableDeck
4651import com.ichi2.anki.services.AlarmManagerService
47- import com.ichi2.anki.settings.Prefs
4852import com.ichi2.anki.showError
4953import com.ichi2.anki.snackbar.BaseSnackbarBuilderProvider
5054import com.ichi2.anki.snackbar.SnackbarBuilder
5155import com.ichi2.anki.snackbar.showSnackbar
5256import com.ichi2.anki.utils.ext.showDialogFragment
5357import com.ichi2.anki.withProgress
54- import com.ichi2.utils.Permissions
55- import com.ichi2.utils.Permissions.requestPermissionThroughDialogOrSettings
5658import dev.androidbroadcast.vbpd.viewBinding
59+ import kotlinx.coroutines.launch
5760import kotlinx.serialization.SerializationException
5861import timber.log.Timber
5962
@@ -78,24 +81,18 @@ class ScheduleReminders :
7881
7982 private val binding by viewBinding(FragmentScheduleRemindersBinding ::bind)
8083
84+ private val troubleshootingViewModel: ReminderTroubleshootingViewModel by activityViewModels {
85+ reminderTroubleshootingViewModelFactory(requireContext())
86+ }
87+
8188 private lateinit var adapter: ScheduleRemindersAdapter
8289
90+ private var troubleshootSnackbar: Snackbar ? = null
91+
8392 override val baseSnackbarBuilder: SnackbarBuilder = {
8493 anchorView = binding.floatingActionButtonAdd
8594 }
8695
87- private var notificationPermissionSnackbar: Snackbar ? = null
88-
89- /* *
90- * Launches the OS dialog for requesting notification permissions.
91- * If notification permissions are not granted, a small persistent Snackbar reminder about it shows up.
92- * When the user clicks the "Enable" action on the Snackbar, this launcher is used.
93- */
94- private val notificationPermissionLauncher =
95- registerForActivityResult(
96- ActivityResultContracts .RequestPermission (),
97- ) { isGranted -> Timber .i(" Notification permission result: $isGranted " ) }
98-
9996 /* *
10097 * The reminders currently being displayed in the UI. To make changes to this list show up on screen,
10198 * use [triggerUIUpdate]. Note that editing this map does not also automatically write to the database.
@@ -119,10 +116,55 @@ class ScheduleReminders :
119116 // Set up toolbar
120117 reloadToolbarText()
121118 (requireActivity() as AppCompatActivity ).setSupportActionBar(binding.toolbar)
119+ requireActivity().addMenuProvider(
120+ object : MenuProvider {
121+ override fun onCreateMenu (
122+ menu : Menu ,
123+ menuInflater : MenuInflater ,
124+ ) {
125+ menuInflater.inflate(R .menu.schedule_reminders, menu)
126+ }
122127
128+ override fun onMenuItemSelected (menuItem : MenuItem ): Boolean =
129+ when (menuItem.itemId) {
130+ R .id.action_troubleshoot -> {
131+ openTroubleshootingScreen()
132+ true
133+ }
134+ else -> false
135+ }
136+ },
137+ viewLifecycleOwner,
138+ )
123139 // Set up add button
124140 binding.floatingActionButtonAdd.setOnClickListener { addReminder() }
125141
142+ // Troubleshoot snackbar: shown persistently when checks find a warning/error.
143+ // Tapping "Fix" opens the full troubleshooting screen.
144+ lifecycleScope.launch {
145+ troubleshootingViewModel.state
146+ .flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle .State .STARTED )
147+ .collect { state ->
148+ val message =
149+ when (state.summaryStatus) {
150+ SummaryStatus .Ok , SummaryStatus .Warning -> {
151+ troubleshootSnackbar?.dismiss()
152+ troubleshootSnackbar = null
153+ return @collect
154+ }
155+ SummaryStatus .Error -> " Reminders are unavailable"
156+ }
157+ if (troubleshootSnackbar?.isShown == true ) {
158+ troubleshootSnackbar?.setText(message)
159+ return @collect
160+ }
161+ troubleshootSnackbar =
162+ showSnackbar(text = message, duration = Snackbar .LENGTH_INDEFINITE ) {
163+ setAction(" Fix" ) { openTroubleshootingScreen() }
164+ }
165+ }
166+ }
167+
126168 // Set up recycler view
127169 val layoutManager = LinearLayoutManager (requireContext())
128170 binding.recyclerView.layoutManager = layoutManager
@@ -396,6 +438,19 @@ class ScheduleReminders :
396438 }
397439 }
398440
441+ // TODO: Docs
442+ private fun openTroubleshootingScreen () {
443+ troubleshootSnackbar?.dismiss()
444+ parentFragmentManager.commit {
445+ replace(
446+ R .id.fragment_container,
447+ ReminderTroubleshootingFragment (),
448+ SingleFragmentActivity .FRAGMENT_TAG ,
449+ )
450+ addToBackStack(null )
451+ }
452+ }
453+
399454 /* *
400455 * The method that runs when the "+" icon is pressed, allowing the user to create a new review reminder.
401456 * Opens [AddEditReminderDialog] in [AddEditReminderDialog.DialogMode.Add] mode.
@@ -454,35 +509,7 @@ class ScheduleReminders :
454509
455510 override fun onResume () {
456511 super .onResume()
457- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
458- checkForNotificationPermissions()
459- }
460- }
461-
462- /* *
463- * Shows a persistent snackbar if the user has not granted notification permissions.
464- */
465- @RequiresApi(Build .VERSION_CODES .TIRAMISU )
466- private fun checkForNotificationPermissions () {
467- if (! Prefs .reminderNotifsRequestShown || Permissions .canPostNotifications(requireContext())) {
468- notificationPermissionSnackbar?.dismiss()
469- return
470- }
471-
472- notificationPermissionSnackbar =
473- showSnackbar(
474- text = " Notifications are disabled" ,
475- duration = Snackbar .LENGTH_INDEFINITE ,
476- ) {
477- setAction(" Enable" ) {
478- requestPermissionThroughDialogOrSettings(
479- activity = requireActivity(),
480- permission = Manifest .permission.POST_NOTIFICATIONS ,
481- permissionRequestedFlag = Prefs ::notificationsPermissionRequested,
482- permissionRequestLauncher = notificationPermissionLauncher,
483- )
484- }
485- }
512+ troubleshootingViewModel.refreshChecks()
486513 }
487514
488515 companion object {
0 commit comments