@@ -4,6 +4,7 @@ import android.content.ComponentName
4
4
import android.content.Context
5
5
import android.content.Intent
6
6
import android.content.IntentFilter
7
+ import android.graphics.Typeface
7
8
import android.media.AudioAttributes
8
9
import android.media.AudioFocusRequest
9
10
import android.media.AudioManager
@@ -12,21 +13,26 @@ import android.speech.tts.TextToSpeech
12
13
import android.speech.tts.UtteranceProgressListener
13
14
import android.speech.tts.Voice
14
15
import android.support.v4.media.session.MediaSessionCompat
16
+ import android.text.Spannable
17
+ import android.text.SpannableString
15
18
import android.text.Spanned
19
+ import android.text.style.StyleSpan
16
20
import android.view.KeyEvent
17
21
import androidx.media.session.MediaButtonReceiver
18
- import com.lagradost.quicknovel.ui.UiText
19
- import com.lagradost.quicknovel.ui.txt
20
22
import com.lagradost.quicknovel.BaseApplication.Companion.removeKey
21
23
import com.lagradost.quicknovel.BaseApplication.Companion.setKey
22
24
import com.lagradost.quicknovel.mvvm.debugAssert
23
25
import com.lagradost.quicknovel.receivers.BecomingNoisyReceiver
26
+ import com.lagradost.quicknovel.ui.UiText
27
+ import com.lagradost.quicknovel.ui.txt
24
28
import com.lagradost.quicknovel.util.UIHelper.requestAudioFocus
25
29
import io.noties.markwon.Markwon
26
30
import kotlinx.coroutines.delay
27
31
import org.jsoup.Jsoup
28
32
import java.util.Locale
29
33
import java.util.Stack
34
+ import kotlin.math.roundToInt
35
+
30
36
31
37
class TTSSession (val context : Context , event : (TTSHelper .TTSActionType ) -> Boolean ) {
32
38
private val intentFilter = IntentFilter (AudioManager .ACTION_AUDIO_BECOMING_NOISY )
@@ -266,6 +272,30 @@ data class TextSpan(
266
272
override val index : Int ,
267
273
override var innerIndex : Int ,
268
274
) : SpanDisplay() {
275
+ val bionicText : Spanned by lazy {
276
+ val wordToSpan: Spannable = SpannableString (text)
277
+ val length = wordToSpan.length
278
+ Regex (" ([a-zà-ýA-ZÀ-ÝåäöÅÄÖ].*?)[^a-zà-ýA-ZÀ-ÝåäöÅÄÖ'’]" ).findAll(text).forEach { match ->
279
+ val range = match.groups[1 ]!! .range
280
+ // https://github.com/gBloxy/Bionic-Reader/blob/main/bionic-reader.py#L167
281
+ val correctLength = when (val rangeLength = range.last + 1 - range.first) {
282
+ 0 -> return @forEach // this should never happened
283
+ 1 , 2 , 3 -> 1
284
+ 4 -> 2
285
+ else -> {
286
+ (rangeLength.toFloat() * 0.4 ).roundToInt()
287
+ }
288
+ }
289
+ wordToSpan.setSpan(
290
+ StyleSpan (Typeface .BOLD ),
291
+ minOf(maxOf(match.range.first, 0 ), length),
292
+ minOf(maxOf(match.range.first + correctLength, 0 ), length),
293
+ Spannable .SPAN_EXCLUSIVE_EXCLUSIVE
294
+ )
295
+ }
296
+
297
+ wordToSpan
298
+ }
269
299
override fun id (): Long {
270
300
return generateId(0 , index, start, end)
271
301
}
0 commit comments