Skip to content

feat(focus-mode): add Do Not Disturb for distraction-free reading#3549

Open
slimatic wants to merge 3 commits intoquran:mainfrom
slimatic:focus-mode-dnd
Open

feat(focus-mode): add Do Not Disturb for distraction-free reading#3549
slimatic wants to merge 3 commits intoquran:mainfrom
slimatic:focus-mode-dnd

Conversation

@slimatic
Copy link

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

  • New permission: ACCESS_NOTIFICATION_POLICY - required to modify DND settings
  • New FocusModeManager: Handles enabling/disabling DND and restoring previous state
  • New settings toggle: Settings → Reading → Focus Mode
  • Automatic activation: DND enables when opening Quran, restores when leaving
  • Quran audio exemption: App's own audio notifications bypass DND

How it works

  1. User enables Focus Mode in Settings → Reading → Focus Mode
  2. User grants Do Not Disturb access permission (one-time)
  3. When user opens Quran to read, DND (Alarms Only) automatically activates
  4. When user leaves reading, their previous DND state is restored
  5. Quran app's own audio playback notifications remain functional during Focus Mode

- 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
@slimatic
Copy link
Author

Salam @ahmedre, Ramadan Mubarak. Is there a specific process for review and creating PRs ?

Copy link
Contributor

@ahmedre ahmedre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot AI review requested due to automatic review settings March 2, 2026 10:49
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 FocusModeManager to enable DND (Alarms Only) and restore the previous interruption filter.
  • Hooks Focus Mode into PagerActivity lifecycle 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

  • Constants is 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.

Comment on lines 1100 to +1104
recentPagePresenter.unbind()
quranSettings.wasShowingTranslation = pagerAdapter.isShowingTranslation

focusModeManager.restorePreviousDndState()

Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +902 to +903
if (QuranSettings.getInstance(this).isFocusModeEnabled()) {
focusModeManager.enableFocusMode()
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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()
}

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +33
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
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +5
import android.provider.Settings
import android.widget.Toast
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +27 to 28
import com.quran.labs.androidquran.util.QuranSettings
import com.quran.labs.androidquran.util.OrientationLockUtils
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

com.quran.labs.androidquran.util.QuranSettings is imported but not referenced in this fragment. Please remove the unused import to keep the file clean.

Suggested change
import com.quran.labs.androidquran.util.QuranSettings
import com.quran.labs.androidquran.util.OrientationLockUtils

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants