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


db3ee3f1 Peter John

1 year ago
fix scroll restore and add splash
app/src/main/AndroidManifest.xml CHANGED
@@ -8,12 +8,12 @@
8
8
  android:icon="@mipmap/ic_launcher"
9
9
  android:roundIcon="@mipmap/ic_launcher_round"
10
10
  android:supportsRtl="true"
11
- android:theme="@style/Theme.BibleApp"
11
+ android:theme="@style/Theme.BibleAppSplash"
12
12
  >
13
13
  <activity
14
14
  android:name=".MainActivity"
15
15
  android:exported="true"
16
- android:theme="@style/Theme.BibleApp">
16
+ android:theme="@style/Theme.BibleAppSplash">
17
17
  <intent-filter>
18
18
  <action android:name="android.intent.action.MAIN" />
19
19
 
app/src/main/java/dev/pyrossh/onlyBible/AppDrawer.kt CHANGED
@@ -38,7 +38,6 @@ 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
- import androidx.navigation.NavController
42
41
  import dev.pyrossh.onlyBible.domain.Bible
43
42
  import dev.pyrossh.onlyBible.domain.Verse
44
43
  import kotlinx.coroutines.Job
@@ -66,7 +65,7 @@ fun LazyGridScope.header(
66
65
  @Composable
67
66
  fun AppDrawer(
68
67
  model: AppViewModel,
69
- navController: NavController,
68
+ navigateToChapter: (ChapterScreenProps) -> Unit,
70
69
  content: @Composable ((MenuType, Int) -> Job) -> Unit
71
70
  ) {
72
71
  val context = LocalContext.current
@@ -224,7 +223,7 @@ fun AppDrawer(
224
223
  items(Verse.chapterSizes[bookIndex]) { c ->
225
224
  QuickButton("${c + 1}") {
226
225
  scope.launch {
227
- navController.navigate(
226
+ navigateToChapter(
228
227
  ChapterScreenProps(
229
228
  bookIndex = bookIndex,
230
229
  chapterIndex = c,
app/src/main/java/dev/pyrossh/onlyBible/AppHost.kt CHANGED
@@ -17,10 +17,57 @@ import androidx.navigation.compose.NavHost
17
17
  import androidx.navigation.compose.composable
18
18
  import androidx.navigation.compose.rememberNavController
19
19
  import androidx.navigation.toRoute
20
+ import dev.pyrossh.onlyBible.domain.Verse
20
21
 
21
22
  @Composable
22
23
  fun AppHost(model: AppViewModel = viewModel()) {
23
24
  val navController = rememberNavController()
25
+ val navigateToChapter = { props: ChapterScreenProps ->
26
+ model.resetScrollState()
27
+ navController.navigate(props)
28
+ }
29
+ val onSwipeLeft = {
30
+ val pair = Verse.getForwardPair(model.bookIndex, model.chapterIndex)
31
+ navigateToChapter(
32
+ ChapterScreenProps(
33
+ bookIndex = pair.first,
34
+ chapterIndex = pair.second,
35
+ )
36
+ )
37
+ }
38
+ val onSwipeRight = {
39
+ val pair = Verse.getBackwardPair(model.bookIndex, model.chapterIndex)
40
+ if (navController.previousBackStackEntry != null) {
41
+ val previousBook =
42
+ navController.previousBackStackEntry?.arguments?.getInt("book")
43
+ ?: 0
44
+ val previousChapter =
45
+ navController.previousBackStackEntry?.arguments?.getInt("chapter")
46
+ ?: 0
47
+ // println("currentBackStackEntry ${previousBook} ${previousChapter} || ${pair.first} ${pair.second}")
48
+ if (previousBook == pair.first && previousChapter == pair.second) {
49
+ println("Popped")
50
+ navController.popBackStack()
51
+ } else {
52
+ navController.navigate(
53
+ ChapterScreenProps(
54
+ bookIndex = pair.first,
55
+ chapterIndex = pair.second,
56
+ dir = Dir.Right.name,
57
+ )
58
+ )
59
+ }
60
+ } else {
61
+ println("navigated navigate")
62
+ navController.navigate(
63
+ ChapterScreenProps(
64
+ bookIndex = pair.first,
65
+ chapterIndex = pair.second,
66
+ dir = Dir.Right.name
67
+ )
68
+ )
69
+ }
70
+ }
24
71
  Box(
25
72
  modifier = Modifier
26
73
  .fillMaxSize()
@@ -29,7 +76,7 @@ fun AppHost(model: AppViewModel = viewModel()) {
29
76
  }
30
77
  ) {
31
78
  if (model.verses.isNotEmpty()) {
32
- AppDrawer(model = model, navController = navController) { openDrawer ->
79
+ AppDrawer(model = model, navigateToChapter = navigateToChapter) { openDrawer ->
33
80
  NavHost(
34
81
  navController = navController,
35
82
  startDestination = ChapterScreenProps(model.bookIndex, model.chapterIndex)
@@ -70,9 +117,10 @@ fun AppHost(model: AppViewModel = viewModel()) {
70
117
  }
71
118
  ChapterScreen(
72
119
  model = model,
120
+ onSwipeLeft = onSwipeLeft,
121
+ onSwipeRight = onSwipeRight,
73
122
  bookIndex = props.bookIndex,
74
123
  chapterIndex = props.chapterIndex,
75
- navController = navController,
76
124
  openDrawer = openDrawer,
77
125
  )
78
126
  }
app/src/main/java/dev/pyrossh/onlyBible/AppViewModel.kt CHANGED
@@ -3,6 +3,7 @@ package dev.pyrossh.onlyBible
3
3
  import android.app.Application
4
4
  import android.content.Context
5
5
  import android.content.Intent
6
+ import androidx.compose.foundation.lazy.LazyListState
6
7
  import androidx.compose.runtime.MutableState
7
8
  import androidx.compose.runtime.getValue
8
9
  import androidx.compose.runtime.mutableStateOf
@@ -68,6 +69,10 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
68
69
  defaultValue = 0,
69
70
  getPreferencesKey = ::intPreferencesKey,
70
71
  )
72
+ var scrollState = LazyListState(
73
+ 0,
74
+ 0
75
+ )
71
76
  var themeType by preferenceMutableState(
72
77
  coroutineScope = viewModelScope,
73
78
  context = context,
@@ -121,6 +126,14 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
121
126
 
122
127
  fun initData(p: Preferences) {
123
128
  bibleName = p[stringPreferencesKey("bibleName")] ?: "English"
129
+ scrollState = LazyListState(
130
+ p[intPreferencesKey("scrollIndex")] ?: 0,
131
+ p[intPreferencesKey("scrollOffset")] ?: 0
132
+ )
133
+ }
134
+
135
+ fun resetScrollState() {
136
+ scrollState = LazyListState(0, 0)
124
137
  }
125
138
 
126
139
  fun loadBible(context: Context) {
app/src/main/java/dev/pyrossh/onlyBible/ChapterScreen.kt CHANGED
@@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.height
23
23
  import androidx.compose.foundation.layout.padding
24
24
  import androidx.compose.foundation.layout.size
25
25
  import androidx.compose.foundation.lazy.LazyColumn
26
+ import androidx.compose.foundation.lazy.LazyListState
26
27
  import androidx.compose.foundation.lazy.items
27
28
  import androidx.compose.material.icons.Icons
28
29
  import androidx.compose.material.icons.outlined.Cancel
@@ -45,7 +46,6 @@ import androidx.compose.runtime.Composable
45
46
  import androidx.compose.runtime.DisposableEffect
46
47
  import androidx.compose.runtime.MutableIntState
47
48
  import androidx.compose.runtime.getValue
48
- import androidx.compose.runtime.mutableFloatStateOf
49
49
  import androidx.compose.runtime.mutableIntStateOf
50
50
  import androidx.compose.runtime.mutableStateOf
51
51
  import androidx.compose.runtime.remember
@@ -66,7 +66,6 @@ import androidx.compose.ui.text.font.FontWeight
66
66
  import androidx.compose.ui.text.withStyle
67
67
  import androidx.compose.ui.unit.dp
68
68
  import androidx.compose.ui.unit.sp
69
- import androidx.navigation.NavController
70
69
  import com.microsoft.cognitiveservices.speech.SpeechSynthesisEventArgs
71
70
  import dev.pyrossh.onlyBible.domain.Bible
72
71
  import dev.pyrossh.onlyBible.domain.Verse
@@ -138,9 +137,10 @@ suspend fun PointerInputScope.detectSwipe(
138
137
  @Composable
139
138
  fun ChapterScreen(
140
139
  model: AppViewModel,
140
+ onSwipeLeft: () -> Unit,
141
+ onSwipeRight: () -> Any,
141
142
  bookIndex: Int,
142
143
  chapterIndex: Int,
143
- navController: NavController,
144
144
  openDrawer: (MenuType, Int) -> Job,
145
145
  ) {
146
146
  val context = LocalContext.current
@@ -155,9 +155,6 @@ fun ChapterScreen(
155
155
  var isPlaying by rememberSaveable {
156
156
  mutableStateOf(false)
157
157
  }
158
- var dragAmount by remember {
159
- mutableFloatStateOf(0.0f)
160
- }
161
158
  DisposableEffect(Unit) {
162
159
  val started = { _: Any, _: SpeechSynthesisEventArgs ->
163
160
  isPlaying = true
@@ -322,6 +319,9 @@ fun ChapterScreen(
322
319
  },
323
320
  ) { innerPadding ->
324
321
  LazyColumn(
322
+ state = rememberSaveable(saver = LazyListState.Saver) {
323
+ model.scrollState
324
+ },
325
325
  verticalArrangement = Arrangement.spacedBy(12.dp),
326
326
  modifier = Modifier
327
327
  .fillMaxSize()
@@ -329,48 +329,8 @@ fun ChapterScreen(
329
329
  .padding(horizontal = 16.dp)
330
330
  .pointerInput(Unit) {
331
331
  detectSwipe(
332
- onSwipeLeft = {
332
+ onSwipeLeft = onSwipeLeft,
333
- val pair = Verse.getForwardPair(bookIndex, chapterIndex)
334
- navController.navigate(
335
- ChapterScreenProps(
336
- bookIndex = pair.first,
337
- chapterIndex = pair.second,
338
- )
339
- )
340
- },
341
- onSwipeRight = {
333
+ onSwipeRight = { onSwipeRight() },
342
- val pair = Verse.getBackwardPair(bookIndex, chapterIndex)
343
- if (navController.previousBackStackEntry != null) {
344
- val previousBook =
345
- navController.previousBackStackEntry?.arguments?.getInt("book")
346
- ?: 0
347
- val previousChapter =
348
- navController.previousBackStackEntry?.arguments?.getInt("chapter")
349
- ?: 0
350
- // println("currentBackStackEntry ${previousBook} ${previousChapter} || ${pair.first} ${pair.second}")
351
- if (previousBook == pair.first && previousChapter == pair.second) {
352
- println("Popped")
353
- navController.popBackStack()
354
- } else {
355
- navController.navigate(
356
- ChapterScreenProps(
357
- bookIndex = pair.first,
358
- chapterIndex = pair.second,
359
- dir = Dir.Right.name,
360
- )
361
- )
362
- }
363
- } else {
364
- // println("navigated navigate")
365
- navController.navigate(
366
- ChapterScreenProps(
367
- bookIndex = pair.first,
368
- chapterIndex = pair.second,
369
- dir = Dir.Right.name
370
- )
371
- )
372
- }
373
- },
374
334
  )
375
335
  }) {
376
336
  items(chapterVerses) { v ->
app/src/main/java/dev/pyrossh/onlyBible/MainActivity.kt CHANGED
@@ -1,11 +1,17 @@
1
1
  package dev.pyrossh.onlyBible
2
2
 
3
+ import android.animation.ObjectAnimator
3
4
  import android.os.Bundle
5
+ import android.view.View
6
+ import android.view.animation.AccelerateInterpolator
4
7
  import androidx.activity.ComponentActivity
5
8
  import androidx.activity.compose.setContent
6
9
  import androidx.activity.enableEdgeToEdge
7
10
  import androidx.activity.viewModels
11
+ import androidx.core.animation.doOnEnd
8
12
  import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
13
+ import androidx.datastore.preferences.core.edit
14
+ import androidx.datastore.preferences.core.intPreferencesKey
9
15
  import androidx.lifecycle.lifecycleScope
10
16
  import kotlinx.coroutines.flow.first
11
17
  import kotlinx.coroutines.launch
@@ -15,9 +21,25 @@ class MainActivity : ComponentActivity() {
15
21
  private val model by viewModels<AppViewModel>()
16
22
 
17
23
  override fun onCreate(savedInstanceState: Bundle?) {
18
- val splashScreen = installSplashScreen()
24
+ val splashScreen = installSplashScreen().apply {
25
+ setOnExitAnimationListener { viewProvider ->
26
+ ObjectAnimator.ofFloat(
27
+ viewProvider.view,
28
+ View.TRANSLATION_Y,
29
+ 0f,
30
+ -viewProvider.view.height.toFloat()
31
+ ).apply {
32
+ interpolator = AccelerateInterpolator()
33
+ duration = 200L
34
+ doOnEnd {
35
+ viewProvider.remove()
36
+ enableEdgeToEdge()
37
+ }
38
+ start()
39
+ }
40
+ }
41
+ }
19
42
  super.onCreate(savedInstanceState)
20
- enableEdgeToEdge()
21
43
  lifecycleScope.launch {
22
44
  val data = applicationContext.dataStore.data.first()
23
45
  model.initData(data)
@@ -33,5 +55,16 @@ class MainActivity : ComponentActivity() {
33
55
  }
34
56
  }
35
57
  }
58
+
59
+ override fun onSaveInstanceState(outState: Bundle) {
60
+ super.onSaveInstanceState(outState)
61
+ lifecycleScope.launch {
62
+ applicationContext.dataStore.edit {
63
+ println("saveData ${model.scrollState.firstVisibleItemIndex}")
64
+ it[intPreferencesKey("scrollIndex")] = model.scrollState.firstVisibleItemIndex
65
+ it[intPreferencesKey("scrollOffset")] = model.scrollState.firstVisibleItemScrollOffset
66
+ }
67
+ }
68
+ }
36
69
  }
37
70
 
app/src/main/res/values/themes.xml CHANGED
@@ -1,5 +1,9 @@
1
1
  <?xml version="1.0" encoding="utf-8"?>
2
2
  <resources>
3
-
3
+ <style name="Theme.BibleAppSplash" parent="Theme.SplashScreen">
4
+ <item name="windowSplashScreenAnimatedIcon">@drawable/cross_2</item>
5
+ <item name="windowSplashScreenBackground">#000000</item>
6
+ <item name="postSplashScreenTheme">@style/Theme.BibleApp</item>
7
+ </style>
4
8
  <style name="Theme.BibleApp" parent="android:Theme.Material.Light.NoActionBar" />
5
9
  </resources>