Skip to content

Play basmallah when a gapless surah repeats back to ayah 1#3698

Merged
ahmedre merged 1 commit into
quran:mainfrom
DoozyDoz:fix/gapless-basmallah-range-repeat-3697
Jul 4, 2026
Merged

Play basmallah when a gapless surah repeats back to ayah 1#3698
ahmedre merged 1 commit into
quran:mainfrom
DoozyDoz:fix/gapless-basmallah-range-repeat-3697

Conversation

@DoozyDoz

Copy link
Copy Markdown
Contributor

Fixes #3697.

When a gapless surah is played on range repeat, the basmallah is skipped every time playback loops back to ayah 1. Reproducible with reciters such as Saud Ash-Shuraym; Husary and Minshawi Murattal are unaffected.

The end of a gapless surah is handled by whichever fires first: the position polling in updateAudioPlayPosition, or onPlayerCompleted on ExoPlayer STATE_ENDED. The polling path detects the end when the playback position reaches ayahTimings[999] and seeks to the basmallah for ayah 1. onPlayerCompleted seeks straight to ayahTimings[getCurrentAyah()], which for ayah 1 is ayahTimings[1] — the start of ayah 1 after the basmallah — so it skips it.

Every other gapless path that begins ayah 1 routes through getSeekPosition(false), which returns ayahTimings[0] (the basmallah) when ayah == 1 and we are not repeating a single ayah. onPlayerCompleted is the only gapless same-sura path that bypasses it.

The race explains the qari dependency. Reciters whose surah files are trimmed to end exactly at the 999 marker leave no window for the polling path, so STATE_ENDED always wins and the basmallah is always skipped. Reciters with audio past the 999 marker let polling handle it and are unaffected.

The fix routes the onPlayerCompleted seek through getSeekPosition(false), so the basmallah plays regardless of which handler wins the race or how the reciter's file is encoded.

Tested with Saud Ash-Shuraym (gapless), Surat As-Saffat 1→182, range repeat = infinity: the basmallah now plays on every loop-back. Before the fix it was skipped on every loop.

onPlayerCompleted sought straight to ayahTimings[getCurrentAyah()] when
a gapless surah ended and range repeat reset the queue to ayah 1 of the
same surah. For ayah 1 that is ayahTimings[1], the start of ayah 1 after
the basmallah, so the basmallah was skipped on every loop.

Route the seek through getSeekPosition(false), matching every other
gapless path (updateAudioPlayPosition, processSkipRequest, playAudio),
which returns ayahTimings[0] for ayah 1 when not repeating a single
ayah.

This also removes the qari dependency. Some gapless reciters (e.g. Saud
Ash-Shuraym) have surah files trimmed to end exactly at the ayah=999
marker, so STATE_ENDED fires before the polling path can detect
end-of-sura and onPlayerCompleted becomes the only handler. With the
seek routed through getSeekPosition(false) the basmallah plays
regardless of which handler runs.

Fixes quran#3697.
@DoozyDoz

DoozyDoz commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Dear Brother @ahmedre

Kindly find time and review this, the changes only cover 3 lines of code, I tested this on a xiaomi 15u.
Regards

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
OLD: app-madani-debug.apk (signature: V1, V2)
NEW: app-madani-debug.apk (signature: V1, V2)

          │             compressed             │           uncompressed            
          ├───────────┬───────────┬────────────┼───────────┬───────────┬───────────
 APK      │ old       │ new       │ diff       │ old       │ new       │ diff      
──────────┼───────────┼───────────┼────────────┼───────────┼───────────┼───────────
      dex │    25 MiB │  24.6 MiB │ -377.3 KiB │  63.9 MiB │  62.8 MiB │  -1.1 MiB 
     arsc │   2.3 MiB │   2.3 MiB │   -3.9 KiB │   2.3 MiB │   2.3 MiB │  -3.9 KiB 
 manifest │     6 KiB │   5.9 KiB │      -86 B │  28.5 KiB │  27.9 KiB │    -672 B 
      res │   1.8 MiB │   1.8 MiB │     -311 B │   2.1 MiB │   2.1 MiB │  -1.4 KiB 
   native │ 199.7 KiB │ 188.3 KiB │  -11.4 KiB │    63 KiB │  58.9 KiB │  -4.2 KiB 
    asset │   1.8 MiB │   1.8 MiB │        0 B │   4.1 MiB │   4.1 MiB │       0 B 
    other │ 226.5 KiB │   218 KiB │   -8.5 KiB │ 562.9 KiB │ 540.8 KiB │ -22.1 KiB 
──────────┼───────────┼───────────┼────────────┼───────────┼───────────┼───────────
    total │  31.3 MiB │  30.9 MiB │ -401.5 KiB │  73.1 MiB │  71.9 MiB │  -1.2 MiB 

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

