Merge branch 'master' into file-tree-browse-feature
This commit is contained in:
commit
24598c79f4
18 changed files with 3210 additions and 127 deletions
73
README.md
73
README.md
|
|
@ -1,13 +1,39 @@
|
||||||
# <p align="center">Book's Story 📖</p>
|
<!------------ Header ------------>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
<a href="">[](https://github.com/Acclorite/book-story/releases)</a>
|
<img src="https://github.com/Acclorite/book-story/blob/master/assets/readme/app_icon.png" width="120" />
|
||||||
<a href="">[]()</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
# <p align="center">Book's Story</p>
|
||||||
|
### <p align="center">Material3 Book Reader for Android</p>
|
||||||
|
<p align="center">Dive into the world of adventure as you discover your favorite book's story!</p>
|
||||||
|
|
||||||
|
<!------------ Badges ------------>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
|
||||||
|
<a href="">[](https://github.com/Acclorite/book-story/releases)</a>
|
||||||
|
<a href="">[](https://f-droid.org/packages/ua.acclorite.book_story/)</a>
|
||||||
|
<a href="">[](https://apt.izzysoft.de/fdroid/index/apk/ua.acclorite.book_story)</a>
|
||||||
|
<a href="">[]()</a>
|
||||||
|
<a href="">[](https://github.com/Acclorite/book-story/stargazers)</a>
|
||||||
|
<a href="">[](https://github.com/Acclorite/book-story/commits/)</a>
|
||||||
|
<a href="">[](https://github.com/Acclorite/book-story/commits/)</a>
|
||||||
|
<a href="">[](/LICENSE)</a>
|
||||||
|
<a href="">[]()</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!------------ Overview ------------>
|
||||||
|
|
||||||
|
# <p align="center">Overview</p>
|
||||||
|
|
||||||
<img src="https://github.com/Acclorite/book-story/blob/master/assets/readme/github_overview.png">
|
<img src="https://github.com/Acclorite/book-story/blob/master/assets/readme/github_overview.png">
|
||||||
Material3 Book Reader built with Jetpack Compose and Kotlin.
|
Built using Jetpack Compose and Kotlin.
|
||||||
|
|
||||||
|
<!------------ Screenshots ------------>
|
||||||
|
|
||||||
## 🖌️ Screenshots + GIF
|
## 🖌️ Screenshots + GIF
|
||||||
|
|
||||||
|
|
@ -24,25 +50,40 @@ Material3 Book Reader built with Jetpack Compose and Kotlin.
|
||||||
|
|
||||||
_You can find more screenshots in [assets](https://github.com/Acclorite/book-story/tree/master/assets) folder._
|
_You can find more screenshots in [assets](https://github.com/Acclorite/book-story/tree/master/assets) folder._
|
||||||
|
|
||||||
|
<!------------ Download guide ------------>
|
||||||
|
|
||||||
## ✈️ Download
|
## ✈️ Download
|
||||||
- _Please note, that the app supports Android versions only from Android 8.0_
|
- _Please note, that the app supports Android versions only from Android 8.0_
|
||||||
|
|
||||||
<div align="start">
|
<div align="left">
|
||||||
|
|
||||||
[<img src="https://raw.githubusercontent.com/ismartcoding/plain-app/main/assets/get-it-on-github.png" alt='Get it on GitHub' height="80">](https://github.com/Acclorite/book-story/releases/latest)
|
[<img src="https://raw.githubusercontent.com/ismartcoding/plain-app/main/assets/get-it-on-github.png" alt='Get it on GitHub' height="80">](https://github.com/Acclorite/book-story/releases/latest)
|
||||||
|
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/en/packages/ua.acclorite.book_story/)
|
||||||
|
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it on IzzyOnDroid" height="80">](https://apt.izzysoft.de/fdroid/index/apk/ua.acclorite.book_story)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
_Currently the app is only available on Github, Fdroid is coming.._
|
_Don't see your favorite app store? Create an [Issue](https://github.com/Acclorite/book-story/issues)! (Except Play Store, this app won't be there)_
|
||||||
|
|
||||||
### 📲 How to download and install
|
### 📲 How to download and install
|
||||||
- _Install from Github_: Click on the "Get it on GitHub" badge above, scroll down and download **book-story.apk** or download the last apk from GitHub [here](https://github.com/Acclorite/book-story/releases/latest/download/book-story.apk). After that, find downloaded file and install it. If you don't understand how to do so, follow [this guide](https://www.lifewire.com/install-apk-on-android-4177185).
|
- _**Install from Github**_: Click on the "Get it on GitHub" badge above, scroll down and download **book-story.apk** or download the last apk from GitHub [here](https://github.com/Acclorite/book-story/releases/latest/download/book-story.apk). After that, find downloaded APK and install it.
|
||||||
- _Install from F-Droid_: Coming soon...
|
- _**Install from F-Droid**_: Click on the "Get it on F-Droid" badge above, scroll down and click "Download APK" under desired Release Version to download the APK file. After that, find downloaded APK and install it.
|
||||||
|
- _**Install from IzzyOnDroid**_: Click on the "Get it on IzzyOnDroid" badge above, scroll down and click "Download" to download the last APK from IzzyOnDroid. After that, find downloaded APK and install it.
|
||||||
|
- If you don't know how to install APK file, follow [this guide](https://www.lifewire.com/install-apk-on-android-4177185).
|
||||||
|
|
||||||
|
### 🗳️ APK Verification
|
||||||
|
- _APK Verification is an important step to ensure APK integrity and prevent risks from getting infected one._
|
||||||
|
- For verification you can use tool called [apksigner](https://developer.android.com/tools/apksigner). Which is the part of Android SDK Build Tools, so you need to install that first.
|
||||||
|
|
||||||
|
| APK SHA-256 certificate |
|
||||||
|
|-------------------------------------------------------------------|
|
||||||
|
| a661a0c81f138e53dff32f8fdc1dd19f8130523914027e2c0296fe245131a7d4 |
|
||||||
|
|
||||||
### 🧭 Afterwards
|
### 🧭 Afterwards
|
||||||
- Open the app and follow basic instructions.
|
- Open the app and follow basic instructions.
|
||||||
- If you don't understand how to use the app, please read everything in the app's Help Screen. (You can find it in: Library, History, Browse → Three Dots → Help)
|
- If you don't understand how to use the app, please read everything in the app's Help Screen. (You can find it in: Library, History, Browse → Three Dots → Help)
|
||||||
- If you still have questions, please feel free to ask in [Discussions](https://github.com/Acclorite/book-story/discussions/categories/q-a), or create an [Issue](https://github.com/Acclorite/book-story/issues) if you feel like some area should be documented better.
|
- If you still have questions, please feel free to ask in [Discussions](https://github.com/Acclorite/book-story/discussions/categories/q-a), or create an [Issue](https://github.com/Acclorite/book-story/issues) if you feel like some area should be documented better.
|
||||||
|
|
||||||
|
<!------------ Features ------------>
|
||||||
|
|
||||||
## ✨ Features
|
## ✨ Features
|
||||||
- 4 supported file formats(.pdf, .txt, .epub, .fb2)
|
- 4 supported file formats(.pdf, .txt, .epub, .fb2)
|
||||||
|
|
@ -61,16 +102,30 @@ _Currently the app is only available on Github, Fdroid is coming.._
|
||||||
- Detailed app functions explanation(Help screen tips)
|
- Detailed app functions explanation(Help screen tips)
|
||||||
- More..
|
- More..
|
||||||
|
|
||||||
|
<!------------ Roadmap ------------>
|
||||||
|
|
||||||
## 😵💫 Upcoming features / Roadmap
|
## 😵💫 Upcoming features / Roadmap
|
||||||
- _You can track project's progress, see what's coming and what is already implemented in [Book's Story: Development Roadmap](https://github.com/users/Acclorite/projects/1)._
|
- _You can track project's progress, see what's coming and what is already implemented in [Book's Story: Development Roadmap](https://github.com/users/Acclorite/projects/1)._
|
||||||
|
|
||||||
|
<!------------ Translation ------------>
|
||||||
|
|
||||||
## 🌐 Translation
|
## 🌐 Translation
|
||||||
- English (Default)
|
- English (Default)
|
||||||
- Ukrainian (Default)
|
- Ukrainian (Default)
|
||||||
|
|
||||||
|
<!------------ Contribution ------------>
|
||||||
|
|
||||||
|
## 🤝 How to contribute
|
||||||
|
- **The most improtant thing is I do not want someone's code.** This project is maintaining only by me and I want it to be this way. Creating Pull Requests will not have much sense as they will probably be closed.
|
||||||
|
- **Share your ideas and suggestions.** Your ideas and suggestions are always welcome, if you miss some feature or got an interesting idea, feel free to create a new [Issue](https://github.com/Acclorite/book-story/issues).
|
||||||
|
- **Report bugs**. Had a crash or something went wrong? Create a new [Issue](https://github.com/Acclorite/book-story/issues), the more information you can give the better it is.
|
||||||
|
- **Enjoy the app**. Your best contribution is using my app and enjoying it!
|
||||||
|
- _**About localization**. Planning to implement localiztion platform, so users can do what I physically cannot - translate the app on multiple languages. You can find more information about this in the corresponding issue([#22](https://github.com/Acclorite/book-story/issues/22))._
|
||||||
|
|
||||||
|
<!------------ Credits and License ------------>
|
||||||
|
|
||||||
## 🤗 Credits
|
## 🤗 Credits
|
||||||
- [Tachiyomi (Mihon)](https://github.com/mihonapp/mihon) (Github)
|
- [Tachiyomi (Mihon)](https://github.com/mihonapp/mihon) (Github)
|
||||||
- [Libre-Sudoku](https://github.com/kaajjo/Libre-Sudoku) (Github)
|
|
||||||
- [Kitsune](https://github.com/Drumber/Kitsune) (Github)
|
- [Kitsune](https://github.com/Drumber/Kitsune) (Github)
|
||||||
- [Material Design Icons](https://fonts.google.com/icons) (Google Fonts)
|
- [Material Design Icons](https://fonts.google.com/icons) (Google Fonts)
|
||||||
- Also listed in About > Credits.
|
- Also listed in About > Credits.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
|
|
@ -17,8 +19,8 @@ android {
|
||||||
applicationId = "ua.acclorite.book_story"
|
applicationId = "ua.acclorite.book_story"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 5
|
versionCode = 6
|
||||||
versionName = "1.1.0"
|
versionName = "1.1.1"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
|
@ -34,21 +36,19 @@ android {
|
||||||
getByName("debug") {
|
getByName("debug") {
|
||||||
applicationIdSuffix = ".debug"
|
applicationIdSuffix = ".debug"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getByName("release") {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = false
|
||||||
|
|
||||||
|
proguardFiles("proguard-rules.pro")
|
||||||
|
}
|
||||||
|
|
||||||
create("release-debug") {
|
create("release-debug") {
|
||||||
initWith(getByName("release"))
|
initWith(getByName("release"))
|
||||||
applicationIdSuffix = ".release.debug"
|
applicationIdSuffix = ".release.debug"
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
}
|
}
|
||||||
|
|
||||||
getByName("release") {
|
|
||||||
isMinifyEnabled = false
|
|
||||||
isShrinkResources = false
|
|
||||||
|
|
||||||
proguardFiles(
|
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
||||||
"proguard-rules.pro"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
|
@ -63,6 +63,10 @@ android {
|
||||||
composeOptions {
|
composeOptions {
|
||||||
kotlinCompilerExtensionVersion = "1.5.7"
|
kotlinCompilerExtensionVersion = "1.5.7"
|
||||||
}
|
}
|
||||||
|
dependenciesInfo {
|
||||||
|
includeInApk = false
|
||||||
|
includeInBundle = false
|
||||||
|
}
|
||||||
packaging {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||||
|
|
@ -72,12 +76,9 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
aboutLibraries {
|
aboutLibraries {
|
||||||
outputFileName = "aboutlibraries.json"
|
registerAndroidTasks = false
|
||||||
|
prettyPrint = true
|
||||||
gitHubApiToken = properties["github-key"] as? String
|
gitHubApiToken = gradleLocalProperties(rootDir, providers)["github-key"] as? String
|
||||||
offlineMode = false
|
|
||||||
fetchRemoteLicense = true
|
|
||||||
fetchRemoteFunding = false
|
|
||||||
|
|
||||||
filterVariants = arrayOf("debug", "release", "release-debug")
|
filterVariants = arrayOf("debug", "release", "release-debug")
|
||||||
excludeFields = arrayOf("generated", "funding", "description")
|
excludeFields = arrayOf("generated", "funding", "description")
|
||||||
|
|
@ -107,13 +108,13 @@ dependencies {
|
||||||
|
|
||||||
// All dependencies
|
// All dependencies
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4")
|
||||||
implementation("com.google.accompanist:accompanist-swiperefresh:0.24.2-alpha")
|
implementation("com.google.accompanist:accompanist-swiperefresh:0.34.0")
|
||||||
|
|
||||||
// Dagger - Hilt
|
// Dagger - Hilt
|
||||||
implementation("com.google.dagger:hilt-android:2.51.1")
|
implementation("com.google.dagger:hilt-android:2.52")
|
||||||
ksp("com.google.dagger:hilt-android-compiler:2.51")
|
ksp("com.google.dagger:hilt-android-compiler:2.52")
|
||||||
implementation("com.google.dagger:hilt-compiler:latest.release")
|
implementation("com.google.dagger:hilt-compiler:2.52")
|
||||||
ksp("androidx.hilt:hilt-compiler:latest.release")
|
ksp("androidx.hilt:hilt-compiler:1.2.0")
|
||||||
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
|
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
|
||||||
|
|
||||||
// Room
|
// Room
|
||||||
|
|
@ -124,47 +125,45 @@ dependencies {
|
||||||
implementation("androidx.room:room-ktx:2.6.1")
|
implementation("androidx.room:room-ktx:2.6.1")
|
||||||
|
|
||||||
// Datastore
|
// Datastore
|
||||||
implementation("androidx.datastore:datastore-preferences:latest.release")
|
implementation("androidx.datastore:datastore-preferences:1.1.1")
|
||||||
|
|
||||||
// Splash API
|
// Splash API
|
||||||
implementation("androidx.core:core-splashscreen:latest.release")
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
|
|
||||||
// Permission Handling
|
// Permission Handling
|
||||||
implementation("com.google.accompanist:accompanist-permissions:latest.release")
|
implementation("com.google.accompanist:accompanist-permissions:0.34.0")
|
||||||
|
|
||||||
// Pdf parser
|
// Pdf parser
|
||||||
implementation("com.tom-roush:pdfbox-android:latest.release")
|
implementation("com.tom-roush:pdfbox-android:2.0.27.0")
|
||||||
|
|
||||||
// Epub parser
|
// Epub parser
|
||||||
implementation("com.positiondev.epublib:epublib-core:3.1") {
|
implementation("com.positiondev.epublib:epublib-core:3.1") {
|
||||||
exclude("org.slf4j")
|
|
||||||
exclude("xmlpull")
|
exclude("xmlpull")
|
||||||
}
|
}
|
||||||
implementation("org.slf4j:slf4j-android:latest.release")
|
implementation("org.jsoup:jsoup:1.18.1")
|
||||||
implementation("org.jsoup:jsoup:latest.release")
|
|
||||||
|
|
||||||
// Fb2 parser
|
// Fb2 parser
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:latest.release")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.1")
|
||||||
|
|
||||||
// App Compat for Language Switcher
|
// App Compat for Language Switcher
|
||||||
implementation("androidx.appcompat:appcompat:latest.release")
|
implementation("androidx.appcompat:appcompat:1.7.0")
|
||||||
implementation("androidx.appcompat:appcompat-resources:latest.release")
|
implementation("androidx.appcompat:appcompat-resources:1.7.0")
|
||||||
|
|
||||||
// Coil for loading bitmaps
|
// Coil for loading bitmaps
|
||||||
implementation("io.coil-kt:coil-compose:2.6.0")
|
implementation("io.coil-kt:coil-compose:2.7.0")
|
||||||
|
|
||||||
// Retrofit
|
// Retrofit
|
||||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
implementation("com.squareup.retrofit2:retrofit:2.11.0")
|
||||||
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
|
implementation("com.squareup.retrofit2:converter-moshi:2.11.0")
|
||||||
implementation("com.squareup.moshi:moshi:1.15.1")
|
implementation("com.squareup.moshi:moshi:1.15.1")
|
||||||
implementation("com.squareup.moshi:moshi-kotlin:1.15.1")
|
implementation("com.squareup.moshi:moshi-kotlin:1.15.1")
|
||||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||||
implementation("com.squareup.okhttp3:logging-interceptor:4.9.1")
|
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
|
||||||
|
|
||||||
// About open source libraries
|
// About open source libraries
|
||||||
implementation("com.mikepenz:aboutlibraries-core:latest.release")
|
implementation("com.mikepenz:aboutlibraries-core:11.2.2")
|
||||||
implementation("com.mikepenz:aboutlibraries-compose-m3:latest.release")
|
implementation("com.mikepenz:aboutlibraries-compose-m3:11.2.2")
|
||||||
|
|
||||||
// Drag & Drop
|
// Drag & Drop
|
||||||
implementation("sh.calvin.reorderable:reorderable:2.2.0")
|
implementation("sh.calvin.reorderable:reorderable:2.3.0")
|
||||||
}
|
}
|
||||||
16
app/proguard-rules.pro
vendored
16
app/proguard-rules.pro
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
# Proguard-rules
|
# Proguard-rules
|
||||||
-dontwarn org.xmlpull.v1.**
|
-dontwarn android.content.res.**
|
||||||
-dontwarn com.gemalto.jp2.JP2Decoder
|
-dontwarn com.gemalto.jp2.JP2Decoder
|
||||||
-dontwarn com.google.auto.service.AutoService
|
-dontwarn com.google.auto.service.AutoService
|
||||||
-dontwarn javax.lang.model.SourceVersion
|
-dontwarn javax.lang.model.SourceVersion
|
||||||
|
|
@ -31,11 +31,17 @@
|
||||||
-dontwarn javax.lang.model.util.SimpleTypeVisitor8
|
-dontwarn javax.lang.model.util.SimpleTypeVisitor8
|
||||||
-dontwarn javax.lang.model.util.Types
|
-dontwarn javax.lang.model.util.Types
|
||||||
-dontwarn javax.tools.Diagnostic$Kind
|
-dontwarn javax.tools.Diagnostic$Kind
|
||||||
-dontwarn org.kxml2.io.**
|
-dontwarn org.kxml2.io.KXml**
|
||||||
|
-dontwarn org.slf4j.Logger
|
||||||
|
-dontwarn org.slf4j.LoggerFactory
|
||||||
|
-dontwarn org.xmlpull.v1.XmlPullParserFactory
|
||||||
|
|
||||||
-keep class com.mikepenz.aboutlibraries.** { *; }
|
# Fixes issue with displaying .xml files, but may produce reproducibility issues
|
||||||
-keep class org.xmlpull.** { *; }
|
-keep class org.xmlpull.** { *; }
|
||||||
-keepclassmembers class org.xmlpull.** { *; }
|
-keepclassmembers class org.xmlpull.** { *; }
|
||||||
|
|
||||||
# Debug rule
|
# Keeping names of classes that produce undeterministic results
|
||||||
#-keepnames class ** { *; }
|
-keepnames class **
|
||||||
|
-keepnames class org.xmlpull.** { *; }
|
||||||
|
-keepclassmembernames class org.xmlpull.** { *; }
|
||||||
|
-keepnames class kotlin.reflect.jvm.internal.impl.builtins.PrimitiveType { values(); }
|
||||||
|
|
@ -12,6 +12,11 @@
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<!-- Removing DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION, which is not used -->
|
||||||
|
<uses-permission
|
||||||
|
android:name="ua.acclorite.book_story.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||||
|
tools:node="remove" />
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
|
|
||||||
<!-- Translator -->
|
<!-- Translator -->
|
||||||
|
|
@ -43,6 +48,7 @@
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:enableOnBackInvokedCallback="true"
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:usesCleartextTraffic="false"
|
android:usesCleartextTraffic="false"
|
||||||
|
android:largeHeap="true"
|
||||||
tools:targetApi="tiramisu"
|
tools:targetApi="tiramisu"
|
||||||
tools:ignore="DataExtractionRules">
|
tools:ignore="DataExtractionRules">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,20 +53,6 @@ class PdfTextParser @Inject constructor(private val application: Application) :
|
||||||
return@forEachIndexed
|
return@forEachIndexed
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
line.all {
|
|
||||||
if (it == ' ') {
|
|
||||||
true
|
|
||||||
} else if (it.isUpperCase() || it.isDigit() || it == '-') {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
return@forEachIndexed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.all { it.isDigit() }) {
|
if (line.all { it.isDigit() }) {
|
||||||
return@forEachIndexed
|
return@forEachIndexed
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -401,10 +401,15 @@ class BookRepositoryImpl @Inject constructor(
|
||||||
val byteArray = stream.toByteArray()
|
val byteArray = stream.toByteArray()
|
||||||
val defaultCover = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
|
val defaultCover = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
|
||||||
|
|
||||||
val currentCover = MediaStore.Images.Media.getBitmap(
|
val currentCover = try {
|
||||||
|
MediaStore.Images.Media.getBitmap(
|
||||||
application.contentResolver,
|
application.contentResolver,
|
||||||
Uri.parse(book.image)
|
Uri.parse(book.image)
|
||||||
)
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
return !defaultCover.sameAs(currentCover)
|
return !defaultCover.sameAs(currentCover)
|
||||||
}
|
}
|
||||||
|
|
@ -485,6 +490,16 @@ class BookRepositoryImpl @Inject constructor(
|
||||||
.map { bookMapper.toBook(it) }
|
.map { bookMapper.toBook(it) }
|
||||||
|
|
||||||
val primaryDirectory = Environment.getExternalStorageDirectory()
|
val primaryDirectory = Environment.getExternalStorageDirectory()
|
||||||
|
|
||||||
|
if (
|
||||||
|
!primaryDirectory.exists() ||
|
||||||
|
(Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED &&
|
||||||
|
Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED_READ_ONLY)
|
||||||
|
) {
|
||||||
|
emit(Resource.Success(null))
|
||||||
|
return@flow
|
||||||
|
}
|
||||||
|
|
||||||
val allFiles = getAllFilesInDirectory(primaryDirectory)
|
val allFiles = getAllFilesInDirectory(primaryDirectory)
|
||||||
|
|
||||||
if (allFiles.isEmpty()) {
|
if (allFiles.isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -83,14 +83,6 @@ object Constants {
|
||||||
),
|
),
|
||||||
website = "https://www.github.com/mihonapp/mihon"
|
website = "https://www.github.com/mihonapp/mihon"
|
||||||
),
|
),
|
||||||
Credit(
|
|
||||||
name = "Libre-Sudoku",
|
|
||||||
source = "GitHub",
|
|
||||||
credits = listOf(
|
|
||||||
UIText.StringValue("Readme")
|
|
||||||
),
|
|
||||||
website = "https://www.github.com/kaajjo/Libre-Sudoku"
|
|
||||||
),
|
|
||||||
Credit(
|
Credit(
|
||||||
name = "Kitsune",
|
name = "Kitsune",
|
||||||
source = "GitHub",
|
source = "GitHub",
|
||||||
|
|
@ -107,15 +99,6 @@ object Constants {
|
||||||
),
|
),
|
||||||
website = "https://fonts.google.com/icons"
|
website = "https://fonts.google.com/icons"
|
||||||
),
|
),
|
||||||
Credit(
|
|
||||||
name = "Crocus",
|
|
||||||
source = null,
|
|
||||||
credits = listOf(
|
|
||||||
UIText.StringResource(R.string.credits_motivation),
|
|
||||||
UIText.StringValue("UwU")
|
|
||||||
),
|
|
||||||
website = null
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Help Tips
|
// Help Tips
|
||||||
|
|
|
||||||
|
|
@ -144,8 +144,10 @@ private fun AboutScreen(
|
||||||
)
|
)
|
||||||
append("\n")
|
append("\n")
|
||||||
append(stringResource(id = R.string.app_version_option_desc_2))
|
append(stringResource(id = R.string.app_version_option_desc_2))
|
||||||
}
|
},
|
||||||
|
showLoading = state.value.updateLoading
|
||||||
) {
|
) {
|
||||||
|
if (!state.value.alreadyCheckedForUpdates) {
|
||||||
onEvent(
|
onEvent(
|
||||||
AboutEvent.OnCheckForUpdates(
|
AboutEvent.OnCheckForUpdates(
|
||||||
context = context,
|
context = context,
|
||||||
|
|
@ -165,6 +167,13 @@ private fun AboutScreen(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.no_updates),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,38 @@
|
||||||
package ua.acclorite.book_story.presentation.screens.about.components
|
package ua.acclorite.book_story.presentation.screens.about.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import ua.acclorite.book_story.presentation.ui.FadeTransitionPreservingSpace
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* About screen item.
|
* About Screen Item.
|
||||||
|
* Clickable item, that has title, description and loading state.
|
||||||
|
*
|
||||||
|
* @param modifier [Modifier].
|
||||||
|
* @param title Title of the item.
|
||||||
|
* @param description Description of the item.
|
||||||
|
* @param verticalPadding Vertical item padding.
|
||||||
|
* @param showLoading Whether loading indicator is shown.
|
||||||
|
* @param isOnClickEnabled Whether this item is clickable.
|
||||||
|
* @param onClick OnClick callback.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun AboutItem(
|
fun AboutItem(
|
||||||
|
|
@ -24,18 +40,20 @@ fun AboutItem(
|
||||||
title: String,
|
title: String,
|
||||||
description: AnnotatedString?,
|
description: AnnotatedString?,
|
||||||
verticalPadding: Dp = 12.dp,
|
verticalPadding: Dp = 12.dp,
|
||||||
|
showLoading: Boolean = false,
|
||||||
isOnClickEnabled: Boolean = true,
|
isOnClickEnabled: Boolean = true,
|
||||||
onClick: () -> Unit = {}
|
onClick: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
Column(
|
Row(
|
||||||
modifier
|
modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable(enabled = isOnClickEnabled) {
|
.clickable(enabled = isOnClickEnabled) {
|
||||||
onClick()
|
onClick()
|
||||||
}
|
}
|
||||||
.padding(horizontal = 18.dp, vertical = verticalPadding),
|
.padding(horizontal = 18.dp, vertical = verticalPadding),
|
||||||
verticalArrangement = Arrangement.Center
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
|
Column(Modifier.weight(1f)) {
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
|
@ -51,4 +69,16 @@ fun AboutItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FadeTransitionPreservingSpace(visible = showLoading) {
|
||||||
|
Row {
|
||||||
|
Spacer(modifier = Modifier.width(18.dp))
|
||||||
|
CircularProgressIndicator(
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
strokeCap = StrokeCap.Round,
|
||||||
|
modifier = Modifier.size(28.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,5 +6,7 @@ import ua.acclorite.book_story.data.remote.dto.LatestReleaseInfo
|
||||||
@Immutable
|
@Immutable
|
||||||
data class AboutState(
|
data class AboutState(
|
||||||
val showUpdateDialog: Boolean = false,
|
val showUpdateDialog: Boolean = false,
|
||||||
|
val alreadyCheckedForUpdates: Boolean = false,
|
||||||
|
val updateLoading: Boolean = false,
|
||||||
val updateInfo: LatestReleaseInfo? = null
|
val updateInfo: LatestReleaseInfo? = null
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,19 @@ class AboutViewModel @Inject constructor(
|
||||||
|
|
||||||
is AboutEvent.OnCheckForUpdates -> {
|
is AboutEvent.OnCheckForUpdates -> {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
_state.update {
|
||||||
|
it.copy(
|
||||||
|
updateLoading = true
|
||||||
|
)
|
||||||
|
}
|
||||||
val result = checkForUpdates.execute(false)
|
val result = checkForUpdates.execute(false)
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
|
_state.update {
|
||||||
|
it.copy(
|
||||||
|
updateLoading = false
|
||||||
|
)
|
||||||
|
}
|
||||||
event.error()
|
event.error()
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
@ -52,12 +62,19 @@ class AboutViewModel @Inject constructor(
|
||||||
|
|
||||||
if (version == currentVersion) {
|
if (version == currentVersion) {
|
||||||
event.noUpdatesFound()
|
event.noUpdatesFound()
|
||||||
|
_state.update {
|
||||||
|
it.copy(
|
||||||
|
updateLoading = false,
|
||||||
|
alreadyCheckedForUpdates = true
|
||||||
|
)
|
||||||
|
}
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
showUpdateDialog = true,
|
showUpdateDialog = true,
|
||||||
|
updateLoading = false,
|
||||||
updateInfo = result
|
updateInfo = result
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@ import android.content.Context
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.mikepenz.aboutlibraries.Libs
|
import com.mikepenz.aboutlibraries.Libs
|
||||||
import com.mikepenz.aboutlibraries.util.withContext
|
import com.mikepenz.aboutlibraries.util.withJson
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import ua.acclorite.book_story.R
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
|
|
@ -29,7 +30,7 @@ class LicensesViewModel @Inject constructor(
|
||||||
|
|
||||||
val licenses = Libs
|
val licenses = Libs
|
||||||
.Builder()
|
.Builder()
|
||||||
.withContext(context)
|
.withJson(context, R.raw.aboutlibraries)
|
||||||
.build()
|
.build()
|
||||||
.libraries
|
.libraries
|
||||||
.toList()
|
.toList()
|
||||||
|
|
|
||||||
|
|
@ -331,6 +331,7 @@ private fun ReaderScreen(
|
||||||
)
|
)
|
||||||
.readerFastColorPresetChange(
|
.readerFastColorPresetChange(
|
||||||
fastColorPresetChangeEnabled = mainState.value.fastColorPresetChange!!,
|
fastColorPresetChangeEnabled = mainState.value.fastColorPresetChange!!,
|
||||||
|
isLoading = state.value.loading,
|
||||||
toolbarHidden = toolbarHidden,
|
toolbarHidden = toolbarHidden,
|
||||||
onSettingsEvent = onSettingsEvent,
|
onSettingsEvent = onSettingsEvent,
|
||||||
presetChanged = {
|
presetChanged = {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
|
||||||
* Detects horizontal swipe gestures.
|
* Detects horizontal swipe gestures.
|
||||||
*
|
*
|
||||||
* @param fastColorPresetChangeEnabled Whether this is enabled.
|
* @param fastColorPresetChangeEnabled Whether this is enabled.
|
||||||
|
* @param isLoading Whether Reader is loading.
|
||||||
* @param toolbarHidden Whether toolbar(selection) is hidden.
|
* @param toolbarHidden Whether toolbar(selection) is hidden.
|
||||||
* @param onSettingsEvent [SettingsEvent] callback.
|
* @param onSettingsEvent [SettingsEvent] callback.
|
||||||
* @param presetChanged Callback when Color Preset was changed.
|
* @param presetChanged Callback when Color Preset was changed.
|
||||||
|
|
@ -21,13 +22,14 @@ import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
|
||||||
@Composable
|
@Composable
|
||||||
fun Modifier.readerFastColorPresetChange(
|
fun Modifier.readerFastColorPresetChange(
|
||||||
fastColorPresetChangeEnabled: Boolean,
|
fastColorPresetChangeEnabled: Boolean,
|
||||||
|
isLoading: Boolean,
|
||||||
toolbarHidden: Boolean,
|
toolbarHidden: Boolean,
|
||||||
onSettingsEvent: (SettingsEvent) -> Unit,
|
onSettingsEvent: (SettingsEvent) -> Unit,
|
||||||
presetChanged: (UIText) -> Unit
|
presetChanged: (UIText) -> Unit
|
||||||
): Modifier {
|
): Modifier {
|
||||||
val offset = remember { mutableFloatStateOf(0f) }
|
val offset = remember { mutableFloatStateOf(0f) }
|
||||||
return this.then(
|
return this.then(
|
||||||
if (fastColorPresetChangeEnabled && toolbarHidden) {
|
if (fastColorPresetChangeEnabled && toolbarHidden && !isLoading) {
|
||||||
Modifier.pointerInput(Unit) {
|
Modifier.pointerInput(Unit) {
|
||||||
detectHorizontalDragGestures(
|
detectHorizontalDragGestures(
|
||||||
onDragStart = { offset.floatValue = 0f },
|
onDragStart = { offset.floatValue = 0f },
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
package ua.acclorite.book_story.presentation.ui
|
package ua.acclorite.book_story.presentation.ui
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.AnimationSpec
|
||||||
|
import androidx.compose.animation.core.EaseInOut
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
|
@ -8,8 +11,11 @@ import androidx.compose.animation.slideInHorizontally
|
||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
import androidx.compose.animation.slideOutHorizontally
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
import androidx.compose.animation.slideOutVertically
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
|
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
|
||||||
|
|
@ -87,3 +93,21 @@ fun SlidingTransition(
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FadeTransitionPreservingSpace(
|
||||||
|
visible: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
animationSpec: AnimationSpec<Float> = tween(durationMillis = 150, easing = EaseInOut),
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val alpha by animateFloatAsState(
|
||||||
|
if (visible) 1f else 0f,
|
||||||
|
label = "",
|
||||||
|
animationSpec = animationSpec
|
||||||
|
)
|
||||||
|
|
||||||
|
Box(modifier = modifier.alpha(alpha)) {
|
||||||
|
content.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
2947
app/src/main/res/raw/aboutlibraries.json
Normal file
2947
app/src/main/res/raw/aboutlibraries.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +1,6 @@
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="InconsistentArrays">
|
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="InconsistentArrays">
|
||||||
<!-- Basic Strings -->
|
<!-- Basic Strings -->
|
||||||
<string name="app_version" translatable="false">1.1.0</string>
|
<string name="app_version" translatable="false">1.1.1</string>
|
||||||
<string name="app_name">Book\'s Story</string>
|
<string name="app_name">Book\'s Story</string>
|
||||||
|
|
||||||
<!-- Book sources -->
|
<!-- Book sources -->
|
||||||
|
|
|
||||||
BIN
assets/readme/app_icon.png
Normal file
BIN
assets/readme/app_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9 KiB |
Loading…
Add table
Add a link
Reference in a new issue