~repos /only-bible-app
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.
946b9db1
—
pyrossh 1 year ago
add verse navigation
- app/src/main/java/dev/pyrossh/onlyBible/AppHost.kt +2 -2
- app/src/main/java/dev/pyrossh/onlyBible/AppViewModel.kt +3 -15
- app/src/main/java/dev/pyrossh/onlyBible/ChapterScreen.kt +27 -45
- app/src/main/java/dev/pyrossh/onlyBible/composables/ChapterSelector.kt +1 -0
- app/src/main/java/dev/pyrossh/onlyBible/composables/VerseHeading.kt +1 -0
- app/src/main/java/dev/pyrossh/onlyBible/utils/Swipe.kt +39 -0
app/src/main/java/dev/pyrossh/onlyBible/AppHost.kt
CHANGED
|
@@ -14,13 +14,12 @@ import androidx.navigation.toRoute
|
|
|
14
14
|
fun AppHost(model: AppViewModel = viewModel()) {
|
|
15
15
|
val navController = rememberNavController()
|
|
16
16
|
val navigateToChapter = { props: ChapterScreenProps ->
|
|
17
|
-
model.resetScrollState()
|
|
18
17
|
navController.navigate(props)
|
|
19
18
|
}
|
|
20
19
|
if (!model.isLoading) {
|
|
21
20
|
NavHost(
|
|
22
21
|
navController = navController,
|
|
23
|
-
startDestination = ChapterScreenProps(model.bookIndex, model.chapterIndex)
|
|
22
|
+
startDestination = ChapterScreenProps(model.bookIndex, model.chapterIndex, model.verseIndex)
|
|
24
23
|
) {
|
|
25
24
|
composable<ChapterScreenProps>(
|
|
26
25
|
enterTransition = {
|
|
@@ -49,6 +48,7 @@ fun AppHost(model: AppViewModel = viewModel()) {
|
|
|
49
48
|
model = model,
|
|
50
49
|
bookIndex = props.bookIndex,
|
|
51
50
|
chapterIndex = props.chapterIndex,
|
|
51
|
+
verseIndex = props.verseIndex,
|
|
52
52
|
navigateToChapter = navigateToChapter,
|
|
53
53
|
)
|
|
54
54
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/AppViewModel.kt
CHANGED
|
@@ -6,7 +6,6 @@ import android.content.Context.MODE_PRIVATE
|
|
|
6
6
|
import android.content.Context.UI_MODE_SERVICE
|
|
7
7
|
import android.content.Intent
|
|
8
8
|
import android.text.Html
|
|
9
|
-
import androidx.compose.foundation.lazy.LazyListState
|
|
10
9
|
import androidx.compose.runtime.getValue
|
|
11
10
|
import androidx.compose.runtime.mutableIntStateOf
|
|
12
11
|
import androidx.compose.runtime.mutableStateOf
|
|
@@ -68,15 +67,12 @@ class AppViewModel : ViewModel() {
|
|
|
68
67
|
var bible by mutableStateOf(bibles.first())
|
|
69
68
|
var bookIndex by mutableIntStateOf(0)
|
|
70
69
|
var chapterIndex by mutableIntStateOf(0)
|
|
70
|
+
var verseIndex by mutableIntStateOf(0)
|
|
71
71
|
var fontType by mutableStateOf(FontType.Sans)
|
|
72
72
|
var fontSizeDelta by mutableIntStateOf(0)
|
|
73
73
|
var fontBoldEnabled by mutableStateOf(false)
|
|
74
74
|
var lineSpacingDelta by mutableIntStateOf(0)
|
|
75
75
|
var nightMode by mutableIntStateOf(UiModeManager.MODE_NIGHT_AUTO)
|
|
76
|
-
var scrollState = LazyListState(
|
|
77
|
-
0,
|
|
78
|
-
0
|
|
79
|
-
)
|
|
80
76
|
val selectedVerses = MutableStateFlow(listOf<Verse>())
|
|
81
77
|
val isSearching = MutableStateFlow(false)
|
|
82
78
|
val searchText = MutableStateFlow("")
|
|
@@ -148,6 +144,7 @@ class AppViewModel : ViewModel() {
|
|
|
148
144
|
val bibleFileName = prefs.getString("bible", "en_kjv") ?: "en_kjv"
|
|
149
145
|
bookIndex = prefs.getInt("bookIndex", 0)
|
|
150
146
|
chapterIndex = prefs.getInt("chapterIndex", 0)
|
|
147
|
+
verseIndex = prefs.getInt("verseIndex", 0)
|
|
151
148
|
fontType = FontType.valueOf(
|
|
152
149
|
prefs.getString("fontType", FontType.Sans.name) ?: FontType.Sans.name
|
|
153
150
|
)
|
|
@@ -156,10 +153,6 @@ class AppViewModel : ViewModel() {
|
|
|
156
153
|
lineSpacingDelta = prefs.getInt("lineSpacingDelta", 0)
|
|
157
154
|
nightMode = prefs.getInt("nightMode", UiModeManager.MODE_NIGHT_AUTO)
|
|
158
155
|
highlightedVerses.value = JSONObject(prefs.getString("highlightedVerses", "{}") ?: "{}")
|
|
159
|
-
scrollState = LazyListState(
|
|
160
|
-
prefs.getInt("scrollIndex", 0),
|
|
161
|
-
prefs.getInt("scrollOffset", 0)
|
|
162
|
-
)
|
|
163
156
|
val localBible = bibles.find { it.filename() == bibleFileName } ?: bibles.first()
|
|
164
157
|
loadBible(localBible, context)
|
|
165
158
|
viewModelScope.launch(Dispatchers.Main) {
|
|
@@ -207,24 +200,19 @@ class AppViewModel : ViewModel() {
|
|
|
207
200
|
putString("bible", bible.filename())
|
|
208
201
|
putInt("bookIndex", bookIndex)
|
|
209
202
|
putInt("chapterIndex", chapterIndex)
|
|
203
|
+
putInt("verseIndex", verseIndex)
|
|
210
204
|
putString("fontType", fontType.name)
|
|
211
205
|
putInt("fontSizeDelta", fontSizeDelta)
|
|
212
206
|
putBoolean("fontBoldEnabled", fontBoldEnabled)
|
|
213
207
|
putInt("lineSpacingDelta", lineSpacingDelta)
|
|
214
208
|
putInt("nightMode", nightMode)
|
|
215
209
|
putString("highlightedVerses", highlightedVerses.value.toString())
|
|
216
|
-
putInt("scrollIndex", scrollState.firstVisibleItemIndex)
|
|
217
|
-
putInt("scrollOffset", scrollState.firstVisibleItemScrollOffset)
|
|
218
210
|
apply()
|
|
219
211
|
commit()
|
|
220
212
|
}
|
|
221
213
|
}
|
|
222
214
|
}
|
|
223
215
|
|
|
224
|
-
fun resetScrollState() {
|
|
225
|
-
scrollState = LazyListState(0, 0)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
216
|
fun getHighlightForVerse(v: Verse): Int? {
|
|
229
217
|
if (highlightedVerses.value.has(v.id))
|
|
230
218
|
return highlightedVerses.value.getInt(v.id)
|
app/src/main/java/dev/pyrossh/onlyBible/ChapterScreen.kt
CHANGED
|
@@ -3,7 +3,6 @@ package dev.pyrossh.onlyBible
|
|
|
3
3
|
import android.os.Parcelable
|
|
4
4
|
import android.view.SoundEffectConstants
|
|
5
5
|
import androidx.compose.animation.AnimatedContentTransitionScope
|
|
6
|
-
import androidx.compose.foundation.gestures.detectDragGestures
|
|
7
6
|
import androidx.compose.foundation.layout.Arrangement
|
|
8
7
|
import androidx.compose.foundation.layout.Column
|
|
9
8
|
import androidx.compose.foundation.layout.PaddingValues
|
|
@@ -26,16 +25,14 @@ import androidx.compose.material3.Text
|
|
|
26
25
|
import androidx.compose.material3.TextButton
|
|
27
26
|
import androidx.compose.runtime.Composable
|
|
28
27
|
import androidx.compose.runtime.LaunchedEffect
|
|
29
|
-
import androidx.compose.runtime.MutableIntState
|
|
30
28
|
import androidx.compose.runtime.collectAsState
|
|
31
29
|
import androidx.compose.runtime.getValue
|
|
32
|
-
import androidx.compose.runtime.mutableIntStateOf
|
|
33
30
|
import androidx.compose.runtime.mutableStateOf
|
|
34
31
|
import androidx.compose.runtime.remember
|
|
32
|
+
import androidx.compose.runtime.saveable.listSaver
|
|
35
33
|
import androidx.compose.runtime.saveable.rememberSaveable
|
|
36
34
|
import androidx.compose.runtime.setValue
|
|
37
35
|
import androidx.compose.ui.Modifier
|
|
38
|
-
import androidx.compose.ui.input.pointer.PointerInputScope
|
|
39
36
|
import androidx.compose.ui.input.pointer.pointerInput
|
|
40
37
|
import androidx.compose.ui.platform.LocalContext
|
|
41
38
|
import androidx.compose.ui.platform.LocalView
|
|
@@ -48,16 +45,16 @@ import dev.pyrossh.onlyBible.composables.ChapterSelector
|
|
|
48
45
|
import dev.pyrossh.onlyBible.composables.EmbeddedSearchBar
|
|
49
46
|
import dev.pyrossh.onlyBible.composables.VerseHeading
|
|
50
47
|
import dev.pyrossh.onlyBible.composables.VerseText
|
|
48
|
+
import dev.pyrossh.onlyBible.utils.detectSwipe
|
|
51
49
|
import kotlinx.parcelize.Parcelize
|
|
52
50
|
import kotlinx.serialization.Serializable
|
|
53
|
-
import kotlin.math.abs
|
|
54
|
-
|
|
55
51
|
|
|
56
52
|
@Serializable
|
|
57
53
|
@Parcelize
|
|
58
54
|
data class ChapterScreenProps(
|
|
59
55
|
val bookIndex: Int,
|
|
60
56
|
val chapterIndex: Int,
|
|
57
|
+
val verseIndex: Int,
|
|
61
58
|
// TODO: fix this
|
|
62
59
|
val dir: String = Dir.Left.name,
|
|
63
60
|
) : Parcelable
|
|
@@ -78,43 +75,13 @@ enum class Dir : Parcelable {
|
|
|
78
75
|
}
|
|
79
76
|
}
|
|
80
77
|
|
|
81
|
-
suspend fun PointerInputScope.detectSwipe(
|
|
82
|
-
swipeState: MutableIntState = mutableIntStateOf(-1),
|
|
83
|
-
onSwipeLeft: () -> Unit = {},
|
|
84
|
-
onSwipeRight: () -> Unit = {},
|
|
85
|
-
onSwipeUp: () -> Unit = {},
|
|
86
|
-
onSwipeDown: () -> Unit = {},
|
|
87
|
-
) = detectDragGestures(
|
|
88
|
-
onDrag = { change, dragAmount ->
|
|
89
|
-
change.consume()
|
|
90
|
-
val (x, y) = dragAmount
|
|
91
|
-
if (abs(x) > abs(y)) {
|
|
92
|
-
when {
|
|
93
|
-
x > 0 -> swipeState.intValue = 0
|
|
94
|
-
x < 0 -> swipeState.intValue = 1
|
|
95
|
-
}
|
|
96
|
-
} else {
|
|
97
|
-
when {
|
|
98
|
-
y > 0 -> swipeState.intValue = 2
|
|
99
|
-
y < 0 -> swipeState.intValue = 3
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
onDragEnd = {
|
|
104
|
-
when (swipeState.intValue) {
|
|
105
|
-
0 -> onSwipeRight()
|
|
106
|
-
1 -> onSwipeLeft()
|
|
107
|
-
2 -> onSwipeDown()
|
|
108
|
-
3 -> onSwipeUp()
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
)
|
|
112
78
|
|
|
113
79
|
@Composable
|
|
114
80
|
fun ChapterScreen(
|
|
115
81
|
model: AppViewModel,
|
|
116
82
|
bookIndex: Int,
|
|
117
83
|
chapterIndex: Int,
|
|
84
|
+
verseIndex: Int,
|
|
118
85
|
navigateToChapter: (ChapterScreenProps) -> Unit,
|
|
119
86
|
) {
|
|
120
87
|
val view = LocalView.current
|
|
@@ -122,15 +89,30 @@ fun ChapterScreen(
|
|
|
122
89
|
val isSearching by model.isSearching.collectAsState()
|
|
123
90
|
var chapterSelectorShown by remember { mutableStateOf(false) }
|
|
124
91
|
var bibleSelectorShown by remember { mutableStateOf(false) }
|
|
125
|
-
val headingColor = MaterialTheme.colorScheme.onSurface
|
|
126
92
|
val bookNames by model.bookNames.collectAsState()
|
|
127
93
|
val verses by model.verses.collectAsState()
|
|
94
|
+
val state = rememberSaveable(saver = listSaver(
|
|
95
|
+
save = {
|
|
96
|
+
model.verseIndex = it.firstVisibleItemIndex
|
|
97
|
+
listOf(it.firstVisibleItemIndex, it.firstVisibleItemScrollOffset)
|
|
98
|
+
},
|
|
99
|
+
restore = {
|
|
100
|
+
LazyListState(
|
|
101
|
+
firstVisibleItemIndex = model.verseIndex,
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
)) {
|
|
105
|
+
LazyListState(
|
|
106
|
+
firstVisibleItemIndex = verseIndex,
|
|
107
|
+
)
|
|
108
|
+
}
|
|
128
109
|
val chapterVerses =
|
|
129
110
|
verses.filter { it.bookIndex == bookIndex && it.chapterIndex == chapterIndex }
|
|
130
111
|
LaunchedEffect(Unit) {
|
|
131
112
|
model.clearSelectedVerses()
|
|
132
113
|
model.bookIndex = bookIndex
|
|
133
114
|
model.chapterIndex = chapterIndex
|
|
115
|
+
model.verseIndex = verseIndex
|
|
134
116
|
}
|
|
135
117
|
Scaffold(
|
|
136
118
|
modifier = Modifier
|
|
@@ -188,7 +170,7 @@ fun ChapterScreen(
|
|
|
188
170
|
style = TextStyle(
|
|
189
171
|
fontSize = 22.sp,
|
|
190
172
|
fontWeight = FontWeight.W500,
|
|
191
|
-
color =
|
|
173
|
+
color = MaterialTheme.colorScheme.onSurface,
|
|
192
174
|
)
|
|
193
175
|
)
|
|
194
176
|
}
|
|
@@ -205,7 +187,7 @@ fun ChapterScreen(
|
|
|
205
187
|
Icon(
|
|
206
188
|
imageVector = Icons.Rounded.Search,
|
|
207
189
|
contentDescription = "Search",
|
|
208
|
-
tint =
|
|
190
|
+
tint = MaterialTheme.colorScheme.onSurface,
|
|
209
191
|
)
|
|
210
192
|
}
|
|
211
193
|
TextButton(onClick = {
|
|
@@ -217,7 +199,7 @@ fun ChapterScreen(
|
|
|
217
199
|
style = TextStyle(
|
|
218
200
|
fontSize = 18.sp,
|
|
219
201
|
fontWeight = FontWeight.W500,
|
|
220
|
-
color =
|
|
202
|
+
color = MaterialTheme.colorScheme.onSurface,
|
|
221
203
|
),
|
|
222
204
|
)
|
|
223
205
|
}
|
|
@@ -229,16 +211,14 @@ fun ChapterScreen(
|
|
|
229
211
|
Icon(
|
|
230
212
|
imageVector = Icons.Outlined.MoreVert,
|
|
231
213
|
contentDescription = "More",
|
|
232
|
-
tint =
|
|
214
|
+
tint = MaterialTheme.colorScheme.onSurface,
|
|
233
215
|
)
|
|
234
216
|
}
|
|
235
217
|
}
|
|
236
218
|
}
|
|
237
219
|
|
|
238
220
|
LazyColumn(
|
|
239
|
-
state = rememberSaveable(saver = LazyListState.Saver) {
|
|
240
|
-
|
|
221
|
+
state = state,
|
|
241
|
-
},
|
|
242
222
|
verticalArrangement = Arrangement.spacedBy(16.dp + (model.lineSpacingDelta * 2).dp),
|
|
243
223
|
modifier = Modifier
|
|
244
224
|
.fillMaxSize()
|
|
@@ -250,6 +230,7 @@ fun ChapterScreen(
|
|
|
250
230
|
ChapterScreenProps(
|
|
251
231
|
bookIndex = pair.first,
|
|
252
232
|
chapterIndex = pair.second,
|
|
233
|
+
verseIndex = 0,
|
|
253
234
|
)
|
|
254
235
|
)
|
|
255
236
|
},
|
|
@@ -259,6 +240,7 @@ fun ChapterScreen(
|
|
|
259
240
|
ChapterScreenProps(
|
|
260
241
|
bookIndex = pair.first,
|
|
261
242
|
chapterIndex = pair.second,
|
|
243
|
+
verseIndex = 0,
|
|
262
244
|
dir = Dir.Right.name,
|
|
263
245
|
)
|
|
264
246
|
)
|
app/src/main/java/dev/pyrossh/onlyBible/composables/ChapterSelector.kt
CHANGED
|
@@ -151,6 +151,7 @@ fun ChapterSelector(
|
|
|
151
151
|
ChapterScreenProps(
|
|
152
152
|
bookIndex = bookIndex,
|
|
153
153
|
chapterIndex = c,
|
|
154
|
+
verseIndex = 0,
|
|
154
155
|
)
|
|
155
156
|
)
|
|
156
157
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/composables/VerseHeading.kt
CHANGED
|
@@ -54,6 +54,7 @@ fun VerseHeading(
|
|
|
54
54
|
ChapterScreenProps(
|
|
55
55
|
bookIndex = parts[0].toInt(),
|
|
56
56
|
chapterIndex = parts[1].toInt(),
|
|
57
|
+
verseIndex = parts[2].toInt(),
|
|
57
58
|
)
|
|
58
59
|
)
|
|
59
60
|
},
|
app/src/main/java/dev/pyrossh/onlyBible/utils/Swipe.kt
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package dev.pyrossh.onlyBible.utils
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.gestures.detectDragGestures
|
|
4
|
+
import androidx.compose.runtime.MutableIntState
|
|
5
|
+
import androidx.compose.runtime.mutableIntStateOf
|
|
6
|
+
import androidx.compose.ui.input.pointer.PointerInputScope
|
|
7
|
+
import kotlin.math.abs
|
|
8
|
+
|
|
9
|
+
suspend fun PointerInputScope.detectSwipe(
|
|
10
|
+
swipeState: MutableIntState = mutableIntStateOf(-1),
|
|
11
|
+
onSwipeLeft: () -> Unit = {},
|
|
12
|
+
onSwipeRight: () -> Unit = {},
|
|
13
|
+
onSwipeUp: () -> Unit = {},
|
|
14
|
+
onSwipeDown: () -> Unit = {},
|
|
15
|
+
) = detectDragGestures(
|
|
16
|
+
onDrag = { change, dragAmount ->
|
|
17
|
+
change.consume()
|
|
18
|
+
val (x, y) = dragAmount
|
|
19
|
+
if (abs(x) > abs(y)) {
|
|
20
|
+
when {
|
|
21
|
+
x > 0 -> swipeState.intValue = 0
|
|
22
|
+
x < 0 -> swipeState.intValue = 1
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
when {
|
|
26
|
+
y > 0 -> swipeState.intValue = 2
|
|
27
|
+
y < 0 -> swipeState.intValue = 3
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
onDragEnd = {
|
|
32
|
+
when (swipeState.intValue) {
|
|
33
|
+
0 -> onSwipeRight()
|
|
34
|
+
1 -> onSwipeLeft()
|
|
35
|
+
2 -> onSwipeDown()
|
|
36
|
+
3 -> onSwipeUp()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
)
|