New Dependencies
com.russhwolf:multiplatform-settings-no-arg:1.3.0
com.russhwolf:multiplatform-settings-no-arg-android:1.3.0
project :common:analytics
project :common:analytics ()
project :common:audio
project :common:audio (
)
project :common:bookmark
project :common:bookmark ()
project :common:data
project :common:data (
)
project :common:di
project :common:di ()
project :common:download
project :common:download (
)
project :common:networking
project :common:pages
project :common:pages ()
project :common:preference
project :common:reading
project :common:reading (
)
project :common:recitation
project :common:recitation ()
project :common:search
project :common:search (
)
project :common:toolbar
project :common:translation
project :common:ui:core
project :common:ui:core ()
project :common:upgrade
project :common:upgrade (
)
project :feature:audio
project :feature:audiobar
project :feature:autoquran
project :feature:downloadmanager
project :feature:firebase-analytics
project :feature:qarilist
project :feature:recitation
project :pages:common:madani
project :pages:common:madani (*)
project :pages:data:madani
project :pages:madani

Removed Dependencies
com.russhwolf:multiplatform-settings-coroutines:1.3.0
com.russhwolf:multiplatform-settings-coroutines-android:1.3.0
com.russhwolf:multiplatform-settings-datastore:1.3.0
com.russhwolf:multiplatform-settings-datastore-android:1.3.0
project ':common:analytics'
project ':common:analytics' ()
project ':common:audio'
project ':common:audio' (
)
project ':common:bookmark'
project ':common:bookmark' ()
project ':common:data'
project ':common:data' (
)
project ':common:di'
project ':common:di' ()
project ':common:download'
project ':common:download' (
)
project ':common:networking'
project ':common:pages'
project ':common:pages' ()
project ':common:preference'
project ':common:reading'
project ':common:reading' (
)
project ':common:recitation'
project ':common:recitation' ()
project ':common:search'
project ':common:search' (
)
project ':common:toolbar'
project ':common:translation'
project ':common:ui:core'
project ':common:ui:core' ()
project ':common:upgrade'
project ':common:upgrade' (
)
project ':feature:audio'
project ':feature:audiobar'
project ':feature:autoquran'
project ':feature:downloadmanager'
project ':feature:firebase-analytics'
project ':feature:qarilist'
project ':feature:recitation'
project ':feature:sync'
project ':pages:common:madani'
project ':pages:common:madani' (*)
project ':pages:data:madani'
project ':pages:madani'

