feat(focus-mode): add Do Not Disturb for distraction-free reading#3549
feat(focus-mode): add Do Not Disturb for distraction-free reading#3549slimatic wants to merge 3 commits intoquran:mainfrom
Conversation
- Add ACCESS_NOTIFICATION_POLICY permission for DND control - Create FocusModeManager to handle enabling/disabling DND - Add Focus Mode toggle in Settings → Reading - Automatically enable DND (Alarms Only) when reading starts - Restore previous DND state when leaving reading - Configure audio notifications to bypass DND - Add permission request flow with user guidance - Fix compilation errors in PagerActivity integration
a5abac2 to
3c9556c
Compare
|
Salam @ahmedre, Ramadan Mubarak. Is there a specific process for review and creating PRs ? |
ahmedre
left a comment
There was a problem hiding this comment.
interesting idea, جزاكم الله خيراً for the pr
in the future also please open an issue before implementing new features in the pr - on my end I should add a policy note about this and will in sha' Allah.
not against this overall, minor comment we can clean up in sha' Allah and then likely will test it out but generally looks good.
app/src/main/java/com/quran/labs/androidquran/util/NotificationChannelUtil.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Adds a “Focus Mode” reading preference that automatically toggles Android Do Not Disturb (Alarms Only) while the user is reading in PagerActivity, including permission-gating via settings and restoring the prior DND state afterward.
Changes:
- Adds a new Reading preference toggle (“Focus Mode”) with strings + preference key wiring.
- Introduces
FocusModeManagerto enable DND (Alarms Only) and restore the previous interruption filter. - Hooks Focus Mode into
PagerActivitylifecycle and adds the required manifest permission.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| app/src/main/res/xml/quran_preferences.xml | Adds Focus Mode checkbox under Reading Preferences. |
| app/src/main/res/values/strings.xml | Adds Focus Mode UI strings and permission-required messaging. |
| app/src/main/res/values/preferences_keys.xml | Adds the prefs_focus_mode preference key string. |
| app/src/main/java/com/quran/labs/androidquran/util/QuranSettings.java | Adds isFocusModeEnabled() accessor for the new pref. |
| app/src/main/java/com/quran/labs/androidquran/util/NotificationChannelUtil.kt | Adds an import (currently unused) related to notification/DND work. |
| app/src/main/java/com/quran/labs/androidquran/util/FocusModeManager.kt | New manager for checking policy access + toggling/restoring DND state. |
| app/src/main/java/com/quran/labs/androidquran/ui/fragment/QuranSettingsFragment.kt | Adds permission gating + settings deep-link when enabling Focus Mode. |
| app/src/main/java/com/quran/labs/androidquran/ui/PagerActivity.kt | Enables Focus Mode on resume and restores DND state on pause. |
| app/src/main/java/com/quran/labs/androidquran/data/Constants.kt | Adds PREF_FOCUS_MODE constant. |
| app/src/main/AndroidManifest.xml | Adds ACCESS_NOTIFICATION_POLICY permission. |
Comments suppressed due to low confidence (1)
app/src/main/java/com/quran/labs/androidquran/util/NotificationChannelUtil.kt:10
Constantsis imported but unused here. Also, the PR description mentions that the app's audio notifications bypass DND, but this helper currently creates the channel without setting any DND-bypass behavior (and only creates the channel if it doesn't already exist). Either remove/adjust that claim in the PR description or implement the intended channel configuration (and update existing channels if needed).
import com.quran.labs.androidquran.data.Constants
object NotificationChannelUtil {
fun setupNotificationChannel(notificationManager: NotificationManager, channelId: String, channelName: String) {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| recentPagePresenter.unbind() | ||
| quranSettings.wasShowingTranslation = pagerAdapter.isShowingTranslation | ||
|
|
||
| focusModeManager.restorePreviousDndState() | ||
|
|
There was a problem hiding this comment.
restorePreviousDndState() is called from onPause(), which can run for transient interruptions (e.g., permission dialogs/overlays). That can cause Focus Mode to toggle off while the user is still in a reading session. Consider restoring DND in onStop()/onDestroy() (paired with enabling in onStart()/onResume()) so DND only restores when the activity is no longer visible.
| if (QuranSettings.getInstance(this).isFocusModeEnabled()) { | ||
| focusModeManager.enableFocusMode() |
There was a problem hiding this comment.
This uses QuranSettings.getInstance(this) even though quranSettings is already injected in the activity. Using the injected instance keeps behavior consistent and easier to test. Also, enableFocusMode() returns false when notification policy access is missing, but the result is ignored—consider handling the failure (e.g., prompt the user or disable the setting) so Focus Mode doesn't silently do nothing.
| if (QuranSettings.getInstance(this).isFocusModeEnabled()) { | |
| focusModeManager.enableFocusMode() | |
| if (quranSettings.isFocusModeEnabled()) { | |
| val focusModeEnabled = focusModeManager.enableFocusMode() | |
| if (!focusModeEnabled) { | |
| Toast.makeText(this, "Focus mode is unavailable on this device.", Toast.LENGTH_LONG).show() | |
| } |
| private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager | ||
| private var previousInterruptionFilter: Int = NotificationManager.INTERRUPTION_FILTER_ALL | ||
| private var focusModeActivatedByApp: Boolean = false | ||
|
|
||
| fun isPermissionGranted(): Boolean = notificationManager.isNotificationPolicyAccessGranted | ||
|
|
||
| fun requestPermission(activity: Activity) { | ||
| val intent = Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS) | ||
| activity.startActivity(intent) | ||
| } | ||
|
|
||
| fun saveCurrentDndState() { | ||
| previousInterruptionFilter = notificationManager.currentInterruptionFilter | ||
| } | ||
|
|
||
| fun enableFocusMode(): Boolean { | ||
| if (!isPermissionGranted()) return false | ||
| if (focusModeActivatedByApp) return true | ||
|
|
||
| previousInterruptionFilter = notificationManager.currentInterruptionFilter | ||
| notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALARMS) | ||
| focusModeActivatedByApp = true | ||
| return true |
There was a problem hiding this comment.
Focus Mode state is tracked only in-memory (previousInterruptionFilter / focusModeActivatedByApp). If the process is killed or the app crashes after setting DND to Alarms Only, the user's DND state may be left altered with no automatic restoration. Consider persisting the previous filter + a "changed by app" flag (e.g., in SharedPreferences) and restoring on next app start / when leaving reading.
| import android.provider.Settings | ||
| import android.widget.Toast |
There was a problem hiding this comment.
android.provider.Settings is imported but not used in this fragment (permission navigation is handled inside FocusModeManager). Please remove the unused import to avoid warnings.
| import com.quran.labs.androidquran.util.QuranSettings | ||
| import com.quran.labs.androidquran.util.OrientationLockUtils |
There was a problem hiding this comment.
com.quran.labs.androidquran.util.QuranSettings is imported but not referenced in this fragment. Please remove the unused import to keep the file clean.
| import com.quran.labs.androidquran.util.QuranSettings | |
| import com.quran.labs.androidquran.util.OrientationLockUtils |
Summary
Add a Focus Mode feature that automatically enables Do Not Disturb (Alarms Only) when the user opens the Quran to read, providing a distraction-free reading experience - free from notifications that interrupt your focus.
Changes
How it works