~repos /only-bible-app

#kotlin#android#ios

git clone https://pyrossh.dev/repos/only-bible-app.git

The only bible app you will ever need. No ads. No in-app purchases. No distractions.


a9101f33 pyrossh

1 year ago
improve stuff
app/src/main/AndroidManifest.xml CHANGED
@@ -15,12 +15,12 @@
15
15
  android:icon="@mipmap/ic_launcher"
16
16
  android:roundIcon="@mipmap/ic_launcher_round"
17
17
  android:supportsRtl="true"
18
- android:theme="@style/Theme.BibleApp">
18
+ android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">
19
19
  <activity
20
20
  android:name=".MainActivity"
21
21
  android:configChanges="uiMode"
22
22
  android:exported="true"
23
- android:theme="@style/Theme.BibleApp">
23
+ android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">
24
24
  <intent-filter>
25
25
  <action android:name="android.intent.action.MAIN" />
26
26
  <category android:name="android.intent.category.LAUNCHER" />
app/src/main/assets/bibles/en_kjv.txt CHANGED
@@ -1,4 +1,4 @@
1
- Genesis|0|0|0|The History of Creation|In the beginning God created the heaven and the earth.
1
+ Genesis|0|0|0|The History of Creation <br> (<a href="42:0:0">John 1:1–5</a>;<a href="57:10:1">Hebrews 11:1–3</a>)|In the beginning God created the heaven and the earth.
2
2
  Genesis|0|0|1||And the earth was without form, and void; and darkness <em>was</em> upon the face of the deep. And the Spirit of God moved upon the face of the waters.
3
3
  Genesis|0|0|2||And God said, Let there be light: and there was light.
4
4
  Genesis|0|0|3||And God saw the light, that <em>it was</em> good: and God divided the light from the darkness.
app/src/main/java/dev/pyrossh/onlyBible/AppHost.kt CHANGED
@@ -4,94 +4,54 @@ import androidx.compose.animation.EnterTransition
4
4
  import androidx.compose.animation.ExitTransition
5
5
  import androidx.compose.animation.core.tween
6
6
  import androidx.compose.runtime.Composable
7
+ import androidx.lifecycle.viewmodel.compose.viewModel
7
8
  import androidx.navigation.compose.NavHost
8
9
  import androidx.navigation.compose.composable
9
10
  import androidx.navigation.compose.rememberNavController
10
11
  import androidx.navigation.toRoute
11
12
 
12
13
  @Composable
