~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.


9f8b0ac2 Peter John

1 year ago
fix datastore and loader
.idea/other.xml ADDED
@@ -0,0 +1,263 @@
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/src/main/java/dev/pyrossh/onlyBible/AppHost.kt CHANGED
@@ -6,56 +6,40 @@ import androidx.compose.foundation.layout.Arrangement
6
6
  import androidx.compose.foundation.layout.Box
7
7
  import androidx.compose.foundation.layout.Column
8
8
  import androidx.compose.foundation.layout.fillMaxSize
9
- import androidx.compose.foundation.layout.fillMaxWidth
10
- import androidx.compose.foundation.layout.wrapContentHeight
11
9
  import androidx.compose.material3.CircularProgressIndicator
12
10
  import androidx.compose.runtime.Composable
13
11
  import androidx.compose.runtime.SideEffect
14
- import androidx.compose.runtime.getValue
15
- import androidx.compose.runtime.setValue
16
12
  import androidx.compose.ui.Alignment
17
13
  import androidx.compose.ui.Modifier
14
+ import androidx.compose.ui.draw.alpha
18
15
  import androidx.lifecycle.viewmodel.compose.viewModel
19
16
  import androidx.navigation.compose.NavHost
20
17
  import androidx.navigation.compose.composable
21
18
  import androidx.navigation.compose.rememberNavController
22
19
  import androidx.navigation.toRoute
23
- import dev.burnoo.compose.rememberpreference.rememberIntPreference
24
20
 
25
21
  @Composable
