~repos /only-bible-app

#kotlin#android#ios

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

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


f15b6a01 Peter John

1 year ago
rewrite state
.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 Drawer(
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(state.bibles) { b ->
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(verses: List<Verse>) {
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 state = LocalSettings.current!!
29
+ val model = LocalSettings.current!!
16
- val bookNames = verses.distinctBy { it.bookName }.map { it.bookName }
17
- Drawer(bookNames, navController) { openDrawer ->
30
+ if (model.uiState.value.isLoading) {
18
- NavHost(
31
+ Box(
19
- navController = navController,
32
+ modifier = Modifier
33
+ .fillMaxWidth()
20
- startDestination = ChapterScreenProps(state.getBookIndex(), state.getChapterIndex())
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
- composable<ChapterScreenProps>(
51
+ composable<ChapterScreenProps>(
23
- enterTransition = {
52
+ enterTransition = {
24
- val props = this.targetState.toRoute<ChapterScreenProps>()
53
+ val props = this.targetState.toRoute<ChapterScreenProps>()
25
- slideIntoContainer(
54
+ slideIntoContainer(
26
- Dir.valueOf(props.dir).slideDirection(),
55
+ Dir.valueOf(props.dir).slideDirection(),
27
- tween(400),
56
+ tween(400),
28
- )
57
+ )
29
- },
58
+ },
30
- // exitTransition = {
59
+ // exitTransition = {
60
+ // val props = this.targetState.toRoute<ChapterScreenProps>()
31
- // slideOutOfContainer(
61
+ // slideOutOfContainer(
32
- // AnimatedContentTransitionScope.SlideDirection.Left,
62
+ // Dir.valueOf(props.dir).slideDirection(),
33
- // tween(300),
63
+ // tween(400),
64
+ // )
34
- // )
65
+ // },
35
- // },
36
- popEnterTransition = {
66
+ popEnterTransition = {
37
- slideIntoContainer(
67
+ slideIntoContainer(
38
- AnimatedContentTransitionScope.SlideDirection.Right,
68
+ AnimatedContentTransitionScope.SlideDirection.Right,
39
- tween(400),
69
+ tween(400),
40
- )
70
+ )
41
- },
71
+ },
42
- popExitTransition = {
72
+ popExitTransition = {
43
- slideOutOfContainer(
73
+ slideOutOfContainer(
44
- AnimatedContentTransitionScope.SlideDirection.Right,
74
+ AnimatedContentTransitionScope.SlideDirection.Right,
45
- tween(400),
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 androidx.compose.foundation.background
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.Icon
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.ui.Modifier
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
- private fun background(isDark: Boolean): Color {
37
+ fun getColorScheme(context: Context, themeType: ThemeType, darkTheme: Boolean): ColorScheme {
47
- return when {
38
+ return when {
48
- this == Light || (this == Auto && !isDark) -> Color.White
39
+ themeType == ThemeType.Light || (themeType == ThemeType.Auto && !darkTheme) ->
49
- else -> if (isDark) Color.Unspecified else Color(0xFF3E4042)
50
- }
51
- }
52
-
53
- private fun tint(isDark: Boolean): Color {
40
+ dynamicLightColorScheme(context).copy(
54
- return when {
55
- this == Light || (this == Auto && !isDark) -> Color(0xFF424547)
56
- else -> Color(0xFFd3d7da)
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
- Dark -> Icon(
45
+ else ->
75
- painter = painterResource(id = R.drawable.text_theme),
76
- contentDescription = "Dark",
46
+ dynamicDarkColorScheme(context).copy(
77
- tint = this.tint(darkTheme),
47
+ background = Color(0xFF090F12),
78
- modifier = Modifier
79
- .background(
80
- if (darkTheme && currentTheme == Dark) MaterialTheme.colorScheme.background
81
- else Color(0xFF3E4042)
48
+ surface = Color(0xFF090F12),
82
- )
83
- .padding(4.dp)
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
- val colorScheme = when {
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 fontFamily = state.fontType.family()
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
- LoadingBox(isLoading = state.isLoading) {
111
+ val headingColor = MaterialTheme.colorScheme.onSurface // MaterialTheme.colorScheme.primary,
110
- Scaffold(
112
+ Scaffold(
111
- modifier = Modifier
113
+ modifier = Modifier
112
- .fillMaxSize(),
114
+ .fillMaxSize(),
113
- topBar = {
115
+ topBar = {
114
- TopAppBar(
116
+ TopAppBar(
115
- modifier = Modifier
117
+ modifier = Modifier
116
- .height(72.dp),
118
+ .height(72.dp),
117
- title = {
119
+ title = {
118
- Row(
120
+ Row(
119
- modifier = Modifier
121
+ modifier = Modifier
120
- .fillMaxWidth(),
122
+ .fillMaxWidth(),
121
- horizontalArrangement = Arrangement.Start,
123
+ horizontalArrangement = Arrangement.Start,
122
- verticalAlignment = Alignment.CenterVertically,
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 = bookNames[bookIndex],
139
+ text = "${chapterIndex + 1}",
129
140
  style = TextStyle(
130
141
  fontSize = 22.sp,
131
142
  fontWeight = FontWeight.W500,
132
- color = MaterialTheme.colorScheme.primary,
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
- actions = {
149
+ actions = {
147
- if (selectedVerses.isNotEmpty()) {
150
+ if (selectedVerses.isNotEmpty()) {
148
- TextButton(onClick = {
151
+ TextButton(onClick = {
149
- scope.launch {
152
+ scope.launch {
150
- convertVersesToSpeech(scope,
153
+ convertVersesToSpeech(
154
+ scope,
151
- selectedVerses.sortedBy { it.verseIndex })
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
- TextButton(onClick = {
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
- Text(
161
+ Icon(
173
- text = state.getBibleName().substring(0, 2).uppercase(),
162
+ imageVector = Icons.Outlined.FaceRetouchingNatural,
174
- style = TextStyle(
175
- fontSize = 18.sp,
176
- fontWeight = FontWeight.W500,
163
+ contentDescription = "Audio",
177
- ),
178
164
  )
179
165
  }
180
- TextButton(
166
+ TextButton(onClick = {
181
- onClick = {
167
+ shareVerses(context, selectedVerses)
182
- state.showSheet()
168
+ selectedVerses = listOf()
183
- }) {
169
+ }) {
170
+ Icon(
171
+ imageVector = Icons.Outlined.Share,
184
- Icon(Icons.Outlined.MoreVert, "More")
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
- ) { innerPadding ->
234
+ ) { innerPadding ->
230
- LazyColumn(
235
+ LazyColumn(
231
- verticalArrangement = Arrangement.spacedBy(12.dp),
236
+ verticalArrangement = Arrangement.spacedBy(12.dp),
232
- modifier = Modifier
237
+ modifier = Modifier
233
- .fillMaxSize()
238
+ .fillMaxSize()
234
- .padding(innerPadding)
239
+ .padding(innerPadding)
235
- .padding(horizontal = 16.dp)
240
+ .padding(horizontal = 16.dp)
236
- .pointerInput(Unit) {
241
+ .pointerInput(Unit) {
237
- detectHorizontalDragGestures(onDragEnd = {
242
+ detectHorizontalDragGestures(onDragEnd = {
238
243
  // println("END " + dragAmount);
239
- if (dragAmount < 0) {
244
+ if (dragAmount < 0) {
240
- val pair = Verse.getForwardPair(bookIndex, chapterIndex)
245
+ val pair = Verse.getForwardPair(bookIndex, chapterIndex)
241
- navController.navigate(
246
+ navController.navigate(
242
- ChapterScreenProps(
247
+ ChapterScreenProps(
243
- bookIndex = pair.first,
248
+ bookIndex = pair.first,
244
- chapterIndex = pair.second,
249
+ chapterIndex = pair.second,
245
- )
246
250
  )
251
+ )
247
- } else if (dragAmount > 0) {
252
+ } else if (dragAmount > 0) {
248
- val pair = Verse.getBackwardPair(bookIndex, chapterIndex)
253
+ val pair = Verse.getBackwardPair(bookIndex, chapterIndex)
249
- if (navController.previousBackStackEntry != null) {
254
+ if (navController.previousBackStackEntry != null) {
250
- val previousBook =
255
+ val previousBook =
251
- navController.previousBackStackEntry?.arguments?.getInt("book")
256
+ navController.previousBackStackEntry?.arguments?.getInt("book")
252
- ?: 0
257
+ ?: 0
253
- val previousChapter =
258
+ val previousChapter =
254
- navController.previousBackStackEntry?.arguments?.getInt("chapter")
259
+ navController.previousBackStackEntry?.arguments?.getInt("chapter")
255
- ?: 0
260
+ ?: 0
256
261
  // println("currentBackStackEntry ${previousBook} ${previousChapter} || ${pair.first} ${pair.second}")
257
- if (previousBook == pair.first && previousChapter == pair.second) {
262
+ if (previousBook == pair.first && previousChapter == pair.second) {
258
- println("Popped")
263
+ println("Popped")
259
- navController.popBackStack()
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
- }, onHorizontalDrag = { change, da ->
285
+ }, onHorizontalDrag = { change, da ->
281
- dragAmount = da
286
+ dragAmount = da
282
- change.consume()
287
+ change.consume()
283
- })
288
+ })
284
- }) {
289
+ }) {
285
- items(chapterVerses) { v ->
290
+ items(chapterVerses) { v ->
286
- if (v.heading.isNotEmpty()) {
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
- selectedVerses = if (selectedVerses.contains(v)) {
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 = fontFamily,
297
+ fontFamily = fontType.family(),
320
- color = if (darkTheme)
321
- Color(0xFFBCBCBC)
322
- else
323
- Color(0xFF000104),
324
- fontWeight = boldWeight,
325
- fontSize = (17 + state.fontSizeDelta).sp,
298
+ fontSize = (16 + fontSizeDelta).sp,
326
- lineHeight = (23 + state.fontSizeDelta).sp,
299
+ fontWeight = FontWeight.W700,
327
- letterSpacing = 0.sp,
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
- val verseNo = "${v.verseIndex + 1} "
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
- append(verseNo)
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
- append(spanned.toString())
349
+ append(spanned.toString())
344
- spans
350
+ spans
345
- .filter { it !is BulletSpan }
351
+ .filter { it !is BulletSpan }
346
- .forEach { span ->
352
+ .forEach { span ->
347
- val start = spanned.getSpanStart(span)
353
+ val start = spanned.getSpanStart(span)
348
- val end = spanned.getSpanEnd(span)
354
+ val end = spanned.getSpanEnd(span)
349
- when (span) {
355
+ when (span) {
350
- is ForegroundColorSpan ->
356
+ is ForegroundColorSpan ->
351
- if (darkTheme) SpanStyle(color = Color(0xFFFF636B))
357
+ if (darkTheme) SpanStyle(color = Color(0xFFFF636B))
352
- else SpanStyle(color = Color(0xFFFF0000))
358
+ else SpanStyle(color = Color(0xFFFF0000))
353
359
 
354
- is StyleSpan -> when (span.style) {
360
+ is StyleSpan -> when (span.style) {
355
- Typeface.BOLD -> SpanStyle(fontWeight = FontWeight.Bold)
361
+ Typeface.BOLD -> SpanStyle(fontWeight = FontWeight.Bold)
356
- Typeface.ITALIC -> SpanStyle(fontStyle = FontStyle.Italic)
362
+ Typeface.ITALIC -> SpanStyle(fontStyle = FontStyle.Italic)
357
- Typeface.BOLD_ITALIC -> SpanStyle(
363
+ Typeface.BOLD_ITALIC -> SpanStyle(
358
- fontWeight = FontWeight.Bold,
364
+ fontWeight = FontWeight.Bold,
359
- fontStyle = FontStyle.Italic,
365
+ fontStyle = FontStyle.Italic,
360
- )
366
+ )
361
367
 
362
- else -> null
368
+ else -> null
363
- }
369
+ }
364
370
 
365
- else -> {
371
+ else -> {
366
- null
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
- @SuppressLint("UnsafeIntentLaunch")
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
- val verses = Verse.parseFromBibleTxt(
19
+ model.loadBible(applicationContext)
25
- bibleName,
26
- assets.open("bibles/${fileName}.txt").bufferedReader()
20
+ splashScreen.setKeepOnScreenCondition{model.uiState.value.isLoading}
27
- )
28
21
  setContent {
29
- CompositionLocalProvider(LocalSettings provides state) {
22
+ CompositionLocalProvider(LocalSettings provides model) {
30
23
  AppTheme {
31
- AppHost(
24
+ AppHost()
32
- verses = verses
33
- )
34
- if (state.showBottomSheet) {
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 kotlinx.coroutines.delay
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 state = LocalSettings.current!!
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
- state.closeSheet()
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
- state.closeSheet()
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
- state.updateFontSize(state.fontSizeDelta - 1)
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
- state.updateFontSize(state.fontSizeDelta + 1)
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
- state.updateBoldEnabled(!state.boldEnabled)
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 (state.boldEnabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground,
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 (state.fontType == it) BorderStroke(
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
- state.updateFontType(it)
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
- color = MaterialTheme.colorScheme.onBackground,
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 { t ->
233
+ ThemeType.entries.map {
224
234
  Surface(
225
235
  shape = RoundedCornerShape(8.dp),
226
- border = if (state.themeType == t) BorderStroke(
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
- state.closeSheet()
255
+ settings.closeSheet()
241
- state.updateTheme(t)
256
+ setThemeType(it)
242
257
  }
243
258
  }
244
259
  ) {
260
+ when (it) {
261
+ Light -> Icon(
245
- t.ThemeIcon(state.themeType)
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" }