13
- fun AppHost(model: AppViewModel) {
14
+ fun AppHost(model: AppViewModel = viewModel()) {
14
15
  val navController = rememberNavController()
15
16
  val navigateToChapter = { props: ChapterScreenProps ->
16
17
  model.resetScrollState()
17
18
  navController.navigate(props)
18
19
  }
19
- val onSwipeLeft = {
20
- val pair = model.getForwardPair()
20
+ if (!model.isLoading) {
21
- navigateToChapter(
21
+ NavHost(
22
- ChapterScreenProps(
23
- bookIndex = pair.first,
24
- chapterIndex = pair.second,
25
- )
26
- )
27
- }
28
- val onSwipeRight = {
29
- val pair = model.getBackwardPair()
30
- if (navController.previousBackStackEntry != null) {
31
- val previousBook =
32
- navController.previousBackStackEntry?.arguments?.getInt("bookIndex")
33
- ?: 0
34
- val previousChapter =
35
- navController.previousBackStackEntry?.arguments?.getInt("chapterIndex")
36
- ?: 0
37
- // println("currentBackStackEntry ${previousBook} ${previousChapter} || ${pair.first} ${pair.second}")
38
- if (previousBook == pair.first && previousChapter == pair.second) {
39
- model.resetScrollState()
40
- navController.popBackStack()
22
+ navController = navController,
23
+ startDestination = ChapterScreenProps(model.bookIndex, model.chapterIndex)
41
- } else {
24
+ ) {
42
- navigateToChapter(
43
- ChapterScreenProps(
25
+ composable<ChapterScreenProps>(
26
+ enterTransition = {
27
+ val props = this.targetState.toRoute<ChapterScreenProps>()
44
- bookIndex = pair.first,
28
+ slideIntoContainer(
45
- chapterIndex = pair.second,
29
+ Dir.valueOf(props.dir).slideDirection(),
46
- dir = Dir.Right.name,
30
+ tween(400),
47
31
  )
32
+ },
33
+ exitTransition = {
34
+ ExitTransition.None
35
+ },
36
+ popEnterTransition = {
37
+ EnterTransition.None
38
+ },
39
+ popExitTransition = {
40
+ val props = this.targetState.toRoute<ChapterScreenProps>()
41
+ slideOutOfContainer(
42
+ Dir.valueOf(props.dir).reverse().slideDirection(),
43
+ tween(400),
44
+ )
45
+ }
46
+ ) {
47
+ val props = it.toRoute<ChapterScreenProps>()
48
+ ChapterScreen(
49
+ model = model,
50
+ bookIndex = props.bookIndex,
51
+ chapterIndex = props.chapterIndex,
52
+ navigateToChapter = navigateToChapter,
48
53
  )
49
54
  }
50
- } else {
51
- navigateToChapter(
52
- ChapterScreenProps(
53
- bookIndex = pair.first,
54
- chapterIndex = pair.second,
55
- dir = Dir.Right.name
56
- )
57
- )
58
- }
59
- }
60
- NavHost(
61
- navController = navController,
62
- startDestination = ChapterScreenProps(0, 0)
63
- ) {
64
- composable<ChapterScreenProps>(
65
- enterTransition = {
66
- val props = this.targetState.toRoute<ChapterScreenProps>()
67
- slideIntoContainer(
68
- Dir.valueOf(props.dir).slideDirection(),
69
- tween(400),
70
- )
71
- },
72
- exitTransition = {
73
- ExitTransition.None
74
- },
75
- popEnterTransition = {
76
- EnterTransition.None
77
- },
78
- popExitTransition = {
79
- val props = this.targetState.toRoute<ChapterScreenProps>()
80
- slideOutOfContainer(
81
- Dir.valueOf(props.dir).reverse().slideDirection(),
82
- tween(400),
83
- )
84
- }
85
- ) {
86
- val props = it.toRoute<ChapterScreenProps>()
87
- ChapterScreen(
88
- model = model,
89
- onSwipeLeft = onSwipeLeft,
90
- onSwipeRight = onSwipeRight,
91
- bookIndex = props.bookIndex,
92
- chapterIndex = props.chapterIndex,
93
- navigateToChapter = navigateToChapter,
94
- )
95
55
  }
96
56
  }
97
57
  }
app/src/main/java/dev/pyrossh/onlyBible/AppTheme.kt CHANGED
@@ -11,9 +11,14 @@ import androidx.compose.ui.graphics.Color
11
11
  import androidx.compose.ui.platform.LocalContext
12
12
  import androidx.compose.ui.text.font.FontFamily
13
13
  import androidx.compose.ui.text.font.GenericFontFamily
14
- import androidx.lifecycle.viewmodel.compose.viewModel
15
14
  import com.google.accompanist.systemuicontroller.rememberSystemUiController
16
15
 
16
+ enum class ThemeType {
17
+ Light,
18
+ Dark,
19
+ Auto;
20
+ }
21
+
17
22
  enum class FontType {
18
23
  Sans,
19
24
  Serif,
@@ -46,12 +51,12 @@ fun isLightTheme(uiMode: Int, isSystemDark: Boolean): Boolean {
46
51
 
47
52
  @Composable
48
53
  fun AppTheme(
49
- model: AppViewModel = viewModel(),
54
+ nightMode: Int,
50
55
  content: @Composable() () -> Unit
51
56
  ) {
52
57
  val context = LocalContext.current
53
58
  val systemUiController = rememberSystemUiController()
54
- val colorScheme = if (isLightTheme(model.nightMode, isSystemInDarkTheme()))
59
+ val colorScheme = if (isLightTheme(nightMode, isSystemInDarkTheme()))
55
60
  dynamicLightColorScheme(context).copy(
56
61
  onSurface = Color.Black,
57
62
  outline = Color.LightGray,
@@ -62,7 +67,7 @@ fun AppTheme(
62
67
  surface = Color(0xFF090F12),
63
68
  outline = Color(0xAA5D4979),
64
69
  )
65
- LaunchedEffect(key1 = model.nightMode) {
70
+ LaunchedEffect(key1 = nightMode) {
66
71
  systemUiController.setSystemBarsColor(
67
72
  color = colorScheme.background
68
73
  )
app/src/main/java/dev/pyrossh/onlyBible/AppViewModel.kt CHANGED
@@ -6,6 +6,7 @@ import android.content.Context
6
6
  import android.content.Context.MODE_PRIVATE
7
7
  import android.content.Context.UI_MODE_SERVICE
8
8
  import android.content.Intent
9
+ import android.text.Html
9
10
  import androidx.compose.foundation.lazy.LazyListState
10
11
  import androidx.compose.runtime.getValue
11
12
  import androidx.compose.runtime.mutableIntStateOf
@@ -17,6 +18,7 @@ import com.microsoft.cognitiveservices.speech.SpeechConfig
17
18
  import com.microsoft.cognitiveservices.speech.SpeechSynthesisEventArgs
18
19
  import com.microsoft.cognitiveservices.speech.SpeechSynthesizer
19
20
  import dev.pyrossh.onlyBible.domain.BOOKS_COUNT
21
+ import dev.pyrossh.onlyBible.domain.Bible
20
22
  import dev.pyrossh.onlyBible.domain.Verse
21
23
  import dev.pyrossh.onlyBible.domain.bibles
22
24
  import dev.pyrossh.onlyBible.domain.chapterSizes
@@ -36,45 +38,31 @@ import java.io.IOException
36
38
  class AppViewModel(application: Application) : AndroidViewModel(application) {
37
39
  private val context
38
40
  get() = getApplication<Application>()
39
- private val prefs
40
- get() = context.getSharedPreferences("data", MODE_PRIVATE)
41
41
  val speechService = SpeechSynthesizer(
42
42
  SpeechConfig.fromSubscription(
43
43
  BuildConfig.subscriptionKey,
44
44
  "centralindia"
45
45
  )
46
46
  )
47
+ val started = { _: Any, _: SpeechSynthesisEventArgs ->
48
+ isPlaying = true
49
+ }
50
+ val completed = { _: Any, _: SpeechSynthesisEventArgs ->
51
+ isPlaying = false
52
+ }
47
53
 
48
54
  init {
49
- val started = { _: Any, _: SpeechSynthesisEventArgs ->
50
- isPlaying = true
51
- }
52
- val completed = { _: Any, _: SpeechSynthesisEventArgs ->
53
- isPlaying = false
54
- }
55
55
  speechService.SynthesisStarted.addEventListener(started)
56
56
  speechService.SynthesisCompleted.addEventListener(completed)
57
- loadData()
58
- }
59
-
60
- fun onSpeechStared(sender: Any, e: Any) {
61
- viewModelScope.launch(Dispatchers.Main) {
62
- isPlaying = true
63
- }
64
- }
65
-
66
- fun onSpeechCompleted(sender: Any) {
67
- viewModelScope.launch(Dispatchers.Main) {
68
- isPlaying = false
69
- }
70
57
  }
71
58
 
72
59
  override fun onCleared() {
73
60
  super.onCleared()
74
- // speechService.SynthesisStarted.removeEventListener()
61
+ speechService.SynthesisStarted.removeEventListener(started)
75
- // speechService.SynthesisCompleted.removeEventListener()
62
+ speechService.SynthesisCompleted.removeEventListener(completed)
76
63
  }
77
64
 
65
+ var isLoading by mutableStateOf(true)
78
66
  var isPlaying by mutableStateOf(false)
79
67
  val verses = MutableStateFlow(listOf<Verse>())
80
68
  val bookNames = MutableStateFlow(engTitles)
@@ -94,16 +82,11 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
94
82
  0
95
83
  )
96
84
  val selectedVerses = MutableStateFlow(listOf<Verse>())
97
-
98
- private val _isSearching = MutableStateFlow(false)
85
+ val isSearching = MutableStateFlow(false)
99
- val isSearching = _isSearching.asStateFlow()
100
-
101
- //second state the text typed by the user
102
- private val _searchText = MutableStateFlow("")
86
+ val searchText = MutableStateFlow("")
103
- val searchText = _searchText.asStateFlow()
104
87
 
105
88
  @OptIn(FlowPreview::class)
106
- val versesList = _searchText.asStateFlow()
89
+ val searchedVerses = searchText.asStateFlow()
107
90
  .debounce(300)
108
91
  .combine(verses.asStateFlow()) { text, verses ->
109
92
  verses.filter { verse ->
@@ -121,19 +104,19 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
121
104
  )
122
105
 
123
106
  fun onSearchTextChange(text: String) {
124
- _searchText.value = text
107
+ searchText.value = text
125
108
  }
126
109
 
127
110
  fun onOpenSearch() {
128
- _isSearching.value = true
111
+ isSearching.value = true
129
- if (!_isSearching.value) {
112
+ if (!isSearching.value) {
130
113
  onSearchTextChange("")
131
114
  }
132
115
  }
133
116
 
134
117
  fun onCloseSearch() {
135
- _isSearching.value = false
118
+ isSearching.value = false
136
- if (!_isSearching.value) {
119
+ if (!isSearching.value) {
137
120
  onSearchTextChange("")
138
121
  }
139
122
  }
@@ -160,16 +143,18 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
160
143
  selectedVerses.value = listOf()
161
144
  }
162
145
 
163
- private fun loadData() {
146
+ fun loadData(context: Context) {
164
147
  viewModelScope.launch(Dispatchers.IO) {
148
+ viewModelScope.launch(Dispatchers.Main) {
149
+ isLoading = true
150
+ }
151
+ val prefs = context.getSharedPreferences("data", MODE_PRIVATE)
165
152
  val bibleFileName = prefs.getString("bible", "en_kjv") ?: "en_kjv"
166
- bible = bibles.find { it.filename() == bibleFileName } ?: bibles.first()
167
153
  bookIndex = prefs.getInt("bookIndex", 0)
168
154
  chapterIndex = prefs.getInt("chapterIndex", 0)
169
- fontType =
170
- FontType.valueOf(
155
+ fontType = FontType.valueOf(
171
- prefs.getString("fontType", FontType.Sans.name) ?: FontType.Sans.name
156
+ prefs.getString("fontType", FontType.Sans.name) ?: FontType.Sans.name
172
- )
157
+ )
173
158
  fontSizeDelta = prefs.getInt("fontSizeDelta", 0)
174
159
  fontBoldEnabled = prefs.getBoolean("fontBoldEnabled", false)
175
160
  lineSpacingDelta = prefs.getInt("lineSpacingDelta", 0)
@@ -179,14 +164,18 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
179
164
  prefs.getInt("scrollIndex", 0),
180
165
  prefs.getInt("scrollOffset", 0)
181
166
  )
167
+ val localBible = bibles.find { it.filename() == bibleFileName } ?: bibles.first()
182
- loadBible()
168
+ loadBible(localBible, context)
169
+ viewModelScope.launch(Dispatchers.Main) {
170
+ isLoading = false
171
+ }
183
172
  }
184
173
  }
185
174
 
186
- fun loadBible() {
175
+ fun loadBible(b: Bible, context: Context) {
187
176
  try {
188
177
  val buffer =
189
- context.assets.open("bibles/${bible.filename()}.txt").bufferedReader()
178
+ context.assets.open("bibles/${b.filename()}.txt").bufferedReader()
190
179
  val localVerses = buffer.readLines().filter { it.isNotEmpty() }.map {
191
180
  val arr = it.split("|")
192
181
  val bookName = arr[0]
@@ -205,6 +194,7 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
205
194
  )
206
195
  }
207
196
  viewModelScope.launch(Dispatchers.Main) {
197
+ bible = b
208
198
  verses.value = localVerses
209
199
  bookNames.value = localVerses.distinctBy { it.bookName }.map { it.bookName }
210
200
  }
@@ -213,8 +203,9 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
213
203
  }
214
204
  }
215
205
 
216
- fun saveData() {
206
+ fun saveData(context: Context) {
217
207
  viewModelScope.launch(Dispatchers.IO) {
208
+ val prefs = context.getSharedPreferences("data", MODE_PRIVATE)
218
209
  with(prefs.edit()) {
219
210
  putString("bible", bible.filename())
220
211
  putInt("bookIndex", bookIndex)
@@ -233,27 +224,6 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
233
224
  }
234
225
  }
235
226
 
236
- fun getForwardPair(): Pair<Int, Int> {
237
- val sizes = chapterSizes[bookIndex]
238
- if (sizes > chapterIndex + 1) {
239
- return Pair(bookIndex, chapterIndex + 1)
240
- }
241
- if (bookIndex + 1 < BOOKS_COUNT) {
242
- return Pair(bookIndex + 1, 0)
243
- }
244
- return Pair(0, 0)
245
- }
246
-
247
- fun getBackwardPair(): Pair<Int, Int> {
248
- if (chapterIndex - 1 >= 0) {
249
- return Pair(bookIndex, chapterIndex - 1)
250
- }
251
- if (bookIndex - 1 >= 0) {
252
- return Pair(bookIndex - 1, chapterSizes[bookIndex - 1] - 1)
253
- }
254
- return Pair(BOOKS_COUNT - 1, chapterSizes[BOOKS_COUNT - 1] - 1)
255
- }
256
-
257
227
  fun resetScrollState() {
258
228
  scrollState = LazyListState(0, 0)
259
229
  }
@@ -278,6 +248,37 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
278
248
  highlightedVerses.value.remove(v.key())
279
249
  }
280
250
  }
251
+
252
+ fun playAudio(text: String) {
253
+ speechService.StartSpeakingSsml("""
254
+ <speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
255
+ <voice name="${bible.voiceName}">
256
+ $text
257
+ </voice>
258
+ </speak>
259
+ """)
260
+ }
261
+ }
262
+
263
+ fun getForwardPair(bookIndex: Int, chapterIndex: Int): Pair<Int, Int> {
264
+ val sizes = chapterSizes[bookIndex]
265
+ if (sizes > chapterIndex + 1) {
266
+ return Pair(bookIndex, chapterIndex + 1)
267
+ }
268
+ if (bookIndex + 1 < BOOKS_COUNT) {
269
+ return Pair(bookIndex + 1, 0)
270
+ }
271
+ return Pair(0, 0)
272
+ }
273
+
274
+ fun getBackwardPair(bookIndex: Int, chapterIndex: Int): Pair<Int, Int> {
275
+ if (chapterIndex - 1 >= 0) {
276
+ return Pair(bookIndex, chapterIndex - 1)
277
+ }
278
+ if (bookIndex - 1 >= 0) {
279
+ return Pair(bookIndex - 1, chapterSizes[bookIndex - 1] - 1)
280
+ }
281
+ return Pair(BOOKS_COUNT - 1, chapterSizes[BOOKS_COUNT - 1] - 1)
281
282
  }
282
283
 
283
284
  fun shareVerses(context: Context, verses: List<Verse>) {
@@ -285,12 +286,8 @@ fun shareVerses(context: Context, verses: List<Verse>) {
285
286
  if (verses.size >= 3) "${verses.first().verseIndex + 1}-${verses.last().verseIndex + 1}" else verses.map { it.verseIndex + 1 }
286
287
  .joinToString(",")
287
288
  val title = "${verses[0].bookName} ${verses[0].chapterIndex + 1}:${versesThrough}"
289
+ val text =
288
- val text = verses.joinToString("\n") {
290
+ Html.fromHtml(verses.joinToString("\n") { it.text }, Html.FROM_HTML_MODE_COMPACT).toString()
289
- it.text.replace("<span style=\"color:red;\">", "")
290
- .replace("<em>", "")
291
- .replace("</span>", "")
292
- .replace("</em>", "")
293
- }
294
291
  val sendIntent = Intent().apply {
295
292
  action = Intent.ACTION_SEND
296
293
  putExtra(Intent.EXTRA_TEXT, "${title}\n${text}")
app/src/main/java/dev/pyrossh/onlyBible/ChapterScreen.kt CHANGED
@@ -37,6 +37,7 @@ import androidx.compose.runtime.setValue
37
37
  import androidx.compose.ui.Modifier
38
38
  import androidx.compose.ui.input.pointer.PointerInputScope
39
39
  import androidx.compose.ui.input.pointer.pointerInput
40
+ import androidx.compose.ui.platform.LocalContext
40
41
  import androidx.compose.ui.platform.LocalView
41
42
  import androidx.compose.ui.text.TextStyle
42
43
  import androidx.compose.ui.text.font.FontWeight
@@ -45,7 +46,8 @@ import androidx.compose.ui.unit.sp
45
46
  import dev.pyrossh.onlyBible.composables.BibleSelector
46
47
  import dev.pyrossh.onlyBible.composables.ChapterSelector
47
48
  import dev.pyrossh.onlyBible.composables.EmbeddedSearchBar
49
+ import dev.pyrossh.onlyBible.composables.VerseHeading
48
- import dev.pyrossh.onlyBible.composables.VerseView
50
+ import dev.pyrossh.onlyBible.composables.VerseText
49
51
  import kotlinx.parcelize.Parcelize
50
52
  import kotlinx.serialization.Serializable
51
53
  import kotlin.math.abs
@@ -111,22 +113,21 @@ suspend fun PointerInputScope.detectSwipe(
111
113
  @Composable
112
114
  fun ChapterScreen(
113
115
  model: AppViewModel,
114
- onSwipeLeft: () -> Unit,
115
- onSwipeRight: () -> Any,
116
116
  bookIndex: Int,
117
117
  chapterIndex: Int,
118
118
  navigateToChapter: (ChapterScreenProps) -> Unit,
119
119
  ) {
120
120
  val view = LocalView.current
121
- val verses by model.verses.collectAsState()
121
+ val context = LocalContext.current
122
- val bookNames by model.bookNames.collectAsState()
123
122
  val isSearching by model.isSearching.collectAsState()
124
123
  var chapterSelectorShown by remember { mutableStateOf(false) }
125
124
  var bibleSelectorShown by remember { mutableStateOf(false) }
126
- val headingColor = MaterialTheme.colorScheme.onSurface // MaterialTheme.colorScheme.primary,
125
+ val headingColor = MaterialTheme.colorScheme.onSurface
126
+ val bookNames by model.bookNames.collectAsState()
127
+ val verses by model.verses.collectAsState()
127
128
  val chapterVerses =
128
129
  verses.filter { it.bookIndex == bookIndex && it.chapterIndex == chapterIndex }
129
- LaunchedEffect(key1 = chapterVerses) {
130
+ LaunchedEffect(Unit) {
130
131
  model.clearSelectedVerses()
131
132
  model.bookIndex = bookIndex
132
133
  model.chapterIndex = chapterIndex
@@ -137,13 +138,20 @@ fun ChapterScreen(
137
138
  ) { innerPadding ->
138
139
  if (bibleSelectorShown) {
139
140
  BibleSelector(
140
- model = model,
141
+ bible = model.bible,
142
+ onSelected = {
143
+ view.playSoundEffect(SoundEffectConstants.CLICK)
144
+ bibleSelectorShown = false
145
+ model.loadBible(it, context)
146
+ },
141
147
  onClose = { bibleSelectorShown = false },
142
148
  )
143
149
  }
144
150
  if (chapterSelectorShown) {
145
151
  ChapterSelector(
146
- model = model,
152
+ bible = model.bible,
153
+ bookNames = bookNames,
154
+ startBookIndex = bookIndex,
147
155
  onClose = { chapterSelectorShown = false },
148
156
  navigateToChapter = navigateToChapter,
149
157
  )
@@ -166,11 +174,12 @@ fun ChapterScreen(
166
174
  ) {
167
175
  TextButton(
168
176
  contentPadding = PaddingValues(
169
- top = ContentPadding.calculateTopPadding(),
177
+ top = ContentPadding.calculateTopPadding(), //8dp
170
178
  end = 12.dp,
171
179
  bottom = ContentPadding.calculateBottomPadding()
172
180
  ),
173
181
  onClick = {
182
+ view.playSoundEffect(SoundEffectConstants.CLICK)
174
183
  chapterSelectorShown = true
175
184
  }
176
185
  ) {
@@ -188,7 +197,10 @@ fun ChapterScreen(
188
197
  horizontalArrangement = Arrangement.End,
189
198
  ) {
190
199
  IconButton(
200
+ onClick = {
201
+ view.playSoundEffect(SoundEffectConstants.CLICK)
191
- onClick = { model.onOpenSearch() },
202
+ model.onOpenSearch()
203
+ },
192
204
  ) {
193
205
  Icon(
194
206
  imageVector = Icons.Rounded.Search,
@@ -197,6 +209,7 @@ fun ChapterScreen(
197
209
  )
198
210
  }
199
211
  TextButton(onClick = {
212
+ view.playSoundEffect(SoundEffectConstants.CLICK)
200
213
  bibleSelectorShown = true
201
214
  }) {
202
215
  Text(
@@ -231,32 +244,46 @@ fun ChapterScreen(
231
244
  .fillMaxSize()
232
245
  .pointerInput(Unit) {
233
246
  detectSwipe(
234
- onSwipeLeft = onSwipeLeft,
247
+ onSwipeLeft = {
248
+ val pair = getForwardPair(bookIndex, chapterIndex)
249
+ navigateToChapter(
250
+ ChapterScreenProps(
251
+ bookIndex = pair.first,
252
+ chapterIndex = pair.second,
253
+ )
254
+ )
255
+ },
235
- onSwipeRight = { onSwipeRight() },
256
+ onSwipeRight = {
257
+ val pair = getBackwardPair(bookIndex, chapterIndex)
258
+ navigateToChapter(
259
+ ChapterScreenProps(
260
+ bookIndex = pair.first,
261
+ chapterIndex = pair.second,
262
+ dir = Dir.Right.name,
263
+ )
264
+ )
265
+ },
236
266
  )
237
267
  }
238
268
  ) {
239
269
  items(chapterVerses) { v ->
240
270
  if (v.heading.isNotEmpty()) {
241
- Text(
242
- modifier = Modifier.padding(
271
+ VerseHeading(
243
- top = if (v.verseIndex != 0) 12.dp else 0.dp, bottom = 12.dp
244
- ),
245
- style = TextStyle(
272
+ text = v.heading,
246
- fontFamily = model.fontType.family(),
273
+ fontType = model.fontType,
247
- fontSize = (16 + model.fontSizeDelta).sp,
274
+ fontSizeDelta = model.fontSizeDelta,
248
- fontWeight = FontWeight.W700,
275
+ navigateToChapter = navigateToChapter,
249
- color = headingColor,
250
- ),
251
- text = v.heading.replace("<br>", "\n")
252
276
  )
253
277
  }
254
- VerseView(
278
+ VerseText(
255
279
  model = model,
280
+ fontType = model.fontType,
281
+ fontSizeDelta = model.fontSizeDelta,
282
+ fontBoldEnabled = model.fontBoldEnabled,
256
283
  verse = v,
257
284
  )
258
285
  }
259
286
  }
260
287
  }
261
288
  }
262
- }
289
+ }
app/src/main/java/dev/pyrossh/onlyBible/MainActivity.kt CHANGED
@@ -16,11 +16,18 @@ class MainActivity : ComponentActivity() {
16
16
  override fun onCreate(savedInstanceState: Bundle?) {
17
17
  super.onCreate(savedInstanceState)
18
18
  enableEdgeToEdge()
19
+ if (savedInstanceState == null) {
20
+ lifecycleScope.launch {
21
+ model.loadData(applicationContext)
22
+ }
23
+ }
19
24
  setContent {
20
- AppTheme {
25
+ AppTheme(
21
- AppHost(model = model)
26
+ nightMode = model.nightMode
27
+ ) {
28
+ AppHost()
22
29
  if (model.showBottomSheet) {
23
- TextSettingsBottomSheet(model = model)
30
+ TextSettingsBottomSheet()
24
31
  }
25
32
  }
26
33
  }
@@ -29,7 +36,7 @@ class MainActivity : ComponentActivity() {
29
36
  override fun onSaveInstanceState(outState: Bundle) {
30
37
  super.onSaveInstanceState(outState)
31
38
  lifecycleScope.launch {
32
- model.saveData()
39
+ model.saveData(applicationContext)
33
40
  }
34
41
  }
35
42
  }
app/src/main/java/dev/pyrossh/onlyBible/composables/BibleSelector.kt CHANGED
@@ -18,18 +18,19 @@ import androidx.compose.ui.platform.LocalContext
18
18
  import androidx.compose.ui.text.font.FontWeight
19
19
  import androidx.compose.ui.unit.dp
20
20
  import androidx.compose.ui.window.Dialog
21
- import dev.pyrossh.onlyBible.AppViewModel
21
+ import dev.pyrossh.onlyBible.domain.Bible
22
22
  import dev.pyrossh.onlyBible.domain.bibles
23
23
  import java.util.Locale
24
24
 
25
25
  @Composable
26
26
  fun BibleSelector(
27
- model: AppViewModel,
27
+ bible: Bible,
28
+ onSelected: (Bible) -> Unit,
28
29
  onClose: () -> Unit,
29
30
  ) {
30
31
  val context = LocalContext.current
31
32
  val height = context.resources.configuration.screenHeightDp.dp / 2
32
- val bibleList = bibles.sortedBy { it != model.bible }
33
+ val bibleList = bibles.sortedBy { it != bible }
33
34
  Dialog(onDismissRequest = { onClose() }) {
34
35
  Card(
35
36
  modifier = Modifier
@@ -42,12 +43,10 @@ fun BibleSelector(
42
43
  val loc = Locale(it.languageCode)
43
44
  ListItem(
44
45
  modifier = Modifier.clickable {
45
- onClose()
46
+ onSelected(it)
46
- model.bible = it
47
- model.loadBible()
48
47
  },
49
48
  colors = ListItemDefaults.colors(
50
- containerColor = if (it == model.bible)
49
+ containerColor = if (it == bible)
51
50
  MaterialTheme.colorScheme.primaryContainer
52
51
  else
53
52
  MaterialTheme.colorScheme.background
app/src/main/java/dev/pyrossh/onlyBible/composables/ChapterSelector.kt CHANGED
@@ -1,5 +1,6 @@
1
1
  package dev.pyrossh.onlyBible.composables
2
2
 
3
+ import android.view.SoundEffectConstants
3
4
  import androidx.compose.foundation.clickable
4
5
  import androidx.compose.foundation.layout.Arrangement
5
6
  import androidx.compose.foundation.layout.Spacer
@@ -25,7 +26,6 @@ import androidx.compose.material3.Text
25
26
  import androidx.compose.material3.TextButton
26
27
  import androidx.compose.runtime.Composable
27
28
  import androidx.compose.runtime.LaunchedEffect
28
- import androidx.compose.runtime.collectAsState
29
29
  import androidx.compose.runtime.getValue
30
30
  import androidx.compose.runtime.mutableIntStateOf
31
31
  import androidx.compose.runtime.mutableStateOf
@@ -34,26 +34,29 @@ import androidx.compose.runtime.setValue
34
34
  import androidx.compose.ui.Modifier
35
35
  import androidx.compose.ui.graphics.Color
36
36
  import androidx.compose.ui.platform.LocalContext
37
+ import androidx.compose.ui.platform.LocalView
37
38
  import androidx.compose.ui.text.font.FontWeight
38
39
  import androidx.compose.ui.unit.dp
39
40
  import androidx.compose.ui.window.Dialog
40
- import dev.pyrossh.onlyBible.AppViewModel
41
41
  import dev.pyrossh.onlyBible.ChapterScreenProps
42
+ import dev.pyrossh.onlyBible.domain.Bible
42
43
  import dev.pyrossh.onlyBible.domain.chapterSizes
43
44
  import dev.pyrossh.onlyBible.domain.engTitles
44
45
 
45
46
  @OptIn(ExperimentalMaterial3Api::class)
46
47
  @Composable
47
48
  fun ChapterSelector(
49
+ bible: Bible,
50
+ bookNames: List<String>,
48
- model: AppViewModel,
51
+ startBookIndex: Int,
49
52
  onClose: () -> Unit,
50
53
  navigateToChapter: (ChapterScreenProps) -> Unit
51
54
  ) {
55
+ val view = LocalView.current
52
56
  val context = LocalContext.current
53
57
  val height = context.resources.configuration.screenHeightDp.dp / 2
54
- val bookNames by model.bookNames.collectAsState()
55
58
  var expanded by remember { mutableStateOf(false) }
56
- var bookIndex by remember { mutableIntStateOf(model.bookIndex) }
59
+ var bookIndex by remember { mutableIntStateOf(startBookIndex) }
57
60
  val scrollState = rememberLazyListState()
58
61
  val bookList = bookNames - bookNames[bookIndex]
59
62
  LaunchedEffect(key1 = bookIndex) {
@@ -69,6 +72,7 @@ fun ChapterSelector(
69
72
  ListItem(
70
73
  modifier = Modifier
71
74
  .clickable {
75
+ view.playSoundEffect(SoundEffectConstants.CLICK)
72
76
  expanded = !expanded
73
77
  },
74
78
  colors = ListItemDefaults.colors(
@@ -106,7 +110,7 @@ fun ChapterSelector(
106
110
  )
107
111
  },
108
112
  supportingContent = {
109
- if (model.bible.languageCode != "en") {
113
+ if (bible.languageCode != "en") {
110
114
  Text(
111
115
  modifier = Modifier.padding(start = 4.dp),
112
116
  text = engTitles[bookNames.indexOf(it)],
@@ -141,6 +145,7 @@ fun ChapterSelector(
141
145
  ),
142
146
  shape = RoundedCornerShape(8.dp),
143
147
  onClick = {
148
+ view.playSoundEffect(SoundEffectConstants.CLICK)
144
149
  onClose()
145
150
  navigateToChapter(
146
151
  ChapterScreenProps(
app/src/main/java/dev/pyrossh/onlyBible/composables/EmbeddedSearchBar.kt CHANGED
@@ -1,5 +1,6 @@
1
1
  package dev.pyrossh.onlyBible.composables
2
2
 
3
+ import android.view.SoundEffectConstants
3
4
  import androidx.compose.foundation.layout.fillMaxWidth
4
5
  import androidx.compose.foundation.layout.padding
5
6
  import androidx.compose.foundation.lazy.LazyColumn
@@ -22,6 +23,7 @@ import androidx.compose.runtime.remember
22
23
  import androidx.compose.ui.Modifier
23
24
  import androidx.compose.ui.focus.FocusRequester
24
25
  import androidx.compose.ui.focus.focusRequester
26
+ import androidx.compose.ui.platform.LocalView
25
27
  import androidx.compose.ui.text.TextStyle
26
28
  import androidx.compose.ui.text.font.FontWeight
27
29
  import androidx.compose.ui.unit.dp
@@ -33,8 +35,9 @@ import dev.pyrossh.onlyBible.AppViewModel
33
35
  fun EmbeddedSearchBar(
34
36
  model: AppViewModel,
35
37
  ) {
38
+ val view = LocalView.current
36
39
  val searchText by model.searchText.collectAsState()
37
- val versesList by model.versesList.collectAsState()
40
+ val searchedVerses by model.searchedVerses.collectAsState()
38
41
  val textFieldFocusRequester = remember { FocusRequester() }
39
42
  SideEffect {
40
43
  textFieldFocusRequester.requestFocus()
@@ -73,6 +76,7 @@ fun EmbeddedSearchBar(
73
76
  trailingIcon = {
74
77
  IconButton(
75
78
  onClick = {
79
+ view.playSoundEffect(SoundEffectConstants.CLICK)
76
80
  model.onCloseSearch()
77
81
  },
78
82
  ) {
@@ -85,7 +89,7 @@ fun EmbeddedSearchBar(
85
89
  },
86
90
  tonalElevation = 0.dp,
87
91
  ) {
88
- val groups = versesList.groupBy { "${it.bookName} ${it.chapterIndex + 1}" }
92
+ val groups = searchedVerses.groupBy { "${it.bookName} ${it.chapterIndex + 1}" }
89
93
  LazyColumn {
90
94
  groups.forEach {
91
95
  item(
@@ -105,8 +109,11 @@ fun EmbeddedSearchBar(
105
109
  )
106
110
  }
107
111
  items(it.value) { v ->
108
- VerseView(
112
+ VerseText(
109
113
  model = model,
114
+ fontType = model.fontType,
115
+ fontSizeDelta = model.fontSizeDelta,
116
+ fontBoldEnabled = model.fontBoldEnabled,
110
117
  verse = v,
111
118
  )
112
119
  }
app/src/main/java/dev/pyrossh/onlyBible/composables/TextSettingsBottomSheet.kt CHANGED
@@ -3,6 +3,7 @@ package dev.pyrossh.onlyBible.composables
3
3
  import android.app.UiModeManager.MODE_NIGHT_AUTO
4
4
  import android.app.UiModeManager.MODE_NIGHT_NO
5
5
  import android.app.UiModeManager.MODE_NIGHT_YES
6
+ import android.view.SoundEffectConstants
6
7
  import androidx.compose.foundation.BorderStroke
7
8
  import androidx.compose.foundation.background
8
9
  import androidx.compose.foundation.layout.Arrangement
@@ -34,17 +35,20 @@ import androidx.compose.runtime.Composable
34
35
  import androidx.compose.runtime.rememberCoroutineScope
35
36
  import androidx.compose.ui.Alignment
36
37
  import androidx.compose.ui.Modifier
38
+ import androidx.compose.ui.platform.LocalView
37
39
  import androidx.compose.ui.text.TextStyle
38
40
  import androidx.compose.ui.text.font.FontWeight
39
41
  import androidx.compose.ui.unit.dp
40
42
  import androidx.compose.ui.unit.sp
43
+ import androidx.lifecycle.viewmodel.compose.viewModel
41
44
  import dev.pyrossh.onlyBible.AppViewModel
42
45
  import dev.pyrossh.onlyBible.FontType
43
46
  import kotlinx.coroutines.launch
44
47
 
45
48
  @Composable
46
49
  @OptIn(ExperimentalMaterial3Api::class)
47
- fun TextSettingsBottomSheet(model: AppViewModel) {
50
+ fun TextSettingsBottomSheet(model: AppViewModel = viewModel()) {
51
+ val view = LocalView.current
48
52
  val scope = rememberCoroutineScope()
49
53
  val sheetState = rememberModalBottomSheetState()
50
54
  return ModalBottomSheet(
@@ -78,6 +82,7 @@ fun TextSettingsBottomSheet(model: AppViewModel) {
78
82
  )
79
83
  Row(horizontalArrangement = Arrangement.End) {
80
84
  IconButton(onClick = {
85
+ view.playSoundEffect(SoundEffectConstants.CLICK)
81
86
  scope.launch {
82
87
  sheetState.hide()
83
88
  }.invokeOnCompletion {
@@ -103,6 +108,7 @@ fun TextSettingsBottomSheet(model: AppViewModel) {
103
108
  .padding(end = 16.dp)
104
109
  .weight(1f),
105
110
  onClick = {
111
+ view.playSoundEffect(SoundEffectConstants.CLICK)
106
112
  model.fontSizeDelta -= 1
107
113
  }) {
108
114
  Column(
@@ -124,6 +130,7 @@ fun TextSettingsBottomSheet(model: AppViewModel) {
124
130
  .padding(end = 16.dp)
125
131
  .weight(1f),
126
132
  onClick = {
133
+ view.playSoundEffect(SoundEffectConstants.CLICK)
127
134
  model.fontSizeDelta += 1
128
135
  }) {
129
136
  Column(
@@ -144,6 +151,7 @@ fun TextSettingsBottomSheet(model: AppViewModel) {
144
151
  .padding(end = 16.dp)
145
152
  .weight(1f),
146
153
  onClick = {
154
+ view.playSoundEffect(SoundEffectConstants.CLICK)
147
155
  model.fontBoldEnabled = !model.fontBoldEnabled
148
156
  }) {
149
157
  Column(
@@ -165,6 +173,7 @@ fun TextSettingsBottomSheet(model: AppViewModel) {
165
173
  .padding(end = 16.dp)
166
174
  .weight(1f),
167
175
  onClick = {
176
+ view.playSoundEffect(SoundEffectConstants.CLICK)
168
177
  if (model.lineSpacingDelta > 5) {
169
178
  model.lineSpacingDelta = 0
170
179
  } else {
@@ -202,6 +211,7 @@ fun TextSettingsBottomSheet(model: AppViewModel) {
202
211
  .padding(end = 16.dp)
203
212
  .weight(1f),
204
213
  onClick = {
214
+ view.playSoundEffect(SoundEffectConstants.CLICK)
205
215
  model.fontType = it
206
216
  }) {
207
217
  Column(
@@ -254,6 +264,7 @@ fun TextSettingsBottomSheet(model: AppViewModel) {
254
264
  .padding(end = 16.dp)
255
265
  .weight(1f),
256
266
  onClick = {
267
+ view.playSoundEffect(SoundEffectConstants.CLICK)
257
268
  scope.launch {
258
269
  sheetState.hide()
259
270
  model.closeSheet()
app/src/main/java/dev/pyrossh/onlyBible/composables/VerseHeading.kt ADDED
@@ -0,0 +1,62 @@
1
+ package dev.pyrossh.onlyBible.composables
2
+
3
+ import android.view.SoundEffectConstants
4
+ import androidx.compose.foundation.layout.padding
5
+ import androidx.compose.material3.MaterialTheme
6
+ import androidx.compose.material3.Text
7
+ import androidx.compose.runtime.Composable
8
+ import androidx.compose.ui.Modifier
9
+ import androidx.compose.ui.graphics.Color
10
+ import androidx.compose.ui.platform.LocalView
11
+ import androidx.compose.ui.text.AnnotatedString
12
+ import androidx.compose.ui.text.LinkAnnotation
13
+ import androidx.compose.ui.text.SpanStyle
14
+ import androidx.compose.ui.text.TextLinkStyles
15
+ import androidx.compose.ui.text.TextStyle
16
+ import androidx.compose.ui.text.font.FontStyle
17
+ import androidx.compose.ui.text.font.FontWeight
18
+ import androidx.compose.ui.text.fromHtml
19
+ import androidx.compose.ui.unit.dp
20
+ import androidx.compose.ui.unit.sp
21
+ import dev.pyrossh.onlyBible.ChapterScreenProps
22
+ import dev.pyrossh.onlyBible.FontType
23
+
24
+ @Composable
25
+ fun VerseHeading(
26
+ text: String,
27
+ fontType: FontType,
28
+ fontSizeDelta: Int,
29
+ navigateToChapter: (ChapterScreenProps) -> Unit
30
+ ) {
31
+ val view = LocalView.current
32
+ Text(
33
+ modifier = Modifier.padding(bottom = 24.dp),
34
+ style = TextStyle(
35
+ fontFamily = fontType.family(),
36
+ fontSize = (16 + fontSizeDelta).sp,
37
+ fontWeight = FontWeight.W700,
38
+ color = MaterialTheme.colorScheme.onSurface,
39
+ ),
40
+ text = AnnotatedString.fromHtml(
41
+ htmlString = text,
42
+ linkStyles = TextLinkStyles(
43
+ style = SpanStyle(
44
+ fontSize = (14 + fontSizeDelta).sp,
45
+ fontStyle = FontStyle.Italic,
46
+ color = Color(0xFF008AE6),
47
+ )
48
+ ),
49
+ linkInteractionListener = {
50
+ view.playSoundEffect(SoundEffectConstants.CLICK)
51
+ val url = (it as LinkAnnotation.Url).url
52
+ val parts = url.split(":")
53
+ navigateToChapter(
54
+ ChapterScreenProps(
55
+ bookIndex = parts[0].toInt(),
56
+ chapterIndex = parts[1].toInt(),
57
+ )
58
+ )
59
+ },
60
+ ),
61
+ )
62
+ }
app/src/main/java/dev/pyrossh/onlyBible/composables/{VerseView.kt → VerseText.kt} RENAMED
@@ -1,10 +1,5 @@
1
1
  package dev.pyrossh.onlyBible.composables
2
2
 
3
- import android.graphics.Typeface
4
- import android.text.Html
5
- import android.text.style.BulletSpan
6
- import android.text.style.ForegroundColorSpan
7
- import android.text.style.StyleSpan
8
3
  import androidx.compose.foundation.border
9
4
  import androidx.compose.foundation.clickable
10
5
  import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -42,17 +37,22 @@ import androidx.compose.ui.graphics.Color
42
37
  import androidx.compose.ui.layout.onPlaced
43
38
  import androidx.compose.ui.layout.positionInRoot
44
39
  import androidx.compose.ui.platform.LocalContext
40
+ import androidx.compose.ui.text.AnnotatedString
41
+ import androidx.compose.ui.text.LinkAnnotation
45
42
  import androidx.compose.ui.text.SpanStyle
43
+ import androidx.compose.ui.text.TextLinkStyles
46
44
  import androidx.compose.ui.text.TextStyle
47
45
  import androidx.compose.ui.text.buildAnnotatedString
48
46
  import androidx.compose.ui.text.font.FontStyle
49
47
  import androidx.compose.ui.text.font.FontWeight
48
+ import androidx.compose.ui.text.fromHtml
50
49
  import androidx.compose.ui.text.withStyle
51
50
  import androidx.compose.ui.unit.IntOffset
52
51
  import androidx.compose.ui.unit.dp
53
52
  import androidx.compose.ui.unit.sp
54
53
  import androidx.compose.ui.window.Popup
55
54
  import dev.pyrossh.onlyBible.AppViewModel
55
+ import dev.pyrossh.onlyBible.FontType
56
56
  import dev.pyrossh.onlyBible.darkHighlights
57
57
  import dev.pyrossh.onlyBible.domain.Verse
58
58
  import dev.pyrossh.onlyBible.isLightTheme
@@ -62,8 +62,11 @@ import kotlinx.coroutines.Dispatchers
62
62
  import kotlinx.coroutines.launch
63
63
 
64
64
  @Composable
65
- fun VerseView(
65
+ fun VerseText(
66
66
  model: AppViewModel,
67
+ fontType: FontType,
68
+ fontSizeDelta: Int,
69
+ fontBoldEnabled: Boolean,
67
70
  verse: Verse,
68
71
  ) {
69
72
  var barYPosition by remember {
@@ -71,8 +74,6 @@ fun VerseView(
71
74
  }
72
75
  val selectedVerses by model.selectedVerses.collectAsState()
73
76
  val isLight = isLightTheme(model.nightMode, isSystemInDarkTheme())
74
- val fontSizeDelta = model.fontSizeDelta
75
- val boldWeight = if (model.fontBoldEnabled) FontWeight.W700 else FontWeight.W400
76
77
  val buttonInteractionSource = remember { MutableInteractionSource() }
77
78
  val isSelected = selectedVerses.contains(verse)
78
79
  val highlightedColorIndex = model.getHighlightForVerse(verse)
@@ -102,7 +103,7 @@ fun VerseView(
102
103
  currentHighlightColors[highlightedColorIndex]
103
104
  else
104
105
  Color.Unspecified,
105
- fontFamily = model.fontType.family(),
106
+ fontFamily = fontType.family(),
106
107
  color = if (isLight)
107
108
  Color(0xFF000104)
108
109
  else
@@ -110,15 +111,15 @@ fun VerseView(
110
111
  currentHighlightColors[highlightedColorIndex]
111
112
  else
112
113
  Color(0xFFBCBCBC),
113
- fontWeight = boldWeight,
114
+ fontWeight = if (fontBoldEnabled)
115
+ FontWeight.W700
116
+ else
117
+ FontWeight.W400,
114
118
  fontSize = (17 + fontSizeDelta).sp,
115
119
  lineHeight = (23 + fontSizeDelta).sp,
116
120
  letterSpacing = 0.sp,
117
121
  ),
118
122
  text = buildAnnotatedString {
119
- val spanned = Html.fromHtml(verse.text, Html.FROM_HTML_MODE_COMPACT)
120
- val spans = spanned.getSpans(0, spanned.length, Any::class.java)
121
- val verseNo = "${verse.verseIndex + 1} "
122
123
  withStyle(
123
124
  style = SpanStyle(
124
125
  fontSize = (13 + fontSizeDelta).sp,
@@ -129,41 +130,23 @@ fun VerseView(
129
130
  fontWeight = FontWeight.W700,
130
131
  )
131
132
  ) {
132
- append(verseNo)
133
+ append("${verse.verseIndex + 1} ")
133
134
  }
134
- append(spanned.toString())
135
- spans
135
+ append(
136
- .filter { it !is BulletSpan }
137
- .forEach { span ->
138
- val start = spanned.getSpanStart(span)
136
+ AnnotatedString.Companion.fromHtml(
139
- val end = spanned.getSpanEnd(span)
140
- when (span) {
141
- is ForegroundColorSpan ->
137
+ htmlString = verse.text,
142
- if (isLight) SpanStyle(color = Color(0xFFFF0000))
143
- else SpanStyle(color = Color(0xFFFF636B))
144
-
145
- is StyleSpan -> when (span.style) {
138
+ linkStyles = TextLinkStyles(
146
- Typeface.BOLD -> SpanStyle(fontWeight = FontWeight.Bold)
147
- Typeface.ITALIC -> SpanStyle(fontStyle = FontStyle.Italic)
148
- Typeface.BOLD_ITALIC -> SpanStyle(
139
+ style = SpanStyle(
149
- fontWeight = FontWeight.Bold,
140
+ fontSize = (14 + model.fontSizeDelta).sp,
150
- fontStyle = FontStyle.Italic,
141
+ fontStyle = FontStyle.Italic,
151
- )
152
-
153
- else -> null
154
- }
155
-
156
- else -> {
157
- null
158
- }
159
- }?.let { spanStyle ->
160
- addStyle(
161
- spanStyle,
162
- start + verseNo.length - 1,
142
+ color = Color(0xFF008AE6),
163
- end + verseNo.length
164
143
  )
144
+ ),
145
+ linkInteractionListener = {
146
+ println("SOUTT ${(it as LinkAnnotation.Url).url}")
165
- }
147
+ },
166
- }
148
+ )
149
+ )
167
150
  }
168
151
  )
169
152
  if (isSelected && selectedVerses.last() == verse) {
@@ -240,9 +223,7 @@ private fun Menu(
240
223
  } else {
241
224
  scope.launch(Dispatchers.IO) {
242
225
  for (v in selectedVerses.sortedBy { it.verseIndex }) {
243
- model.speechService.StartSpeakingSsml(
244
- v.toSSML(model.bible.voiceName),
226
+ model.playAudio(v.text)
245
- )
246
227
  }
247
228
  }
248
229
  }
app/src/main/java/dev/pyrossh/onlyBible/domain/Verse.kt CHANGED
@@ -181,14 +181,4 @@ data class Verse(
181
181
  ) : Parcelable {
182
182
 
183
183
  fun key() = "${bookIndex}:${chapterIndex}:${verseIndex}"
184
-
185
- fun toSSML(voice: String): String {
186
- return """
187
- <speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
188
- <voice name="$voice">
189
- $text
190
- </voice>
191
- </speak>
192
- """.trimIndent()
193
- }
194
184
  }
app/src/main/res/drawable/ic_launcher_foreground.xml CHANGED
@@ -4,10 +4,10 @@
4
4
  android:viewportWidth="108"
5
5
  android:viewportHeight="108">
6
6
  <group
7
- android:scaleX="0.12"
7
+ android:scaleX="0.11"
8
- android:scaleY="0.12"
8
+ android:scaleY="0.11"
9
- android:translateX="23"
9
+ android:translateX="26"
10
- android:translateY="23">
10
+ android:translateY="26">
11
11
  <path
12
12
  android:pathData="m389.57,122.38h-93.69V48.43c0,-22.08 -17.9,-39.98 -39.98,-39.98v0c-22.08,0 -39.98,17.9 -39.98,39.98v73.95h-93.69c-22.08,0 -39.98,17.9 -39.98,39.98v0c0,22.08 17.9,39.98 39.98,39.98h93.69v261.23c0,22.08 17.9,39.98 39.98,39.98v0c22.08,0 39.98,-17.9 39.98,-39.98V202.34h93.69c22.08,0 39.98,-17.9 39.98,-39.98v0c0,-22.08 -17.9,-39.98 -39.98,-39.98z"
13
13
  android:fillColor="#FFFCC447"/>
app/src/main/res/values-night/colors.xml DELETED
@@ -1,4 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <resources>
3
- <color name="splash_bg">#00000000</color>
4
- </resources>
app/src/main/res/values/colors.xml DELETED
@@ -1,4 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <resources>
3
- <color name="splash_bg">#FFFFFFFF</color>
4
- </resources>
app/src/main/res/values/themes.xml DELETED
@@ -1,4 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <resources>
3
- <style name="Theme.BibleApp" parent="Theme.AppCompat.DayNight.NoActionBar" />
4
- </resources>