26
22
  fun AppHost(model: AppViewModel = viewModel()) {
27
23
  val navController = rememberNavController()
28
- var bookIndex by rememberIntPreference(keyName = "bookIndex", initialValue = 0, defaultValue = 0)
29
- var chapterIndex by rememberIntPreference(keyName = "chapterIndex", initialValue = 0, defaultValue = 0)
30
- if (model.isLoading) {
31
- Box(
24
+ Box(
32
- modifier = Modifier
33
- .fillMaxWidth()
34
- .wrapContentHeight()
35
- ) {
36
- Column(
37
- modifier = Modifier.fillMaxSize(),
25
+ modifier = Modifier.fillMaxSize().let {
38
- verticalArrangement = Arrangement.Center,
26
+ if (model.isLoading) it.alpha(0.5f) else it
39
- horizontalAlignment = Alignment.CenterHorizontally
40
- ) {
41
- CircularProgressIndicator()
42
- }
43
27
  }
44
- } else {
28
+ ) {
45
- val bookNames = model.verses.distinctBy { it.bookName }.map { it.bookName }
29
+ if (model.verses.isNotEmpty()) {
46
- AppDrawer(navController = navController) { openDrawer ->
30
+ AppDrawer(navController = navController) { openDrawer ->
47
- NavHost(
31
+ NavHost(
48
- navController = navController,
32
+ navController = navController,
49
- startDestination = ChapterScreenProps(bookIndex, chapterIndex)
33
+ startDestination = ChapterScreenProps(model.bookIndex, model.chapterIndex)
50
- ) {
34
+ ) {
51
- composable<ChapterScreenProps>(
35
+ composable<ChapterScreenProps>(
52
- enterTransition = {
36
+ enterTransition = {
53
- val props = this.targetState.toRoute<ChapterScreenProps>()
37
+ val props = this.targetState.toRoute<ChapterScreenProps>()
54
- slideIntoContainer(
38
+ slideIntoContainer(
55
- Dir.valueOf(props.dir).slideDirection(),
39
+ Dir.valueOf(props.dir).slideDirection(),
56
- tween(400),
40
+ tween(400),
57
- )
41
+ )
58
- },
42
+ },
59
43
  // exitTransition = {
60
44
  // val props = this.targetState.toRoute<ChapterScreenProps>()
61
45
  // slideOutOfContainer(
@@ -63,34 +47,43 @@ fun AppHost(model: AppViewModel = viewModel()) {
63
47
  // tween(400),
64
48
  // )
65
49
  // },
66
- popEnterTransition = {
50
+ popEnterTransition = {
67
- slideIntoContainer(
51
+ slideIntoContainer(
68
- AnimatedContentTransitionScope.SlideDirection.Right,
52
+ AnimatedContentTransitionScope.SlideDirection.Right,
69
- tween(400),
53
+ tween(400),
70
- )
54
+ )
71
- },
55
+ },
72
- popExitTransition = {
56
+ popExitTransition = {
73
- slideOutOfContainer(
57
+ slideOutOfContainer(
74
- AnimatedContentTransitionScope.SlideDirection.Right,
58
+ AnimatedContentTransitionScope.SlideDirection.Right,
75
- tween(400),
59
+ tween(400),
60
+ )
61
+ }
62
+ ) {
63
+ val props = it.toRoute<ChapterScreenProps>()
64
+ SideEffect {
65
+ model.bookIndex = props.bookIndex
66
+ model.chapterIndex = props.chapterIndex
67
+ }
68
+ ChapterScreen(
69
+ model = model,
70
+ bookIndex = props.bookIndex,
71
+ chapterIndex = props.chapterIndex,
72
+ navController = navController,
73
+ openDrawer = openDrawer,
76
74
  )
77
75
  }
78
- ) {
79
- val props = it.toRoute<ChapterScreenProps>()
80
- SideEffect {
81
- bookIndex = props.bookIndex
82
- chapterIndex = props.chapterIndex
83
- }
84
- ChapterScreen(
85
- model = model,
86
- bookIndex = props.bookIndex,
87
- chapterIndex = props.chapterIndex,
88
- navController = navController,
89
- openDrawer = openDrawer,
90
- )
91
76
  }
92
77
  }
93
78
  }
79
+ if (model.isLoading) {
80
+ Column(
81
+ modifier = Modifier.fillMaxSize(),
82
+ verticalArrangement = Arrangement.Center,
83
+ horizontalAlignment = Alignment.CenterHorizontally
84
+ ) {
85
+ CircularProgressIndicator()
86
+ }
87
+ }
94
88
  }
95
-
96
89
  }
app/src/main/java/dev/pyrossh/onlyBible/AppViewModel.kt CHANGED
@@ -35,10 +35,33 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
35
35
  get() = getApplication<Application>()
36
36
  var isLoading by mutableStateOf(true)
37
37
  var isOnError by mutableStateOf(false)
38
- var bibleName by mutableStateOf("English")
39
38
  var verses by mutableStateOf(listOf<Verse>())
40
39
  var bookNames by mutableStateOf(listOf<String>())
41
40
  var showBottomSheet by mutableStateOf(false)
41
+ var bibleName by preferenceMutableState(
42
+ coroutineScope = viewModelScope,
43
+ context = context,
44
+ keyName = "bibleName",
45
+ initialValue = "English",
46
+ defaultValue = "English",
47
+ getPreferencesKey = ::stringPreferencesKey,
48
+ )
49
+ var bookIndex by preferenceMutableState(
50
+ coroutineScope = viewModelScope,
51
+ context = context,
52
+ keyName = "bookIndex",
53
+ initialValue = 0,
54
+ defaultValue = 0,
55
+ getPreferencesKey = ::intPreferencesKey,
56
+ )
57
+ var chapterIndex by preferenceMutableState(
58
+ coroutineScope = viewModelScope,
59
+ context = context,
60
+ keyName = "chapterIndex",
61
+ initialValue = 0,
62
+ defaultValue = 0,
63
+ getPreferencesKey = ::intPreferencesKey,
64
+ )
42
65
  var themeType by preferenceMutableState(
43
66
  coroutineScope = viewModelScope,
44
67
  context = context,
@@ -88,10 +111,15 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
88
111
 
89
112
  fun setBibleName(context: Context, b: String) {
90
113
  bibleName = b
91
- context.getSharedPreferences("settings", Context.MODE_PRIVATE).edit().putString("bibleName", b).apply()
114
+ context.getSharedPreferences("settings", Context.MODE_PRIVATE).edit()
115
+ .putString("bibleName", b).apply()
92
116
  loadBible(context)
93
117
  }
94
118
 
119
+ fun initData(p: Preferences) {
120
+ bibleName = p[stringPreferencesKey("bibleName")] ?: "English"
121
+ }
122
+
95
123
  fun loadBible(context: Context) {
96
124
  viewModelScope.launch(Dispatchers.IO) {
97
125
  launch(Dispatchers.Main) {
app/src/main/java/dev/pyrossh/onlyBible/MainActivity.kt CHANGED
@@ -6,6 +6,9 @@ import androidx.activity.compose.setContent
6
6
  import androidx.activity.enableEdgeToEdge
7
7
  import androidx.activity.viewModels
8
8
  import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
9
+ import androidx.lifecycle.lifecycleScope
10
+ import kotlinx.coroutines.flow.collectLatest
11
+ import kotlinx.coroutines.launch
9
12
 
10
13
  class MainActivity : ComponentActivity() {
11
14
 
@@ -15,7 +18,12 @@ class MainActivity : ComponentActivity() {
15
18
  val splashScreen = installSplashScreen()
16
19
  super.onCreate(savedInstanceState)
17
20
  enableEdgeToEdge()
21
+ lifecycleScope.launch {
22
+ applicationContext.dataStore.data.collectLatest {
23
+ model.initData(it)
18
- model.loadBible(applicationContext)
24
+ model.loadBible(applicationContext)
25
+ }
26
+ }
19
27
  splashScreen.setKeepOnScreenCondition { model.isLoading }
20
28
  setContent {
21
29
  AppTheme {
readme.md CHANGED
@@ -1,5 +1,7 @@
1
1
  ## TODO
2
2
 
3
3
  * Fix swipe calculations (look at horizontal pager)
4
- * Improve Paging in dark scheme
4
+ * Improve Paging in dark mode
5
+ * Improve splash screen in dark mode
6
+ * Fix Audio play/pause
5
- * Use DataManager
7
+ * Use toolbar for actions