Upgraded Dependencies
androidx.compose.animation:animation:1.11.2, (changed from 1.11.3)
androidx.compose.animation:animation-android:1.11.2, (changed from 1.11.3)
androidx.compose.animation:animation-core:1.11.2, (changed from 1.11.3)
androidx.compose.animation:animation-core-android:1.11.2, (changed from 1.11.3)
androidx.compose.foundation:foundation:1.11.2, (changed from 1.11.3)
androidx.compose.foundation:foundation-android:1.11.2, (changed from 1.11.3)
androidx.compose.foundation:foundation-layout:1.11.2, (changed from 1.11.3)
androidx.compose.foundation:foundation-layout-android:1.11.2, (changed from 1.11.3)
androidx.compose.material:material:1.11.2, (changed from 1.11.3)
androidx.compose.material:material-android:1.11.2, (changed from 1.11.3)
androidx.compose.material:material-ripple:1.11.2, (changed from 1.11.3)
androidx.compose.material:material-ripple-android:1.11.2, (changed from 1.11.3)
androidx.compose.runtime:runtime:1.11.2, (changed from 1.11.3)
androidx.compose.runtime:runtime-android:1.11.2, (changed from 1.11.3)
androidx.compose.runtime:runtime-annotation:1.11.2, (changed from 1.11.3)
androidx.compose.runtime:runtime-annotation-android:1.11.2, (changed from 1.11.3)
androidx.compose.runtime:runtime-retain:1.11.2, (changed from 1.11.3)
androidx.compose.runtime:runtime-retain-android:1.11.2, (changed from 1.11.3)
androidx.compose.runtime:runtime-saveable:1.11.2, (changed from 1.11.3)
androidx.compose.runtime:runtime-saveable-android:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-android:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-geometry:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-geometry-android:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-graphics:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-graphics-android:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-text:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-text-android:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-tooling-preview:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-tooling-preview-android:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-unit:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-unit-android:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-util:1.11.2, (changed from 1.11.3)
androidx.compose.ui:ui-util-android:1.11.2, (changed from 1.11.3)
androidx.compose:compose-bom:2026.05.01, (changed from 2026.06.00)
androidx.datastore:datastore:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-android:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-core:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-core-android:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-core-okio:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-core-okio-jvm:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-preferences:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-preferences-android:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-preferences-core:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-preferences-core-android:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-preferences-external-protobuf:1.1.7, (changed from 1.2.1)
androidx.datastore:datastore-preferences-proto:1.1.7, (changed from 1.2.1)
com.google.firebase:firebase-bom:34.14.1, (changed from 34.15.0)
com.google.firebase:firebase-common:22.0.1, (changed from 22.1.0)
com.google.firebase:firebase-installations:19.1.0, (changed from 19.1.1)
com.quran:auth:0.1.7, (changed from 0.1.13)
com.quran:auth-android:0.1.7, (changed from 0.1.13)
com.quran:mobile-sync:0.1.7, (changed from 0.1.13)
com.quran:mobile-sync-android:0.1.7, (changed from 0.1.13)
com.quran:mutations-definitions:0.1.7, (changed from 0.1.13)
com.quran:mutations-definitions-jvm:0.1.7, (changed from 0.1.13)
com.quran:persistence:0.1.7, (changed from 0.1.13)
com.quran:persistence-android:0.1.7, (changed from 0.1.13)
com.quran:syncengine:0.1.7, (changed from 0.1.13)
com.quran:syncengine-jvm:0.1.7, (changed from 0.1.13)
com.rickclephas.kmp:kmp-nativecoroutines-annotations:1.0.3, (changed from 1.0.4)
com.rickclephas.kmp:kmp-nativecoroutines-annotations-jvm:1.0.3, (changed from 1.0.4)
com.rickclephas.kmp:kmp-nativecoroutines-core:1.0.3, (changed from 1.0.4)
com.rickclephas.kmp:kmp-nativecoroutines-core-jvm:1.0.3, (changed from 1.0.4)
io.ktor:ktor-client-content-negotiation:3.4.3, (changed from 3.5.1)
io.ktor:ktor-client-content-negotiation-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-client-core:3.4.3, (changed from 3.5.1)
io.ktor:ktor-client-core-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-client-logging:3.4.3, (changed from 3.5.1)
io.ktor:ktor-client-logging-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-client-okhttp:3.4.3, (changed from 3.5.1)
io.ktor:ktor-client-okhttp-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-events:3.4.3, (changed from 3.5.1)
io.ktor:ktor-events-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-http:3.4.3, (changed from 3.5.1)
io.ktor:ktor-http-cio:3.4.3, (changed from 3.5.1)
io.ktor:ktor-http-cio-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-http-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-io:3.4.3, (changed from 3.5.1)
io.ktor:ktor-io-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-network:3.4.3, (changed from 3.5.1)
io.ktor:ktor-network-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-serialization:3.4.3, (changed from 3.5.1)
io.ktor:ktor-serialization-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-serialization-kotlinx:3.4.3, (changed from 3.5.1)
io.ktor:ktor-serialization-kotlinx-json:3.4.3, (changed from 3.5.1)
io.ktor:ktor-serialization-kotlinx-json-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-serialization-kotlinx-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-sse:3.4.3, (changed from 3.5.1)
io.ktor:ktor-sse-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-utils:3.4.3, (changed from 3.5.1)
io.ktor:ktor-utils-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-websocket-serialization:3.4.3, (changed from 3.5.1)
io.ktor:ktor-websocket-serialization-jvm:3.4.3, (changed from 3.5.1)
io.ktor:ktor-websockets:3.4.3, (changed from 3.5.1)
io.ktor:ktor-websockets-jvm:3.4.3, (changed from 3.5.1)
org.jetbrains.kotlinx:kotlinx-io-bytestring:0.8.2, (changed from 0.9.0)
org.jetbrains.kotlinx:kotlinx-io-bytestring-jvm:0.8.2, (changed from 0.9.0)
org.jetbrains.kotlinx:kotlinx-io-core:0.8.2, (changed from 0.9.0)
org.jetbrains.kotlinx:kotlinx-io-core-jvm:0.8.2, (changed from 0.9.0)
org.slf4j:slf4j-api:2.0.17, (changed from 2.0.18)

// seek to the proper place. so let's seek anyway. route through getSeekPosition
// so that when we loop back to ayah 1 of a surah, the basmallah (ayahTimings[0])
// is played instead of skipping straight to ayah 1 (ayahTimings[1]).
val seekPosition = getSeekPosition(false)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the reason this works is really this part of that method:

      return if (ayah == 1 && !isRepeating) {
        gaplessSuraData.ayahTimings[0]
      } else time

because you pass repeating as false and ayah is 1, it'll return the 0th ayah timings (hence the basmala).

@ahmedre ahmedre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

jazakumAllah khair looks good barak Allah feek

@ahmedre ahmedre added this pull request to the merge queue Jul 4, 2026
Merged via the queue into quran:main with commit 566fb5a Jul 4, 2026
2 checks passed
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.

Basmallah not played when a gapless surah repeats back to ayah 1

2 participants