~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.
f15b6a01
—
Peter John 1 year ago
rewrite state
- .idea/other.xml +0 -263
- app/build.gradle.kts +2 -0
- app/src/main/java/dev/pyrossh/onlyBible/{Drawer.kt → AppDrawer.kt} +19 -8
- app/src/main/java/dev/pyrossh/onlyBible/AppHost.kt +78 -44
- app/src/main/java/dev/pyrossh/onlyBible/AppSettings.kt +0 -110
- app/src/main/java/dev/pyrossh/onlyBible/AppTheme.kt +21 -83
- app/src/main/java/dev/pyrossh/onlyBible/AppViewModel.kt +172 -0
- app/src/main/java/dev/pyrossh/onlyBible/ChapterScreen.kt +206 -201
- app/src/main/java/dev/pyrossh/onlyBible/Grid.kt +0 -42
- app/src/main/java/dev/pyrossh/onlyBible/Hooks.kt +25 -0
- app/src/main/java/dev/pyrossh/onlyBible/LoadingBox.kt +0 -46
- app/src/main/java/dev/pyrossh/onlyBible/MainActivity.kt +10 -19
- app/src/main/java/dev/pyrossh/onlyBible/Model.kt +0 -196
- app/src/main/java/dev/pyrossh/onlyBible/TextSettingsBottomSheet.kt +66 -20
- app/src/main/java/dev/pyrossh/onlyBible/Utils.kt +0 -59
- app/src/main/java/dev/pyrossh/onlyBible/Verse.kt +174 -0
- app/src/main/res/drawable/text_theme.xml +0 -29
- gradle/libs.versions.toml +4 -0
.idea/other.xml
DELETED
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="direct_access_persist.xml">
|
|
4
|
-
<option name="deviceSelectionList">
|
|
5
|
-
<list>
|
|
6
|
-
<PersistentDeviceSelectionData>
|
|
7
|
-
<option name="api" value="27" />
|
|
8
|
-
<option name="brand" value="DOCOMO" />
|
|
9
|
-
<option name="codename" value="F01L" />
|
|
10
|
-
<option name="id" value="F01L" />
|
|
11
|
-
<option name="manufacturer" value="FUJITSU" />
|
|
12
|
-
<option name="name" value="F-01L" />
|
|
13
|
-
<option name="screenDensity" value="360" />
|
|
14
|
-
<option name="screenX" value="720" />
|
|
15
|
-
<option name="screenY" value="1280" />
|
|
16
|
-
</PersistentDeviceSelectionData>
|
|
17
|
-
<PersistentDeviceSelectionData>
|
|
18
|
-
<option name="api" value="28" />
|
|
19
|
-
<option name="brand" value="DOCOMO" />
|
|
20
|
-
<option name="codename" value="SH-01L" />
|
|
21
|
-
<option name="id" value="SH-01L" />
|
|
22
|
-
<option name="manufacturer" value="SHARP" />
|
|
23
|
-
<option name="name" value="AQUOS sense2 SH-01L" />
|
|
24
|
-
<option name="screenDensity" value="480" />
|
|
25
|
-
<option name="screenX" value="1080" />
|
|
26
|
-
<option name="screenY" value="2160" />
|
|
27
|
-
</PersistentDeviceSelectionData>
|
|
28
|
-
<PersistentDeviceSelectionData>
|
|
29
|
-
<option name="api" value="31" />
|
|
30
|
-
<option name="brand" value="samsung" />
|
|
31
|
-
<option name="codename" value="a51" />
|
|
32
|
-
<option name="id" value="a51" />
|
|
33
|
-
<option name="manufacturer" value="Samsung" />
|
|
34
|
-
<option name="name" value="Galaxy A51" />
|
|
35
|
-
<option name="screenDensity" value="420" />
|
|
36
|
-
<option name="screenX" value="1080" />
|
|
37
|
-
<option name="screenY" value="2400" />
|
|
38
|
-
</PersistentDeviceSelectionData>
|
|
39
|
-
<PersistentDeviceSelectionData>
|
|
40
|
-
<option name="api" value="34" />
|
|
41
|
-
<option name="brand" value="google" />
|
|
42
|
-
<option name="codename" value="akita" />
|
|
43
|
-
<option name="id" value="akita" />
|
|
44
|
-
<option name="manufacturer" value="Google" />
|
|
45
|
-
<option name="name" value="Pixel 8a" />
|
|
46
|
-
<option name="screenDensity" value="420" />
|
|
47
|
-
<option name="screenX" value="1080" />
|
|
48
|
-
<option name="screenY" value="2400" />
|
|
49
|
-
</PersistentDeviceSelectionData>
|
|
50
|
-
<PersistentDeviceSelectionData>
|
|
51
|
-
<option name="api" value="33" />
|
|
52
|
-
<option name="brand" value="samsung" />
|
|
53
|
-
<option name="codename" value="b0q" />
|
|
54
|
-
<option name="id" value="b0q" />
|
|
55
|
-
<option name="manufacturer" value="Samsung" />
|
|
56
|
-
<option name="name" value="Galaxy S22 Ultra" />
|
|
57
|
-
<option name="screenDensity" value="600" />
|
|
58
|
-
<option name="screenX" value="1440" />
|
|
59
|
-
<option name="screenY" value="3088" />
|
|
60
|
-
</PersistentDeviceSelectionData>
|
|
61
|
-
<PersistentDeviceSelectionData>
|
|
62
|
-
<option name="api" value="32" />
|
|
63
|
-
<option name="brand" value="google" />
|
|
64
|
-
<option name="codename" value="bluejay" />
|
|
65
|
-
<option name="id" value="bluejay" />
|
|
66
|
-
<option name="manufacturer" value="Google" />
|
|
67
|
-
<option name="name" value="Pixel 6a" />
|
|
68
|
-
<option name="screenDensity" value="420" />
|
|
69
|
-
<option name="screenX" value="1080" />
|
|
70
|
-
<option name="screenY" value="2400" />
|
|
71
|
-
</PersistentDeviceSelectionData>
|
|
72
|
-
<PersistentDeviceSelectionData>
|
|
73
|
-
<option name="api" value="29" />
|
|
74
|
-
<option name="brand" value="samsung" />
|
|
75
|
-
<option name="codename" value="crownqlteue" />
|
|
76
|
-
<option name="id" value="crownqlteue" />
|
|
77
|
-
<option name="manufacturer" value="Samsung" />
|
|
78
|
-
<option name="name" value="Galaxy Note9" />
|
|
79
|
-
<option name="screenDensity" value="420" />
|
|
80
|
-
<option name="screenX" value="2220" />
|
|
81
|
-
<option name="screenY" value="1080" />
|
|
82
|
-
</PersistentDeviceSelectionData>
|
|
83
|
-
<PersistentDeviceSelectionData>
|
|
84
|
-
<option name="api" value="34" />
|
|
85
|
-
<option name="brand" value="samsung" />
|
|
86
|
-
<option name="codename" value="dm3q" />
|
|
87
|
-
<option name="id" value="dm3q" />
|
|
88
|
-
<option name="manufacturer" value="Samsung" />
|
|
89
|
-
<option name="name" value="Galaxy S23 Ultra" />
|
|
90
|
-
<option name="screenDensity" value="600" />
|
|
91
|
-
<option name="screenX" value="1440" />
|
|
92
|
-
<option name="screenY" value="3088" />
|
|
93
|
-
</PersistentDeviceSelectionData>
|
|
94
|
-
<PersistentDeviceSelectionData>
|
|
95
|
-
<option name="api" value="33" />
|
|
96
|
-
<option name="brand" value="google" />
|
|
97
|
-
<option name="codename" value="felix" />
|
|
98
|
-
<option name="id" value="felix" />
|
|
99
|
-
<option name="manufacturer" value="Google" />
|
|
100
|
-
<option name="name" value="Pixel Fold" />
|
|
101
|
-
<option name="screenDensity" value="420" />
|
|
102
|
-
<option name="screenX" value="2208" />
|
|
103
|
-
<option name="screenY" value="1840" />
|
|
104
|
-
</PersistentDeviceSelectionData>
|
|
105
|
-
<PersistentDeviceSelectionData>
|
|
106
|
-
<option name="api" value="33" />
|
|
107
|
-
<option name="brand" value="google" />
|
|
108
|
-
<option name="codename" value="felix_camera" />
|
|
109
|
-
<option name="id" value="felix_camera" />
|
|
110
|
-
<option name="manufacturer" value="Google" />
|
|
111
|
-
<option name="name" value="Pixel Fold (Camera-enabled)" />
|
|
112
|
-
<option name="screenDensity" value="420" />
|
|
113
|
-
<option name="screenX" value="2208" />
|
|
114
|
-
<option name="screenY" value="1840" />
|
|
115
|
-
</PersistentDeviceSelectionData>
|
|
116
|
-
<PersistentDeviceSelectionData>
|
|
117
|
-
<option name="api" value="33" />
|
|
118
|
-
<option name="brand" value="samsung" />
|
|
119
|
-
<option name="codename" value="gts8uwifi" />
|
|
120
|
-
<option name="id" value="gts8uwifi" />
|
|
121
|
-
<option name="manufacturer" value="Samsung" />
|
|
122
|
-
<option name="name" value="Galaxy Tab S8 Ultra" />
|
|
123
|
-
<option name="screenDensity" value="320" />
|
|
124
|
-
<option name="screenX" value="1848" />
|
|
125
|
-
<option name="screenY" value="2960" />
|
|
126
|
-
</PersistentDeviceSelectionData>
|
|
127
|
-
<PersistentDeviceSelectionData>
|
|
128
|
-
<option name="api" value="34" />
|
|
129
|
-
<option name="brand" value="google" />
|
|
130
|
-
<option name="codename" value="husky" />
|
|
131
|
-
<option name="id" value="husky" />
|
|
132
|
-
<option name="manufacturer" value="Google" />
|
|
133
|
-
<option name="name" value="Pixel 8 Pro" />
|
|
134
|
-
<option name="screenDensity" value="390" />
|
|
135
|
-
<option name="screenX" value="1008" />
|
|
136
|
-
<option name="screenY" value="2244" />
|
|
137
|
-
</PersistentDeviceSelectionData>
|
|
138
|
-
<PersistentDeviceSelectionData>
|
|
139
|
-
<option name="api" value="30" />
|
|
140
|
-
<option name="brand" value="motorola" />
|
|
141
|
-
<option name="codename" value="java" />
|
|
142
|
-
<option name="id" value="java" />
|
|
143
|
-
<option name="manufacturer" value="Motorola" />
|
|
144
|
-
<option name="name" value="G20" />
|
|
145
|
-
<option name="screenDensity" value="280" />
|
|
146
|
-
<option name="screenX" value="720" />
|
|
147
|
-
<option name="screenY" value="1600" />
|
|
148
|
-
</PersistentDeviceSelectionData>
|
|
149
|
-
<PersistentDeviceSelectionData>
|
|
150
|
-
<option name="api" value="33" />
|
|
151
|
-
<option name="brand" value="google" />
|
|
152
|
-
<option name="codename" value="lynx" />
|
|
153
|
-
<option name="id" value="lynx" />
|
|
154
|
-
<option name="manufacturer" value="Google" />
|
|
155
|
-
<option name="name" value="Pixel 7a" />
|
|
156
|
-
<option name="screenDensity" value="420" />
|
|
157
|
-
<option name="screenX" value="1080" />
|
|
158
|
-
<option name="screenY" value="2400" />
|
|
159
|
-
</PersistentDeviceSelectionData>
|
|
160
|
-
<PersistentDeviceSelectionData>
|
|
161
|
-
<option name="api" value="31" />
|
|
162
|
-
<option name="brand" value="google" />
|
|
163
|
-
<option name="codename" value="oriole" />
|
|
164
|
-
<option name="id" value="oriole" />
|
|
165
|
-
<option name="manufacturer" value="Google" />
|
|
166
|
-
<option name="name" value="Pixel 6" />
|
|
167
|
-
<option name="screenDensity" value="420" />
|
|
168
|
-
<option name="screenX" value="1080" />
|
|
169
|
-
<option name="screenY" value="2400" />
|
|
170
|
-
</PersistentDeviceSelectionData>
|
|
171
|
-
<PersistentDeviceSelectionData>
|
|
172
|
-
<option name="api" value="33" />
|
|
173
|
-
<option name="brand" value="google" />
|
|
174
|
-
<option name="codename" value="panther" />
|
|
175
|
-
<option name="id" value="panther" />
|
|
176
|
-
<option name="manufacturer" value="Google" />
|
|
177
|
-
<option name="name" value="Pixel 7" />
|
|
178
|
-
<option name="screenDensity" value="420" />
|
|
179
|
-
<option name="screenX" value="1080" />
|
|
180
|
-
<option name="screenY" value="2400" />
|
|
181
|
-
</PersistentDeviceSelectionData>
|
|
182
|
-
<PersistentDeviceSelectionData>
|
|
183
|
-
<option name="api" value="31" />
|
|
184
|
-
<option name="brand" value="samsung" />
|
|
185
|
-
<option name="codename" value="q2q" />
|
|
186
|
-
<option name="id" value="q2q" />
|
|
187
|
-
<option name="manufacturer" value="Samsung" />
|
|
188
|
-
<option name="name" value="Galaxy Z Fold3" />
|
|
189
|
-
<option name="screenDensity" value="420" />
|
|
190
|
-
<option name="screenX" value="1768" />
|
|
191
|
-
<option name="screenY" value="2208" />
|
|
192
|
-
</PersistentDeviceSelectionData>
|
|
193
|
-
<PersistentDeviceSelectionData>
|
|
194
|
-
<option name="api" value="34" />
|
|
195
|
-
<option name="brand" value="samsung" />
|
|
196
|
-
<option name="codename" value="q5q" />
|
|
197
|
-
<option name="id" value="q5q" />
|
|
198
|
-
<option name="manufacturer" value="Samsung" />
|
|
199
|
-
<option name="name" value="Galaxy Z Fold5" />
|
|
200
|
-
<option name="screenDensity" value="420" />
|
|
201
|
-
<option name="screenX" value="1812" />
|
|
202
|
-
<option name="screenY" value="2176" />
|
|
203
|
-
</PersistentDeviceSelectionData>
|
|
204
|
-
<PersistentDeviceSelectionData>
|
|
205
|
-
<option name="api" value="30" />
|
|
206
|
-
<option name="brand" value="google" />
|
|
207
|
-
<option name="codename" value="r11" />
|
|
208
|
-
<option name="id" value="r11" />
|
|
209
|
-
<option name="manufacturer" value="Google" />
|
|
210
|
-
<option name="name" value="Pixel Watch" />
|
|
211
|
-
<option name="screenDensity" value="320" />
|
|
212
|
-
<option name="screenX" value="384" />
|
|
213
|
-
<option name="screenY" value="384" />
|
|
214
|
-
<option name="type" value="WEAR_OS" />
|
|
215
|
-
</PersistentDeviceSelectionData>
|
|
216
|
-
<PersistentDeviceSelectionData>
|
|
217
|
-
<option name="api" value="30" />
|
|
218
|
-
<option name="brand" value="google" />
|
|
219
|
-
<option name="codename" value="redfin" />
|
|
220
|
-
<option name="id" value="redfin" />
|
|
221
|
-
<option name="manufacturer" value="Google" />
|
|
222
|
-
<option name="name" value="Pixel 5" />
|
|
223
|
-
<option name="screenDensity" value="440" />
|
|
224
|
-
<option name="screenX" value="1080" />
|
|
225
|
-
<option name="screenY" value="2340" />
|
|
226
|
-
</PersistentDeviceSelectionData>
|
|
227
|
-
<PersistentDeviceSelectionData>
|
|
228
|
-
<option name="api" value="34" />
|
|
229
|
-
<option name="brand" value="google" />
|
|
230
|
-
<option name="codename" value="shiba" />
|
|
231
|
-
<option name="id" value="shiba" />
|
|
232
|
-
<option name="manufacturer" value="Google" />
|
|
233
|
-
<option name="name" value="Pixel 8" />
|
|
234
|
-
<option name="screenDensity" value="420" />
|
|
235
|
-
<option name="screenX" value="1080" />
|
|
236
|
-
<option name="screenY" value="2400" />
|
|
237
|
-
</PersistentDeviceSelectionData>
|
|
238
|
-
<PersistentDeviceSelectionData>
|
|
239
|
-
<option name="api" value="33" />
|
|
240
|
-
<option name="brand" value="google" />
|
|
241
|
-
<option name="codename" value="tangorpro" />
|
|
242
|
-
<option name="id" value="tangorpro" />
|
|
243
|
-
<option name="manufacturer" value="Google" />
|
|
244
|
-
<option name="name" value="Pixel Tablet" />
|
|
245
|
-
<option name="screenDensity" value="320" />
|
|
246
|
-
<option name="screenX" value="1600" />
|
|
247
|
-
<option name="screenY" value="2560" />
|
|
248
|
-
</PersistentDeviceSelectionData>
|
|
249
|
-
<PersistentDeviceSelectionData>
|
|
250
|
-
<option name="api" value="29" />
|
|
251
|
-
<option name="brand" value="samsung" />
|
|
252
|
-
<option name="codename" value="x1q" />
|
|
253
|
-
<option name="id" value="x1q" />
|
|
254
|
-
<option name="manufacturer" value="Samsung" />
|
|
255
|
-
<option name="name" value="Galaxy S20" />
|
|
256
|
-
<option name="screenDensity" value="480" />
|
|
257
|
-
<option name="screenX" value="1440" />
|
|
258
|
-
<option name="screenY" value="3200" />
|
|
259
|
-
</PersistentDeviceSelectionData>
|
|
260
|
-
</list>
|
|
261
|
-
</option>
|
|
262
|
-
</component>
|
|
263
|
-
</project>
|
app/build.gradle.kts
CHANGED
|
@@ -85,4 +85,6 @@ dependencies {
|
|
|
85
85
|
implementation(libs.client.sdk)
|
|
86
86
|
implementation(libs.kotlinx.coroutines.core)
|
|
87
87
|
implementation(libs.androidx.datastore.preferences)
|
|
88
|
+
implementation(libs.androidx.core.splashscreen)
|
|
89
|
+
implementation(libs.compose.remember.preference)
|
|
88
90
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/{Drawer.kt → AppDrawer.kt}
RENAMED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
package dev.pyrossh.onlyBible
|
|
2
2
|
|
|
3
|
-
import Verse
|
|
4
3
|
import androidx.compose.foundation.layout.Arrangement
|
|
5
4
|
import androidx.compose.foundation.layout.Column
|
|
6
5
|
import androidx.compose.foundation.layout.PaddingValues
|
|
@@ -34,13 +33,13 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|
|
34
33
|
import androidx.compose.runtime.setValue
|
|
35
34
|
import androidx.compose.ui.Alignment
|
|
36
35
|
import androidx.compose.ui.Modifier
|
|
36
|
+
import androidx.compose.ui.platform.LocalContext
|
|
37
37
|
import androidx.compose.ui.text.TextStyle
|
|
38
38
|
import androidx.compose.ui.text.font.FontWeight
|
|
39
39
|
import androidx.compose.ui.unit.dp
|
|
40
40
|
import androidx.compose.ui.unit.sp
|
|
41
41
|
import androidx.navigation.NavController
|
|
42
42
|
import kotlinx.coroutines.Job
|
|
43
|
-
import kotlinx.coroutines.delay
|
|
44
43
|
import kotlinx.coroutines.launch
|
|
45
44
|
|
|
46
45
|
enum class MenuType {
|
|
@@ -62,12 +61,27 @@ fun LazyGridScope.header(
|
|
|
62
61
|
item(span = { GridItemSpan(this.maxLineSpan) }, content = content)
|
|
63
62
|
}
|
|
64
63
|
|
|
64
|
+
val bibles = listOf(
|
|
65
|
+
"Bengali",
|
|
66
|
+
"English",
|
|
67
|
+
"Gujarati",
|
|
68
|
+
"Hindi",
|
|
69
|
+
"Kannada",
|
|
70
|
+
"Malayalam",
|
|
71
|
+
"Nepali",
|
|
72
|
+
"Oriya",
|
|
73
|
+
"Punjabi",
|
|
74
|
+
"Tamil",
|
|
75
|
+
"Telugu"
|
|
76
|
+
)
|
|
77
|
+
|
|
65
78
|
@Composable
|
|
66
|
-
fun
|
|
79
|
+
fun AppDrawer(
|
|
67
80
|
bookNames: List<String>,
|
|
68
81
|
navController: NavController,
|
|
69
82
|
content: @Composable ((MenuType, Int) -> Job) -> Unit
|
|
70
83
|
) {
|
|
84
|
+
val context = LocalContext.current
|
|
71
85
|
val state = LocalSettings.current!!
|
|
72
86
|
val scope = rememberCoroutineScope()
|
|
73
87
|
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
|
@@ -125,14 +139,11 @@ fun Drawer(
|
|
|
125
139
|
}
|
|
126
140
|
}
|
|
127
141
|
}
|
|
128
|
-
items(
|
|
142
|
+
items(bibles) { b ->
|
|
129
143
|
QuickButton(b) {
|
|
144
|
+
state.setBibleName(context, b)
|
|
130
145
|
scope.launch {
|
|
131
146
|
drawerState.close()
|
|
132
|
-
state.isLoading = true
|
|
133
|
-
delay(500L)
|
|
134
|
-
}.invokeOnCompletion {
|
|
135
|
-
state.setBibleName(b)
|
|
136
147
|
}
|
|
137
148
|
}
|
|
138
149
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/AppHost.kt
CHANGED
|
@@ -1,63 +1,97 @@
|
|
|
1
1
|
package dev.pyrossh.onlyBible
|
|
2
2
|
|
|
3
|
-
import Verse
|
|
4
3
|
import androidx.compose.animation.AnimatedContentTransitionScope
|
|
5
4
|
import androidx.compose.animation.core.tween
|
|
5
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
6
|
+
import androidx.compose.foundation.layout.Box
|
|
7
|
+
import androidx.compose.foundation.layout.Column
|
|
8
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
9
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
10
|
+
import androidx.compose.foundation.layout.wrapContentHeight
|
|
11
|
+
import androidx.compose.material3.CircularProgressIndicator
|
|
6
12
|
import androidx.compose.runtime.Composable
|
|
13
|
+
import androidx.compose.runtime.SideEffect
|
|
14
|
+
import androidx.compose.runtime.getValue
|
|
15
|
+
import androidx.compose.runtime.setValue
|
|
16
|
+
import androidx.compose.ui.Alignment
|
|
17
|
+
import androidx.compose.ui.Modifier
|
|
7
18
|
import androidx.navigation.compose.NavHost
|
|
8
19
|
import androidx.navigation.compose.composable
|
|
9
20
|
import androidx.navigation.compose.rememberNavController
|
|
10
21
|
import androidx.navigation.toRoute
|
|
22
|
+
import dev.burnoo.compose.rememberpreference.rememberIntPreference
|
|
11
23
|
|
|
12
24
|
@Composable
|
|
13
|
-
fun AppHost(
|
|
25
|
+
fun AppHost() {
|
|
14
26
|
val navController = rememberNavController()
|
|
27
|
+
var bookIndex by rememberIntPreference(keyName = "bookIndex", initialValue = 0, defaultValue = 0)
|
|
28
|
+
var chapterIndex by rememberIntPreference(keyName = "chapterIndex", initialValue = 0, defaultValue = 0)
|
|
15
|
-
val
|
|
29
|
+
val model = LocalSettings.current!!
|
|
16
|
-
val bookNames = verses.distinctBy { it.bookName }.map { it.bookName }
|
|
17
|
-
|
|
30
|
+
if (model.uiState.value.isLoading) {
|
|
18
|
-
|
|
31
|
+
Box(
|
|
19
|
-
|
|
32
|
+
modifier = Modifier
|
|
33
|
+
.fillMaxWidth()
|
|
20
|
-
|
|
34
|
+
.wrapContentHeight()
|
|
21
35
|
) {
|
|
36
|
+
Column(
|
|
37
|
+
modifier = Modifier.fillMaxSize(),
|
|
38
|
+
verticalArrangement = Arrangement.Center,
|
|
39
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
40
|
+
) {
|
|
41
|
+
CircularProgressIndicator()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
val bookNames = model.uiState.value.verses.distinctBy { it.bookName }.map { it.bookName }
|
|
46
|
+
AppDrawer(bookNames, navController) { openDrawer ->
|
|
47
|
+
NavHost(
|
|
48
|
+
navController = navController,
|
|
49
|
+
startDestination = ChapterScreenProps(bookIndex, chapterIndex)
|
|
50
|
+
) {
|
|
22
|
-
|
|
51
|
+
composable<ChapterScreenProps>(
|
|
23
|
-
|
|
52
|
+
enterTransition = {
|
|
24
|
-
|
|
53
|
+
val props = this.targetState.toRoute<ChapterScreenProps>()
|
|
25
|
-
|
|
54
|
+
slideIntoContainer(
|
|
26
|
-
|
|
55
|
+
Dir.valueOf(props.dir).slideDirection(),
|
|
27
|
-
|
|
56
|
+
tween(400),
|
|
28
|
-
|
|
57
|
+
)
|
|
29
|
-
|
|
58
|
+
},
|
|
30
|
-
//
|
|
59
|
+
// exitTransition = {
|
|
60
|
+
// val props = this.targetState.toRoute<ChapterScreenProps>()
|
|
31
|
-
//
|
|
61
|
+
// slideOutOfContainer(
|
|
32
|
-
//
|
|
62
|
+
// Dir.valueOf(props.dir).slideDirection(),
|
|
33
|
-
//
|
|
63
|
+
// tween(400),
|
|
64
|
+
// )
|
|
34
|
-
//
|
|
65
|
+
// },
|
|
35
|
-
// },
|
|
36
|
-
|
|
66
|
+
popEnterTransition = {
|
|
37
|
-
|
|
67
|
+
slideIntoContainer(
|
|
38
|
-
|
|
68
|
+
AnimatedContentTransitionScope.SlideDirection.Right,
|
|
39
|
-
|
|
69
|
+
tween(400),
|
|
40
|
-
|
|
70
|
+
)
|
|
41
|
-
|
|
71
|
+
},
|
|
42
|
-
|
|
72
|
+
popExitTransition = {
|
|
43
|
-
|
|
73
|
+
slideOutOfContainer(
|
|
44
|
-
|
|
74
|
+
AnimatedContentTransitionScope.SlideDirection.Right,
|
|
45
|
-
|
|
75
|
+
tween(400),
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
) {
|
|
79
|
+
val props = it.toRoute<ChapterScreenProps>()
|
|
80
|
+
SideEffect {
|
|
81
|
+
bookIndex = props.bookIndex
|
|
82
|
+
chapterIndex = props.chapterIndex
|
|
83
|
+
}
|
|
84
|
+
ChapterScreen(
|
|
85
|
+
bookNames = bookNames,
|
|
86
|
+
verses = model.uiState.value.verses,
|
|
87
|
+
bookIndex = props.bookIndex,
|
|
88
|
+
chapterIndex = props.chapterIndex,
|
|
89
|
+
navController = navController,
|
|
90
|
+
openDrawer = openDrawer,
|
|
46
91
|
)
|
|
47
92
|
}
|
|
48
|
-
) {
|
|
49
|
-
val props = it.toRoute<ChapterScreenProps>()
|
|
50
|
-
state.setBookIndex(props.bookIndex)
|
|
51
|
-
state.setChapterIndex(props.chapterIndex)
|
|
52
|
-
ChapterScreen(
|
|
53
|
-
bookNames = bookNames,
|
|
54
|
-
verses = verses,
|
|
55
|
-
bookIndex = props.bookIndex,
|
|
56
|
-
chapterIndex = props.chapterIndex,
|
|
57
|
-
navController = navController,
|
|
58
|
-
openDrawer = openDrawer,
|
|
59
|
-
)
|
|
60
93
|
}
|
|
61
94
|
}
|
|
62
95
|
}
|
|
96
|
+
|
|
63
97
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/AppSettings.kt
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
package dev.pyrossh.onlyBible
|
|
2
|
-
|
|
3
|
-
import android.content.SharedPreferences
|
|
4
|
-
import androidx.compose.foundation.isSystemInDarkTheme
|
|
5
|
-
import androidx.compose.runtime.Composable
|
|
6
|
-
import androidx.compose.runtime.ReadOnlyComposable
|
|
7
|
-
import androidx.compose.runtime.getValue
|
|
8
|
-
import androidx.compose.runtime.mutableIntStateOf
|
|
9
|
-
import androidx.compose.runtime.mutableStateOf
|
|
10
|
-
import androidx.compose.runtime.setValue
|
|
11
|
-
import androidx.compose.runtime.staticCompositionLocalOf
|
|
12
|
-
import androidx.lifecycle.ViewModel
|
|
13
|
-
import java.util.Locale
|
|
14
|
-
|
|
15
|
-
val LocalSettings = staticCompositionLocalOf<State?> { null }
|
|
16
|
-
|
|
17
|
-
class State(p: SharedPreferences, val bibles: List<String>, val reload: () -> Unit) : ViewModel() {
|
|
18
|
-
private val prefs: SharedPreferences = p
|
|
19
|
-
var isLoading by mutableStateOf(false)
|
|
20
|
-
var showBottomSheet by mutableStateOf(false)
|
|
21
|
-
var fontType by mutableStateOf(
|
|
22
|
-
FontType.valueOf(
|
|
23
|
-
prefs.getString("fontType", FontType.Sans.name) ?: FontType.Sans.name
|
|
24
|
-
)
|
|
25
|
-
)
|
|
26
|
-
var fontSizeDelta by mutableIntStateOf(prefs.getInt("fontSizeDelta", 0))
|
|
27
|
-
var boldEnabled by mutableStateOf(prefs.getBoolean("bold", false))
|
|
28
|
-
var themeType by mutableStateOf(
|
|
29
|
-
ThemeType.valueOf(
|
|
30
|
-
prefs.getString(
|
|
31
|
-
"themeType",
|
|
32
|
-
ThemeType.Auto.name
|
|
33
|
-
) ?: ThemeType.Auto.name
|
|
34
|
-
)
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
fun updateTheme(v: ThemeType) {
|
|
38
|
-
val editor = prefs.edit()
|
|
39
|
-
editor.putString("themeType", v.name)
|
|
40
|
-
editor.apply()
|
|
41
|
-
reload()
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
fun getBibleName(): String {
|
|
45
|
-
val defValue = Locale.getDefault().displayLanguage
|
|
46
|
-
return prefs.getString("bibleName", defValue) ?: defValue
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
fun setBibleName(v: String) {
|
|
50
|
-
val editor = prefs.edit()
|
|
51
|
-
editor.putString("bibleName", v)
|
|
52
|
-
editor.apply()
|
|
53
|
-
reload()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
fun getBookIndex(): Int {
|
|
57
|
-
return prefs.getInt("bookIndex", 0)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
fun setBookIndex(v: Int) {
|
|
61
|
-
val editor = prefs.edit()
|
|
62
|
-
editor.putInt("bookIndex", v)
|
|
63
|
-
editor.apply()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
fun getChapterIndex(): Int {
|
|
67
|
-
return prefs.getInt("chapterIndex", 0)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
fun setChapterIndex(v: Int) {
|
|
71
|
-
val editor = prefs.edit()
|
|
72
|
-
editor.putInt("chapterIndex", v)
|
|
73
|
-
editor.apply()
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
fun showSheet() {
|
|
77
|
-
showBottomSheet = true
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
fun closeSheet() {
|
|
81
|
-
showBottomSheet = false
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
fun updateFontSize(v: Int) {
|
|
85
|
-
fontSizeDelta = v
|
|
86
|
-
val editor = prefs.edit()
|
|
87
|
-
editor.putInt("fontSizeDelta", v)
|
|
88
|
-
editor.apply()
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
fun updateBoldEnabled(v: Boolean) {
|
|
92
|
-
boldEnabled = v
|
|
93
|
-
val editor = prefs.edit()
|
|
94
|
-
editor.putBoolean("bold", v)
|
|
95
|
-
editor.apply()
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
fun updateFontType(v: FontType) {
|
|
99
|
-
fontType = v
|
|
100
|
-
val editor = prefs.edit()
|
|
101
|
-
editor.putString("fontType", v.name)
|
|
102
|
-
editor.apply()
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
@Composable
|
|
107
|
-
@ReadOnlyComposable
|
|
108
|
-
fun isDarkMode(): Boolean {
|
|
109
|
-
return LocalSettings.current!!.themeType == ThemeType.Dark || (LocalSettings.current!!.themeType == ThemeType.Auto && isSystemInDarkTheme())
|
|
110
|
-
}
|
app/src/main/java/dev/pyrossh/onlyBible/AppTheme.kt
CHANGED
|
@@ -1,27 +1,17 @@
|
|
|
1
1
|
package dev.pyrossh.onlyBible
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import android.content.Context
|
|
4
4
|
import androidx.compose.foundation.isSystemInDarkTheme
|
|
5
|
-
import androidx.compose.foundation.layout.Arrangement
|
|
6
|
-
import androidx.compose.foundation.layout.Column
|
|
7
|
-
import androidx.compose.foundation.layout.padding
|
|
8
|
-
import androidx.compose.material3.
|
|
5
|
+
import androidx.compose.material3.ColorScheme
|
|
9
6
|
import androidx.compose.material3.MaterialTheme
|
|
10
|
-
import androidx.compose.material3.Text
|
|
11
7
|
import androidx.compose.material3.dynamicDarkColorScheme
|
|
12
8
|
import androidx.compose.material3.dynamicLightColorScheme
|
|
13
9
|
import androidx.compose.runtime.Composable
|
|
14
|
-
import androidx.compose.ui.Alignment
|
|
15
|
-
import androidx.compose.
|
|
10
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
16
11
|
import androidx.compose.ui.graphics.Color
|
|
17
12
|
import androidx.compose.ui.platform.LocalContext
|
|
18
|
-
import androidx.compose.ui.res.painterResource
|
|
19
|
-
import androidx.compose.ui.text.TextStyle
|
|
20
13
|
import androidx.compose.ui.text.font.FontFamily
|
|
21
|
-
import androidx.compose.ui.text.font.FontWeight
|
|
22
14
|
import androidx.compose.ui.text.font.GenericFontFamily
|
|
23
|
-
import androidx.compose.ui.unit.dp
|
|
24
|
-
import androidx.compose.ui.unit.sp
|
|
25
15
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
|
26
16
|
|
|
27
17
|
enum class FontType {
|
|
@@ -42,63 +32,22 @@ enum class ThemeType {
|
|
|
42
32
|
Light,
|
|
43
33
|
Dark,
|
|
44
34
|
Auto;
|
|
35
|
+
}
|
|
45
36
|
|
|
46
|
-
|
|
37
|
+
fun getColorScheme(context: Context, themeType: ThemeType, darkTheme: Boolean): ColorScheme {
|
|
47
|
-
|
|
38
|
+
return when {
|
|
48
|
-
|
|
39
|
+
themeType == ThemeType.Light || (themeType == ThemeType.Auto && !darkTheme) ->
|
|
49
|
-
else -> if (isDark) Color.Unspecified else Color(0xFF3E4042)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
40
|
+
dynamicLightColorScheme(context).copy(
|
|
54
|
-
return when {
|
|
55
|
-
this == Light || (this == Auto && !isDark) -> Color(0xFF424547)
|
|
56
|
-
|
|
41
|
+
outline = Color.LightGray,
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
42
|
|
|
60
|
-
@Composable
|
|
61
|
-
fun ThemeIcon(currentTheme: ThemeType) {
|
|
62
|
-
val name = this.name;
|
|
63
|
-
val darkTheme = isSystemInDarkTheme()
|
|
64
|
-
when (this) {
|
|
65
|
-
Light -> Icon(
|
|
66
|
-
painter = painterResource(id = R.drawable.text_theme),
|
|
67
|
-
contentDescription = "Light",
|
|
68
|
-
tint = this.tint(darkTheme),
|
|
69
|
-
modifier = Modifier
|
|
70
|
-
.background(Color.White)
|
|
71
|
-
.padding(4.dp)
|
|
72
|
-
|
|
43
|
+
)
|
|
73
44
|
|
|
74
|
-
|
|
45
|
+
else ->
|
|
75
|
-
painter = painterResource(id = R.drawable.text_theme),
|
|
76
|
-
|
|
46
|
+
dynamicDarkColorScheme(context).copy(
|
|
77
|
-
|
|
47
|
+
background = Color(0xFF090F12),
|
|
78
|
-
modifier = Modifier
|
|
79
|
-
.background(
|
|
80
|
-
if (darkTheme && currentTheme == Dark) MaterialTheme.colorScheme.background
|
|
81
|
-
|
|
48
|
+
surface = Color(0xFF090F12),
|
|
82
|
-
)
|
|
83
|
-
|
|
49
|
+
outline = Color(0xAA5D4979),
|
|
84
50
|
)
|
|
85
|
-
|
|
86
|
-
Auto -> Column(
|
|
87
|
-
modifier = Modifier.background(
|
|
88
|
-
color = MaterialTheme.colorScheme.background,
|
|
89
|
-
),
|
|
90
|
-
verticalArrangement = Arrangement.Center,
|
|
91
|
-
horizontalAlignment = Alignment.CenterHorizontally
|
|
92
|
-
) {
|
|
93
|
-
Text(
|
|
94
|
-
text = name,
|
|
95
|
-
style = TextStyle(
|
|
96
|
-
fontSize = 18.sp,
|
|
97
|
-
fontWeight = FontWeight.Medium,
|
|
98
|
-
),
|
|
99
|
-
)
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
51
|
}
|
|
103
52
|
}
|
|
104
53
|
|
|
@@ -107,26 +56,15 @@ fun AppTheme(
|
|
|
107
56
|
content: @Composable() () -> Unit
|
|
108
57
|
) {
|
|
109
58
|
val context = LocalContext.current
|
|
110
|
-
val state = LocalSettings.current!!
|
|
111
59
|
val darkTheme = isSystemInDarkTheme()
|
|
112
60
|
val systemUiController = rememberSystemUiController()
|
|
61
|
+
val (themeType) = rememberThemeType()
|
|
62
|
+
val colorScheme = getColorScheme(context, themeType, darkTheme)
|
|
63
|
+
LaunchedEffect(key1 = themeType) {
|
|
64
|
+
systemUiController.setSystemBarsColor(
|
|
113
|
-
|
|
65
|
+
color = colorScheme.background
|
|
114
|
-
state.themeType == ThemeType.Light || (state.themeType == ThemeType.Auto && !darkTheme) ->
|
|
115
|
-
dynamicLightColorScheme(context).copy(
|
|
116
|
-
outline = Color.LightGray,
|
|
117
|
-
|
|
118
|
-
|
|
66
|
+
)
|
|
119
|
-
|
|
120
|
-
else ->
|
|
121
|
-
dynamicDarkColorScheme(context).copy(
|
|
122
|
-
background = Color(0xFF090F12),
|
|
123
|
-
surface = Color(0xFF090F12),
|
|
124
|
-
outline = Color(0xAA5D4979),
|
|
125
|
-
)
|
|
126
67
|
}
|
|
127
|
-
systemUiController.setSystemBarsColor(
|
|
128
|
-
color = colorScheme.background
|
|
129
|
-
)
|
|
130
68
|
MaterialTheme(
|
|
131
69
|
colorScheme = colorScheme,
|
|
132
70
|
content = content
|
app/src/main/java/dev/pyrossh/onlyBible/AppViewModel.kt
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
package dev.pyrossh.onlyBible
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.content.SharedPreferences
|
|
6
|
+
import androidx.compose.runtime.State
|
|
7
|
+
import androidx.compose.runtime.getValue
|
|
8
|
+
import androidx.compose.runtime.mutableStateOf
|
|
9
|
+
import androidx.compose.runtime.setValue
|
|
10
|
+
import androidx.compose.runtime.staticCompositionLocalOf
|
|
11
|
+
import androidx.lifecycle.ViewModel
|
|
12
|
+
import androidx.lifecycle.viewModelScope
|
|
13
|
+
import com.microsoft.cognitiveservices.speech.SpeechConfig
|
|
14
|
+
import com.microsoft.cognitiveservices.speech.SpeechSynthesizer
|
|
15
|
+
import kotlinx.coroutines.CoroutineScope
|
|
16
|
+
import kotlinx.coroutines.Dispatchers
|
|
17
|
+
import kotlinx.coroutines.delay
|
|
18
|
+
import kotlinx.coroutines.launch
|
|
19
|
+
import kotlinx.coroutines.withContext
|
|
20
|
+
import java.io.IOException
|
|
21
|
+
import java.util.concurrent.Future
|
|
22
|
+
|
|
23
|
+
val LocalSettings = staticCompositionLocalOf<AppViewModel?> { null }
|
|
24
|
+
|
|
25
|
+
data class UiState(
|
|
26
|
+
val isLoading: Boolean,
|
|
27
|
+
val isOnError: Boolean,
|
|
28
|
+
val bibleName: String,
|
|
29
|
+
val verses: List<Verse>,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
class AppViewModel() : ViewModel() {
|
|
33
|
+
|
|
34
|
+
private fun getPrefs(context: Context): SharedPreferences {
|
|
35
|
+
return context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private val _uiState = mutableStateOf(
|
|
39
|
+
UiState(
|
|
40
|
+
isLoading = true,
|
|
41
|
+
isOnError = false,
|
|
42
|
+
bibleName = "English",
|
|
43
|
+
verses = listOf(),
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
val uiState: State<UiState> = _uiState
|
|
47
|
+
var showBottomSheet by mutableStateOf(false)
|
|
48
|
+
|
|
49
|
+
fun showSheet() {
|
|
50
|
+
showBottomSheet = true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fun closeSheet() {
|
|
54
|
+
showBottomSheet = false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fun setBibleName(context: Context, b: String) {
|
|
58
|
+
_uiState.value = uiState.value.copy(
|
|
59
|
+
bibleName = b,
|
|
60
|
+
)
|
|
61
|
+
getPrefs(context).edit().putString("bibleName", b).apply()
|
|
62
|
+
loadBible(context)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
fun loadBible(context: Context) {
|
|
67
|
+
viewModelScope.launch(Dispatchers.IO) {
|
|
68
|
+
launch(Dispatchers.Main) {
|
|
69
|
+
_uiState.value = uiState.value.copy(
|
|
70
|
+
isLoading = true,
|
|
71
|
+
isOnError = false,
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
val buffer =
|
|
76
|
+
context.assets.open("bibles/${_uiState.value.bibleName}.txt").bufferedReader()
|
|
77
|
+
val verses = buffer.readLines().filter { it.isNotEmpty() }.map {
|
|
78
|
+
val arr = it.split("|")
|
|
79
|
+
val bookName = arr[0]
|
|
80
|
+
val book = arr[1].toInt()
|
|
81
|
+
val chapter = arr[2].toInt()
|
|
82
|
+
val verseNo = arr[3].toInt()
|
|
83
|
+
val heading = arr[4]
|
|
84
|
+
val verseText = arr.subList(5, arr.size).joinToString("|")
|
|
85
|
+
Verse(
|
|
86
|
+
bookIndex = book,
|
|
87
|
+
bookName = bookName,
|
|
88
|
+
chapterIndex = chapter,
|
|
89
|
+
verseIndex = verseNo,
|
|
90
|
+
heading = heading,
|
|
91
|
+
text = verseText,
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
launch(Dispatchers.Main) {
|
|
95
|
+
_uiState.value = uiState.value.copy(
|
|
96
|
+
isLoading = false,
|
|
97
|
+
isOnError = false,
|
|
98
|
+
verses = verses
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
} catch (e: IOException) {
|
|
102
|
+
e.printStackTrace()
|
|
103
|
+
launch(Dispatchers.Main) {
|
|
104
|
+
_uiState.value = uiState.value.copy(
|
|
105
|
+
isLoading = false,
|
|
106
|
+
isOnError = true,
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
val speechService = SpeechSynthesizer(
|
|
115
|
+
SpeechConfig.fromSubscription(
|
|
116
|
+
BuildConfig.subscriptionKey,
|
|
117
|
+
"centralindia"
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
suspend fun <T> Future<T>.wait(timeoutMs: Int = 30000): T? {
|
|
122
|
+
val start = System.currentTimeMillis()
|
|
123
|
+
while (!isDone) {
|
|
124
|
+
if (System.currentTimeMillis() - start > timeoutMs)
|
|
125
|
+
return null
|
|
126
|
+
delay(500)
|
|
127
|
+
}
|
|
128
|
+
return withContext(Dispatchers.IO) {
|
|
129
|
+
get()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
suspend fun convertVersesToSpeech(scope: CoroutineScope, verses: List<Verse>) {
|
|
134
|
+
withContext(Dispatchers.IO) {
|
|
135
|
+
for (v in verses) {
|
|
136
|
+
speechService.SpeakSsmlAsync(
|
|
137
|
+
"""
|
|
138
|
+
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
|
|
139
|
+
<voice name="en-US-AvaMultilingualNeural">
|
|
140
|
+
${v.text}
|
|
141
|
+
</voice>
|
|
142
|
+
</speak>
|
|
143
|
+
""".trimIndent()
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
fun stopVerses(scope: CoroutineScope, verses: List<Verse>) {
|
|
150
|
+
//TODOD
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fun shareVerses(context: Context, verses: List<Verse>) {
|
|
154
|
+
val items = verses.sortedBy { it.verseIndex }
|
|
155
|
+
val versesThrough =
|
|
156
|
+
if (items.size >= 3) "${items.first().verseIndex + 1}-${items.last().verseIndex + 1}" else items.map { it.verseIndex + 1 }
|
|
157
|
+
.joinToString(",");
|
|
158
|
+
val title = "${verses[0].bookName} ${verses[0].chapterIndex + 1}:${versesThrough}"
|
|
159
|
+
val text = verses.joinToString("\n") {
|
|
160
|
+
it.text.replace("<span style=\"color:red;\">", "")
|
|
161
|
+
.replace("<em>", "")
|
|
162
|
+
.replace("</span>", "")
|
|
163
|
+
.replace("</em>", "")
|
|
164
|
+
};
|
|
165
|
+
val sendIntent = Intent().apply {
|
|
166
|
+
action = Intent.ACTION_SEND
|
|
167
|
+
putExtra(Intent.EXTRA_TEXT, "${title}\n${text}")
|
|
168
|
+
type = "text/plain"
|
|
169
|
+
}
|
|
170
|
+
val shareIntent = Intent.createChooser(sendIntent, null)
|
|
171
|
+
context.startActivity(shareIntent)
|
|
172
|
+
}
|
app/src/main/java/dev/pyrossh/onlyBible/ChapterScreen.kt
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
package dev.pyrossh.onlyBible
|
|
2
2
|
|
|
3
|
-
import Verse
|
|
4
3
|
import android.annotation.SuppressLint
|
|
5
4
|
import android.graphics.Typeface
|
|
6
5
|
import android.os.Parcelable
|
|
@@ -53,12 +52,10 @@ import androidx.compose.ui.text.withStyle
|
|
|
53
52
|
import androidx.compose.ui.unit.dp
|
|
54
53
|
import androidx.compose.ui.unit.sp
|
|
55
54
|
import androidx.navigation.NavController
|
|
56
|
-
import convertVersesToSpeech
|
|
57
55
|
import kotlinx.coroutines.Job
|
|
58
56
|
import kotlinx.coroutines.launch
|
|
59
57
|
import kotlinx.parcelize.Parcelize
|
|
60
58
|
import kotlinx.serialization.Serializable
|
|
61
|
-
import shareVerses
|
|
62
59
|
|
|
63
60
|
@Serializable
|
|
64
61
|
@Parcelize
|
|
@@ -79,6 +76,10 @@ enum class Dir : Parcelable {
|
|
|
79
76
|
Right -> AnimatedContentTransitionScope.SlideDirection.Right
|
|
80
77
|
}
|
|
81
78
|
}
|
|
79
|
+
|
|
80
|
+
fun reverse(): Dir {
|
|
81
|
+
return if (this == Left) Right else Left
|
|
82
|
+
}
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
@@ -95,8 +96,9 @@ fun ChapterScreen(
|
|
|
95
96
|
val context = LocalContext.current
|
|
96
97
|
val state = LocalSettings.current!!
|
|
97
98
|
val darkTheme = isDarkMode()
|
|
98
|
-
val
|
|
99
|
+
val (fontType) = rememberFontType()
|
|
100
|
+
val fontSizeDelta = 0 // state.fontSizeDelta
|
|
99
|
-
val boldWeight = if (state.boldEnabled) FontWeight.W700 else FontWeight.W400
|
|
101
|
+
val boldWeight = FontWeight.W400 //if (state.boldEnabled) FontWeight.W700 else FontWeight.W400
|
|
100
102
|
val scope = rememberCoroutineScope()
|
|
101
103
|
var selectedVerses by rememberSaveable {
|
|
102
104
|
mutableStateOf(listOf<Verse>())
|
|
@@ -106,86 +108,89 @@ fun ChapterScreen(
|
|
|
106
108
|
}
|
|
107
109
|
val chapterVerses =
|
|
108
110
|
verses.filter { it.bookIndex == bookIndex && it.chapterIndex == chapterIndex }
|
|
109
|
-
|
|
111
|
+
val headingColor = MaterialTheme.colorScheme.onSurface // MaterialTheme.colorScheme.primary,
|
|
110
|
-
|
|
112
|
+
Scaffold(
|
|
111
|
-
|
|
113
|
+
modifier = Modifier
|
|
112
|
-
|
|
114
|
+
.fillMaxSize(),
|
|
113
|
-
|
|
115
|
+
topBar = {
|
|
114
|
-
|
|
116
|
+
TopAppBar(
|
|
115
|
-
|
|
117
|
+
modifier = Modifier
|
|
116
|
-
|
|
118
|
+
.height(72.dp),
|
|
117
|
-
|
|
119
|
+
title = {
|
|
118
|
-
|
|
120
|
+
Row(
|
|
119
|
-
|
|
121
|
+
modifier = Modifier
|
|
120
|
-
|
|
122
|
+
.fillMaxWidth(),
|
|
121
|
-
|
|
123
|
+
horizontalArrangement = Arrangement.Start,
|
|
122
|
-
|
|
124
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
123
|
-
|
|
125
|
+
) {
|
|
126
|
+
Text(
|
|
127
|
+
modifier = Modifier.clickable {
|
|
128
|
+
openDrawer(MenuType.Book, bookIndex)
|
|
129
|
+
},
|
|
130
|
+
text = bookNames[bookIndex],
|
|
131
|
+
style = TextStyle(
|
|
132
|
+
fontSize = 22.sp,
|
|
133
|
+
fontWeight = FontWeight.W500,
|
|
134
|
+
color = headingColor,
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
TextButton(onClick = { openDrawer(MenuType.Chapter, bookIndex) }) {
|
|
124
138
|
Text(
|
|
125
|
-
modifier = Modifier.clickable {
|
|
126
|
-
openDrawer(MenuType.Book, bookIndex)
|
|
127
|
-
},
|
|
128
|
-
text =
|
|
139
|
+
text = "${chapterIndex + 1}",
|
|
129
140
|
style = TextStyle(
|
|
130
141
|
fontSize = 22.sp,
|
|
131
142
|
fontWeight = FontWeight.W500,
|
|
132
|
-
color =
|
|
143
|
+
color = headingColor,
|
|
133
144
|
)
|
|
134
145
|
)
|
|
135
|
-
TextButton(onClick = { openDrawer(MenuType.Chapter, bookIndex) }) {
|
|
136
|
-
Text(
|
|
137
|
-
text = "${chapterIndex + 1}",
|
|
138
|
-
style = TextStyle(
|
|
139
|
-
fontSize = 22.sp,
|
|
140
|
-
fontWeight = FontWeight.W500,
|
|
141
|
-
)
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
146
|
}
|
|
147
|
+
}
|
|
145
|
-
|
|
148
|
+
},
|
|
146
|
-
|
|
149
|
+
actions = {
|
|
147
|
-
|
|
150
|
+
if (selectedVerses.isNotEmpty()) {
|
|
148
|
-
|
|
151
|
+
TextButton(onClick = {
|
|
149
|
-
|
|
152
|
+
scope.launch {
|
|
150
|
-
|
|
153
|
+
convertVersesToSpeech(
|
|
154
|
+
scope,
|
|
151
|
-
|
|
155
|
+
selectedVerses.sortedBy { it.verseIndex },
|
|
152
|
-
}.invokeOnCompletion {
|
|
153
|
-
selectedVerses = listOf()
|
|
154
|
-
}
|
|
155
|
-
}) {
|
|
156
|
-
Icon(
|
|
157
|
-
imageVector = Icons.Outlined.FaceRetouchingNatural,
|
|
158
|
-
contentDescription = "Audio",
|
|
159
156
|
)
|
|
160
|
-
}
|
|
161
|
-
|
|
157
|
+
}.invokeOnCompletion {
|
|
162
|
-
shareVerses(context, selectedVerses)
|
|
163
158
|
selectedVerses = listOf()
|
|
164
|
-
}) {
|
|
165
|
-
Icon(
|
|
166
|
-
imageVector = Icons.Outlined.Share,
|
|
167
|
-
contentDescription = "Share",
|
|
168
|
-
)
|
|
169
159
|
}
|
|
170
|
-
}
|
|
160
|
+
}) {
|
|
171
|
-
TextButton(onClick = { openDrawer(MenuType.Bible, bookIndex) }) {
|
|
172
|
-
|
|
161
|
+
Icon(
|
|
173
|
-
|
|
162
|
+
imageVector = Icons.Outlined.FaceRetouchingNatural,
|
|
174
|
-
style = TextStyle(
|
|
175
|
-
fontSize = 18.sp,
|
|
176
|
-
|
|
163
|
+
contentDescription = "Audio",
|
|
177
|
-
),
|
|
178
164
|
)
|
|
179
165
|
}
|
|
180
|
-
TextButton(
|
|
166
|
+
TextButton(onClick = {
|
|
181
|
-
|
|
167
|
+
shareVerses(context, selectedVerses)
|
|
182
|
-
|
|
168
|
+
selectedVerses = listOf()
|
|
183
|
-
|
|
169
|
+
}) {
|
|
170
|
+
Icon(
|
|
171
|
+
imageVector = Icons.Outlined.Share,
|
|
184
|
-
|
|
172
|
+
contentDescription = "Share",
|
|
173
|
+
)
|
|
185
174
|
}
|
|
175
|
+
}
|
|
176
|
+
TextButton(onClick = { openDrawer(MenuType.Bible, bookIndex) }) {
|
|
177
|
+
Text(
|
|
178
|
+
text = state.uiState.value.bibleName.substring(0, 2).uppercase(),
|
|
179
|
+
style = TextStyle(
|
|
180
|
+
fontSize = 18.sp,
|
|
181
|
+
fontWeight = FontWeight.W500,
|
|
182
|
+
),
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
TextButton(
|
|
186
|
+
onClick = {
|
|
187
|
+
state.showSheet()
|
|
188
|
+
}) {
|
|
189
|
+
Icon(Icons.Outlined.MoreVert, "More")
|
|
190
|
+
}
|
|
186
|
-
|
|
191
|
+
},
|
|
187
|
-
|
|
192
|
+
)
|
|
188
|
-
|
|
193
|
+
},
|
|
189
194
|
// bottomBar = {
|
|
190
195
|
// if (selectedVerses.isNotEmpty()) {
|
|
191
196
|
// BottomAppBar(
|
|
@@ -226,156 +231,156 @@ fun ChapterScreen(
|
|
|
226
231
|
// )
|
|
227
232
|
// }
|
|
228
233
|
// },
|
|
229
|
-
|
|
234
|
+
) { innerPadding ->
|
|
230
|
-
|
|
235
|
+
LazyColumn(
|
|
231
|
-
|
|
236
|
+
verticalArrangement = Arrangement.spacedBy(12.dp),
|
|
232
|
-
|
|
237
|
+
modifier = Modifier
|
|
233
|
-
|
|
238
|
+
.fillMaxSize()
|
|
234
|
-
|
|
239
|
+
.padding(innerPadding)
|
|
235
|
-
|
|
240
|
+
.padding(horizontal = 16.dp)
|
|
236
|
-
|
|
241
|
+
.pointerInput(Unit) {
|
|
237
|
-
|
|
242
|
+
detectHorizontalDragGestures(onDragEnd = {
|
|
238
243
|
// println("END " + dragAmount);
|
|
239
|
-
|
|
244
|
+
if (dragAmount < 0) {
|
|
240
|
-
|
|
245
|
+
val pair = Verse.getForwardPair(bookIndex, chapterIndex)
|
|
241
|
-
|
|
246
|
+
navController.navigate(
|
|
242
|
-
|
|
247
|
+
ChapterScreenProps(
|
|
243
|
-
|
|
248
|
+
bookIndex = pair.first,
|
|
244
|
-
|
|
249
|
+
chapterIndex = pair.second,
|
|
245
|
-
)
|
|
246
250
|
)
|
|
251
|
+
)
|
|
247
|
-
|
|
252
|
+
} else if (dragAmount > 0) {
|
|
248
|
-
|
|
253
|
+
val pair = Verse.getBackwardPair(bookIndex, chapterIndex)
|
|
249
|
-
|
|
254
|
+
if (navController.previousBackStackEntry != null) {
|
|
250
|
-
|
|
255
|
+
val previousBook =
|
|
251
|
-
|
|
256
|
+
navController.previousBackStackEntry?.arguments?.getInt("book")
|
|
252
|
-
|
|
257
|
+
?: 0
|
|
253
|
-
|
|
258
|
+
val previousChapter =
|
|
254
|
-
|
|
259
|
+
navController.previousBackStackEntry?.arguments?.getInt("chapter")
|
|
255
|
-
|
|
260
|
+
?: 0
|
|
256
261
|
// println("currentBackStackEntry ${previousBook} ${previousChapter} || ${pair.first} ${pair.second}")
|
|
257
|
-
|
|
262
|
+
if (previousBook == pair.first && previousChapter == pair.second) {
|
|
258
|
-
|
|
263
|
+
println("Popped")
|
|
259
|
-
|
|
264
|
+
navController.popBackStack()
|
|
260
|
-
} else {
|
|
261
|
-
navController.navigate(
|
|
262
|
-
ChapterScreenProps(
|
|
263
|
-
bookIndex = pair.first,
|
|
264
|
-
chapterIndex = pair.second,
|
|
265
|
-
dir = Dir.Right.name,
|
|
266
|
-
)
|
|
267
|
-
)
|
|
268
|
-
}
|
|
269
265
|
} else {
|
|
270
|
-
// println("navigated navigate")
|
|
271
266
|
navController.navigate(
|
|
272
267
|
ChapterScreenProps(
|
|
273
268
|
bookIndex = pair.first,
|
|
274
269
|
chapterIndex = pair.second,
|
|
275
|
-
dir = Dir.Right.name
|
|
270
|
+
dir = Dir.Right.name,
|
|
276
271
|
)
|
|
277
272
|
)
|
|
278
273
|
}
|
|
274
|
+
} else {
|
|
275
|
+
// println("navigated navigate")
|
|
276
|
+
navController.navigate(
|
|
277
|
+
ChapterScreenProps(
|
|
278
|
+
bookIndex = pair.first,
|
|
279
|
+
chapterIndex = pair.second,
|
|
280
|
+
dir = Dir.Right.name
|
|
281
|
+
)
|
|
282
|
+
)
|
|
279
283
|
}
|
|
284
|
+
}
|
|
280
|
-
|
|
285
|
+
}, onHorizontalDrag = { change, da ->
|
|
281
|
-
|
|
286
|
+
dragAmount = da
|
|
282
|
-
|
|
287
|
+
change.consume()
|
|
283
|
-
|
|
288
|
+
})
|
|
284
|
-
|
|
289
|
+
}) {
|
|
285
|
-
|
|
290
|
+
items(chapterVerses) { v ->
|
|
286
|
-
|
|
291
|
+
if (v.heading.isNotEmpty()) {
|
|
287
|
-
Text(
|
|
288
|
-
modifier = Modifier.padding(
|
|
289
|
-
top = if (v.verseIndex != 0) 12.dp else 0.dp, bottom = 12.dp
|
|
290
|
-
),
|
|
291
|
-
style = TextStyle(
|
|
292
|
-
fontFamily = fontFamily,
|
|
293
|
-
fontSize = (16 + state.fontSizeDelta).sp,
|
|
294
|
-
fontWeight = FontWeight.W700,
|
|
295
|
-
color = MaterialTheme.colorScheme.primary,
|
|
296
|
-
),
|
|
297
|
-
text = v.heading
|
|
298
|
-
)
|
|
299
|
-
}
|
|
300
|
-
val isSelected = selectedVerses.contains(v);
|
|
301
|
-
val buttonInteractionSource = remember { MutableInteractionSource() }
|
|
302
292
|
Text(
|
|
303
|
-
modifier = Modifier
|
|
293
|
+
modifier = Modifier.padding(
|
|
304
|
-
.clickable(
|
|
305
|
-
interactionSource = buttonInteractionSource,
|
|
306
|
-
indication = null
|
|
307
|
-
) {
|
|
308
|
-
|
|
294
|
+
top = if (v.verseIndex != 0) 12.dp else 0.dp, bottom = 12.dp
|
|
309
|
-
selectedVerses - v
|
|
310
|
-
} else {
|
|
311
|
-
selectedVerses + v
|
|
312
|
-
}
|
|
313
|
-
|
|
295
|
+
),
|
|
314
296
|
style = TextStyle(
|
|
315
|
-
background = if (isSelected)
|
|
316
|
-
MaterialTheme.colorScheme.outline
|
|
317
|
-
else
|
|
318
|
-
Color.Unspecified,
|
|
319
|
-
fontFamily =
|
|
297
|
+
fontFamily = fontType.family(),
|
|
320
|
-
color = if (darkTheme)
|
|
321
|
-
Color(0xFFBCBCBC)
|
|
322
|
-
else
|
|
323
|
-
Color(0xFF000104),
|
|
324
|
-
fontWeight = boldWeight,
|
|
325
|
-
fontSize = (
|
|
298
|
+
fontSize = (16 + fontSizeDelta).sp,
|
|
326
|
-
|
|
299
|
+
fontWeight = FontWeight.W700,
|
|
327
|
-
|
|
300
|
+
color = headingColor,
|
|
328
301
|
),
|
|
329
|
-
text = buildAnnotatedString {
|
|
330
|
-
val spanned = Html.fromHtml(v.text, Html.FROM_HTML_MODE_COMPACT)
|
|
331
|
-
val spans = spanned.getSpans(0, spanned.length, Any::class.java)
|
|
332
|
-
|
|
302
|
+
text = v.heading.replace("<br>", "\n")
|
|
333
|
-
withStyle(
|
|
334
|
-
style = SpanStyle(
|
|
335
|
-
fontSize = (13 + state.fontSizeDelta).sp,
|
|
336
|
-
color = if (darkTheme) Color(0xFFBBBBBB)
|
|
337
|
-
else Color(0xFFA20101),
|
|
338
|
-
fontWeight = FontWeight.W700,
|
|
339
|
-
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
val isSelected = selectedVerses.contains(v);
|
|
306
|
+
val buttonInteractionSource = remember { MutableInteractionSource() }
|
|
307
|
+
Text(
|
|
308
|
+
modifier = Modifier
|
|
309
|
+
.clickable(
|
|
310
|
+
interactionSource = buttonInteractionSource,
|
|
311
|
+
indication = null
|
|
340
|
-
|
|
312
|
+
) {
|
|
313
|
+
selectedVerses = if (selectedVerses.contains(v)) {
|
|
341
|
-
|
|
314
|
+
selectedVerses - v
|
|
315
|
+
} else {
|
|
316
|
+
selectedVerses + v
|
|
342
317
|
}
|
|
318
|
+
},
|
|
319
|
+
style = TextStyle(
|
|
320
|
+
background = if (isSelected)
|
|
321
|
+
MaterialTheme.colorScheme.outline
|
|
322
|
+
else
|
|
323
|
+
Color.Unspecified,
|
|
324
|
+
fontFamily = fontType.family(),
|
|
325
|
+
color = if (darkTheme)
|
|
326
|
+
Color(0xFFBCBCBC)
|
|
327
|
+
else
|
|
328
|
+
Color(0xFF000104),
|
|
329
|
+
fontWeight = boldWeight,
|
|
330
|
+
fontSize = (17 + fontSizeDelta).sp,
|
|
331
|
+
lineHeight = (23 + fontSizeDelta).sp,
|
|
332
|
+
letterSpacing = 0.sp,
|
|
333
|
+
),
|
|
334
|
+
text = buildAnnotatedString {
|
|
335
|
+
val spanned = Html.fromHtml(v.text, Html.FROM_HTML_MODE_COMPACT)
|
|
336
|
+
val spans = spanned.getSpans(0, spanned.length, Any::class.java)
|
|
337
|
+
val verseNo = "${v.verseIndex + 1} "
|
|
338
|
+
withStyle(
|
|
339
|
+
style = SpanStyle(
|
|
340
|
+
fontSize = (13 + fontSizeDelta).sp,
|
|
341
|
+
color = if (darkTheme)
|
|
342
|
+
Color(0xFFCCCCCC)
|
|
343
|
+
else Color(0xFFA20101),
|
|
344
|
+
fontWeight = FontWeight.W700,
|
|
345
|
+
)
|
|
346
|
+
) {
|
|
347
|
+
append(verseNo)
|
|
348
|
+
}
|
|
343
|
-
|
|
349
|
+
append(spanned.toString())
|
|
344
|
-
|
|
350
|
+
spans
|
|
345
|
-
|
|
351
|
+
.filter { it !is BulletSpan }
|
|
346
|
-
|
|
352
|
+
.forEach { span ->
|
|
347
|
-
|
|
353
|
+
val start = spanned.getSpanStart(span)
|
|
348
|
-
|
|
354
|
+
val end = spanned.getSpanEnd(span)
|
|
349
|
-
|
|
355
|
+
when (span) {
|
|
350
|
-
|
|
356
|
+
is ForegroundColorSpan ->
|
|
351
|
-
|
|
357
|
+
if (darkTheme) SpanStyle(color = Color(0xFFFF636B))
|
|
352
|
-
|
|
358
|
+
else SpanStyle(color = Color(0xFFFF0000))
|
|
353
359
|
|
|
354
|
-
|
|
360
|
+
is StyleSpan -> when (span.style) {
|
|
355
|
-
|
|
361
|
+
Typeface.BOLD -> SpanStyle(fontWeight = FontWeight.Bold)
|
|
356
|
-
|
|
362
|
+
Typeface.ITALIC -> SpanStyle(fontStyle = FontStyle.Italic)
|
|
357
|
-
|
|
363
|
+
Typeface.BOLD_ITALIC -> SpanStyle(
|
|
358
|
-
|
|
364
|
+
fontWeight = FontWeight.Bold,
|
|
359
|
-
|
|
365
|
+
fontStyle = FontStyle.Italic,
|
|
360
|
-
|
|
366
|
+
)
|
|
361
367
|
|
|
362
|
-
|
|
368
|
+
else -> null
|
|
363
|
-
|
|
369
|
+
}
|
|
364
370
|
|
|
365
|
-
|
|
371
|
+
else -> {
|
|
366
|
-
|
|
372
|
+
null
|
|
367
|
-
}
|
|
368
|
-
}?.let { spanStyle ->
|
|
369
|
-
addStyle(
|
|
370
|
-
spanStyle,
|
|
371
|
-
start + verseNo.length - 1,
|
|
372
|
-
end + verseNo.length
|
|
373
|
-
)
|
|
374
373
|
}
|
|
374
|
+
}?.let { spanStyle ->
|
|
375
|
+
addStyle(
|
|
376
|
+
spanStyle,
|
|
377
|
+
start + verseNo.length - 1,
|
|
378
|
+
end + verseNo.length
|
|
379
|
+
)
|
|
375
380
|
}
|
|
376
|
-
|
|
381
|
+
}
|
|
382
|
+
}
|
|
377
|
-
|
|
383
|
+
)
|
|
378
|
-
}
|
|
379
384
|
}
|
|
380
385
|
}
|
|
381
386
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/Grid.kt
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
package dev.pyrossh.onlyBible
|
|
2
|
-
|
|
3
|
-
import androidx.compose.foundation.layout.Box
|
|
4
|
-
import androidx.compose.foundation.layout.Column
|
|
5
|
-
import androidx.compose.foundation.layout.Row
|
|
6
|
-
import androidx.compose.foundation.layout.fillMaxWidth
|
|
7
|
-
import androidx.compose.runtime.Composable
|
|
8
|
-
import androidx.compose.ui.Modifier
|
|
9
|
-
|
|
10
|
-
@Composable
|
|
11
|
-
fun Grid(
|
|
12
|
-
columns: Int,
|
|
13
|
-
itemCount: Int,
|
|
14
|
-
modifier: Modifier = Modifier,
|
|
15
|
-
content: @Composable() (Int) -> Unit
|
|
16
|
-
) {
|
|
17
|
-
Column(modifier = modifier) {
|
|
18
|
-
var rows = (itemCount / columns)
|
|
19
|
-
if (itemCount.mod(columns) > 0) {
|
|
20
|
-
rows += 1
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
for (rowId in 0 until rows) {
|
|
24
|
-
val firstIndex = rowId * columns
|
|
25
|
-
|
|
26
|
-
Row {
|
|
27
|
-
for (columnId in 0 until columns) {
|
|
28
|
-
val index = firstIndex + columnId
|
|
29
|
-
Box(
|
|
30
|
-
modifier = Modifier
|
|
31
|
-
.fillMaxWidth()
|
|
32
|
-
.weight(1f)
|
|
33
|
-
) {
|
|
34
|
-
if (index < itemCount) {
|
|
35
|
-
content(index)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
app/src/main/java/dev/pyrossh/onlyBible/Hooks.kt
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package dev.pyrossh.onlyBible
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.isSystemInDarkTheme
|
|
4
|
+
import androidx.compose.runtime.Composable
|
|
5
|
+
import androidx.compose.runtime.getValue
|
|
6
|
+
import androidx.compose.runtime.setValue
|
|
7
|
+
import dev.burnoo.compose.rememberpreference.rememberStringPreference
|
|
8
|
+
|
|
9
|
+
@Composable
|
|
10
|
+
fun isDarkMode(): Boolean {
|
|
11
|
+
val (themeType) = rememberThemeType()
|
|
12
|
+
return themeType == ThemeType.Dark || (themeType == ThemeType.Auto && isSystemInDarkTheme())
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@Composable
|
|
16
|
+
fun rememberThemeType(): Pair<ThemeType, (ThemeType) -> Unit> {
|
|
17
|
+
var data by rememberStringPreference(keyName = "themeType", initialValue = ThemeType.Auto.name, defaultValue = ThemeType.Auto.name)
|
|
18
|
+
return Pair(ThemeType.valueOf(data)) { data = it.name }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Composable
|
|
22
|
+
fun rememberFontType(): Pair<FontType, (FontType) -> Unit> {
|
|
23
|
+
var data by rememberStringPreference(keyName = "fontType", initialValue = FontType.Sans.name, defaultValue = FontType.Sans.name)
|
|
24
|
+
return Pair(FontType.valueOf(data)) { data = it.name }
|
|
25
|
+
}
|
app/src/main/java/dev/pyrossh/onlyBible/LoadingBox.kt
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
package dev.pyrossh.onlyBible
|
|
2
|
-
|
|
3
|
-
import androidx.compose.foundation.layout.Arrangement
|
|
4
|
-
import androidx.compose.foundation.layout.Box
|
|
5
|
-
import androidx.compose.foundation.layout.Column
|
|
6
|
-
import androidx.compose.foundation.layout.fillMaxSize
|
|
7
|
-
import androidx.compose.foundation.layout.fillMaxWidth
|
|
8
|
-
import androidx.compose.foundation.layout.wrapContentHeight
|
|
9
|
-
import androidx.compose.material3.CircularProgressIndicator
|
|
10
|
-
import androidx.compose.material3.Surface
|
|
11
|
-
import androidx.compose.runtime.Composable
|
|
12
|
-
import androidx.compose.ui.Alignment
|
|
13
|
-
import androidx.compose.ui.Modifier
|
|
14
|
-
import androidx.compose.ui.draw.alpha
|
|
15
|
-
|
|
16
|
-
@Composable
|
|
17
|
-
fun LoadingBox(
|
|
18
|
-
modifier: Modifier = Modifier,
|
|
19
|
-
isLoading: Boolean,
|
|
20
|
-
content: @Composable() () -> Unit
|
|
21
|
-
) {
|
|
22
|
-
Box(
|
|
23
|
-
modifier = modifier
|
|
24
|
-
.fillMaxWidth()
|
|
25
|
-
.wrapContentHeight()
|
|
26
|
-
) {
|
|
27
|
-
|
|
28
|
-
content()
|
|
29
|
-
if (isLoading) {
|
|
30
|
-
|
|
31
|
-
Surface(
|
|
32
|
-
modifier = Modifier
|
|
33
|
-
.fillMaxSize()
|
|
34
|
-
.alpha(0.5f),
|
|
35
|
-
) {
|
|
36
|
-
Column(
|
|
37
|
-
modifier = Modifier.fillMaxSize(),
|
|
38
|
-
verticalArrangement = Arrangement.Center,
|
|
39
|
-
horizontalAlignment = Alignment.CenterHorizontally
|
|
40
|
-
) {
|
|
41
|
-
CircularProgressIndicator()
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
app/src/main/java/dev/pyrossh/onlyBible/MainActivity.kt
CHANGED
|
@@ -1,37 +1,28 @@
|
|
|
1
1
|
package dev.pyrossh.onlyBible
|
|
2
2
|
|
|
3
|
-
import Verse
|
|
4
|
-
import android.annotation.SuppressLint
|
|
5
|
-
import android.content.Context
|
|
6
3
|
import android.os.Bundle
|
|
7
4
|
import androidx.activity.ComponentActivity
|
|
8
5
|
import androidx.activity.compose.setContent
|
|
9
6
|
import androidx.activity.enableEdgeToEdge
|
|
7
|
+
import androidx.activity.viewModels
|
|
10
8
|
import androidx.compose.runtime.CompositionLocalProvider
|
|
9
|
+
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|
11
10
|
|
|
12
11
|
class MainActivity : ComponentActivity() {
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
private val model: AppViewModel by viewModels()
|
|
14
|
+
|
|
15
15
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
16
|
+
val splashScreen = installSplashScreen()
|
|
16
17
|
super.onCreate(savedInstanceState)
|
|
17
18
|
enableEdgeToEdge()
|
|
18
|
-
val prefs = applicationContext.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
|
19
|
-
val bibles =
|
|
20
|
-
assets.list("bibles")?.map { it.replace("bibles/", "").replace(".txt", "") } ?: listOf()
|
|
21
|
-
val state = State(prefs, bibles) { recreate() }
|
|
22
|
-
val bibleName = state.getBibleName()
|
|
23
|
-
val fileName = bibles.find { it.contains(bibleName) } ?: "English"
|
|
24
|
-
|
|
19
|
+
model.loadBible(applicationContext)
|
|
25
|
-
bibleName,
|
|
26
|
-
|
|
20
|
+
splashScreen.setKeepOnScreenCondition{model.uiState.value.isLoading}
|
|
27
|
-
)
|
|
28
21
|
setContent {
|
|
29
|
-
CompositionLocalProvider(LocalSettings provides
|
|
22
|
+
CompositionLocalProvider(LocalSettings provides model) {
|
|
30
23
|
AppTheme {
|
|
31
|
-
AppHost(
|
|
24
|
+
AppHost()
|
|
32
|
-
verses = verses
|
|
33
|
-
)
|
|
34
|
-
if (
|
|
25
|
+
if (model.showBottomSheet) {
|
|
35
26
|
TextSettingsBottomSheet()
|
|
36
27
|
}
|
|
37
28
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/Model.kt
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import android.util.Log
|
|
2
|
-
import java.io.BufferedReader
|
|
3
|
-
|
|
4
|
-
data class Verse(
|
|
5
|
-
val bookIndex: Int,
|
|
6
|
-
val bookName: String,
|
|
7
|
-
val chapterIndex: Int,
|
|
8
|
-
val verseIndex: Int,
|
|
9
|
-
val heading: String,
|
|
10
|
-
val text: String,
|
|
11
|
-
) {
|
|
12
|
-
|
|
13
|
-
companion object {
|
|
14
|
-
|
|
15
|
-
val engTitles = listOf(
|
|
16
|
-
"Genesis",
|
|
17
|
-
"Exodus",
|
|
18
|
-
"Leviticus",
|
|
19
|
-
"Numbers",
|
|
20
|
-
"Deuteronomy",
|
|
21
|
-
"Joshua",
|
|
22
|
-
"Judges",
|
|
23
|
-
"Ruth",
|
|
24
|
-
"1 Samuel",
|
|
25
|
-
"2 Samuel",
|
|
26
|
-
"1 Kings",
|
|
27
|
-
"2 Kings",
|
|
28
|
-
"1 Chronicles",
|
|
29
|
-
"2 Chronicles",
|
|
30
|
-
"Ezra",
|
|
31
|
-
"Nehemiah",
|
|
32
|
-
"Esther",
|
|
33
|
-
"Job",
|
|
34
|
-
"Psalms",
|
|
35
|
-
"Proverbs",
|
|
36
|
-
"Ecclesiastes",
|
|
37
|
-
"Song of Solomon",
|
|
38
|
-
"Isaiah",
|
|
39
|
-
"Jeremiah",
|
|
40
|
-
"Lamentations",
|
|
41
|
-
"Ezekiel",
|
|
42
|
-
"Daniel",
|
|
43
|
-
"Hosea",
|
|
44
|
-
"Joel",
|
|
45
|
-
"Amos",
|
|
46
|
-
"Obadiah",
|
|
47
|
-
"Jonah",
|
|
48
|
-
"Micah",
|
|
49
|
-
"Nahum",
|
|
50
|
-
"Habakkuk",
|
|
51
|
-
"Zephaniah",
|
|
52
|
-
"Haggai",
|
|
53
|
-
"Zechariah",
|
|
54
|
-
"Malachi",
|
|
55
|
-
"Matthew",
|
|
56
|
-
"Mark",
|
|
57
|
-
"Luke",
|
|
58
|
-
"John",
|
|
59
|
-
"Acts",
|
|
60
|
-
"Romans",
|
|
61
|
-
"1 Corinthians",
|
|
62
|
-
"2 Corinthians",
|
|
63
|
-
"Galatians",
|
|
64
|
-
"Ephesians",
|
|
65
|
-
"Philippians",
|
|
66
|
-
"Colossians",
|
|
67
|
-
"1 Thessalonians",
|
|
68
|
-
"2 Thessalonians",
|
|
69
|
-
"1 Timothy",
|
|
70
|
-
"2 Timothy",
|
|
71
|
-
"Titus",
|
|
72
|
-
"Philemon",
|
|
73
|
-
"Hebrews",
|
|
74
|
-
"James",
|
|
75
|
-
"1 Peter",
|
|
76
|
-
"2 Peter",
|
|
77
|
-
"1 John",
|
|
78
|
-
"2 John",
|
|
79
|
-
"3 John",
|
|
80
|
-
"Jude",
|
|
81
|
-
"Revelation",
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
val booksCount = 66;
|
|
85
|
-
val chapterSizes = listOf(
|
|
86
|
-
50,
|
|
87
|
-
40,
|
|
88
|
-
27,
|
|
89
|
-
36,
|
|
90
|
-
34,
|
|
91
|
-
24,
|
|
92
|
-
21,
|
|
93
|
-
4,
|
|
94
|
-
31,
|
|
95
|
-
24,
|
|
96
|
-
22,
|
|
97
|
-
25,
|
|
98
|
-
29,
|
|
99
|
-
36,
|
|
100
|
-
10,
|
|
101
|
-
13,
|
|
102
|
-
10,
|
|
103
|
-
42,
|
|
104
|
-
150,
|
|
105
|
-
31,
|
|
106
|
-
12,
|
|
107
|
-
8,
|
|
108
|
-
66,
|
|
109
|
-
52,
|
|
110
|
-
5,
|
|
111
|
-
48,
|
|
112
|
-
12,
|
|
113
|
-
14,
|
|
114
|
-
3,
|
|
115
|
-
9,
|
|
116
|
-
1,
|
|
117
|
-
4,
|
|
118
|
-
7,
|
|
119
|
-
3,
|
|
120
|
-
3,
|
|
121
|
-
3,
|
|
122
|
-
2,
|
|
123
|
-
14,
|
|
124
|
-
4,
|
|
125
|
-
28,
|
|
126
|
-
16,
|
|
127
|
-
24,
|
|
128
|
-
21,
|
|
129
|
-
28,
|
|
130
|
-
16,
|
|
131
|
-
16,
|
|
132
|
-
13,
|
|
133
|
-
6,
|
|
134
|
-
6,
|
|
135
|
-
4,
|
|
136
|
-
4,
|
|
137
|
-
5,
|
|
138
|
-
3,
|
|
139
|
-
6,
|
|
140
|
-
4,
|
|
141
|
-
3,
|
|
142
|
-
1,
|
|
143
|
-
13,
|
|
144
|
-
5,
|
|
145
|
-
5,
|
|
146
|
-
3,
|
|
147
|
-
5,
|
|
148
|
-
1,
|
|
149
|
-
1,
|
|
150
|
-
1,
|
|
151
|
-
22
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
fun parseFromBibleTxt(name: String, buffer: BufferedReader): List<Verse> {
|
|
155
|
-
Log.i("loading", "parsing bible $name")
|
|
156
|
-
return buffer.readLines().filter { it.isNotEmpty() }.map {
|
|
157
|
-
val arr = it.split("|")
|
|
158
|
-
val bookName = arr[0]
|
|
159
|
-
val book = arr[1].toInt()
|
|
160
|
-
val chapter = arr[2].toInt()
|
|
161
|
-
val verseNo = arr[3].toInt()
|
|
162
|
-
val heading = arr[4]
|
|
163
|
-
val verseText = arr.subList(5, arr.size).joinToString("|")
|
|
164
|
-
Verse(
|
|
165
|
-
bookIndex = book,
|
|
166
|
-
bookName = bookName,
|
|
167
|
-
chapterIndex = chapter,
|
|
168
|
-
verseIndex = verseNo,
|
|
169
|
-
heading = heading,
|
|
170
|
-
text = verseText,
|
|
171
|
-
)
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
fun getForwardPair(book: Int, chapter: Int): Pair<Int, Int> {
|
|
176
|
-
val sizes = chapterSizes[book];
|
|
177
|
-
if (sizes > chapter + 1) {
|
|
178
|
-
return Pair(book, chapter + 1)
|
|
179
|
-
}
|
|
180
|
-
if (book + 1 < booksCount) {
|
|
181
|
-
return Pair(book + 1, 0)
|
|
182
|
-
}
|
|
183
|
-
return Pair(0, 0)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
fun getBackwardPair(book: Int, chapter: Int): Pair<Int, Int> {
|
|
187
|
-
if (chapter - 1 >= 0) {
|
|
188
|
-
return Pair(book, chapter - 1)
|
|
189
|
-
}
|
|
190
|
-
if (book - 1 >= 0) {
|
|
191
|
-
return Pair(book - 1, chapterSizes[book - 1] - 1)
|
|
192
|
-
}
|
|
193
|
-
return Pair(booksCount - 1, chapterSizes[booksCount - 1] - 1)
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
app/src/main/java/dev/pyrossh/onlyBible/TextSettingsBottomSheet.kt
CHANGED
|
@@ -13,9 +13,11 @@ import androidx.compose.foundation.layout.size
|
|
|
13
13
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
14
14
|
import androidx.compose.material.icons.Icons
|
|
15
15
|
import androidx.compose.material.icons.filled.Close
|
|
16
|
+
import androidx.compose.material.icons.filled.DarkMode
|
|
16
17
|
import androidx.compose.material.icons.filled.FormatBold
|
|
17
18
|
import androidx.compose.material.icons.filled.FormatLineSpacing
|
|
18
19
|
import androidx.compose.material.icons.filled.FormatSize
|
|
20
|
+
import androidx.compose.material.icons.filled.LightMode
|
|
19
21
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
20
22
|
import androidx.compose.material3.HorizontalDivider
|
|
21
23
|
import androidx.compose.material3.Icon
|
|
@@ -33,7 +35,9 @@ import androidx.compose.ui.text.TextStyle
|
|
|
33
35
|
import androidx.compose.ui.text.font.FontWeight
|
|
34
36
|
import androidx.compose.ui.unit.dp
|
|
35
37
|
import androidx.compose.ui.unit.sp
|
|
36
|
-
import
|
|
38
|
+
import dev.pyrossh.onlyBible.ThemeType.Auto
|
|
39
|
+
import dev.pyrossh.onlyBible.ThemeType.Dark
|
|
40
|
+
import dev.pyrossh.onlyBible.ThemeType.Light
|
|
37
41
|
import kotlinx.coroutines.launch
|
|
38
42
|
|
|
39
43
|
@Composable
|
|
@@ -41,7 +45,10 @@ import kotlinx.coroutines.launch
|
|
|
41
45
|
fun TextSettingsBottomSheet() {
|
|
42
46
|
val scope = rememberCoroutineScope()
|
|
43
47
|
val sheetState = rememberModalBottomSheetState()
|
|
44
|
-
val
|
|
48
|
+
val settings = LocalSettings.current!!
|
|
49
|
+
val fontSizeDelta = 0
|
|
50
|
+
val (fontType, setFontType) = rememberFontType()
|
|
51
|
+
val (themeType, setThemeType) = rememberThemeType()
|
|
45
52
|
return ModalBottomSheet(
|
|
46
53
|
tonalElevation = 2.dp,
|
|
47
54
|
sheetState = sheetState,
|
|
@@ -49,7 +56,7 @@ fun TextSettingsBottomSheet() {
|
|
|
49
56
|
scope.launch {
|
|
50
57
|
sheetState.hide()
|
|
51
58
|
}.invokeOnCompletion {
|
|
52
|
-
|
|
59
|
+
settings.closeSheet()
|
|
53
60
|
}
|
|
54
61
|
},
|
|
55
62
|
) {
|
|
@@ -76,7 +83,7 @@ fun TextSettingsBottomSheet() {
|
|
|
76
83
|
scope.launch {
|
|
77
84
|
sheetState.hide()
|
|
78
85
|
}.invokeOnCompletion {
|
|
79
|
-
|
|
86
|
+
settings.closeSheet()
|
|
80
87
|
}
|
|
81
88
|
}) {
|
|
82
89
|
Icon(Icons.Filled.Close, "Close")
|
|
@@ -98,7 +105,7 @@ fun TextSettingsBottomSheet() {
|
|
|
98
105
|
.padding(end = 16.dp)
|
|
99
106
|
.weight(1f),
|
|
100
107
|
onClick = {
|
|
101
|
-
|
|
108
|
+
// settings.updateFontSize(fontSizeDelta - 1)
|
|
102
109
|
}) {
|
|
103
110
|
Column(
|
|
104
111
|
modifier = Modifier.background(MaterialTheme.colorScheme.background),
|
|
@@ -119,7 +126,7 @@ fun TextSettingsBottomSheet() {
|
|
|
119
126
|
.padding(end = 16.dp)
|
|
120
127
|
.weight(1f),
|
|
121
128
|
onClick = {
|
|
122
|
-
|
|
129
|
+
// settings.updateFontSize(fontSizeDelta + 1)
|
|
123
130
|
}) {
|
|
124
131
|
Column(
|
|
125
132
|
modifier = Modifier.background(MaterialTheme.colorScheme.background),
|
|
@@ -139,7 +146,7 @@ fun TextSettingsBottomSheet() {
|
|
|
139
146
|
.padding(end = 16.dp)
|
|
140
147
|
.weight(1f),
|
|
141
148
|
onClick = {
|
|
142
|
-
|
|
149
|
+
// settings.updateBoldEnabled(!settings.boldEnabled)
|
|
143
150
|
}) {
|
|
144
151
|
Column(
|
|
145
152
|
modifier = Modifier.background(MaterialTheme.colorScheme.background),
|
|
@@ -149,7 +156,7 @@ fun TextSettingsBottomSheet() {
|
|
|
149
156
|
Icon(
|
|
150
157
|
imageVector = Icons.Filled.FormatBold,
|
|
151
158
|
contentDescription = "Bold",
|
|
152
|
-
tint = if (
|
|
159
|
+
// tint = if (settings.boldEnabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground,
|
|
153
160
|
)
|
|
154
161
|
}
|
|
155
162
|
}
|
|
@@ -182,7 +189,7 @@ fun TextSettingsBottomSheet() {
|
|
|
182
189
|
FontType.entries.map {
|
|
183
190
|
Surface(
|
|
184
191
|
shape = RoundedCornerShape(8.dp),
|
|
185
|
-
border = if (
|
|
192
|
+
border = if (fontType == it) BorderStroke(
|
|
186
193
|
2.dp, MaterialTheme.colorScheme.primary
|
|
187
194
|
) else null,
|
|
188
195
|
modifier = Modifier
|
|
@@ -191,7 +198,7 @@ fun TextSettingsBottomSheet() {
|
|
|
191
198
|
.padding(end = 16.dp)
|
|
192
199
|
.weight(1f),
|
|
193
200
|
onClick = {
|
|
194
|
-
|
|
201
|
+
setFontType(it)
|
|
195
202
|
}) {
|
|
196
203
|
Column(
|
|
197
204
|
modifier = Modifier.background(
|
|
@@ -206,7 +213,10 @@ fun TextSettingsBottomSheet() {
|
|
|
206
213
|
fontFamily = it.family(),
|
|
207
214
|
fontSize = 18.sp,
|
|
208
215
|
fontWeight = FontWeight.W600,
|
|
216
|
+
color = if (fontType == it)
|
|
217
|
+
MaterialTheme.colorScheme.primary
|
|
218
|
+
else
|
|
209
|
-
|
|
219
|
+
MaterialTheme.colorScheme.onSurface,
|
|
210
220
|
)
|
|
211
221
|
)
|
|
212
222
|
}
|
|
@@ -220,12 +230,20 @@ fun TextSettingsBottomSheet() {
|
|
|
220
230
|
horizontalArrangement = Arrangement.SpaceBetween,
|
|
221
231
|
verticalAlignment = Alignment.CenterVertically,
|
|
222
232
|
) {
|
|
223
|
-
ThemeType.entries.map {
|
|
233
|
+
ThemeType.entries.map {
|
|
224
234
|
Surface(
|
|
225
235
|
shape = RoundedCornerShape(8.dp),
|
|
226
|
-
border = if (
|
|
236
|
+
border = if (themeType == it) BorderStroke(
|
|
227
237
|
2.dp, MaterialTheme.colorScheme.primary
|
|
228
238
|
) else null,
|
|
239
|
+
color = if (themeType == it)
|
|
240
|
+
MaterialTheme.colorScheme.primary
|
|
241
|
+
else
|
|
242
|
+
MaterialTheme.colorScheme.onSurface,
|
|
243
|
+
contentColor = if (themeType == it)
|
|
244
|
+
MaterialTheme.colorScheme.primary
|
|
245
|
+
else
|
|
246
|
+
MaterialTheme.colorScheme.onSurface,
|
|
229
247
|
modifier = Modifier
|
|
230
248
|
.fillMaxWidth()
|
|
231
249
|
.height(60.dp)
|
|
@@ -234,17 +252,45 @@ fun TextSettingsBottomSheet() {
|
|
|
234
252
|
onClick = {
|
|
235
253
|
scope.launch {
|
|
236
254
|
sheetState.hide()
|
|
237
|
-
state.isLoading = true
|
|
238
|
-
delay(500L)
|
|
239
|
-
}.invokeOnCompletion {
|
|
240
|
-
|
|
255
|
+
settings.closeSheet()
|
|
241
|
-
|
|
256
|
+
setThemeType(it)
|
|
242
257
|
}
|
|
243
258
|
}
|
|
244
259
|
) {
|
|
260
|
+
when (it) {
|
|
261
|
+
Light -> Icon(
|
|
245
|
-
|
|
262
|
+
imageVector = Icons.Filled.LightMode,
|
|
263
|
+
contentDescription = "Light",
|
|
264
|
+
modifier = Modifier
|
|
265
|
+
.background(MaterialTheme.colorScheme.background)
|
|
266
|
+
.padding(12.dp)
|
|
246
|
-
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
Dark -> Icon(
|
|
270
|
+
imageVector = Icons.Filled.DarkMode,
|
|
271
|
+
contentDescription = "Dark",
|
|
272
|
+
modifier = Modifier
|
|
273
|
+
.background(MaterialTheme.colorScheme.background)
|
|
274
|
+
.padding(12.dp)
|
|
275
|
+
)
|
|
247
276
|
|
|
277
|
+
Auto -> Column(
|
|
278
|
+
modifier = Modifier.background(
|
|
279
|
+
color = MaterialTheme.colorScheme.background,
|
|
280
|
+
),
|
|
281
|
+
verticalArrangement = Arrangement.Center,
|
|
282
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
283
|
+
) {
|
|
284
|
+
Text(
|
|
285
|
+
text = "Auto",
|
|
286
|
+
style = TextStyle(
|
|
287
|
+
fontSize = 18.sp,
|
|
288
|
+
fontWeight = FontWeight.Medium,
|
|
289
|
+
),
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
248
294
|
}
|
|
249
295
|
}
|
|
250
296
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/Utils.kt
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import android.content.Context
|
|
2
|
-
import android.content.Intent
|
|
3
|
-
import com.microsoft.cognitiveservices.speech.SpeechConfig
|
|
4
|
-
import com.microsoft.cognitiveservices.speech.SpeechSynthesizer
|
|
5
|
-
import dev.pyrossh.onlyBible.BuildConfig
|
|
6
|
-
import kotlinx.coroutines.CoroutineScope
|
|
7
|
-
import kotlinx.coroutines.Dispatchers
|
|
8
|
-
import kotlinx.coroutines.delay
|
|
9
|
-
import kotlinx.coroutines.withContext
|
|
10
|
-
import java.util.concurrent.Future
|
|
11
|
-
|
|
12
|
-
val speechService = SpeechSynthesizer(
|
|
13
|
-
SpeechConfig.fromSubscription(
|
|
14
|
-
BuildConfig.subscriptionKey,
|
|
15
|
-
"centralindia"
|
|
16
|
-
)
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
suspend fun <T> Future<T>.wait(timeoutMs: Int = 30000): T? {
|
|
20
|
-
val start = System.currentTimeMillis()
|
|
21
|
-
while (!isDone) {
|
|
22
|
-
if (System.currentTimeMillis() - start > timeoutMs)
|
|
23
|
-
return null
|
|
24
|
-
delay(500)
|
|
25
|
-
}
|
|
26
|
-
return withContext(Dispatchers.IO) {
|
|
27
|
-
get()
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
suspend fun convertVersesToSpeech(scope: CoroutineScope, verses: List<Verse>) {
|
|
32
|
-
for (v in verses) {
|
|
33
|
-
speechService.SpeakSsmlAsync(
|
|
34
|
-
"""
|
|
35
|
-
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
|
|
36
|
-
<voice name="en-US-AvaMultilingualNeural">
|
|
37
|
-
${v.text}
|
|
38
|
-
</voice>
|
|
39
|
-
</speak>
|
|
40
|
-
""".trimIndent()
|
|
41
|
-
).wait()
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
fun shareVerses(context: Context, verses: List<Verse>) {
|
|
46
|
-
val items = verses.sortedBy { it.verseIndex }
|
|
47
|
-
val versesThrough =
|
|
48
|
-
if (items.size >= 3) "${items.first().verseIndex + 1}-${items.last().verseIndex + 1}" else items.map { it.verseIndex + 1 }
|
|
49
|
-
.joinToString(",");
|
|
50
|
-
val title = "${verses[0].bookName} ${verses[0].chapterIndex + 1}:${versesThrough}"
|
|
51
|
-
val text = verses.joinToString("\n") { it.text };
|
|
52
|
-
val sendIntent = Intent().apply {
|
|
53
|
-
action = Intent.ACTION_SEND
|
|
54
|
-
putExtra(Intent.EXTRA_TEXT, "${title}\n${text}")
|
|
55
|
-
type = "text/plain"
|
|
56
|
-
}
|
|
57
|
-
val shareIntent = Intent.createChooser(sendIntent, null)
|
|
58
|
-
context.startActivity(shareIntent)
|
|
59
|
-
}
|
app/src/main/java/dev/pyrossh/onlyBible/Verse.kt
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
package dev.pyrossh.onlyBible
|
|
2
|
+
|
|
3
|
+
const val BOOKS_COUNT = 66;
|
|
4
|
+
|
|
5
|
+
val engTitles = listOf(
|
|
6
|
+
"Genesis",
|
|
7
|
+
"Exodus",
|
|
8
|
+
"Leviticus",
|
|
9
|
+
"Numbers",
|
|
10
|
+
"Deuteronomy",
|
|
11
|
+
"Joshua",
|
|
12
|
+
"Judges",
|
|
13
|
+
"Ruth",
|
|
14
|
+
"1 Samuel",
|
|
15
|
+
"2 Samuel",
|
|
16
|
+
"1 Kings",
|
|
17
|
+
"2 Kings",
|
|
18
|
+
"1 Chronicles",
|
|
19
|
+
"2 Chronicles",
|
|
20
|
+
"Ezra",
|
|
21
|
+
"Nehemiah",
|
|
22
|
+
"Esther",
|
|
23
|
+
"Job",
|
|
24
|
+
"Psalms",
|
|
25
|
+
"Proverbs",
|
|
26
|
+
"Ecclesiastes",
|
|
27
|
+
"Song of Solomon",
|
|
28
|
+
"Isaiah",
|
|
29
|
+
"Jeremiah",
|
|
30
|
+
"Lamentations",
|
|
31
|
+
"Ezekiel",
|
|
32
|
+
"Daniel",
|
|
33
|
+
"Hosea",
|
|
34
|
+
"Joel",
|
|
35
|
+
"Amos",
|
|
36
|
+
"Obadiah",
|
|
37
|
+
"Jonah",
|
|
38
|
+
"Micah",
|
|
39
|
+
"Nahum",
|
|
40
|
+
"Habakkuk",
|
|
41
|
+
"Zephaniah",
|
|
42
|
+
"Haggai",
|
|
43
|
+
"Zechariah",
|
|
44
|
+
"Malachi",
|
|
45
|
+
"Matthew",
|
|
46
|
+
"Mark",
|
|
47
|
+
"Luke",
|
|
48
|
+
"John",
|
|
49
|
+
"Acts",
|
|
50
|
+
"Romans",
|
|
51
|
+
"1 Corinthians",
|
|
52
|
+
"2 Corinthians",
|
|
53
|
+
"Galatians",
|
|
54
|
+
"Ephesians",
|
|
55
|
+
"Philippians",
|
|
56
|
+
"Colossians",
|
|
57
|
+
"1 Thessalonians",
|
|
58
|
+
"2 Thessalonians",
|
|
59
|
+
"1 Timothy",
|
|
60
|
+
"2 Timothy",
|
|
61
|
+
"Titus",
|
|
62
|
+
"Philemon",
|
|
63
|
+
"Hebrews",
|
|
64
|
+
"James",
|
|
65
|
+
"1 Peter",
|
|
66
|
+
"2 Peter",
|
|
67
|
+
"1 John",
|
|
68
|
+
"2 John",
|
|
69
|
+
"3 John",
|
|
70
|
+
"Jude",
|
|
71
|
+
"Revelation",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
data class Verse(
|
|
75
|
+
val bookIndex: Int,
|
|
76
|
+
val bookName: String,
|
|
77
|
+
val chapterIndex: Int,
|
|
78
|
+
val verseIndex: Int,
|
|
79
|
+
val heading: String,
|
|
80
|
+
val text: String,
|
|
81
|
+
) {
|
|
82
|
+
|
|
83
|
+
companion object {
|
|
84
|
+
val chapterSizes = listOf(
|
|
85
|
+
50,
|
|
86
|
+
40,
|
|
87
|
+
27,
|
|
88
|
+
36,
|
|
89
|
+
34,
|
|
90
|
+
24,
|
|
91
|
+
21,
|
|
92
|
+
4,
|
|
93
|
+
31,
|
|
94
|
+
24,
|
|
95
|
+
22,
|
|
96
|
+
25,
|
|
97
|
+
29,
|
|
98
|
+
36,
|
|
99
|
+
10,
|
|
100
|
+
13,
|
|
101
|
+
10,
|
|
102
|
+
42,
|
|
103
|
+
150,
|
|
104
|
+
31,
|
|
105
|
+
12,
|
|
106
|
+
8,
|
|
107
|
+
66,
|
|
108
|
+
52,
|
|
109
|
+
5,
|
|
110
|
+
48,
|
|
111
|
+
12,
|
|
112
|
+
14,
|
|
113
|
+
3,
|
|
114
|
+
9,
|
|
115
|
+
1,
|
|
116
|
+
4,
|
|
117
|
+
7,
|
|
118
|
+
3,
|
|
119
|
+
3,
|
|
120
|
+
3,
|
|
121
|
+
2,
|
|
122
|
+
14,
|
|
123
|
+
4,
|
|
124
|
+
28,
|
|
125
|
+
16,
|
|
126
|
+
24,
|
|
127
|
+
21,
|
|
128
|
+
28,
|
|
129
|
+
16,
|
|
130
|
+
16,
|
|
131
|
+
13,
|
|
132
|
+
6,
|
|
133
|
+
6,
|
|
134
|
+
4,
|
|
135
|
+
4,
|
|
136
|
+
5,
|
|
137
|
+
3,
|
|
138
|
+
6,
|
|
139
|
+
4,
|
|
140
|
+
3,
|
|
141
|
+
1,
|
|
142
|
+
13,
|
|
143
|
+
5,
|
|
144
|
+
5,
|
|
145
|
+
3,
|
|
146
|
+
5,
|
|
147
|
+
1,
|
|
148
|
+
1,
|
|
149
|
+
1,
|
|
150
|
+
22
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
fun getForwardPair(book: Int, chapter: Int): Pair<Int, Int> {
|
|
154
|
+
val sizes = chapterSizes[book];
|
|
155
|
+
if (sizes > chapter + 1) {
|
|
156
|
+
return Pair(book, chapter + 1)
|
|
157
|
+
}
|
|
158
|
+
if (book + 1 < BOOKS_COUNT) {
|
|
159
|
+
return Pair(book + 1, 0)
|
|
160
|
+
}
|
|
161
|
+
return Pair(0, 0)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
fun getBackwardPair(book: Int, chapter: Int): Pair<Int, Int> {
|
|
165
|
+
if (chapter - 1 >= 0) {
|
|
166
|
+
return Pair(book, chapter - 1)
|
|
167
|
+
}
|
|
168
|
+
if (book - 1 >= 0) {
|
|
169
|
+
return Pair(book - 1, chapterSizes[book - 1] - 1)
|
|
170
|
+
}
|
|
171
|
+
return Pair(BOOKS_COUNT - 1, chapterSizes[BOOKS_COUNT - 1] - 1)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
app/src/main/res/drawable/text_theme.xml
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
-
android:width="43dp"
|
|
3
|
-
android:height="56dp"
|
|
4
|
-
android:viewportWidth="43"
|
|
5
|
-
android:viewportHeight="56">
|
|
6
|
-
<path
|
|
7
|
-
android:pathData="M22,12L40,12"
|
|
8
|
-
android:strokeWidth="1.25"
|
|
9
|
-
android:fillColor="#00000000"
|
|
10
|
-
android:strokeColor="#5E6266"/>
|
|
11
|
-
<path
|
|
12
|
-
android:pathData="M3,24H40"
|
|
13
|
-
android:strokeWidth="1.25"
|
|
14
|
-
android:fillColor="#00000000"
|
|
15
|
-
android:strokeColor="#5E6266"/>
|
|
16
|
-
<path
|
|
17
|
-
android:pathData="M3,36H40"
|
|
18
|
-
android:strokeWidth="1.25"
|
|
19
|
-
android:fillColor="#00000000"
|
|
20
|
-
android:strokeColor="#5E6266"/>
|
|
21
|
-
<path
|
|
22
|
-
android:pathData="M3,48H40"
|
|
23
|
-
android:strokeWidth="1.25"
|
|
24
|
-
android:fillColor="#00000000"
|
|
25
|
-
android:strokeColor="#5E6266"/>
|
|
26
|
-
<path
|
|
27
|
-
android:pathData="M13.06,14.96H11V16H16.6V14.96H14.92L10.48,2.86H9.08L4.32,14.96H2.92V16H7.3V14.96H5.54L6.92,11.3H11.76L13.06,14.96ZM9.4,4.82H9.46L11.42,10.36H7.28L9.4,4.82Z"
|
|
28
|
-
android:fillColor="#5E6266"/>
|
|
29
|
-
</vector>
|
gradle/libs.versions.toml
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
accompanistSystemuicontroller = "0.27.0"
|
|
3
3
|
agp = "8.4.2"
|
|
4
4
|
clientSdk = "1.34.0"
|
|
5
|
+
composeRememberPreference = "1.1.0"
|
|
6
|
+
coreSplashscreen = "1.0.0"
|
|
5
7
|
datastorePreferences = "1.1.1"
|
|
6
8
|
foundation = "1.6.8"
|
|
7
9
|
kotlin = "2.0.0"
|
|
@@ -23,6 +25,7 @@ uiUtilAndroid = "1.6.8"
|
|
|
23
25
|
[libraries]
|
|
24
26
|
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
|
|
25
27
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
|
28
|
+
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
|
|
26
29
|
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
|
|
27
30
|
androidx-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "foundation" }
|
|
28
31
|
androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" }
|
|
@@ -31,6 +34,7 @@ androidx-navigation-fragment-ktx = { module = "androidx.navigation:navigation-fr
|
|
|
31
34
|
androidx-navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigationFragmentKtx" }
|
|
32
35
|
androidx-ui-text-google-fonts = { module = "androidx.compose.ui:ui-text-google-fonts", version.ref = "uiTextGoogleFonts" }
|
|
33
36
|
client-sdk = { module = "com.microsoft.cognitiveservices.speech:client-sdk", version.ref = "clientSdk" }
|
|
37
|
+
compose-remember-preference = { module = "dev.burnoo:compose-remember-preference", version.ref = "composeRememberPreference" }
|
|
34
38
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
|
35
39
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
|
36
40
|
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|