~repos /only-bible-app
git clone https://pyrossh.dev/repos/only-bible-app.git
The only bible app you will ever need. No ads. No in-app purchases. No distractions.
a6c55e90
—
pyrossh 1 year ago
use compose multiplatform
- app/build.gradle.kts +67 -47
- app/src/androidMain/AndroidManifest.xml +28 -0
- app/src/androidMain/kotlin/Platform.android.kt +7 -0
- app/src/androidTest/java/dev/pyrossh/onlyBible/ExampleInstrumentedTest.kt +0 -22
- app/src/{main/assets/bibles → commonMain/composeResources/files}/bn.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/en_bsb.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/en_kjv.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/gu.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/hi.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/kn.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/ml.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/ne.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/or.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/pa.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/ta.txt +0 -0
- app/src/{main/assets/bibles → commonMain/composeResources/files}/te.txt +0 -0
- app/src/commonMain/kotlin/Platform.kt +5 -0
- app/src/iosMain/kotlin/MainViewController.kt +3 -0
- app/src/iosMain/kotlin/Platform.ios.kt +7 -0
- app/src/main/AndroidManifest.xml +2 -1
- app/src/main/java/dev/pyrossh/onlyBible/AppHost.kt +18 -16
- app/src/main/java/dev/pyrossh/onlyBible/AppViewModel.kt +10 -52
- app/src/main/java/dev/pyrossh/onlyBible/ChapterScreen.kt +12 -24
- app/src/main/java/dev/pyrossh/onlyBible/MainActivity.kt +4 -2
- app/src/main/java/dev/pyrossh/onlyBible/Platform.android.kt +22 -0
- app/src/main/java/dev/pyrossh/onlyBible/composables/ChapterSelector.kt +1 -8
- app/src/main/java/dev/pyrossh/onlyBible/composables/VerseHeading.kt +1 -8
- app/src/main/java/dev/pyrossh/onlyBible/composables/VerseText.kt +1 -8
- app/src/main/java/dev/pyrossh/onlyBible/utils/Navigation.kt +24 -1
- app/src/test/java/dev/pyrossh/onlyBible/ExampleUnitTest.kt +0 -16
- build.gradle.kts +2 -1
- app/src/main/assets/cross.svg → cross.svg +0 -0
- gradle.properties +0 -1
- gradle/libs.versions.toml +6 -2
app/build.gradle.kts
CHANGED
|
@@ -1,49 +1,87 @@
|
|
|
1
|
+
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
|
1
|
-
import
|
|
2
|
+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
2
3
|
import java.io.FileInputStream
|
|
3
4
|
import java.util.Properties
|
|
4
5
|
|
|
5
6
|
val keystorePropertiesFile = rootProject.file("keystore.properties")
|
|
6
7
|
val keystoreProperties = Properties()
|
|
7
8
|
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
|
9
|
+
val pkgName = "dev.pyrossh.onlyBible" //"dev.pyrossh.only_bible_app"
|
|
8
10
|
|
|
9
11
|
plugins {
|
|
12
|
+
alias(libs.plugins.kotlinMultiplatform)
|
|
10
13
|
alias(libs.plugins.android.application)
|
|
14
|
+
alias(libs.plugins.jetbrainsCompose)
|
|
11
15
|
alias(libs.plugins.compose.compiler)
|
|
12
|
-
alias(libs.plugins.jetbrains.kotlin.android)
|
|
13
16
|
alias(libs.plugins.kotlinx.serializer)
|
|
14
17
|
alias(libs.plugins.secrets.gradle.plugin)
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
kotlin {
|
|
21
|
+
androidTarget {
|
|
22
|
+
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
|
23
|
+
compilerOptions {
|
|
24
|
+
jvmTarget.set(JvmTarget.JVM_11)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// listOf(
|
|
29
|
+
// iosX64(),
|
|
30
|
+
// iosArm64(),
|
|
31
|
+
// iosSimulatorArm64()
|
|
32
|
+
// ).forEach { iosTarget ->
|
|
33
|
+
// iosTarget.binaries.framework {
|
|
34
|
+
// baseName = "ComposeApp"
|
|
35
|
+
// isStatic = true
|
|
36
|
+
// }
|
|
37
|
+
// }
|
|
38
|
+
|
|
39
|
+
sourceSets {
|
|
40
|
+
androidMain.dependencies {
|
|
41
|
+
implementation(libs.androidx.activity.compose)
|
|
42
|
+
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
commonMain.dependencies {
|
|
46
|
+
implementation(compose.runtime)
|
|
47
|
+
implementation(compose.foundation)
|
|
48
|
+
implementation(compose.material3)
|
|
49
|
+
implementation(compose.materialIconsExtended)
|
|
50
|
+
implementation(compose.ui)
|
|
51
|
+
implementation(compose.components.resources)
|
|
52
|
+
implementation(libs.androidx.activity.compose)
|
|
53
|
+
implementation(libs.androidx.navigation.compose)
|
|
54
|
+
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
|
55
|
+
implementation(libs.speech.client.sdk)
|
|
56
|
+
implementation(libs.kotlinx.serialization.json)
|
|
57
|
+
implementation(libs.kotlinx.coroutines.core)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
17
62
|
android {
|
|
18
|
-
namespace =
|
|
63
|
+
namespace = pkgName
|
|
19
64
|
compileSdk = 34
|
|
20
65
|
|
|
66
|
+
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
|
|
67
|
+
sourceSets["main"].res.srcDirs("src/androidMain/res")
|
|
68
|
+
sourceSets["main"].resources.srcDirs("src/commonMain/resources")
|
|
69
|
+
|
|
21
70
|
defaultConfig {
|
|
22
|
-
applicationId =
|
|
71
|
+
applicationId = pkgName
|
|
23
|
-
minSdk = 31
|
|
24
72
|
targetSdk = 34
|
|
73
|
+
minSdk = 31
|
|
25
|
-
versionCode =
|
|
74
|
+
versionCode = 5
|
|
26
|
-
versionName = "1.
|
|
75
|
+
versionName = "1.1.0"
|
|
27
|
-
|
|
28
|
-
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
29
76
|
vectorDrawables {
|
|
30
77
|
useSupportLibrary = true
|
|
31
78
|
}
|
|
32
|
-
resourceConfigurations += listOf(
|
|
33
|
-
"en",
|
|
34
|
-
"bn",
|
|
35
|
-
"gu",
|
|
36
|
-
"hi",
|
|
37
|
-
"kn",
|
|
38
|
-
"ml",
|
|
39
|
-
"ne",
|
|
40
|
-
"or",
|
|
41
|
-
"pa",
|
|
42
|
-
"te",
|
|
43
|
-
"ta"
|
|
44
|
-
)
|
|
45
79
|
}
|
|
46
|
-
|
|
80
|
+
packaging {
|
|
81
|
+
resources {
|
|
82
|
+
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
47
85
|
signingConfigs {
|
|
48
86
|
create("release") {
|
|
49
87
|
keyAlias = keystoreProperties["keyAlias"] as String
|
|
@@ -65,39 +103,21 @@ android {
|
|
|
65
103
|
}
|
|
66
104
|
}
|
|
67
105
|
compileOptions {
|
|
68
|
-
sourceCompatibility =
|
|
106
|
+
sourceCompatibility = JavaVersion.VERSION_11
|
|
69
|
-
targetCompatibility =
|
|
107
|
+
targetCompatibility = JavaVersion.VERSION_11
|
|
70
108
|
}
|
|
71
109
|
buildFeatures {
|
|
72
110
|
compose = true
|
|
73
111
|
buildConfig = true
|
|
74
112
|
}
|
|
75
|
-
packaging {
|
|
76
|
-
resources {
|
|
77
|
-
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
kotlinOptions {
|
|
81
|
-
jvmTarget = JavaLanguageVersion.of(17).toString()
|
|
82
|
-
}
|
|
83
113
|
}
|
|
84
114
|
|
|
85
115
|
composeCompiler {
|
|
86
116
|
enableStrongSkippingMode = true
|
|
87
117
|
}
|
|
88
118
|
|
|
89
|
-
|
|
119
|
+
compose.resources {
|
|
90
|
-
implementation(libs.androidx.activity.compose)
|
|
91
|
-
|
|
120
|
+
publicResClass = true
|
|
92
|
-
|
|
121
|
+
packageOfResClass = pkgName
|
|
93
|
-
implementation(libs.androidx.material.icons.extended)
|
|
94
|
-
implementation(libs.androidx.navigation.compose)
|
|
95
|
-
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
96
|
-
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
|
97
|
-
|
|
122
|
+
generateResClass = always
|
|
98
|
-
implementation(libs.kotlinx.serialization.json)
|
|
99
|
-
implementation(libs.kotlinx.coroutines.core)
|
|
100
|
-
|
|
101
|
-
testImplementation(libs.junit)
|
|
102
|
-
androidTestImplementation(libs.androidx.junit)
|
|
103
123
|
}
|
app/src/androidMain/AndroidManifest.xml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
|
3
|
+
|
|
4
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
5
|
+
|
|
6
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO"
|
|
7
|
+
tools:node="remove" />
|
|
8
|
+
<uses-permission
|
|
9
|
+
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
|
10
|
+
tools:node="remove" />
|
|
11
|
+
|
|
12
|
+
<application
|
|
13
|
+
android:label="@string/app_name"
|
|
14
|
+
android:icon="@mipmap/ic_launcher"
|
|
15
|
+
android:roundIcon="@mipmap/ic_launcher_round"
|
|
16
|
+
android:supportsRtl="true">
|
|
17
|
+
<activity
|
|
18
|
+
android:name=".MainActivity"
|
|
19
|
+
android:exported="true"
|
|
20
|
+
android:theme="@android:style/Theme.Material.Light.NoActionBar">
|
|
21
|
+
<!-- TODO: change to Theme.Material3.DayNight.NoActionBar -->
|
|
22
|
+
<intent-filter>
|
|
23
|
+
<action android:name="android.intent.action.MAIN" />
|
|
24
|
+
<category android:name="android.intent.category.LAUNCHER" />
|
|
25
|
+
</intent-filter>
|
|
26
|
+
</activity>
|
|
27
|
+
</application>
|
|
28
|
+
</manifest>
|
app/src/androidMain/kotlin/Platform.android.kt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import android.os.Build
|
|
2
|
+
|
|
3
|
+
class AndroidPlatform : Platform {
|
|
4
|
+
override val name: String = "Android ${Build.VERSION.SDK_INT}"
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
actual fun getPlatform(): Platform = AndroidPlatform()
|
app/src/androidTest/java/dev/pyrossh/onlyBible/ExampleInstrumentedTest.kt
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
package dev.pyrossh.onlyBible
|
|
2
|
-
|
|
3
|
-
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
4
|
-
import androidx.test.platform.app.InstrumentationRegistry
|
|
5
|
-
import org.junit.Assert.assertEquals
|
|
6
|
-
import org.junit.Test
|
|
7
|
-
import org.junit.runner.RunWith
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Instrumented test, which will execute on an Android device.
|
|
11
|
-
*
|
|
12
|
-
* See [testing documentation](http://d.android.com/tools/testing).
|
|
13
|
-
*/
|
|
14
|
-
@RunWith(AndroidJUnit4::class)
|
|
15
|
-
class ExampleInstrumentedTest {
|
|
16
|
-
@Test
|
|
17
|
-
fun useAppContext() {
|
|
18
|
-
// Context of the app under test.
|
|
19
|
-
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
|
20
|
-
assertEquals("dev.pyrossh.onlyBible", appContext.packageName)
|
|
21
|
-
}
|
|
22
|
-
}
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/bn.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/en_bsb.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/en_kjv.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/gu.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/hi.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/kn.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/ml.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/ne.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/or.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/pa.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/ta.txt
RENAMED
|
File without changes
|
app/src/{main/assets/bibles → commonMain/composeResources/files}/te.txt
RENAMED
|
File without changes
|
app/src/commonMain/kotlin/Platform.kt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
interface Platform {
|
|
2
|
+
val name: String
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
expect fun getPlatform(): Platform
|
app/src/iosMain/kotlin/MainViewController.kt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import androidx.compose.ui.window.ComposeUIViewController
|
|
2
|
+
|
|
3
|
+
fun MainViewController() = ComposeUIViewController { App() }
|
app/src/iosMain/kotlin/Platform.ios.kt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import platform.UIKit.UIDevice
|
|
2
|
+
|
|
3
|
+
class IOSPlatform: Platform {
|
|
4
|
+
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
actual fun getPlatform(): Platform = IOSPlatform()
|
app/src/main/AndroidManifest.xml
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
<activity
|
|
18
18
|
android:name=".MainActivity"
|
|
19
19
|
android:exported="true"
|
|
20
|
-
android:theme="@android:style/Theme.DeviceDefault.
|
|
20
|
+
android:theme="@android:style/Theme.DeviceDefault.DayNight">
|
|
21
|
+
<!-- TODO: change to Theme.Material3.DayNight.NoActionBar -->
|
|
21
22
|
<intent-filter>
|
|
22
23
|
<action android:name="android.intent.action.MAIN" />
|
|
23
24
|
<category android:name="android.intent.category.LAUNCHER" />
|
app/src/main/java/dev/pyrossh/onlyBible/AppHost.kt
CHANGED
|
@@ -5,10 +5,11 @@ import androidx.compose.animation.ExitTransition
|
|
|
5
5
|
import androidx.compose.animation.core.tween
|
|
6
6
|
import androidx.compose.runtime.Composable
|
|
7
7
|
import androidx.compose.runtime.CompositionLocalProvider
|
|
8
|
+
import androidx.navigation.NavType
|
|
8
9
|
import androidx.navigation.compose.NavHost
|
|
9
10
|
import androidx.navigation.compose.composable
|
|
10
11
|
import androidx.navigation.compose.rememberNavController
|
|
11
|
-
import androidx.navigation.
|
|
12
|
+
import androidx.navigation.navArgument
|
|
12
13
|
import dev.pyrossh.onlyBible.utils.LocalNavController
|
|
13
14
|
|
|
14
15
|
@Composable
|
|
@@ -20,17 +21,13 @@ fun AppHost(
|
|
|
20
21
|
CompositionLocalProvider(LocalNavController provides navController) {
|
|
21
22
|
NavHost(
|
|
22
23
|
navController = navController,
|
|
23
|
-
startDestination =
|
|
24
|
+
startDestination = "{bookId}:{chapterId}:{verseId}"
|
|
24
|
-
model.bookIndex,
|
|
25
|
-
model.chapterIndex,
|
|
26
|
-
model.verseIndex
|
|
27
|
-
)
|
|
28
25
|
) {
|
|
29
|
-
composable
|
|
26
|
+
composable(
|
|
30
27
|
enterTransition = {
|
|
31
|
-
val props = this.targetState.toRoute<ChapterScreenProps>()
|
|
28
|
+
// val props = this.targetState.toRoute<ChapterScreenProps>()
|
|
32
29
|
slideIntoContainer(
|
|
33
|
-
Dir.valueOf(
|
|
30
|
+
Dir.valueOf(Dir.Left.name).slideDirection(),
|
|
34
31
|
tween(400),
|
|
35
32
|
)
|
|
36
33
|
},
|
|
@@ -41,19 +38,24 @@ fun AppHost(
|
|
|
41
38
|
EnterTransition.None
|
|
42
39
|
},
|
|
43
40
|
popExitTransition = {
|
|
44
|
-
val props = this.targetState.toRoute<ChapterScreenProps>()
|
|
41
|
+
// val props = this.targetState.toRoute<ChapterScreenProps>()
|
|
45
42
|
slideOutOfContainer(
|
|
46
|
-
Dir.valueOf(
|
|
43
|
+
Dir.valueOf(Dir.Left.name).reverse().slideDirection(),
|
|
47
44
|
tween(400),
|
|
48
45
|
)
|
|
49
|
-
}
|
|
46
|
+
},
|
|
47
|
+
route = "{bookId}:{chapterId}:{verseId}",
|
|
48
|
+
arguments = listOf(
|
|
49
|
+
navArgument("bookId") { type = NavType.IntType },
|
|
50
|
+
navArgument("chapterId") { type = NavType.IntType },
|
|
51
|
+
navArgument("verseId") { type = NavType.IntType },
|
|
52
|
+
),
|
|
50
53
|
) {
|
|
51
|
-
val props = it.toRoute<ChapterScreenProps>()
|
|
52
54
|
ChapterScreen(
|
|
53
55
|
model = model,
|
|
54
|
-
bookIndex =
|
|
56
|
+
bookIndex = it.arguments?.getInt("bookId") ?: 0,
|
|
55
|
-
chapterIndex =
|
|
57
|
+
chapterIndex = it.arguments?.getInt("chapterId") ?: 0,
|
|
56
|
-
verseIndex =
|
|
58
|
+
verseIndex = it.arguments?.getInt("verseId") ?: 0,
|
|
57
59
|
)
|
|
58
60
|
}
|
|
59
61
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/AppViewModel.kt
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
package dev.pyrossh.onlyBible
|
|
2
2
|
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.content.Context.MODE_PRIVATE
|
|
5
|
-
import android.content.
|
|
3
|
+
import android.content.SharedPreferences
|
|
6
|
-
import android.text.Html
|
|
7
4
|
import androidx.compose.runtime.getValue
|
|
8
5
|
import androidx.compose.runtime.mutableIntStateOf
|
|
9
6
|
import androidx.compose.runtime.mutableStateOf
|
|
@@ -13,11 +10,9 @@ import androidx.lifecycle.viewModelScope
|
|
|
13
10
|
import com.microsoft.cognitiveservices.speech.SpeechConfig
|
|
14
11
|
import com.microsoft.cognitiveservices.speech.SpeechSynthesisEventArgs
|
|
15
12
|
import com.microsoft.cognitiveservices.speech.SpeechSynthesizer
|
|
16
|
-
import dev.pyrossh.onlyBible.domain.BOOKS_COUNT
|
|
17
13
|
import dev.pyrossh.onlyBible.domain.Bible
|
|
18
14
|
import dev.pyrossh.onlyBible.domain.Verse
|
|
19
15
|
import dev.pyrossh.onlyBible.domain.bibles
|
|
20
|
-
import dev.pyrossh.onlyBible.domain.chapterSizes
|
|
21
16
|
import kotlinx.coroutines.Dispatchers
|
|
22
17
|
import kotlinx.coroutines.FlowPreview
|
|
23
18
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
@@ -27,6 +22,7 @@ import kotlinx.coroutines.flow.combine
|
|
|
27
22
|
import kotlinx.coroutines.flow.debounce
|
|
28
23
|
import kotlinx.coroutines.flow.stateIn
|
|
29
24
|
import kotlinx.coroutines.launch
|
|
25
|
+
import org.jetbrains.compose.resources.ExperimentalResourceApi
|
|
30
26
|
import org.json.JSONObject
|
|
31
27
|
import java.io.IOException
|
|
32
28
|
|
|
@@ -103,12 +99,11 @@ class AppViewModel : ViewModel() {
|
|
|
103
99
|
selectedVerses.value = listOf()
|
|
104
100
|
}
|
|
105
101
|
|
|
106
|
-
fun loadData(
|
|
102
|
+
fun loadData(prefs: SharedPreferences) {
|
|
107
103
|
viewModelScope.launch(Dispatchers.IO) {
|
|
108
104
|
viewModelScope.launch(Dispatchers.Main) {
|
|
109
105
|
isLoading = true
|
|
110
106
|
}
|
|
111
|
-
val prefs = context.getSharedPreferences("data", MODE_PRIVATE)
|
|
112
107
|
val bibleFileName = prefs.getString("bible", "en_kjv") ?: "en_kjv"
|
|
113
108
|
bookIndex = prefs.getInt("bookIndex", 0)
|
|
114
109
|
chapterIndex = prefs.getInt("chapterIndex", 0)
|
|
@@ -124,18 +119,18 @@ class AppViewModel : ViewModel() {
|
|
|
124
119
|
)
|
|
125
120
|
highlightedVerses.value = JSONObject(prefs.getString("highlightedVerses", "{}") ?: "{}")
|
|
126
121
|
val localBible = bibles.find { it.filename() == bibleFileName } ?: bibles.first()
|
|
127
|
-
loadBible(localBible
|
|
122
|
+
loadBible(localBible)
|
|
128
123
|
viewModelScope.launch(Dispatchers.Main) {
|
|
129
124
|
isLoading = false
|
|
130
125
|
}
|
|
131
126
|
}
|
|
132
127
|
}
|
|
133
128
|
|
|
129
|
+
@OptIn(ExperimentalResourceApi::class)
|
|
134
|
-
fun loadBible(b: Bible
|
|
130
|
+
suspend fun loadBible(b: Bible) {
|
|
135
131
|
try {
|
|
136
|
-
val buffer =
|
|
137
|
-
|
|
132
|
+
val buffer = Res.readBytes("files/${b.filename()}.txt").decodeToString()
|
|
138
|
-
val localVerses = buffer.
|
|
133
|
+
val localVerses = buffer.split("\n").filter { it.isNotEmpty() }.map {
|
|
139
134
|
val arr = it.split("|")
|
|
140
135
|
val bookName = arr[0]
|
|
141
136
|
val book = arr[1].toInt()
|
|
@@ -159,13 +154,13 @@ class AppViewModel : ViewModel() {
|
|
|
159
154
|
bookNames.value = localVerses.distinctBy { it.bookName }.map { it.bookName }
|
|
160
155
|
}
|
|
161
156
|
} catch (e: IOException) {
|
|
157
|
+
println("-----------------------COULD NOT LOAD FILE")
|
|
162
158
|
e.printStackTrace()
|
|
163
159
|
}
|
|
164
160
|
}
|
|
165
161
|
|
|
166
|
-
fun saveData(
|
|
162
|
+
fun saveData(prefs: SharedPreferences) {
|
|
167
163
|
viewModelScope.launch(Dispatchers.IO) {
|
|
168
|
-
val prefs = context.getSharedPreferences("data", MODE_PRIVATE)
|
|
169
164
|
with(prefs.edit()) {
|
|
170
165
|
putString("bible", bible.filename())
|
|
171
166
|
putInt("bookIndex", bookIndex)
|
|
@@ -215,41 +210,4 @@ class AppViewModel : ViewModel() {
|
|
|
215
210
|
"""
|
|
216
211
|
)
|
|
217
212
|
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
fun getForwardPair(bookIndex: Int, chapterIndex: Int): Pair<Int, Int> {
|
|
221
|
-
val sizes = chapterSizes[bookIndex]
|
|
222
|
-
if (sizes > chapterIndex + 1) {
|
|
223
|
-
return Pair(bookIndex, chapterIndex + 1)
|
|
224
|
-
}
|
|
225
|
-
if (bookIndex + 1 < BOOKS_COUNT) {
|
|
226
|
-
return Pair(bookIndex + 1, 0)
|
|
227
|
-
}
|
|
228
|
-
return Pair(0, 0)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
fun getBackwardPair(bookIndex: Int, chapterIndex: Int): Pair<Int, Int> {
|
|
232
|
-
if (chapterIndex - 1 >= 0) {
|
|
233
|
-
return Pair(bookIndex, chapterIndex - 1)
|
|
234
|
-
}
|
|
235
|
-
if (bookIndex - 1 >= 0) {
|
|
236
|
-
return Pair(bookIndex - 1, chapterSizes[bookIndex - 1] - 1)
|
|
237
|
-
}
|
|
238
|
-
return Pair(BOOKS_COUNT - 1, chapterSizes[BOOKS_COUNT - 1] - 1)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
fun shareVerses(context: Context, verses: List<Verse>) {
|
|
242
|
-
val versesThrough =
|
|
243
|
-
if (verses.size >= 3) "${verses.first().verseIndex + 1}-${verses.last().verseIndex + 1}" else verses.map { it.verseIndex + 1 }
|
|
244
|
-
.joinToString(",")
|
|
245
|
-
val title = "${verses[0].bookName} ${verses[0].chapterIndex + 1}:${versesThrough}"
|
|
246
|
-
val text =
|
|
247
|
-
Html.fromHtml(verses.joinToString("\n") { it.text }, Html.FROM_HTML_MODE_COMPACT).toString()
|
|
248
|
-
val sendIntent = Intent().apply {
|
|
249
|
-
action = Intent.ACTION_SEND
|
|
250
|
-
putExtra(Intent.EXTRA_TEXT, "${title}\n${text}")
|
|
251
|
-
type = "text/plain"
|
|
252
|
-
}
|
|
253
|
-
val shareIntent = Intent.createChooser(sendIntent, null)
|
|
254
|
-
context.startActivity(shareIntent)
|
|
255
213
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/ChapterScreen.kt
CHANGED
|
@@ -28,6 +28,7 @@ import androidx.compose.runtime.collectAsState
|
|
|
28
28
|
import androidx.compose.runtime.getValue
|
|
29
29
|
import androidx.compose.runtime.mutableStateOf
|
|
30
30
|
import androidx.compose.runtime.remember
|
|
31
|
+
import androidx.compose.runtime.rememberCoroutineScope
|
|
31
32
|
import androidx.compose.runtime.saveable.listSaver
|
|
32
33
|
import androidx.compose.runtime.saveable.rememberSaveable
|
|
33
34
|
import androidx.compose.runtime.setValue
|
|
@@ -47,16 +48,12 @@ import dev.pyrossh.onlyBible.composables.VerseHeading
|
|
|
47
48
|
import dev.pyrossh.onlyBible.composables.VerseText
|
|
48
49
|
import dev.pyrossh.onlyBible.utils.LocalNavController
|
|
49
50
|
import dev.pyrossh.onlyBible.utils.detectSwipe
|
|
51
|
+
import dev.pyrossh.onlyBible.utils.getBackwardPair
|
|
52
|
+
import dev.pyrossh.onlyBible.utils.getForwardPair
|
|
53
|
+
import kotlinx.coroutines.Dispatchers
|
|
54
|
+
import kotlinx.coroutines.launch
|
|
50
55
|
import kotlinx.serialization.Serializable
|
|
51
56
|
|
|
52
|
-
@Serializable
|
|
53
|
-
data class ChapterScreenProps(
|
|
54
|
-
val bookIndex: Int,
|
|
55
|
-
val chapterIndex: Int,
|
|
56
|
-
val verseIndex: Int,
|
|
57
|
-
val dir: String = Dir.Left.name,
|
|
58
|
-
)
|
|
59
|
-
|
|
60
57
|
enum class Dir {
|
|
61
58
|
Left, Right;
|
|
62
59
|
|
|
@@ -82,6 +79,7 @@ fun ChapterScreen(
|
|
|
82
79
|
) {
|
|
83
80
|
val view = LocalView.current
|
|
84
81
|
val context = LocalContext.current
|
|
82
|
+
val scope = rememberCoroutineScope()
|
|
85
83
|
val navController = LocalNavController.current
|
|
86
84
|
var isSettingsShown by remember { mutableStateOf(false) }
|
|
87
85
|
var isSearchShown by remember { mutableStateOf(false) }
|
|
@@ -122,7 +120,9 @@ fun ChapterScreen(
|
|
|
122
120
|
onSelected = {
|
|
123
121
|
view.playSoundEffect(SoundEffectConstants.CLICK)
|
|
124
122
|
bibleSelectorShown = false
|
|
123
|
+
scope.launch(Dispatchers.IO) {
|
|
125
|
-
|
|
124
|
+
model.loadBible(it)
|
|
125
|
+
}
|
|
126
126
|
},
|
|
127
127
|
onClose = { bibleSelectorShown = false },
|
|
128
128
|
)
|
|
@@ -233,24 +233,12 @@ fun ChapterScreen(
|
|
|
233
233
|
detectSwipe(
|
|
234
234
|
onSwipeLeft = {
|
|
235
235
|
val pair = getForwardPair(bookIndex, chapterIndex)
|
|
236
|
-
navController.navigate(
|
|
236
|
+
navController.navigate("${pair.first}:${pair.second}:0")
|
|
237
|
-
ChapterScreenProps(
|
|
238
|
-
bookIndex = pair.first,
|
|
239
|
-
chapterIndex = pair.second,
|
|
240
|
-
verseIndex = 0,
|
|
241
|
-
)
|
|
242
|
-
)
|
|
243
237
|
},
|
|
244
238
|
onSwipeRight = {
|
|
245
239
|
val pair = getBackwardPair(bookIndex, chapterIndex)
|
|
246
|
-
navController.navigate(
|
|
240
|
+
navController.navigate("${pair.first}:${pair.second}:0")
|
|
247
|
-
ChapterScreenProps(
|
|
248
|
-
bookIndex = pair.first,
|
|
249
|
-
chapterIndex = pair.second,
|
|
250
|
-
verseIndex = 0,
|
|
251
|
-
|
|
241
|
+
// Dir.Right.name,
|
|
252
|
-
)
|
|
253
|
-
)
|
|
254
242
|
},
|
|
255
243
|
)
|
|
256
244
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/MainActivity.kt
CHANGED
|
@@ -17,7 +17,8 @@ class MainActivity : ComponentActivity() {
|
|
|
17
17
|
enableEdgeToEdge()
|
|
18
18
|
if (savedInstanceState == null) {
|
|
19
19
|
lifecycleScope.launch {
|
|
20
|
+
val prefs = applicationContext.getSharedPreferences("data", MODE_PRIVATE)
|
|
20
|
-
model.loadData(
|
|
21
|
+
model.loadData(prefs)
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
setContent {
|
|
@@ -30,7 +31,8 @@ class MainActivity : ComponentActivity() {
|
|
|
30
31
|
override fun onSaveInstanceState(outState: Bundle) {
|
|
31
32
|
super.onSaveInstanceState(outState)
|
|
32
33
|
lifecycleScope.launch {
|
|
34
|
+
val prefs = applicationContext.getSharedPreferences("data", MODE_PRIVATE)
|
|
33
|
-
model.saveData(
|
|
35
|
+
model.saveData(prefs)
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
}
|
app/src/main/java/dev/pyrossh/onlyBible/Platform.android.kt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package dev.pyrossh.onlyBible
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.text.Html
|
|
6
|
+
import dev.pyrossh.onlyBible.domain.Verse
|
|
7
|
+
|
|
8
|
+
fun shareVerses(context: Context, verses: List<Verse>) {
|
|
9
|
+
val versesThrough =
|
|
10
|
+
if (verses.size >= 3) "${verses.first().verseIndex + 1}-${verses.last().verseIndex + 1}" else verses.map { it.verseIndex + 1 }
|
|
11
|
+
.joinToString(",")
|
|
12
|
+
val title = "${verses[0].bookName} ${verses[0].chapterIndex + 1}:${versesThrough}"
|
|
13
|
+
val text =
|
|
14
|
+
Html.fromHtml(verses.joinToString("\n") { it.text }, Html.FROM_HTML_MODE_COMPACT).toString()
|
|
15
|
+
val sendIntent = Intent().apply {
|
|
16
|
+
action = Intent.ACTION_SEND
|
|
17
|
+
putExtra(Intent.EXTRA_TEXT, "${title}\n${text}")
|
|
18
|
+
type = "text/plain"
|
|
19
|
+
}
|
|
20
|
+
val shareIntent = Intent.createChooser(sendIntent, null)
|
|
21
|
+
context.startActivity(shareIntent)
|
|
22
|
+
}
|
app/src/main/java/dev/pyrossh/onlyBible/composables/ChapterSelector.kt
CHANGED
|
@@ -36,7 +36,6 @@ import androidx.compose.ui.platform.LocalView
|
|
|
36
36
|
import androidx.compose.ui.text.font.FontWeight
|
|
37
37
|
import androidx.compose.ui.unit.dp
|
|
38
38
|
import androidx.compose.ui.window.Dialog
|
|
39
|
-
import dev.pyrossh.onlyBible.ChapterScreenProps
|
|
40
39
|
import dev.pyrossh.onlyBible.domain.Bible
|
|
41
40
|
import dev.pyrossh.onlyBible.domain.chapterSizes
|
|
42
41
|
import dev.pyrossh.onlyBible.domain.engTitles
|
|
@@ -150,13 +149,7 @@ fun ChapterSelector(
|
|
|
150
149
|
onClick = {
|
|
151
150
|
view.playSoundEffect(SoundEffectConstants.CLICK)
|
|
152
151
|
onClose()
|
|
153
|
-
navController.navigate(
|
|
152
|
+
navController.navigate("${bookIndex}:${c}:0")
|
|
154
|
-
ChapterScreenProps(
|
|
155
|
-
bookIndex = bookIndex,
|
|
156
|
-
chapterIndex = c,
|
|
157
|
-
verseIndex = 0,
|
|
158
|
-
)
|
|
159
|
-
)
|
|
160
153
|
}
|
|
161
154
|
) {
|
|
162
155
|
Text(
|
app/src/main/java/dev/pyrossh/onlyBible/composables/VerseHeading.kt
CHANGED
|
@@ -18,7 +18,6 @@ import androidx.compose.ui.text.font.FontWeight
|
|
|
18
18
|
import androidx.compose.ui.text.fromHtml
|
|
19
19
|
import androidx.compose.ui.unit.dp
|
|
20
20
|
import androidx.compose.ui.unit.sp
|
|
21
|
-
import dev.pyrossh.onlyBible.ChapterScreenProps
|
|
22
21
|
import dev.pyrossh.onlyBible.FontType
|
|
23
22
|
import dev.pyrossh.onlyBible.utils.LocalNavController
|
|
24
23
|
|
|
@@ -51,13 +50,7 @@ fun VerseHeading(
|
|
|
51
50
|
view.playSoundEffect(SoundEffectConstants.CLICK)
|
|
52
51
|
val url = (it as LinkAnnotation.Url).url
|
|
53
52
|
val parts = url.split(":")
|
|
54
|
-
navController.navigate(
|
|
53
|
+
navController.navigate("${parts[0]}:${parts[1]}:${parts[2]}")
|
|
55
|
-
ChapterScreenProps(
|
|
56
|
-
bookIndex = parts[0].toInt(),
|
|
57
|
-
chapterIndex = parts[1].toInt(),
|
|
58
|
-
verseIndex = parts[2].toInt(),
|
|
59
|
-
)
|
|
60
|
-
)
|
|
61
54
|
},
|
|
62
55
|
),
|
|
63
56
|
)
|
app/src/main/java/dev/pyrossh/onlyBible/composables/VerseText.kt
CHANGED
|
@@ -55,7 +55,6 @@ import androidx.compose.ui.unit.dp
|
|
|
55
55
|
import androidx.compose.ui.unit.sp
|
|
56
56
|
import androidx.compose.ui.window.Popup
|
|
57
57
|
import dev.pyrossh.onlyBible.AppViewModel
|
|
58
|
-
import dev.pyrossh.onlyBible.ChapterScreenProps
|
|
59
58
|
import dev.pyrossh.onlyBible.FontType
|
|
60
59
|
import dev.pyrossh.onlyBible.darkHighlights
|
|
61
60
|
import dev.pyrossh.onlyBible.domain.Verse
|
|
@@ -282,13 +281,7 @@ private fun Menu(
|
|
|
282
281
|
if (highlightWord != null) {
|
|
283
282
|
IconButton(onClick = {
|
|
284
283
|
view.playSoundEffect(SoundEffectConstants.CLICK)
|
|
285
|
-
navController.navigate(
|
|
286
|
-
ChapterScreenProps(
|
|
287
|
-
bookIndex = verse.bookIndex,
|
|
288
|
-
|
|
284
|
+
navController.navigate("${verse.bookIndex}:${verse.chapterIndex}:${verse.verseIndex}")
|
|
289
|
-
verseIndex = verse.verseIndex,
|
|
290
|
-
)
|
|
291
|
-
)
|
|
292
285
|
}) {
|
|
293
286
|
Icon(
|
|
294
287
|
// modifier = Modifier.size(32.dp),
|
app/src/main/java/dev/pyrossh/onlyBible/utils/Navigation.kt
CHANGED
|
@@ -2,5 +2,28 @@ package dev.pyrossh.onlyBible.utils
|
|
|
2
2
|
|
|
3
3
|
import androidx.compose.runtime.compositionLocalOf
|
|
4
4
|
import androidx.navigation.NavController
|
|
5
|
+
import dev.pyrossh.onlyBible.domain.BOOKS_COUNT
|
|
6
|
+
import dev.pyrossh.onlyBible.domain.chapterSizes
|
|
5
7
|
|
|
6
|
-
val LocalNavController = compositionLocalOf<NavController> { error("No NavController found!") }
|
|
8
|
+
val LocalNavController = compositionLocalOf<NavController> { error("No NavController found!") }
|
|
9
|
+
|
|
10
|
+
fun getForwardPair(bookIndex: Int, chapterIndex: Int): Pair<Int, Int> {
|
|
11
|
+
val sizes = chapterSizes[bookIndex]
|
|
12
|
+
if (sizes > chapterIndex + 1) {
|
|
13
|
+
return Pair(bookIndex, chapterIndex + 1)
|
|
14
|
+
}
|
|
15
|
+
if (bookIndex + 1 < BOOKS_COUNT) {
|
|
16
|
+
return Pair(bookIndex + 1, 0)
|
|
17
|
+
}
|
|
18
|
+
return Pair(0, 0)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
fun getBackwardPair(bookIndex: Int, chapterIndex: Int): Pair<Int, Int> {
|
|
22
|
+
if (chapterIndex - 1 >= 0) {
|
|
23
|
+
return Pair(bookIndex, chapterIndex - 1)
|
|
24
|
+
}
|
|
25
|
+
if (bookIndex - 1 >= 0) {
|
|
26
|
+
return Pair(bookIndex - 1, chapterSizes[bookIndex - 1] - 1)
|
|
27
|
+
}
|
|
28
|
+
return Pair(BOOKS_COUNT - 1, chapterSizes[BOOKS_COUNT - 1] - 1)
|
|
29
|
+
}
|
app/src/test/java/dev/pyrossh/onlyBible/ExampleUnitTest.kt
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
package dev.pyrossh.onlyBible
|
|
2
|
-
|
|
3
|
-
import org.junit.Assert.assertEquals
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Example local unit test, which will execute on the development machine (host).
|
|
8
|
-
*
|
|
9
|
-
* See [testing documentation](http://d.android.com/tools/testing).
|
|
10
|
-
*/
|
|
11
|
-
class ExampleUnitTest {
|
|
12
|
-
@Test
|
|
13
|
-
fun addition_isCorrect() {
|
|
14
|
-
assertEquals(4, 2 + 2)
|
|
15
|
-
}
|
|
16
|
-
}
|
build.gradle.kts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
|
2
2
|
plugins {
|
|
3
|
+
alias(libs.plugins.kotlinMultiplatform) apply false
|
|
3
4
|
alias(libs.plugins.android.application) apply false
|
|
5
|
+
alias(libs.plugins.jetbrainsCompose) apply false
|
|
4
6
|
alias(libs.plugins.compose.compiler) apply false
|
|
5
|
-
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
|
6
7
|
alias(libs.plugins.secrets.gradle.plugin) apply false
|
|
7
8
|
alias(libs.plugins.kotlinx.serializer) apply false
|
|
8
9
|
}
|
app/src/main/assets/cross.svg → cross.svg
RENAMED
|
File without changes
|
gradle.properties
CHANGED
|
@@ -21,4 +21,3 @@ kotlin.code.style=official
|
|
|
21
21
|
# resources declared in the library itself and none from the library's dependencies,
|
|
22
22
|
# thereby reducing the size of the R class for that library
|
|
23
23
|
android.nonTransitiveRClass=true
|
|
24
|
-
android.experimental.androidTest.enableEmulatorControl=true
|
gradle/libs.versions.toml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[versions]
|
|
2
|
-
agp = "8.
|
|
2
|
+
agp = "8.3.0"
|
|
3
3
|
speechClientSdk = "1.34.0"
|
|
4
4
|
foundation = "1.6.8"
|
|
5
5
|
kotlin = "2.0.0"
|
|
@@ -13,9 +13,12 @@ lifecycleRuntimeKtx = "2.8.4"
|
|
|
13
13
|
lifecycleViewmodelCompose = "2.8.4"
|
|
14
14
|
material3 = "1.2.1"
|
|
15
15
|
materialIconsExtended = "1.6.8"
|
|
16
|
+
composePlugin = "1.6.11"
|
|
16
17
|
secretsGradlePlugin = "2.0.1"
|
|
17
18
|
|
|
18
19
|
[libraries]
|
|
20
|
+
#androidx-lifecycle-viewmodel-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "viewmodel" }
|
|
21
|
+
#androidx-navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
|
19
22
|
androidx-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "foundation" }
|
|
20
23
|
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
|
|
21
24
|
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
|
@@ -32,7 +35,8 @@ secrets-gradle-plugin = { module = "com.google.android.libraries.mapsplatform.se
|
|
|
32
35
|
|
|
33
36
|
[plugins]
|
|
34
37
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
|
38
|
+
jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "composePlugin" }
|
|
39
|
+
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
|
35
40
|
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
|
36
|
-
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
|
37
41
|
kotlinx-serializer = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"}
|
|
38
42
|
secrets-gradle-plugin = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secretsGradlePlugin" }
|