diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml
index e6b3672cea..cd1d259b4e 100644
--- a/.github/workflows/beta.yml
+++ b/.github/workflows/beta.yml
@@ -16,12 +16,50 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+
+ - name: Download last SHA artifact
+ uses: dawidd6/action-download-artifact@v3
+ with:
+ workflow: beta.yml
+ name: last-sha
+ path: .
+
+ continue-on-error: true
+
+ - name: Get Commits Since Last Run
+ run: |
+ if [ -f last_sha.txt ]; then
+ LAST_SHA=$(cat last_sha.txt)
+ else
+ # Fallback to first commit if no previous SHA available
+ LAST_SHA=$(git rev-list --max-parents=0 HEAD)
+ fi
+ echo "Commits since $LAST_SHA:"
+ # Accumulate commit logs in a shell variable
+ COMMIT_LOGS=$(git log $LAST_SHA..HEAD --pretty=format:"%h - %s")
+ # URL-encode the newline characters for GitHub Actions
+ COMMIT_LOGS="${COMMIT_LOGS//'%'/'%25'}"
+ COMMIT_LOGS="${COMMIT_LOGS//$'\n'/'%0A'}"
+ COMMIT_LOGS="${COMMIT_LOGS//$'\r'/'%0D'}"
+ # Append the encoded commit logs to the COMMIT_LOG environment variable
+ echo "COMMIT_LOG=${COMMIT_LOGS}" >> $GITHUB_ENV
+ # Debugging: Print the variable to check its content
+ echo "$COMMIT_LOGS"
+ shell: /usr/bin/bash -e {0}
+ env:
+ CI: true
+
+ - name: Save Current SHA for Next Run
+ run: echo ${{ github.sha }} > last_sha.txt
- name: Set variables
run: |
VER=$(grep -E -o "versionName \".*\"" app/build.gradle | sed -e 's/versionName //g' | tr -d '"')
SHA=${{ github.sha }}
- VERSION="$VER.${SHA:0:7}"
+ VERSION="$VER+${SHA:0:7}"
echo "Version $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV
@@ -31,30 +69,54 @@ jobs:
distribution: 'temurin'
java-version: 17
cache: gradle
-
+
- name: Decode Keystore File
run: echo "${{ secrets.KEYSTORE_FILE }}" | base64 -d > $GITHUB_WORKSPACE/key.keystore
-
+
- name: List files in the directory
run: ls -l
-
+
- name: Make gradlew executable
run: chmod +x ./gradlew
-
+
- name: Build with Gradle
- run: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$GITHUB_WORKSPACE/key.keystore -Pandroid.injected.signing.store.password=${{ secrets.KEYSTORE_PASSWORD }} -Pandroid.injected.signing.key.alias=${{ secrets.KEY_ALIAS }} -Pandroid.injected.signing.key.password=${{ secrets.KEY_PASSWORD }}
-
+ run: ./gradlew assembleGoogleAlpha -Pandroid.injected.signing.store.file=$GITHUB_WORKSPACE/key.keystore -Pandroid.injected.signing.store.password=${{ secrets.KEYSTORE_PASSWORD }} -Pandroid.injected.signing.key.alias=${{ secrets.KEY_ALIAS }} -Pandroid.injected.signing.key.password=${{ secrets.KEY_PASSWORD }}
+
- name: Upload a Build Artifact
uses: actions/upload-artifact@v3.0.0
with:
name: Dantotsu
- path: "app/build/outputs/apk/debug/app-debug.apk"
-
- - name: Upload APK to Discord
+ path: "app/build/outputs/apk/google/alpha/app-google-alpha.apk"
+
+ - name: Upload APK to Discord and Telegram
shell: bash
run: |
- contentbody=$( jq -Rsa . <<< "${{ github.event.head_commit.message }}" )
- curl -F "payload_json={\"content\":\" Debug-Build: <@719439449423085569> **${{ env.VERSION }}**\n\n${contentbody:1:-1}\"}" -F "dantotsu_debug=@app/build/outputs/apk/debug/app-debug.apk" ${{ secrets.DISCORD_WEBHOOK }}
+ #Discord
+ commit_messages=$(echo "$COMMIT_LOG" | sed 's/%0A/\n/g')
+ # Truncate commit messages if they are too long
+ max_length=1900 # Adjust this value as needed
+ if [ ${#commit_messages} -gt $max_length ]; then
+ commit_messages="${commit_messages:0:$max_length}... (truncated)"
+ fi
+ contentbody=$( jq -nc --arg msg "Alpha-Build: <@719439449423085569> **$VERSION**:" --arg commits "$commit_messages" '{"content": ($msg + "\n" + $commits)}' )
+ curl -F "payload_json=${contentbody}" -F "dantotsu_debug=@app/build/outputs/apk/google/alpha/app-google-alpha.apk" ${{ secrets.DISCORD_WEBHOOK }}
+
+ #Telegram
+ curl -F "chat_id=${{ secrets.TELEGRAM_CHANNEL_ID }}" \
+ -F "document=@app/build/outputs/apk/google/alpha/app-google-alpha.apk" \
+ -F "caption=[Alpha-Build: ${VERSION}] Change logs :${commit_messages}" \
+ https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendDocument
+
+ env:
+ COMMIT_LOG: ${{ env.COMMIT_LOG }}
+ VERSION: ${{ env.VERSION }}
+
+ - name: Upload Current SHA as Artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: last-sha
+ path: last_sha.txt
+
- name: Delete Old Pre-Releases
id: delete-pre-releases
diff --git a/app/.gitignore b/app/.gitignore
index 92118fcdea..6a334e3068 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -1,4 +1,8 @@
/build
/debug
/debug/output-metadata.json
+/alpha
+/alpha/output-metadata.json
+/google/*
+/fdroid/*
/release
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index df3a788932..1762fa5413 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,12 +1,9 @@
plugins {
id 'com.android.application'
- id 'com.google.gms.google-services'
- id 'com.google.firebase.crashlytics'
id 'kotlin-android'
id 'kotlinx-serialization'
id 'org.jetbrains.kotlin.android'
id 'com.google.devtools.ksp'
-
}
def gitCommitHash = providers.exec {
@@ -20,14 +17,39 @@ android {
applicationId "ani.dantotsu"
minSdk 23
targetSdk 34
- versionCode ((System.currentTimeMillis() / 60000).toInteger())
- versionName "2.0.0-beta01-iv1"
+ versionCode((System.currentTimeMillis() / 60000).toInteger())
+ versionName "2.1.0"
+ versionCode 210000000
signingConfig signingConfigs.debug
}
+ flavorDimensions "store"
+ productFlavors {
+ fdroid {
+ // F-Droid specific configuration
+ dimension "store"
+ versionNameSuffix "-fdroid"
+ }
+ google {
+ // Google Play specific configuration
+ dimension "store"
+ isDefault true
+ apply plugin: 'com.google.gms.google-services'
+ apply plugin: 'com.google.firebase.crashlytics'
+ }
+ }
+
buildTypes {
+ alpha {
+ applicationIdSuffix ".beta" // keep as beta by popular request
+ versionNameSuffix "-alpha01"
+ manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher_alpha", icon_placeholder_round: "@mipmap/ic_launcher_alpha_round"]
+ debuggable System.getenv("CI") == null
+ isDefault true
+ }
debug {
applicationIdSuffix ".beta"
+ versionNameSuffix "-beta01"
manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher_beta", icon_placeholder_round: "@mipmap/ic_launcher_beta_round"]
debuggable System.getenv("CI") == null
}
@@ -39,6 +61,7 @@ android {
}
buildFeatures {
viewBinding true
+ buildConfig true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
@@ -52,6 +75,11 @@ android {
}
dependencies {
+
+ // FireBase
+ googleImplementation platform('com.google.firebase:firebase-bom:32.2.3')
+ googleImplementation 'com.google.firebase:firebase-analytics-ktx:21.5.0'
+ googleImplementation 'com.google.firebase:firebase-crashlytics-ktx:18.6.1'
// Core
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.browser:browser:1.7.0'
@@ -67,7 +95,7 @@ dependencies {
implementation 'com.github.Blatzar:NiceHttp:0.4.4'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2'
implementation 'androidx.preference:preference-ktx:1.2.1'
- implementation 'androidx.webkit:webkit:1.9.0'
+ implementation 'androidx.webkit:webkit:1.10.0'
// Glide
ext.glide_version = '4.16.0'
@@ -77,13 +105,8 @@ dependencies {
implementation "com.github.bumptech.glide:okhttp3-integration:$glide_version"
implementation 'jp.wasabeef:glide-transformations:4.3.0'
-// FireBase
- implementation platform('com.google.firebase:firebase-bom:32.2.3')
- implementation 'com.google.firebase:firebase-analytics-ktx:21.5.0'
- implementation 'com.google.firebase:firebase-crashlytics-ktx:18.6.0'
-
// Exoplayer
- ext.exo_version = '1.2.0'
+ ext.exo_version = '1.2.1'
implementation "androidx.media3:media3-exoplayer:$exo_version"
implementation "androidx.media3:media3-ui:$exo_version"
implementation "androidx.media3:media3-exoplayer-hls:$exo_version"
diff --git a/app/google-services.json b/app/google-services.json
index 7e54b99aff..b53de23d4b 100644
--- a/app/google-services.json
+++ b/app/google-services.json
@@ -43,6 +43,25 @@
}
}
},
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:1039200814590:android:40e14720ee97917e1aacaf",
+ "android_client_info": {
+ "package_name": "ani.dantotsu.alpha"
+ }
+ },
+ "oauth_client": [],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCiXo_q4S2ofA5oCztsoLnlDqJi3GtTJjY"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": []
+ }
+ }
+ },
{
"client_info": {
"mobilesdk_app_id": "1:1039200814590:android:40e14720ee97917e1aacaf",
diff --git a/app/src/alpha/res/drawable/anim_splash.xml b/app/src/alpha/res/drawable/anim_splash.xml
new file mode 100644
index 0000000000..ac1d1ee91f
--- /dev/null
+++ b/app/src/alpha/res/drawable/anim_splash.xml
@@ -0,0 +1,376 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/alpha/res/values/strings.xml b/app/src/alpha/res/values/strings.xml
new file mode 100644
index 0000000000..9a2e65a240
--- /dev/null
+++ b/app/src/alpha/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ Dantotsu α
+
diff --git a/app/src/main/res/drawable/anim_splash_beta.xml b/app/src/debug/res/drawable/anim_splash.xml
similarity index 86%
rename from app/src/main/res/drawable/anim_splash_beta.xml
rename to app/src/debug/res/drawable/anim_splash.xml
index 3338cb411b..43638d8de3 100644
--- a/app/src/main/res/drawable/anim_splash_beta.xml
+++ b/app/src/debug/res/drawable/anim_splash.xml
@@ -1,5 +1,4 @@
-
+ android:pathData="M 384 128.04 C 329.836 127.869 276.99 144.889 233.11 176.638 C 189.23 208.387 156.539 253.255 139.769 304.75 C 122.999 356.244 122.999 411.756 139.769 463.25 C 156.539 514.745 189.23 559.613 233.11 591.362 C 276.99 623.111 329.836 640.131 384 639.96 C 451.869 639.96 517.028 612.974 565.019 564.991 C 613.01 517.008 640 451.859 640 384 C 640 316.141 613.01 250.992 565.019 203.009 C 517.028 155.026 451.869 128.04 384 128.04 Z" />
+ android:pathData="M 128 128 L 640 128 L 640 639.96 L 128 639.96 Z"
+ android:strokeWidth="1" />
+ android:pathData="M 384 211.74 C 338.331 211.74 294.486 229.901 262.194 262.194 C 229.901 294.486 211.74 338.331 211.74 384 C 211.74 429.669 229.901 473.514 262.194 505.806 C 294.486 538.099 338.331 556.26 384 556.26 C 429.669 556.26 473.514 538.099 505.806 505.806 C 538.099 473.514 556.26 429.669 556.26 384 C 556.26 338.331 538.099 294.486 505.806 262.194 C 473.514 229.901 429.669 211.74 384 211.74 Z"
+ android:strokeWidth="1" />
@@ -43,12 +42,12 @@
android:rotation="-90">
+ android:pathData="M 128 128 L 128 463.26 C 151.32 466.96 175.23 468.89 199.58 468.89 C 411.17 468.89 588.92 323.99 639.01 128 L 128 128 Z"
+ android:strokeWidth="1" />
+ android:pathData="M 128 128 L 128 463.26 C 151.32 466.96 175.23 468.89 199.58 468.89 C 411.17 468.89 588.92 323.99 639.01 128 L 128 128 Z" />
+ android:pathData="M 384 211.74 C 338.331 211.74 294.486 229.901 262.194 262.194 C 229.901 294.486 211.74 338.331 211.74 384 C 211.74 429.669 229.901 473.514 262.194 505.806 C 294.486 538.099 338.331 556.26 384 556.26 C 429.669 556.26 473.514 538.099 505.806 505.806 C 538.099 473.514 556.26 429.669 556.26 384 C 556.26 338.331 538.099 294.486 505.806 262.194 C 473.514 229.901 429.669 211.74 384 211.74 Z" />
+ android:pathData="M 128 128 L 128 463.26 C 151.32 466.96 175.23 468.89 199.58 468.89 C 411.17 468.89 588.92 323.99 639.01 128 L 128 128 Z"
+ android:strokeWidth="1" />
+ android:scaleY="1.2" />
+ android:pathData="M 539.28 128 C 503.71 317.07 337.72 460.12 138.31 460.12 C 134.86 460.12 131.42 460.06 128 459.98 L 128 465.73 C 168.23 476.19 210.43 481.78 253.93 481.78 C 409.53 481.78 548.48 410.55 640 298.94 L 640 128.01 L 539.28 128.01 Z"
+ android:strokeWidth="1" />
@@ -100,9 +99,9 @@
android:translateX="-360">
+ android:pathData="M 481.82 384 C 481.82 438.03 438.02 481.82 384 481.82 L 0 481.82 L 0 286.18 L 384 286.18 C 438.02 286.18 481.82 329.98 481.82 384 Z"
+ android:strokeWidth="1" />
+ android:pathData="M 44.26 128 C 44.26 174.25 81.75 211.74 128 211.74 L 384 211.74 C 479.13 211.74 556.26 288.86 556.26 384 C 556.26 479.13 479.14 556.26 384 556.26 L 128 556.26 C 81.76 556.26 44.28 593.73 44.26 639.97 L 768 639.97 L 768 128 L 44.26 128 Z"
+ android:strokeWidth="1" />
+ android:scaleY="3">
+ android:fillColor="#efe7ff"
+ android:pathData="M 442 366.7 L 365.98 322.81 C 352.66 315.12 336.02 324.73 336.02 340.11 L 336.02 427.89 C 336.02 443.27 352.67 452.88 365.98 445.19 L 442 401.3 C 455.32 393.61 455.32 374.39 442 366.7 Z"
+ android:strokeWidth="1" />
@@ -138,19 +137,19 @@
+ android:valueType="floatType" />
+ android:valueType="floatType" />
@@ -158,177 +157,177 @@
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
+ android:valueType="floatType" />
@@ -336,19 +335,19 @@
+ android:valueType="floatType" />
+ android:valueType="floatType" />
@@ -356,21 +355,21 @@
+ android:valueType="floatType" />
+ android:valueType="floatType" />
diff --git a/app/src/fdroid/java/ani/dantotsu/connections/crashlytics/CrashlyticsFactory.kt b/app/src/fdroid/java/ani/dantotsu/connections/crashlytics/CrashlyticsFactory.kt
new file mode 100644
index 0000000000..467fe71a31
--- /dev/null
+++ b/app/src/fdroid/java/ani/dantotsu/connections/crashlytics/CrashlyticsFactory.kt
@@ -0,0 +1,9 @@
+package ani.dantotsu.connections.crashlytics
+
+class CrashlyticsFactory {
+ companion object {
+ fun createCrashlytics(): CrashlyticsInterface {
+ return CrashlyticsStub()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/fdroid/java/ani/dantotsu/others/AppUpdater.kt b/app/src/fdroid/java/ani/dantotsu/others/AppUpdater.kt
new file mode 100644
index 0000000000..bb9893f606
--- /dev/null
+++ b/app/src/fdroid/java/ani/dantotsu/others/AppUpdater.kt
@@ -0,0 +1,9 @@
+package ani.dantotsu.others
+
+import androidx.fragment.app.FragmentActivity
+
+object AppUpdater {
+ suspend fun check(activity: FragmentActivity, post: Boolean = false) {
+ //no-op
+ }
+}
\ No newline at end of file
diff --git a/app/src/google/java/ani/dantotsu/connections/crashlytics/CrashlyticsFactory.kt b/app/src/google/java/ani/dantotsu/connections/crashlytics/CrashlyticsFactory.kt
new file mode 100644
index 0000000000..2a3715d96d
--- /dev/null
+++ b/app/src/google/java/ani/dantotsu/connections/crashlytics/CrashlyticsFactory.kt
@@ -0,0 +1,9 @@
+package ani.dantotsu.connections.crashlytics
+
+class CrashlyticsFactory {
+ companion object {
+ fun createCrashlytics(): CrashlyticsInterface {
+ return FirebaseCrashlytics()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/google/java/ani/dantotsu/connections/crashlytics/FirebaseCrashlytics.kt b/app/src/google/java/ani/dantotsu/connections/crashlytics/FirebaseCrashlytics.kt
new file mode 100644
index 0000000000..64ab524ffe
--- /dev/null
+++ b/app/src/google/java/ani/dantotsu/connections/crashlytics/FirebaseCrashlytics.kt
@@ -0,0 +1,34 @@
+package ani.dantotsu.connections.crashlytics
+
+import android.content.Context
+import com.google.firebase.FirebaseApp
+import com.google.firebase.crashlytics.FirebaseCrashlytics
+import com.google.firebase.crashlytics.ktx.crashlytics
+import com.google.firebase.ktx.Firebase
+import com.google.firebase.ktx.app
+
+class FirebaseCrashlytics : CrashlyticsInterface {
+ override fun initialize(context: Context) {
+ FirebaseApp.initializeApp(context)
+ }
+ override fun logException(e: Throwable) {
+ FirebaseCrashlytics.getInstance().recordException(e)
+ }
+
+ override fun log(message: String) {
+ FirebaseCrashlytics.getInstance().log(message)
+ }
+
+ override fun setUserId(id: String) {
+ Firebase.crashlytics.setUserId(id)
+ }
+
+ override fun setCustomKey(key: String, value: String) {
+ FirebaseCrashlytics.getInstance().setCustomKey(key, value)
+ }
+
+ override fun setCrashlyticsCollectionEnabled(enabled: Boolean) {
+ FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(enabled)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/others/AppUpdater.kt b/app/src/google/java/ani/dantotsu/others/AppUpdater.kt
similarity index 97%
rename from app/src/main/java/ani/dantotsu/others/AppUpdater.kt
rename to app/src/google/java/ani/dantotsu/others/AppUpdater.kt
index eea658e02f..3c64edbe75 100644
--- a/app/src/main/java/ani/dantotsu/others/AppUpdater.kt
+++ b/app/src/google/java/ani/dantotsu/others/AppUpdater.kt
@@ -15,6 +15,7 @@ import androidx.core.content.FileProvider
import androidx.core.content.getSystemService
import androidx.fragment.app.FragmentActivity
import ani.dantotsu.*
+import ani.dantotsu.settings.saving.PrefManager
import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin
import kotlinx.coroutines.Dispatchers
@@ -50,7 +51,7 @@ object AppUpdater {
}
logger("Git Version : $version")
- val dontShow = loadData("dont_ask_for_update_$version") ?: false
+ val dontShow = PrefManager.getCustomVal("dont_ask_for_update_$version", false)
if (compareVersion(version) && !dontShow && !activity.isDestroyed) activity.runOnUiThread {
CustomBottomDialog.newInstance().apply {
setTitleText(
@@ -71,7 +72,7 @@ object AppUpdater {
false
) { isChecked ->
if (isChecked) {
- saveData("dont_ask_for_update_$version", true)
+ PrefManager.setCustomVal("dont_ask_for_update_$version", true)
}
}
setPositiveButton(currContext()!!.getString(R.string.lets_go)) {
@@ -186,7 +187,7 @@ object AppUpdater {
return true
}
- fun openApk(context: Context, uri: Uri) {
+ private fun openApk(context: Context, uri: Uri) {
try {
uri.path?.let {
val contentUri = FileProvider.getUriForFile(
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c61e37dd07..145c6febb4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -10,7 +10,8 @@
android:required="false" />
-
@@ -51,7 +52,7 @@
android:icon="${icon_placeholder}"
android:label="@string/app_name"
android:largeHeap="true"
- android:requestLegacyExternalStorage="true"
+ android:requestLegacyExternalStorage="false"
android:roundIcon="${icon_placeholder_round}"
android:supportsRtl="true"
android:theme="@style/Theme.Dantotsu"
@@ -289,13 +290,13 @@
+ android:exported="true"
+ android:permission="android.permission.BIND_REMOTEVIEWS" />
-
+
@@ -317,19 +318,22 @@
android:name=".download.novel.NovelDownloaderService"
android:exported="false"
android:foregroundServiceType="dataSync" />
-
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/ic_launcher_alpha-playstore.png b/app/src/main/ic_launcher_alpha-playstore.png
new file mode 100644
index 0000000000..fc55f0acae
Binary files /dev/null and b/app/src/main/ic_launcher_alpha-playstore.png differ
diff --git a/app/src/main/java/ani/dantotsu/App.kt b/app/src/main/java/ani/dantotsu/App.kt
index 5b6869ea99..5c11e74f75 100644
--- a/app/src/main/java/ani/dantotsu/App.kt
+++ b/app/src/main/java/ani/dantotsu/App.kt
@@ -8,16 +8,16 @@ import androidx.multidex.MultiDex
import androidx.multidex.MultiDexApplication
import ani.dantotsu.aniyomi.anime.custom.AppModule
import ani.dantotsu.aniyomi.anime.custom.PreferenceModule
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.others.DisabledReports
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.MangaSources
import ani.dantotsu.parsers.NovelSources
import ani.dantotsu.parsers.novel.NovelExtensionManager
import ani.dantotsu.settings.SettingsActivity
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import com.google.android.material.color.DynamicColors
-import com.google.firebase.crashlytics.FirebaseCrashlytics
-import com.google.firebase.crashlytics.ktx.crashlytics
-import com.google.firebase.ktx.Firebase
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
@@ -51,36 +51,35 @@ class App : MultiDexApplication() {
override fun onCreate() {
super.onCreate()
- val sharedPreferences = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- val useMaterialYou = sharedPreferences.getBoolean("use_material_you", false)
+
+ PrefManager.init(this)
+ Injekt.importModule(AppModule(this))
+ Injekt.importModule(PreferenceModule(this))
+
+ val crashlytics = Injekt.get()
+ crashlytics.initialize(this)
+
+ val useMaterialYou: Boolean = PrefManager.getVal(PrefName.UseMaterialYou)
if (useMaterialYou) {
DynamicColors.applyToActivitiesIfAvailable(this)
- //TODO: HarmonizedColors
}
registerActivityLifecycleCallbacks(mFTActivityLifecycleCallbacks)
- Firebase.crashlytics.setCrashlyticsCollectionEnabled(!DisabledReports)
- getSharedPreferences(
- getString(R.string.preference_file_key),
- Context.MODE_PRIVATE
- ).getBoolean("shared_user_id", true).let {
+ crashlytics.setCrashlyticsCollectionEnabled(!DisabledReports)
+ (PrefManager.getVal(PrefName.SharedUserID) as Boolean).let {
if (!it) return@let
- val dUsername = getSharedPreferences(
- getString(R.string.preference_file_key),
- Context.MODE_PRIVATE
- ).getString("discord_username", null)
- val aUsername = getSharedPreferences(
- getString(R.string.preference_file_key),
- Context.MODE_PRIVATE
- ).getString("anilist_username", null)
- if (dUsername != null || aUsername != null) {
- Firebase.crashlytics.setUserId("$dUsername - $aUsername")
+ val dUsername = PrefManager.getVal(PrefName.DiscordUserName, null as String?)
+ val aUsername = PrefManager.getVal(PrefName.AnilistUserName, null as String?)
+ if (dUsername != null) {
+ crashlytics.setCustomKey("dUsername", dUsername)
+ }
+ if (aUsername != null) {
+ crashlytics.setCustomKey("aUsername", aUsername)
}
}
- FirebaseCrashlytics.getInstance().setCustomKey("device Info", SettingsActivity.getDeviceInfo())
+ crashlytics.setCustomKey("device Info", SettingsActivity.getDeviceInfo())
+
- Injekt.importModule(AppModule(this))
- Injekt.importModule(PreferenceModule(this))
initializeNetwork(baseContext)
@@ -97,13 +96,13 @@ class App : MultiDexApplication() {
animeScope.launch {
animeExtensionManager.findAvailableExtensions()
logger("Anime Extensions: ${animeExtensionManager.installedExtensionsFlow.first()}")
- AnimeSources.init(animeExtensionManager.installedExtensionsFlow, this@App)
+ AnimeSources.init(animeExtensionManager.installedExtensionsFlow)
}
val mangaScope = CoroutineScope(Dispatchers.Default)
mangaScope.launch {
mangaExtensionManager.findAvailableExtensions()
logger("Manga Extensions: ${mangaExtensionManager.installedExtensionsFlow.first()}")
- MangaSources.init(mangaExtensionManager.installedExtensionsFlow, this@App)
+ MangaSources.init(mangaExtensionManager.installedExtensionsFlow)
}
val novelScope = CoroutineScope(Dispatchers.Default)
novelScope.launch {
diff --git a/app/src/main/java/ani/dantotsu/Functions.kt b/app/src/main/java/ani/dantotsu/Functions.kt
index 9578aed668..623b8aa74b 100644
--- a/app/src/main/java/ani/dantotsu/Functions.kt
+++ b/app/src/main/java/ani/dantotsu/Functions.kt
@@ -11,11 +11,12 @@ import android.content.ClipboardManager
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
+import android.content.pm.PackageManager
import android.content.res.Configuration
import android.content.res.Resources.getSystem
import android.graphics.Bitmap
import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
+import android.Manifest
import android.media.MediaScannerConnection
import android.net.ConnectivityManager
import android.net.NetworkCapabilities.*
@@ -31,8 +32,11 @@ import android.view.*
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.animation.*
import android.widget.*
+import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
+import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
+import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.content.FileProvider
import androidx.core.math.MathUtils.clamp
@@ -45,12 +49,15 @@ import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.BuildConfig.APPLICATION_ID
import ani.dantotsu.connections.anilist.Genre
import ani.dantotsu.connections.anilist.api.FuzzyDate
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.databinding.ItemCountDownBinding
import ani.dantotsu.media.Media
import ani.dantotsu.parsers.ShowResponse
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
+import ani.dantotsu.settings.saving.internal.PreferenceKeystore
+import ani.dantotsu.settings.saving.internal.PreferenceKeystore.Companion.generateSalt
import ani.dantotsu.subcriptions.NotificationClickReceiver
-import ani.dantotsu.themes.ThemeManager
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade
@@ -60,10 +67,11 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.internal.ViewUtils
import com.google.android.material.snackbar.Snackbar
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import eu.kanade.tachiyomi.data.notification.Notifications
import kotlinx.coroutines.*
import nl.joery.animatedbottombar.AnimatedBottomBar
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
import java.io.*
import java.lang.Runnable
import java.lang.reflect.Field
@@ -105,63 +113,22 @@ fun logger(e: Any?, print: Boolean = true) {
println(e)
}
-fun saveData(fileName: String, data: Any?, context: Context? = null) {
- tryWith {
- val a = context ?: currContext()
- if (a != null) {
- val fos: FileOutputStream = a.openFileOutput(fileName, Context.MODE_PRIVATE)
- val os = ObjectOutputStream(fos)
- os.writeObject(data)
- os.close()
- fos.close()
- }
- }
-}
-
-@Suppress("UNCHECKED_CAST")
-fun loadData(fileName: String, context: Context? = null, toast: Boolean = true): T? {
- val a = context ?: currContext()
- try {
- if (a?.fileList() != null)
- if (fileName in a.fileList()) {
- val fileIS: FileInputStream = a.openFileInput(fileName)
- val objIS = ObjectInputStream(fileIS)
- val data = objIS.readObject() as T
- objIS.close()
- fileIS.close()
- return data
- }
- } catch (e: Exception) {
- if (toast) snackString(a?.getString(R.string.error_loading_data, fileName))
- //try to delete the file
- try {
- a?.deleteFile(fileName)
- } catch (e: Exception) {
- FirebaseCrashlytics.getInstance().log("Failed to delete file $fileName")
- FirebaseCrashlytics.getInstance().recordException(e)
- }
- e.printStackTrace()
- }
- return null
-}
fun initActivity(a: Activity) {
val window = a.window
WindowCompat.setDecorFitsSystemWindows(window, false)
- val uiSettings = loadData("ui_settings", toast = false)
- ?: UserInterfaceSettings().apply {
- saveData("ui_settings", this)
- }
- uiSettings.darkMode.apply {
+ val darkMode = PrefManager.getVal(PrefName.DarkMode)
+ val immersiveMode: Boolean = PrefManager.getVal(PrefName.ImmersiveMode)
+ darkMode.apply {
AppCompatDelegate.setDefaultNightMode(
when (this) {
- true -> AppCompatDelegate.MODE_NIGHT_YES
- false -> AppCompatDelegate.MODE_NIGHT_NO
+ 2 -> AppCompatDelegate.MODE_NIGHT_YES
+ 1 -> AppCompatDelegate.MODE_NIGHT_NO
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
)
}
- if (uiSettings.immersiveMode) {
+ if (immersiveMode) {
if (navBarHeight == 0) {
ViewCompat.getRootWindowInsets(window.decorView.findViewById(android.R.id.content))
?.apply {
@@ -216,7 +183,11 @@ open class BottomSheetDialogFragment : BottomSheetDialogFragment() {
}
val typedValue = TypedValue()
val theme = requireContext().theme
- theme.resolveAttribute(com.google.android.material.R.attr.colorOnSurfaceInverse, typedValue, true)
+ theme.resolveAttribute(
+ com.google.android.material.R.attr.colorSurface,
+ typedValue,
+ true
+ )
window.navigationBarColor = typedValue.data
}
@@ -325,20 +296,20 @@ class InputFilterMinMax(
}
-class ZoomOutPageTransformer(private val uiSettings: UserInterfaceSettings) :
+class ZoomOutPageTransformer() :
ViewPager2.PageTransformer {
override fun transformPage(view: View, position: Float) {
- if (position == 0.0f && uiSettings.layoutAnimations) {
+ if (position == 0.0f && PrefManager.getVal(PrefName.LayoutAnimations)) {
setAnimation(
view.context,
view,
- uiSettings,
300,
floatArrayOf(1.3f, 1f, 1.3f, 1f),
0.5f to 0f
)
ObjectAnimator.ofFloat(view, "alpha", 0f, 1.0f)
- .setDuration((200 * uiSettings.animationSpeed).toLong()).start()
+ .setDuration((200 * (PrefManager.getVal(PrefName.AnimationSpeed) as Float)).toLong())
+ .start()
}
}
}
@@ -346,12 +317,11 @@ class ZoomOutPageTransformer(private val uiSettings: UserInterfaceSettings) :
fun setAnimation(
context: Context,
viewToAnimate: View,
- uiSettings: UserInterfaceSettings,
duration: Long = 150,
list: FloatArray = floatArrayOf(0.0f, 1.0f, 0.0f, 1.0f),
pivot: Pair = 0.5f to 0.5f
) {
- if (uiSettings.layoutAnimations) {
+ if (PrefManager.getVal(PrefName.LayoutAnimations)) {
val anim = ScaleAnimation(
list[0],
list[1],
@@ -362,7 +332,7 @@ fun setAnimation(
Animation.RELATIVE_TO_SELF,
pivot.second
)
- anim.duration = (duration * uiSettings.animationSpeed).toLong()
+ anim.duration = (duration * (PrefManager.getVal(PrefName.AnimationSpeed) as Float)).toLong()
anim.setInterpolator(context, R.anim.over_shoot)
viewToAnimate.startAnimation(anim)
}
@@ -615,7 +585,7 @@ fun openLinkInBrowser(link: String?) {
}
}
-fun saveImageToDownloads(title: String, bitmap: Bitmap, context: Context) {
+fun saveImageToDownloads(title: String, bitmap: Bitmap, context: Activity) {
FileProvider.getUriForFile(
context,
"$APPLICATION_ID.provider",
@@ -627,6 +597,105 @@ fun saveImageToDownloads(title: String, bitmap: Bitmap, context: Context) {
)
}
+fun savePrefsToDownloads(
+ title: String,
+ serialized: String,
+ context: Activity,
+ password: CharArray? = null
+) {
+ FileProvider.getUriForFile(
+ context,
+ "$APPLICATION_ID.provider",
+ if (password != null) {
+ savePrefs(
+ serialized,
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath,
+ title,
+ context,
+ password
+ ) ?: return
+ } else {
+ savePrefs(
+ serialized,
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath,
+ title,
+ context
+ ) ?: return
+ }
+ )
+}
+
+fun savePrefs(serialized: String, path: String, title: String, context: Context): File? {
+ var file = File(path, "$title.ani")
+ var counter = 1
+ while (file.exists()) {
+ file = File(path, "${title}_${counter}.ani")
+ counter++
+ }
+
+ return try {
+ file.writeText(serialized)
+ scanFile(file.absolutePath, context)
+ toast(String.format(context.getString(R.string.saved_to_path, file.absolutePath)))
+ file
+ } catch (e: Exception) {
+ snackString("Failed to save settings: ${e.localizedMessage}")
+ null
+ }
+}
+
+fun savePrefs(
+ serialized: String,
+ path: String,
+ title: String,
+ context: Context,
+ password: CharArray
+): File? {
+ var file = File(path, "$title.ani")
+ var counter = 1
+ while (file.exists()) {
+ file = File(path, "${title}_${counter}.sani")
+ counter++
+ }
+
+ val salt = generateSalt()
+
+ return try {
+ val encryptedData = PreferenceKeystore.encryptWithPassword(password, serialized, salt)
+
+ // Combine salt and encrypted data
+ val dataToSave = salt + encryptedData
+
+ file.writeBytes(dataToSave)
+ scanFile(file.absolutePath, context)
+ toast(String.format(context.getString(R.string.saved_to_path, file.absolutePath)))
+ file
+ } catch (e: Exception) {
+ snackString("Failed to save settings: ${e.localizedMessage}")
+ null
+ }
+}
+
+fun downloadsPermission(activity: AppCompatActivity): Boolean {
+ val permissions = arrayOf(
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ )
+
+ val requiredPermissions = permissions.filter {
+ ContextCompat.checkSelfPermission(activity, it) != PackageManager.PERMISSION_GRANTED
+ }.toTypedArray()
+
+ return if (requiredPermissions.isNotEmpty()) {
+ ActivityCompat.requestPermissions(activity, requiredPermissions, DOWNLOADS_PERMISSION_REQUEST_CODE)
+ false
+ } else {
+ true
+ }
+}
+
+private const val DOWNLOADS_PERMISSION_REQUEST_CODE = 100
+
fun shareImage(title: String, bitmap: Bitmap, context: Context) {
val contentUri = FileProvider.getUriForFile(
@@ -743,10 +812,11 @@ fun MutableMap.checkGenreTime(genre: String): Boolean {
return true
}
-fun setSlideIn(uiSettings: UserInterfaceSettings) = AnimationSet(false).apply {
- if (uiSettings.layoutAnimations) {
+fun setSlideIn() = AnimationSet(false).apply {
+ if (PrefManager.getVal(PrefName.LayoutAnimations)) {
var animation: Animation = AlphaAnimation(0.0f, 1.0f)
- animation.duration = (500 * uiSettings.animationSpeed).toLong()
+ val animationSpeed: Float = PrefManager.getVal(PrefName.AnimationSpeed)
+ animation.duration = (500 * animationSpeed).toLong()
animation.interpolator = AccelerateDecelerateInterpolator()
addAnimation(animation)
@@ -757,16 +827,17 @@ fun setSlideIn(uiSettings: UserInterfaceSettings) = AnimationSet(false).apply {
Animation.RELATIVE_TO_SELF, 0f
)
- animation.duration = (750 * uiSettings.animationSpeed).toLong()
+ animation.duration = (750 * animationSpeed).toLong()
animation.interpolator = OvershootInterpolator(1.1f)
addAnimation(animation)
}
}
-fun setSlideUp(uiSettings: UserInterfaceSettings) = AnimationSet(false).apply {
- if (uiSettings.layoutAnimations) {
+fun setSlideUp() = AnimationSet(false).apply {
+ if (PrefManager.getVal(PrefName.LayoutAnimations)) {
var animation: Animation = AlphaAnimation(0.0f, 1.0f)
- animation.duration = (500 * uiSettings.animationSpeed).toLong()
+ val animationSpeed: Float = PrefManager.getVal(PrefName.AnimationSpeed)
+ animation.duration = (500 * animationSpeed).toLong()
animation.interpolator = AccelerateDecelerateInterpolator()
addAnimation(animation)
@@ -777,7 +848,7 @@ fun setSlideUp(uiSettings: UserInterfaceSettings) = AnimationSet(false).apply {
Animation.RELATIVE_TO_SELF, 0f
)
- animation.duration = (750 * uiSettings.animationSpeed).toLong()
+ animation.duration = (750 * animationSpeed).toLong()
animation.interpolator = OvershootInterpolator(1.1f)
addAnimation(animation)
}
@@ -839,7 +910,7 @@ fun snackString(s: String?, activity: Activity? = null, clipboard: String? = nul
}
} catch (e: Exception) {
logger(e.stackTraceToString())
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
}
}
@@ -964,8 +1035,7 @@ const val INCOGNITO_CHANNEL_ID = 26
fun incognitoNotification(context: Context) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- val incognito = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- .getBoolean("incognito", false)
+ val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
if (incognito) {
val intent = Intent(context, NotificationClickReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
diff --git a/app/src/main/java/ani/dantotsu/MainActivity.kt b/app/src/main/java/ani/dantotsu/MainActivity.kt
index 2e8cfccee4..8f5dd79f59 100644
--- a/app/src/main/java/ani/dantotsu/MainActivity.kt
+++ b/app/src/main/java/ani/dantotsu/MainActivity.kt
@@ -2,7 +2,6 @@ package ani.dantotsu
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
-import android.content.Context
import android.content.Intent
import android.graphics.drawable.Animatable
import android.graphics.drawable.GradientDrawable
@@ -45,15 +44,13 @@ import ani.dantotsu.home.MangaFragment
import ani.dantotsu.home.NoInternet
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.others.CustomBottomDialog
-import ani.dantotsu.others.LangSet
-import ani.dantotsu.others.SharedPreferenceBooleanLiveData
-import ani.dantotsu.parsers.novel.NovelExtensionManager
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefManager.asLiveBool
+import ani.dantotsu.settings.saving.PrefName
+import ani.dantotsu.settings.saving.SharedPreferenceBooleanLiveData
import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription
import ani.dantotsu.themes.ThemeManager
import eu.kanade.domain.source.service.SourcePreferences
-import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
-import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin
import kotlinx.coroutines.Dispatchers
@@ -73,18 +70,16 @@ class MainActivity : AppCompatActivity() {
private val scope = lifecycleScope
private var load = false
- private var uiSettings = UserInterfaceSettings()
-
@SuppressLint("InternalInsetResource", "DiscouragedApi")
@OptIn(UnstableApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
ThemeManager(this).applyTheme()
- LangSet.setLocale(this)
+
super.onCreate(savedInstanceState)
//get FRAGMENT_CLASS_NAME from intent
- val FRAGMENT_CLASS_NAME = intent.getStringExtra("FRAGMENT_CLASS_NAME")
+ val fragment = intent.getStringExtra("FRAGMENT_CLASS_NAME")
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -98,12 +93,8 @@ class MainActivity : AppCompatActivity() {
backgroundDrawable.setColor(semiTransparentColor)
_bottomBar.background = backgroundDrawable
}
- val sharedPreferences = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- val colorOverflow = sharedPreferences.getBoolean("colorOverflow", false)
- if (!colorOverflow) {
- _bottomBar.background = ContextCompat.getDrawable(this, R.drawable.bottom_nav_gray)
+ _bottomBar.background = ContextCompat.getDrawable(this, R.drawable.bottom_nav_gray)
- }
val offset = try {
val statusBarHeightId = resources.getIdentifier("status_bar_height", "dimen", "android")
@@ -114,11 +105,10 @@ class MainActivity : AppCompatActivity() {
val layoutParams = binding.incognito.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.topMargin = 11 * offset / 12
binding.incognito.layoutParams = layoutParams
- incognitoLiveData = SharedPreferenceBooleanLiveData(
- sharedPreferences,
- "incognito",
+ incognitoLiveData = PrefManager.getLiveVal(
+ PrefName.Incognito,
false
- )
+ ).asLiveBool()
incognitoLiveData.observe(this) {
if (it) {
val slideDownAnim = ObjectAnimator.ofFloat(
@@ -162,7 +152,9 @@ class MainActivity : AppCompatActivity() {
}
val preferences: SourcePreferences = Injekt.get()
- if (preferences.animeExtensionUpdatesCount().get() > 0 || preferences.mangaExtensionUpdatesCount().get() > 0) {
+ if (preferences.animeExtensionUpdatesCount()
+ .get() > 0 || preferences.mangaExtensionUpdatesCount().get() > 0
+ ) {
Toast.makeText(
this,
"You have extension updates available!",
@@ -213,24 +205,22 @@ class MainActivity : AppCompatActivity() {
binding.root.doOnAttach {
initActivity(this)
- uiSettings = loadData("ui_settings") ?: uiSettings
- selectedOption = if (FRAGMENT_CLASS_NAME != null) {
- when (FRAGMENT_CLASS_NAME) {
+ selectedOption = if (fragment != null) {
+ when (fragment) {
AnimeFragment::class.java.name -> 0
HomeFragment::class.java.name -> 1
MangaFragment::class.java.name -> 2
else -> 1
}
} else {
- uiSettings.defaultStartUpTab
+ PrefManager.getVal(PrefName.DefaultStartUpTab)
}
binding.includedNavbar.navbarContainer.updateLayoutParams {
bottomMargin = navBarHeight
}
}
- val offlineMode = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- .getBoolean("offlineMode", false)
+ val offlineMode: Boolean = PrefManager.getVal(PrefName.OfflineMode)
if (!isOnline(this)) {
snackString(this@MainActivity.getString(R.string.no_internet_connection))
startActivity(Intent(this, NoInternet::class.java))
@@ -251,7 +241,7 @@ class MainActivity : AppCompatActivity() {
mainViewPager.isUserInputEnabled = false
mainViewPager.adapter =
ViewPagerAdapter(supportFragmentManager, lifecycle)
- mainViewPager.setPageTransformer(ZoomOutPageTransformer(uiSettings))
+ mainViewPager.setPageTransformer(ZoomOutPageTransformer())
navbar.setOnTabSelectListener(object :
AnimatedBottomBar.OnTabSelectListener {
override fun onTabSelected(
@@ -305,7 +295,7 @@ class MainActivity : AppCompatActivity() {
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- if (loadData("allow_opening_links", this) != true) {
+ if (!(PrefManager.getVal(PrefName.AllowOpeningLinks) as Boolean)) {
CustomBottomDialog.newInstance().apply {
title = "Allow Dantotsu to automatically open Anilist & MAL Links?"
val md = "Open settings & click +Add Links & select Anilist & Mal urls"
@@ -317,18 +307,19 @@ class MainActivity : AppCompatActivity() {
})
setNegativeButton(this@MainActivity.getString(R.string.no)) {
- saveData("allow_opening_links", true, this@MainActivity)
+ PrefManager.setVal(PrefName.AllowOpeningLinks, true)
dismiss()
}
setPositiveButton(this@MainActivity.getString(R.string.yes)) {
- saveData("allow_opening_links", true, this@MainActivity)
+ PrefManager.setVal(PrefName.AllowOpeningLinks, true)
tryWith(true) {
startActivity(
Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS)
.setData(Uri.parse("package:$packageName"))
)
}
+ dismiss()
}
}.show(supportFragmentManager, "dialog")
}
@@ -342,7 +333,7 @@ class MainActivity : AppCompatActivity() {
while (downloadCursor.moveToNext()) {
val download = downloadCursor.download
Log.e("Downloader", download.request.uri.toString())
- Log.e("Downloader", download.request.id.toString())
+ Log.e("Downloader", download.request.id)
Log.e("Downloader", download.request.mimeType.toString())
Log.e("Downloader", download.request.data.size.toString())
Log.e("Downloader", download.bytesDownloaded.toString())
diff --git a/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt b/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt
index 19e45a589f..c3a190087f 100644
--- a/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt
+++ b/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt
@@ -2,11 +2,11 @@ package ani.dantotsu.aniyomi.anime.custom
import android.app.Application
-import android.content.Context
import androidx.annotation.OptIn
import androidx.core.content.ContextCompat
import androidx.media3.common.util.UnstableApi
import androidx.media3.database.StandaloneDatabaseProvider
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.parsers.novel.NovelExtensionManager
@@ -16,7 +16,6 @@ import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import eu.kanade.tachiyomi.network.NetworkHelper
-import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.source.anime.AndroidAnimeSourceManager
import eu.kanade.tachiyomi.source.manga.AndroidMangaSourceManager
import kotlinx.serialization.json.Json
@@ -36,7 +35,7 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { DownloadsManager(app) }
- addSingletonFactory { NetworkHelper(app, get()) }
+ addSingletonFactory { NetworkHelper(app) }
addSingletonFactory { AnimeExtensionManager(app) }
addSingletonFactory { MangaExtensionManager(app) }
@@ -45,9 +44,6 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { AndroidAnimeSourceManager(app, get()) }
addSingletonFactory { AndroidMangaSourceManager(app, get()) }
- val sharedPreferences = app.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- addSingleton(sharedPreferences)
-
addSingletonFactory {
Json {
ignoreUnknownKeys = true
@@ -57,6 +53,10 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { StandaloneDatabaseProvider(app) }
+ addSingletonFactory {
+ ani.dantotsu.connections.crashlytics.CrashlyticsFactory.createCrashlytics()
+ }
+
addSingletonFactory { MangaCache() }
ContextCompat.getMainExecutor(app).execute {
@@ -72,13 +72,6 @@ class PreferenceModule(val application: Application) : InjektModule {
AndroidPreferenceStore(application)
}
- addSingletonFactory {
- NetworkPreferences(
- preferenceStore = get(),
- verboseLogging = false,
- )
- }
-
addSingletonFactory {
SourcePreferences(get())
}
diff --git a/app/src/main/java/ani/dantotsu/connections/UpdateProgress.kt b/app/src/main/java/ani/dantotsu/connections/UpdateProgress.kt
index fb08c59d7e..acfd0cdbb3 100644
--- a/app/src/main/java/ani/dantotsu/connections/UpdateProgress.kt
+++ b/app/src/main/java/ani/dantotsu/connections/UpdateProgress.kt
@@ -6,14 +6,15 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.mal.MAL
import ani.dantotsu.currContext
import ani.dantotsu.media.Media
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
fun updateProgress(media: Media, number: String) {
- val incognito = currContext()?.getSharedPreferences("Dantotsu", 0)
- ?.getBoolean("incognito", false) ?: false
+ val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
if (!incognito) {
if (Anilist.userid != null) {
CoroutineScope(Dispatchers.IO).launch {
diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt b/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt
index 77325d0b76..117b2fa829 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt
@@ -8,8 +8,10 @@ import ani.dantotsu.R
import ani.dantotsu.client
import ani.dantotsu.currContext
import ani.dantotsu.openLinkInBrowser
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
+import ani.dantotsu.toast
import ani.dantotsu.tryWithSuspend
-import java.io.File
import java.util.Calendar
object Anilist {
@@ -28,6 +30,8 @@ object Anilist {
var genres: ArrayList? = null
var tags: Map>? = null
+ var rateLimitReset: Long = 0
+
val sortBy = listOf(
"SCORE_DESC",
"POPULARITY_DESC",
@@ -94,15 +98,12 @@ object Anilist {
}
}
- fun getSavedToken(context: Context): Boolean {
- if ("anilistToken" in context.fileList()) {
- token = File(context.filesDir, "anilistToken").readText()
- return true
- }
- return false
+ fun getSavedToken(): Boolean {
+ token = PrefManager.getVal(PrefName.AnilistToken, null as String?)
+ return !token.isNullOrEmpty()
}
- fun removeSavedToken(context: Context) {
+ fun removeSavedToken() {
token = null
username = null
adult = false
@@ -111,9 +112,7 @@ object Anilist {
bg = null
episodesWatched = null
chapterRead = null
- if ("anilistToken" in context.fileList()) {
- File(context.filesDir, "anilistToken").delete()
- }
+ PrefManager.removeVal(PrefName.AnilistToken)
}
suspend inline fun executeQuery(
@@ -125,6 +124,11 @@ object Anilist {
cache: Int? = null
): T? {
return tryWithSuspend {
+ if (rateLimitReset > System.currentTimeMillis() / 1000) {
+ toast("Rate limited. Try after ${rateLimitReset - (System.currentTimeMillis() / 1000)} seconds")
+ throw Exception("Rate limited after ${rateLimitReset - (System.currentTimeMillis() / 1000)} seconds")
+ }
+
val data = mapOf(
"query" to query,
"variables" to variables
@@ -143,6 +147,16 @@ object Anilist {
data = data,
cacheTime = cache ?: 10
)
+ if (json.code == 429) {
+ val retry = json.headers["Retry-After"]?.toIntOrNull() ?: -1
+ val passedLimitReset = json.headers["X-RateLimit-Reset"]?.toLongOrNull() ?: 0
+ if (retry > 0) {
+ rateLimitReset = passedLimitReset
+ }
+
+ toast("Rate limited. Try after $retry seconds")
+ throw Exception("Rate limited after $retry seconds")
+ }
if (!json.text.startsWith("{")) throw Exception(currContext()?.getString(R.string.anilist_down))
if (show) println("Response : ${json.text}")
json.parsed()
diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt
index 5c344b5937..14bb86b729 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt
@@ -1,7 +1,7 @@
package ani.dantotsu.connections.anilist
import android.app.Activity
-import android.content.Context
+import android.util.Base64
import ani.dantotsu.R
import ani.dantotsu.checkGenreTime
import ani.dantotsu.checkId
@@ -12,18 +12,23 @@ import ani.dantotsu.connections.anilist.api.Page
import ani.dantotsu.connections.anilist.api.Query
import ani.dantotsu.currContext
import ani.dantotsu.isOnline
-import ani.dantotsu.loadData
import ani.dantotsu.logError
import ani.dantotsu.media.Author
import ani.dantotsu.media.Character
import ani.dantotsu.media.Media
import ani.dantotsu.media.Studio
import ani.dantotsu.others.MalScraper
-import ani.dantotsu.saveData
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.ObjectInputStream
+import java.io.ObjectOutputStream
+import java.io.Serializable
import kotlin.system.measureTimeMillis
class AnilistQueries {
@@ -35,12 +40,7 @@ class AnilistQueries {
}.also { println("time : $it") }
val user = response?.data?.user ?: return false
- currContext()?.let {
- it.getSharedPreferences(it.getString(R.string.preference_file_key), Context.MODE_PRIVATE)
- .edit()
- .putString("anilist_username", user.name)
- .apply()
- }
+ PrefManager.setVal(PrefName.AnilistUserName, user.name)
Anilist.userid = user.id
Anilist.username = user.name
@@ -281,8 +281,8 @@ class AnilistQueries {
}
statuses.forEach { repeat(it) }
- val set = loadData>("continue_$type")
- if (set != null) {
+ val set = PrefManager.getCustomVal>("continue_$type", setOf()).toMutableSet()
+ if (set.isNotEmpty()) {
set.reversed().forEach {
if (map.containsKey(it)) returnArray.add(map[it]!!)
}
@@ -355,7 +355,11 @@ class AnilistQueries {
}
private suspend fun bannerImage(type: String): String? {
- var image = loadData("banner_$type")
+ //var image = loadData("banner_$type")
+ val image: BannerImage? = BannerImage(
+ PrefManager.getCustomVal("banner_${type}_url", null),
+ PrefManager.getCustomVal("banner_${type}_time", 0L)
+ )
if (image == null || image.checkTime()) {
val response =
executeQuery("""{ MediaListCollection(userId: ${Anilist.userid}, type: $type, chunk:1,perChunk:25, sort: [SCORE_DESC,UPDATED_TIME_DESC]) { lists { entries{ media { id bannerImage } } } } } """)
@@ -366,13 +370,9 @@ class AnilistQueries {
else null
}
}?.flatten()?.randomOrNull() ?: return null
-
- image = BannerImage(
- random,
- System.currentTimeMillis()
- )
- saveData("banner_$type", image)
- return image.url
+ PrefManager.setCustomVal("banner_${type}_url", random)
+ PrefManager.setCustomVal("banner_${type}_time", System.currentTimeMillis())
+ return random
} else return image.url
}
@@ -419,11 +419,17 @@ class AnilistQueries {
sorted["Favourites"] = favMedia(anime)
sorted["Favourites"]?.sortWith(compareBy { it.userFavOrder })
+ //favMedia doesn't fill userProgress, so we need to fill it manually by searching :(
+ sorted["Favourites"]?.forEach { fav ->
+ all.find { it.id == fav.id }?.let {
+ fav.userProgress = it.userProgress
+ }
+ }
sorted["All"] = all
- val listsort = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getString("sort_order", "score")
- val sort = listsort ?: sortOrder ?: options?.rowOrder
+ val listSort: String = if (anime) PrefManager.getVal(PrefName.AnimeListSortOrder)
+ else PrefManager.getVal(PrefName.MangaListSortOrder)
+ val sort = listSort ?: sortOrder ?: options?.rowOrder
for (i in sorted.keys) {
when (sort) {
"score" -> sorted[i]?.sortWith { b, a ->
@@ -444,11 +450,19 @@ class AnilistQueries {
}
- suspend fun getGenresAndTags(activity: Activity): Boolean {
- var genres: ArrayList? = loadData("genres_list", activity)
- var tags: Map>? = loadData("tags_map", activity)
+ suspend fun getGenresAndTags(): Boolean {
+ var genres: ArrayList? = PrefManager.getVal>(PrefName.GenresList)
+ .toMutableList() as ArrayList?
+ val adultTags = PrefManager.getVal>(PrefName.TagsListIsAdult).toMutableList()
+ val nonAdultTags =
+ PrefManager.getVal>(PrefName.TagsListNonAdult).toMutableList()
+ var tags = if (adultTags.isEmpty() || nonAdultTags.isEmpty()) null else
+ mapOf(
+ true to adultTags,
+ false to nonAdultTags
+ )
- if (genres == null) {
+ if (genres.isNullOrEmpty()) {
executeQuery(
"""{GenreCollection}""",
force = true,
@@ -458,7 +472,7 @@ class AnilistQueries {
forEach {
genres?.add(it)
}
- saveData("genres_list", genres!!)
+ PrefManager.setVal(PrefName.GenresList, genres?.toSet())
}
}
if (tags == null) {
@@ -476,10 +490,11 @@ class AnilistQueries {
true to adult,
false to good
)
- saveData("tags_map", tags)
+ PrefManager.setVal(PrefName.TagsListIsAdult, adult.toSet())
+ PrefManager.setVal(PrefName.TagsListNonAdult, good.toSet())
}
}
- return if (genres != null && tags != null) {
+ return if (!genres.isNullOrEmpty() && tags != null) {
Anilist.genres = genres
Anilist.tags = tags
true
@@ -496,8 +511,37 @@ class AnilistQueries {
}
}
+ private fun saveSerializableMap(prefKey: String, map: Map) {
+ val byteStream = ByteArrayOutputStream()
+
+ ObjectOutputStream(byteStream).use { outputStream ->
+ outputStream.writeObject(map)
+ }
+ val serializedMap = Base64.encodeToString(byteStream.toByteArray(), Base64.DEFAULT)
+ PrefManager.setCustomVal(prefKey, serializedMap)
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun loadSerializableMap(prefKey: String): Map? {
+ try {
+ val serializedMap = PrefManager.getCustomVal(prefKey, "")
+ if (serializedMap.isEmpty()) return null
+
+ val bytes = Base64.decode(serializedMap, Base64.DEFAULT)
+ val byteArrayStream = ByteArrayInputStream(bytes)
+
+ return ObjectInputStream(byteArrayStream).use { inputStream ->
+ inputStream.readObject() as? Map
+ }
+ } catch (e: Exception) {
+ return null
+ }
+ }
+
private suspend fun getGenreThumbnail(genre: String): Genre? {
- val genres = loadData>("genre_thumb") ?: mutableMapOf()
+ val genres: MutableMap =
+ loadSerializableMap("genre_thumb")?.toMutableMap()
+ ?: mutableMapOf()
if (genres.checkGenreTime(genre)) {
try {
val genreQuery =
@@ -510,7 +554,7 @@ class AnilistQueries {
it.bannerImage!!,
System.currentTimeMillis()
)
- saveData("genre_thumb", genres)
+ saveSerializableMap("genre_thumb", genres)
return genres[genre]
}
}
@@ -665,7 +709,7 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
page = pageInfo.currentPage.toString().toIntOrNull() ?: 0,
hasNextPage = pageInfo.hasNextPage == true,
)
- } else snackString(currContext()?.getString(R.string.empty_response))
+ }
return null
}
@@ -723,7 +767,7 @@ Page(page:$page,perPage:50) {
if (smaller) {
val response = execute()?.airingSchedules ?: return null
val idArr = mutableListOf()
- val listOnly = loadData("recently_list_only") ?: false
+ val listOnly: Boolean = PrefManager.getVal(PrefName.RecentlyListOnly)
return response.mapNotNull { i ->
i.media?.let {
if (!idArr.contains(it.id))
diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt
index 4cc8451f36..5e3bacb9e7 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt
@@ -5,12 +5,14 @@ import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
+import ani.dantotsu.BuildConfig
import ani.dantotsu.R
import ani.dantotsu.connections.discord.Discord
import ani.dantotsu.connections.mal.MAL
-import ani.dantotsu.loadData
import ani.dantotsu.media.Media
import ani.dantotsu.others.AppUpdater
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import ani.dantotsu.tryWithSuspend
import kotlinx.coroutines.CoroutineScope
@@ -19,12 +21,8 @@ import kotlinx.coroutines.launch
suspend fun getUserId(context: Context, block: () -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
- val sharedPref = context.getSharedPreferences(
- context.getString(R.string.preference_file_key),
- Context.MODE_PRIVATE
- )
- val token = sharedPref.getString("discord_token", null)
- val userid = sharedPref.getString("discord_id", null)
+ val token = PrefManager.getVal(PrefName.DiscordToken, null as String?)
+ val userid = PrefManager.getVal(PrefName.DiscordId, null as String?)
if (userid == null && token != null) {
/*if (!Discord.getUserData())
snackString(context.getString(R.string.error_loading_discord_user_data))*/
@@ -100,11 +98,13 @@ class AnilistHomeViewModel : ViewModel() {
suspend fun setRecommendation() = recommendation.postValue(Anilist.query.recommendations())
suspend fun loadMain(context: FragmentActivity) {
- Anilist.getSavedToken(context)
+ Anilist.getSavedToken()
MAL.getSavedToken(context)
Discord.getSavedToken(context)
- if (loadData("check_update") != false) AppUpdater.check(context)
- genres.postValue(Anilist.query.getGenresAndTags(context))
+ if (!BuildConfig.FLAVOR.contains("fdroid")) {
+ if (PrefManager.getVal(PrefName.CheckUpdate)) AppUpdater.check(context)
+ }
+ genres.postValue(Anilist.query.getGenresAndTags())
}
val empty = MutableLiveData(null)
diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/Login.kt b/app/src/main/java/ani/dantotsu/connections/anilist/Login.kt
index fdba6d3a84..7512fc5d55 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/Login.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/Login.kt
@@ -1,29 +1,26 @@
package ani.dantotsu.connections.anilist
-import android.content.Context
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import ani.dantotsu.logError
import ani.dantotsu.logger
-import ani.dantotsu.others.LangSet
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.startMainActivity
import ani.dantotsu.themes.ThemeManager
class Login : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
val data: Uri? = intent?.data
logger(data.toString())
try {
Anilist.token =
Regex("""(?<=access_token=).+(?=&token_type)""").find(data.toString())!!.value
- val filename = "anilistToken"
- this.openFileOutput(filename, Context.MODE_PRIVATE).use {
- it.write(Anilist.token!!.toByteArray())
- }
+ PrefManager.setVal(PrefName.AnilistToken, Anilist.token ?: "")
} catch (e: Exception) {
logError(e)
}
diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt b/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt
index 30be1706ba..98d63d27ed 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt
@@ -5,14 +5,13 @@ import android.net.Uri
import android.os.Bundle
import androidx.core.os.bundleOf
import ani.dantotsu.loadMedia
-import ani.dantotsu.others.LangSet
import ani.dantotsu.startMainActivity
import ani.dantotsu.themes.ThemeManager
class UrlMedia : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
var isMAL = false
diff --git a/app/src/main/java/ani/dantotsu/connections/crashlytics/CrashlyticsInterface.kt b/app/src/main/java/ani/dantotsu/connections/crashlytics/CrashlyticsInterface.kt
new file mode 100644
index 0000000000..54307d245c
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/connections/crashlytics/CrashlyticsInterface.kt
@@ -0,0 +1,12 @@
+package ani.dantotsu.connections.crashlytics
+
+import android.content.Context
+
+interface CrashlyticsInterface {
+ fun initialize(context: Context)
+ fun logException(e: Throwable)
+ fun log(message: String)
+ fun setUserId(id: String)
+ fun setCustomKey(key: String, value: String)
+ fun setCrashlyticsCollectionEnabled(enabled: Boolean)
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/connections/crashlytics/CrashlyticsStub.kt b/app/src/main/java/ani/dantotsu/connections/crashlytics/CrashlyticsStub.kt
new file mode 100644
index 0000000000..524d42bf51
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/connections/crashlytics/CrashlyticsStub.kt
@@ -0,0 +1,29 @@
+package ani.dantotsu.connections.crashlytics
+
+import android.content.Context
+
+class CrashlyticsStub : CrashlyticsInterface {
+ override fun initialize(context: Context) {
+ //no-op
+ }
+ override fun logException(e: Throwable) {
+ //no-op
+ }
+
+ override fun log(message: String) {
+ //no-op
+ }
+
+ override fun setUserId(id: String) {
+ //no-op
+ }
+
+ override fun setCustomKey(key: String, value: String) {
+ //no-op
+ }
+
+ override fun setCrashlyticsCollectionEnabled(enabled: Boolean) {
+ //no-op
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt b/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt
index 9545030552..c151850d2c 100644
--- a/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt
+++ b/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt
@@ -3,9 +3,10 @@ package ani.dantotsu.connections.discord
import android.content.Context
import android.content.Intent
import android.widget.TextView
-import androidx.core.content.edit
import ani.dantotsu.R
import ani.dantotsu.others.CustomBottomDialog
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.toast
import ani.dantotsu.tryWith
import io.noties.markwon.Markwon
@@ -18,37 +19,20 @@ object Discord {
var userid: String? = null
var avatar: String? = null
- const val TOKEN = "discord_token"
fun getSavedToken(context: Context): Boolean {
- val sharedPref = context.getSharedPreferences(
- context.getString(R.string.preference_file_key),
- Context.MODE_PRIVATE
+ token = PrefManager.getVal(
+ PrefName.DiscordToken, null as String?
)
- token = sharedPref.getString(TOKEN, null)
return token != null
}
fun saveToken(context: Context, token: String) {
- val sharedPref = context.getSharedPreferences(
- context.getString(R.string.preference_file_key),
- Context.MODE_PRIVATE
- )
- sharedPref.edit {
- putString(TOKEN, token)
- commit()
- }
+ PrefManager.setVal(PrefName.DiscordToken, token)
}
fun removeSavedToken(context: Context) {
- val sharedPref = context.getSharedPreferences(
- context.getString(R.string.preference_file_key),
- Context.MODE_PRIVATE
- )
- sharedPref.edit {
- remove(TOKEN)
- commit()
- }
+ PrefManager.removeVal(PrefName.DiscordToken)
tryWith(true) {
val dir = File(context.filesDir?.parentFile, "app_webview")
diff --git a/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt b/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt
index 3506ea259f..761dbeac47 100644
--- a/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt
+++ b/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt
@@ -24,6 +24,8 @@ import ani.dantotsu.R
import ani.dantotsu.connections.discord.serializers.Presence
import ani.dantotsu.connections.discord.serializers.User
import ani.dantotsu.isOnline
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
@@ -149,19 +151,11 @@ class DiscordService : Service() {
}
fun saveProfile(response: String) {
- val sharedPref = baseContext.getSharedPreferences(
- baseContext.getString(R.string.preference_file_key),
- Context.MODE_PRIVATE
- )
val user = json.decodeFromString(response).d.user
log("User data: $user")
- with(sharedPref.edit()) {
- putString("discord_username", user.username)
- putString("discord_id", user.id)
- putString("discord_avatar", user.avatar)
- apply()
- }
-
+ PrefManager.setVal(PrefName.DiscordUserName, user.username)
+ PrefManager.setVal(PrefName.DiscordId, user.id)
+ PrefManager.setVal(PrefName.DiscordAvatar, user.avatar)
}
override fun onBind(p0: Intent?): IBinder? = null
@@ -318,17 +312,13 @@ class DiscordService : Service() {
}
fun getToken(context: Context): String {
- val sharedPref = context.getSharedPreferences(
- context.getString(R.string.preference_file_key),
- Context.MODE_PRIVATE
- )
- val token = sharedPref.getString(Discord.TOKEN, null)
- if (token == null) {
+ val token = PrefManager.getVal(PrefName.DiscordToken, null as String?)
+ return if (token == null) {
log("WebSocket: Token not found")
errorNotification("Could not set the presence", "token not found")
- return ""
+ ""
} else {
- return token
+ token
}
}
diff --git a/app/src/main/java/ani/dantotsu/connections/discord/Login.kt b/app/src/main/java/ani/dantotsu/connections/discord/Login.kt
index 98048844b8..676f11e210 100644
--- a/app/src/main/java/ani/dantotsu/connections/discord/Login.kt
+++ b/app/src/main/java/ani/dantotsu/connections/discord/Login.kt
@@ -11,7 +11,6 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import ani.dantotsu.R
import ani.dantotsu.connections.discord.Discord.saveToken
-import ani.dantotsu.others.LangSet
import ani.dantotsu.startMainActivity
import ani.dantotsu.themes.ThemeManager
@@ -20,7 +19,7 @@ class Login : AppCompatActivity() {
@SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val process = getProcessName()
diff --git a/app/src/main/java/ani/dantotsu/connections/mal/Login.kt b/app/src/main/java/ani/dantotsu/connections/mal/Login.kt
index 9ec2ca5ab8..c06baf3835 100644
--- a/app/src/main/java/ani/dantotsu/connections/mal/Login.kt
+++ b/app/src/main/java/ani/dantotsu/connections/mal/Login.kt
@@ -8,9 +8,9 @@ import ani.dantotsu.R
import ani.dantotsu.client
import ani.dantotsu.connections.mal.MAL.clientId
import ani.dantotsu.connections.mal.MAL.saveResponse
-import ani.dantotsu.loadData
import ani.dantotsu.logError
-import ani.dantotsu.others.LangSet
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import ani.dantotsu.startMainActivity
import ani.dantotsu.themes.ThemeManager
@@ -21,12 +21,12 @@ import kotlinx.coroutines.launch
class Login : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
try {
val data: Uri = intent?.data
?: throw Exception(getString(R.string.mal_login_uri_not_found))
- val codeChallenge: String = loadData("malCodeChallenge", this)
+ val codeChallenge = PrefManager.getVal(PrefName.MALCodeChallenge, null as String?)
?: throw Exception(getString(R.string.mal_login_code_challenge_not_found))
val code = data.getQueryParameter("code")
?: throw Exception(getString(R.string.mal_login_code_not_present))
diff --git a/app/src/main/java/ani/dantotsu/connections/mal/MAL.kt b/app/src/main/java/ani/dantotsu/connections/mal/MAL.kt
index 0391cd47d0..9eaffdca70 100644
--- a/app/src/main/java/ani/dantotsu/connections/mal/MAL.kt
+++ b/app/src/main/java/ani/dantotsu/connections/mal/MAL.kt
@@ -9,13 +9,12 @@ import androidx.fragment.app.FragmentActivity
import ani.dantotsu.R
import ani.dantotsu.client
import ani.dantotsu.currContext
-import ani.dantotsu.loadData
import ani.dantotsu.openLinkInBrowser
-import ani.dantotsu.saveData
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.tryWithSuspend
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
-import java.io.File
import java.security.SecureRandom
object MAL {
@@ -34,7 +33,7 @@ object MAL {
.replace("/", "_")
.replace("\n", "")
- saveData("malCodeChallenge", codeChallenge, context)
+ PrefManager.setVal(PrefName.MALCodeChallenge, codeChallenge)
val request =
"https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id=$clientId&code_challenge=$codeChallenge"
try {
@@ -47,11 +46,9 @@ object MAL {
}
}
- private const val MAL_TOKEN = "malToken"
-
private suspend fun refreshToken(): ResponseToken? {
return tryWithSuspend {
- val token = loadData(MAL_TOKEN)
+ val token = PrefManager.getNullableVal(PrefName.MALToken, null)
?: throw Exception(currContext()?.getString(R.string.refresh_token_load_failed))
val res = client.post(
"https://myanimelist.net/v1/oauth2/token",
@@ -69,8 +66,9 @@ object MAL {
suspend fun getSavedToken(context: FragmentActivity): Boolean {
return tryWithSuspend(false) {
- var res: ResponseToken = loadData(MAL_TOKEN, context)
- ?: return@tryWithSuspend false
+ var res: ResponseToken =
+ PrefManager.getNullableVal(PrefName.MALToken, null)
+ ?: return@tryWithSuspend false
if (System.currentTimeMillis() > res.expiresIn)
res = refreshToken()
?: throw Exception(currContext()?.getString(R.string.refreshing_token_failed))
@@ -84,14 +82,12 @@ object MAL {
username = null
userid = null
avatar = null
- if (MAL_TOKEN in context.fileList()) {
- File(context.filesDir, MAL_TOKEN).delete()
- }
+ PrefManager.removeVal(PrefName.MALToken)
}
fun saveResponse(res: ResponseToken) {
res.expiresIn += System.currentTimeMillis()
- saveData(MAL_TOKEN, res)
+ PrefManager.setVal(PrefName.MALToken, res)
}
@Serializable
@@ -100,6 +96,10 @@ object MAL {
@SerialName("expires_in") var expiresIn: Long,
@SerialName("access_token") val accessToken: String,
@SerialName("refresh_token") val refreshToken: String,
- ) : java.io.Serializable
+ ) : java.io.Serializable {
+ companion object {
+ private const val serialVersionUID = 1L
+ }
+ }
}
diff --git a/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt b/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt
index ef7765628c..0cbfa1bc0c 100644
--- a/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt
+++ b/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt
@@ -1,17 +1,16 @@
package ani.dantotsu.download
import android.content.Context
-import android.content.SharedPreferences
import android.os.Environment
import android.widget.Toast
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.io.File
import java.io.Serializable
class DownloadsManager(private val context: Context) {
- private val prefs: SharedPreferences =
- context.getSharedPreferences("downloads_pref", Context.MODE_PRIVATE)
private val gson = Gson()
private val downloadsList = loadDownloads().toMutableList()
@@ -24,11 +23,11 @@ class DownloadsManager(private val context: Context) {
private fun saveDownloads() {
val jsonString = gson.toJson(downloadsList)
- prefs.edit().putString("downloads_key", jsonString).apply()
+ PrefManager.setVal(PrefName.DownloadsKeys, jsonString)
}
private fun loadDownloads(): List {
- val jsonString = prefs.getString("downloads_key", null)
+ val jsonString = PrefManager.getVal(PrefName.DownloadsKeys, null as String?)
return if (jsonString != null) {
val type = object : TypeToken>() {}.type
gson.fromJson(jsonString, type)
@@ -75,9 +74,11 @@ class DownloadsManager(private val context: Context) {
DownloadedType.Type.MANGA -> {
downloadsList.removeAll { it.title == title && it.type == DownloadedType.Type.MANGA }
}
+
DownloadedType.Type.ANIME -> {
downloadsList.removeAll { it.title == title && it.type == DownloadedType.Type.ANIME }
}
+
DownloadedType.Type.NOVEL -> {
downloadsList.removeAll { it.title == title && it.type == DownloadedType.Type.NOVEL }
}
@@ -252,7 +253,12 @@ class DownloadsManager(private val context: Context) {
const val mangaLocation = "Dantotsu/Manga"
const val animeLocation = "Dantotsu/Anime"
- fun getDirectory(context: Context, type: DownloadedType.Type, title: String, chapter: String? = null): File {
+ fun getDirectory(
+ context: Context,
+ type: DownloadedType.Type,
+ title: String,
+ chapter: String? = null
+ ): File {
return if (type == DownloadedType.Type.MANGA) {
if (chapter != null) {
File(
diff --git a/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt
index a69ef446d1..dd0198ec22 100644
--- a/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt
@@ -21,6 +21,7 @@ import androidx.media3.exoplayer.offline.DownloadManager
import androidx.media3.exoplayer.offline.DownloadService
import ani.dantotsu.FileUrl
import ani.dantotsu.R
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.currActivity
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
@@ -32,8 +33,8 @@ import ani.dantotsu.media.SubtitleDownloader
import ani.dantotsu.media.anime.AnimeWatchFragment
import ani.dantotsu.parsers.Subtitle
import ani.dantotsu.parsers.Video
+import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.snackString
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.gson.GsonBuilder
import com.google.gson.InstanceCreator
import eu.kanade.tachiyomi.animesource.model.SAnime
@@ -84,7 +85,7 @@ class AnimeDownloaderService : Service() {
builder =
NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
setContentTitle("Anime Download Progress")
- setSmallIcon(R.drawable.ic_round_download_24)
+ setSmallIcon(R.drawable.ic_download_24)
priority = NotificationCompat.PRIORITY_DEFAULT
setOnlyAlertOnce(true)
}
@@ -224,8 +225,6 @@ class AnimeDownloaderService : Service() {
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
- broadcastDownloadStarted(task.episode)
-
currActivity()?.let {
Helper.downloadVideo(
it,
@@ -276,7 +275,7 @@ class AnimeDownloaderService : Service() {
DownloadedType.Type.ANIME,
)
)
- FirebaseCrashlytics.getInstance().recordException(
+ Injekt.get().logException(
Exception(
"Anime Download failed:" +
" ${download.failureReason}" +
@@ -294,10 +293,7 @@ class AnimeDownloaderService : Service() {
builder.setContentText("${task.title} - ${task.episode} Download completed")
notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download completed")
- getSharedPreferences(
- getString(R.string.anime_downloads),
- Context.MODE_PRIVATE
- ).edit().putString(
+ PrefManager.getAnimeDownloadPreferences().edit().putString(
task.getTaskName(),
task.video.file.url
).apply()
@@ -335,7 +331,7 @@ class AnimeDownloaderService : Service() {
logger("Exception while downloading file: ${e.message}")
snackString("Exception while downloading file: ${e.message}")
e.printStackTrace()
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
}
broadcastDownloadFailed(task.episode)
}
diff --git a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeAdapter.kt b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeAdapter.kt
index 4cd57f050f..ef0c4148f9 100644
--- a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeAdapter.kt
@@ -12,6 +12,8 @@ import android.widget.LinearLayout
import android.widget.TextView
import androidx.cardview.widget.CardView
import ani.dantotsu.R
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
class OfflineAnimeAdapter(
@@ -22,8 +24,7 @@ class OfflineAnimeAdapter(
private val inflater: LayoutInflater =
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
private var originalItems: List = items
- private var style =
- context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
+ private var style: Int = PrefManager.getVal(PrefName.OfflineView)
override fun getCount(): Int {
return items.size
@@ -105,8 +106,7 @@ class OfflineAnimeAdapter(
}
fun notifyNewGrid() {
- style =
- context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
+ style = PrefManager.getVal(PrefName.OfflineView)
notifyDataSetChanged()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt
index e52647e250..4c91a43b85 100644
--- a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt
+++ b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt
@@ -1,11 +1,8 @@
package ani.dantotsu.download.anime
-import android.animation.ObjectAnimator
-import android.content.Context
import android.content.Intent
import android.net.Uri
-import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.text.Editable
@@ -16,7 +13,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.animation.AlphaAnimation
import android.view.animation.LayoutAnimationController
-import android.view.animation.OvershootInterpolator
import android.widget.AbsListView
import android.widget.AutoCompleteTextView
import android.widget.GridView
@@ -25,34 +21,30 @@ import android.widget.TextView
import androidx.annotation.OptIn
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
-import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat
-import androidx.core.util.Pair
-import androidx.core.view.ViewCompat
import androidx.core.view.marginBottom
import androidx.fragment.app.Fragment
import androidx.media3.common.util.UnstableApi
import ani.dantotsu.R
import ani.dantotsu.bottomBar
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.currActivity
import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.initActivity
-import ani.dantotsu.loadData
import ani.dantotsu.logger
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.settings.SettingsDialogFragment
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
-import ani.dantotsu.statusBarHeight
import com.google.android.material.card.MaterialCardView
import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.textfield.TextInputLayout
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.gson.GsonBuilder
import com.google.gson.InstanceCreator
import eu.kanade.tachiyomi.animesource.model.SAnime
@@ -64,8 +56,6 @@ import eu.kanade.tachiyomi.source.model.SChapterImpl
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
-import kotlin.math.max
-import kotlin.math.min
class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
@@ -73,9 +63,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
private var downloads: List = listOf()
private lateinit var gridView: GridView
private lateinit var adapter: OfflineAnimeAdapter
- private lateinit var total : TextView
- private var uiSettings: UserInterfaceSettings =
- loadData("ui_settings") ?: UserInterfaceSettings()
+ private lateinit var total: TextView
override fun onCreateView(
inflater: LayoutInflater,
@@ -101,15 +89,12 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
SettingsDialogFragment.newInstance(SettingsDialogFragment.Companion.PageType.OfflineANIME)
dialogFragment.show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
}
- if (!uiSettings.immersiveMode) {
+ if (!(PrefManager.getVal(PrefName.ImmersiveMode) as Boolean)) {
view.rootView.fitsSystemWindows = true
}
- val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getBoolean("colorOverflow", false) ?: false
- if (!colorOverflow) {
- textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
- materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
- }
+
+ textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000
+ materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000)
val searchView = view.findViewById(R.id.animeSearchBarText)
searchView.addTextChangedListener(object : TextWatcher {
@@ -123,8 +108,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
onSearchQuery(s.toString())
}
})
- var style = context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getInt("offline_view", 0)
+ var style: Int = PrefManager.getVal(PrefName.OfflineView)
val layoutList = view.findViewById(R.id.downloadedList)
val layoutcompact = view.findViewById(R.id.downloadedGrid)
var selected = when (style) {
@@ -143,8 +127,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
layoutList.setOnClickListener {
selected(it as ImageView)
style = 0
- context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
- ?.putInt("offline_view", style!!)?.apply()
+ PrefManager.setVal(PrefName.OfflineView, style)
gridView.visibility = View.GONE
gridView = view.findViewById(R.id.gridView)
adapter.notifyNewGrid()
@@ -154,15 +137,15 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
layoutcompact.setOnClickListener {
selected(it as ImageView)
style = 1
- context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
- ?.putInt("offline_view", style!!)?.apply()
+ PrefManager.setVal(PrefName.OfflineView, style)
gridView.visibility = View.GONE
gridView = view.findViewById(R.id.gridView1)
adapter.notifyNewGrid()
grid()
}
- gridView = if (style == 0) view.findViewById(R.id.gridView) else view.findViewById(R.id.gridView1)
+ gridView =
+ if (style == 0) view.findViewById(R.id.gridView) else view.findViewById(R.id.gridView1)
total = view.findViewById(R.id.total)
grid()
return view
@@ -195,13 +178,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
requireActivity(),
Intent(requireContext(), MediaDetailsActivity::class.java)
.putExtra("download", true),
- ActivityOptionsCompat.makeSceneTransitionAnimation(
- requireActivity(),
- Pair.create(
- requireActivity().findViewById(R.id.itemCompactImage),
- ViewCompat.getTransitionName(requireActivity().findViewById(R.id.itemCompactImage))
- ),
- ).toBundle()
+ null
)
} ?: run {
snackString("no media found")
@@ -220,11 +197,9 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
builder.setMessage("Are you sure you want to delete ${item.title}?")
builder.setPositiveButton("Yes") { _, _ ->
downloadManager.removeMedia(item.title, type)
- val mediaIds = requireContext().getSharedPreferences(
- getString(R.string.anime_downloads),
- Context.MODE_PRIVATE
- )
- ?.all?.filter { it.key.contains(item.title) }?.values ?: emptySet()
+ val mediaIds =
+ PrefManager.getAnimeDownloadPreferences().all?.filter { it.key.contains(item.title) }?.values
+ ?: emptySet()
if (mediaIds.isEmpty()) {
snackString("No media found") // if this happens, terrible things have happened
}
@@ -252,41 +227,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- var height = statusBarHeight
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- val displayCutout = activity?.window?.decorView?.rootWindowInsets?.displayCutout
- if (displayCutout != null) {
- if (displayCutout.boundingRects.size > 0) {
- height = max(
- statusBarHeight,
- min(
- displayCutout.boundingRects[0].width(),
- displayCutout.boundingRects[0].height()
- )
- )
- }
- }
- }
val scrollTop = view.findViewById(R.id.mangaPageScrollTop)
- scrollTop.translationY =
- -(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
- val visible = false
-
- fun animate() {
- val start = if (visible) 0f else 1f
- val end = if (!visible) 0f else 1f
- ObjectAnimator.ofFloat(scrollTop, "scaleX", start, end).apply {
- duration = 300
- interpolator = OvershootInterpolator(2f)
- start()
- }
- ObjectAnimator.ofFloat(scrollTop, "scaleY", start, end).apply {
- duration = 300
- interpolator = OvershootInterpolator(2f)
- start()
- }
- }
-
scrollTop.setOnClickListener {
gridView.smoothScrollToPositionFromTop(0, 0)
}
@@ -306,7 +247,9 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
totalItemCount: Int
) {
val first = view.getChildAt(0)
- val visibility = first != null && first.top < -height
+ val visibility = first != null && first.top < 0
+ scrollTop.translationY =
+ -(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
scrollTop.visibility = if (visibility) View.VISIBLE else View.GONE
}
})
@@ -340,8 +283,8 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
val animeTitles = downloadManager.animeDownloadedTypes.map { it.title }.distinct()
val newAnimeDownloads = mutableListOf()
for (title in animeTitles) {
- val _downloads = downloadManager.animeDownloadedTypes.filter { it.title == title }
- val download = _downloads.first()
+ val tDownloads = downloadManager.animeDownloadedTypes.filter { it.title == title }
+ val download = tDownloads.first()
val offlineAnimeModel = loadOfflineAnimeModel(download)
newAnimeDownloads += offlineAnimeModel
}
@@ -349,12 +292,10 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
}
private fun getMedia(downloadedType: DownloadedType): Media? {
- val type = if (downloadedType.type == DownloadedType.Type.ANIME) {
- "Anime"
- } else if (downloadedType.type == DownloadedType.Type.MANGA) {
- "Manga"
- } else {
- "Novel"
+ val type = when (downloadedType.type) {
+ DownloadedType.Type.MANGA -> "Manga"
+ DownloadedType.Type.ANIME -> "Anime"
+ else -> "Novel"
}
val directory = File(
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
@@ -379,18 +320,16 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
} catch (e: Exception) {
logger("Error loading media.json: ${e.message}")
logger(e.printStackTrace())
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
null
}
}
private fun loadOfflineAnimeModel(downloadedType: DownloadedType): OfflineAnimeModel {
- val type = if (downloadedType.type == DownloadedType.Type.MANGA) {
- "Manga"
- } else if (downloadedType.type == DownloadedType.Type.ANIME) {
- "Anime"
- } else {
- "Novel"
+ val type = when (downloadedType.type) {
+ DownloadedType.Type.MANGA -> "Manga"
+ DownloadedType.Type.ANIME -> "Anime"
+ else -> "Novel"
}
val directory = File(
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
@@ -398,8 +337,6 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
)
//load media.json and convert to media class with gson
try {
- val media = File(directory, "media.json")
- val mediaJson = media.readText()
val mediaModel = getMedia(downloadedType)!!
val cover = File(directory, "cover.jpg")
val coverUri: Uri? = if (cover.exists()) {
@@ -439,7 +376,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
} catch (e: Exception) {
logger("Error loading media.json: ${e.message}")
logger(e.printStackTrace())
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
return OfflineAnimeModel(
"unknown",
"0",
@@ -448,8 +385,8 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
"??",
"movie",
"hmm",
- false,
- false,
+ isOngoing = false,
+ isUserScored = false,
null,
null
)
diff --git a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
index 0a1337dfeb..de91960ca8 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
@@ -18,6 +18,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import ani.dantotsu.R
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger
@@ -29,7 +30,6 @@ import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_PROG
import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_STARTED
import ani.dantotsu.media.manga.MangaReadFragment.Companion.EXTRA_CHAPTER_NUMBER
import ani.dantotsu.snackString
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.gson.GsonBuilder
import com.google.gson.InstanceCreator
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_DOWNLOADER_PROGRESS
@@ -76,7 +76,7 @@ class MangaDownloaderService : Service() {
notificationManager = NotificationManagerCompat.from(this)
builder = NotificationCompat.Builder(this, CHANNEL_DOWNLOADER_PROGRESS).apply {
setContentTitle("Manga Download Progress")
- setSmallIcon(R.drawable.ic_round_download_24)
+ setSmallIcon(R.drawable.ic_download_24)
priority = NotificationCompat.PRIORITY_DEFAULT
setOnlyAlertOnce(true)
setProgress(0, 0, false)
@@ -253,7 +253,7 @@ class MangaDownloaderService : Service() {
} catch (e: Exception) {
logger("Exception while downloading file: ${e.message}")
snackString("Exception while downloading file: ${e.message}")
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
broadcastDownloadFailed(task.chapter)
}
}
@@ -283,7 +283,7 @@ class MangaDownloaderService : Service() {
} catch (e: Exception) {
println("Exception while saving image: ${e.message}")
snackString("Exception while saving image: ${e.message}")
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
}
}
diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt
index 4ec6ab4343..4ef12c03b8 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt
@@ -11,6 +11,8 @@ import android.widget.LinearLayout
import android.widget.TextView
import androidx.cardview.widget.CardView
import ani.dantotsu.R
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
class OfflineMangaAdapter(
@@ -21,8 +23,7 @@ class OfflineMangaAdapter(
private val inflater: LayoutInflater =
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
private var originalItems: List = items
- private var style =
- context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
+ private var style: Int = PrefManager.getVal(PrefName.OfflineView)
override fun getCount(): Int {
return items.size
@@ -104,8 +105,7 @@ class OfflineMangaAdapter(
}
fun notifyNewGrid() {
- style =
- context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
+ style = PrefManager.getVal(PrefName.OfflineView)
notifyDataSetChanged()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt
index 3c3b5e62a3..7bd43c9ad6 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt
@@ -1,10 +1,7 @@
package ani.dantotsu.download.manga
-import android.animation.ObjectAnimator
-import android.content.Context
import android.content.Intent
import android.net.Uri
-import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.text.Editable
@@ -15,7 +12,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.animation.AlphaAnimation
import android.view.animation.LayoutAnimationController
-import android.view.animation.OvershootInterpolator
import android.widget.AbsListView
import android.widget.AutoCompleteTextView
import android.widget.GridView
@@ -23,33 +19,29 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
-import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat
-import androidx.core.util.Pair
-import androidx.core.view.ViewCompat
import androidx.core.view.marginBottom
import androidx.fragment.app.Fragment
import ani.dantotsu.R
import ani.dantotsu.bottomBar
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.currActivity
import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.initActivity
-import ani.dantotsu.loadData
import ani.dantotsu.logger
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.settings.SettingsDialogFragment
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
-import ani.dantotsu.statusBarHeight
import com.google.android.material.card.MaterialCardView
import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.textfield.TextInputLayout
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.gson.GsonBuilder
import com.google.gson.InstanceCreator
import eu.kanade.tachiyomi.source.model.SChapter
@@ -57,8 +49,6 @@ import eu.kanade.tachiyomi.source.model.SChapterImpl
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
-import kotlin.math.max
-import kotlin.math.min
class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
@@ -67,8 +57,6 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
private lateinit var gridView: GridView
private lateinit var adapter: OfflineMangaAdapter
private lateinit var total: TextView
- private var uiSettings: UserInterfaceSettings =
- loadData("ui_settings") ?: UserInterfaceSettings()
override fun onCreateView(
inflater: LayoutInflater,
@@ -94,15 +82,12 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
SettingsDialogFragment.newInstance(SettingsDialogFragment.Companion.PageType.OfflineMANGA)
dialogFragment.show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
}
- if (!uiSettings.immersiveMode) {
+ if (!(PrefManager.getVal(PrefName.ImmersiveMode) as Boolean)) {
view.rootView.fitsSystemWindows = true
}
- val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getBoolean("colorOverflow", false) ?: false
- if (!colorOverflow) {
- textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
- materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
- }
+
+ textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000
+ materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000)
val searchView = view.findViewById(R.id.animeSearchBarText)
searchView.addTextChangedListener(object : TextWatcher {
@@ -116,8 +101,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
onSearchQuery(s.toString())
}
})
- var style = context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getInt("offline_view", 0)
+ var style: Int = PrefManager.getVal(PrefName.OfflineView)
val layoutList = view.findViewById(R.id.downloadedList)
val layoutcompact = view.findViewById(R.id.downloadedGrid)
var selected = when (style) {
@@ -136,8 +120,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
layoutList.setOnClickListener {
selected(it as ImageView)
style = 0
- requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit()
- .putInt("offline_view", style!!).apply()
+ PrefManager.setVal(PrefName.OfflineView, style)
gridView.visibility = View.GONE
gridView = view.findViewById(R.id.gridView)
adapter.notifyNewGrid()
@@ -148,8 +131,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
layoutcompact.setOnClickListener {
selected(it as ImageView)
style = 1
- requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit()
- .putInt("offline_view", style!!).apply()
+ PrefManager.setVal(PrefName.OfflineView, style)
gridView.visibility = View.GONE
gridView = view.findViewById(R.id.gridView1)
adapter.notifyNewGrid()
@@ -180,19 +162,13 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
downloadManager.mangaDownloadedTypes.firstOrNull { it.title == item.title }
?: downloadManager.novelDownloadedTypes.firstOrNull { it.title == item.title }
media?.let {
+
ContextCompat.startActivity(
requireActivity(),
Intent(requireContext(), MediaDetailsActivity::class.java)
.putExtra("media", getMedia(it))
.putExtra("download", true),
- ActivityOptionsCompat.makeSceneTransitionAnimation(
- requireActivity(),
- Pair.create(
- gridView.getChildAt(position)
- .findViewById(R.id.itemCompactImage),
- ViewCompat.getTransitionName(requireActivity().findViewById(R.id.itemCompactImage))
- )
- ).toBundle()
+ null
)
} ?: run {
snackString("no media found")
@@ -236,41 +212,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initActivity(requireActivity())
- var height = statusBarHeight
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- val displayCutout = activity?.window?.decorView?.rootWindowInsets?.displayCutout
- if (displayCutout != null) {
- if (displayCutout.boundingRects.size > 0) {
- height = max(
- statusBarHeight,
- min(
- displayCutout.boundingRects[0].width(),
- displayCutout.boundingRects[0].height()
- )
- )
- }
- }
- }
- val scrollTop = view.findViewById(R.id.mangaPageScrollTop)
- scrollTop.translationY =
- -(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
- val visible = false
-
- fun animate() {
- val start = if (visible) 0f else 1f
- val end = if (!visible) 0f else 1f
- ObjectAnimator.ofFloat(scrollTop, "scaleX", start, end).apply {
- duration = 300
- interpolator = OvershootInterpolator(2f)
- start()
- }
- ObjectAnimator.ofFloat(scrollTop, "scaleY", start, end).apply {
- duration = 300
- interpolator = OvershootInterpolator(2f)
- start()
- }
- }
+ val scrollTop = view.findViewById(R.id.mangaPageScrollTop)
scrollTop.setOnClickListener {
gridView.smoothScrollToPositionFromTop(0, 0)
}
@@ -290,8 +233,10 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
totalItemCount: Int
) {
val first = view.getChildAt(0)
- val visibility = first != null && first.top < -height
+ val visibility = first != null && first.top < 0
scrollTop.visibility = if (visibility) View.VISIBLE else View.GONE
+ scrollTop.translationY =
+ -(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
}
})
@@ -324,8 +269,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
val mangaTitles = downloadManager.mangaDownloadedTypes.map { it.title }.distinct()
val newMangaDownloads = mutableListOf()
for (title in mangaTitles) {
- val _downloads = downloadManager.mangaDownloadedTypes.filter { it.title == title }
- val download = _downloads.first()
+ val tDownloads = downloadManager.mangaDownloadedTypes.filter { it.title == title }
+ val download = tDownloads.first()
val offlineMangaModel = loadOfflineMangaModel(download)
newMangaDownloads += offlineMangaModel
}
@@ -333,8 +278,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
val novelTitles = downloadManager.novelDownloadedTypes.map { it.title }.distinct()
val newNovelDownloads = mutableListOf()
for (title in novelTitles) {
- val _downloads = downloadManager.novelDownloadedTypes.filter { it.title == title }
- val download = _downloads.first()
+ val tDownloads = downloadManager.novelDownloadedTypes.filter { it.title == title }
+ val download = tDownloads.first()
val offlineMangaModel = loadOfflineMangaModel(download)
newNovelDownloads += offlineMangaModel
}
@@ -343,12 +288,10 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
}
private fun getMedia(downloadedType: DownloadedType): Media? {
- val type = if (downloadedType.type == DownloadedType.Type.MANGA) {
- "Manga"
- } else if (downloadedType.type == DownloadedType.Type.ANIME) {
- "Anime"
- } else {
- "Novel"
+ val type = when (downloadedType.type) {
+ DownloadedType.Type.MANGA -> "Manga"
+ DownloadedType.Type.ANIME -> "Anime"
+ else -> "Novel"
}
val directory = File(
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
@@ -367,18 +310,16 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
} catch (e: Exception) {
logger("Error loading media.json: ${e.message}")
logger(e.printStackTrace())
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
null
}
}
private fun loadOfflineMangaModel(downloadedType: DownloadedType): OfflineMangaModel {
- val type = if (downloadedType.type == DownloadedType.Type.MANGA) {
- "Manga"
- } else if (downloadedType.type == DownloadedType.Type.ANIME) {
- "Anime"
- } else {
- "Novel"
+ val type = when (downloadedType.type) {
+ DownloadedType.Type.MANGA -> "Manga"
+ DownloadedType.Type.ANIME -> "Anime"
+ else -> "Novel"
}
val directory = File(
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
@@ -386,8 +327,6 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
)
//load media.json and convert to media class with gson
try {
- val media = File(directory, "media.json")
- val mediaJson = media.readText()
val mediaModel = getMedia(downloadedType)!!
val cover = File(directory, "cover.jpg")
val coverUri: Uri? = if (cover.exists()) {
@@ -421,7 +360,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
} catch (e: Exception) {
logger("Error loading media.json: ${e.message}")
logger(e.printStackTrace())
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
return OfflineMangaModel(
"unknown",
"0",
@@ -429,8 +368,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
"??",
"movie",
"hmm",
- false,
- false,
+ isOngoing = false,
+ isUserScored = false,
null,
null
)
diff --git a/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt
index d729ef5789..f6a7e2ed10 100644
--- a/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt
@@ -17,13 +17,13 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import ani.dantotsu.R
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger
import ani.dantotsu.media.Media
import ani.dantotsu.media.novel.NovelReadFragment
import ani.dantotsu.snackString
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.gson.GsonBuilder
import com.google.gson.InstanceCreator
import eu.kanade.tachiyomi.data.notification.Notifications
@@ -75,7 +75,7 @@ class NovelDownloaderService : Service() {
builder =
NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
setContentTitle("Novel Download Progress")
- setSmallIcon(R.drawable.ic_round_download_24)
+ setSmallIcon(R.drawable.ic_download_24)
priority = NotificationCompat.PRIORITY_DEFAULT
setOnlyAlertOnce(true)
setProgress(0, 0, false)
@@ -342,7 +342,7 @@ class NovelDownloaderService : Service() {
} catch (e: Exception) {
logger("Exception while downloading .epub: ${e.message}")
snackString("Exception while downloading .epub: ${e.message}")
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
broadcastDownloadFailed(task.originalLink)
}
}
diff --git a/app/src/main/java/ani/dantotsu/download/video/Helper.kt b/app/src/main/java/ani/dantotsu/download/video/Helper.kt
index 25d0d47669..7f92eda6f8 100644
--- a/app/src/main/java/ani/dantotsu/download/video/Helper.kt
+++ b/app/src/main/java/ani/dantotsu/download/video/Helper.kt
@@ -13,11 +13,9 @@ import android.util.Log
import androidx.annotation.OptIn
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
-import androidx.core.content.ContextCompat.getString
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.MimeTypes
-import androidx.media3.common.TrackSelectionParameters
import androidx.media3.common.util.UnstableApi
import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.DataSource
@@ -31,7 +29,6 @@ import androidx.media3.exoplayer.offline.DownloadHelper
import androidx.media3.exoplayer.offline.DownloadManager
import androidx.media3.exoplayer.offline.DownloadService
import androidx.media3.exoplayer.scheduler.Requirements
-import androidx.media3.ui.TrackSelectionDialogBuilder
import ani.dantotsu.R
import ani.dantotsu.defaultHeaders
import ani.dantotsu.download.DownloadedType
@@ -45,6 +42,7 @@ import ani.dantotsu.parsers.Subtitle
import ani.dantotsu.parsers.SubtitleType
import ani.dantotsu.parsers.Video
import ani.dantotsu.parsers.VideoType
+import ani.dantotsu.settings.saving.PrefManager
import eu.kanade.tachiyomi.network.NetworkHelper
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -231,19 +229,13 @@ object Helper {
DownloadService.sendRemoveDownload(
context,
ExoplayerDownloadService::class.java,
- context.getSharedPreferences(
- getString(context, R.string.anime_downloads),
- Context.MODE_PRIVATE
- ).getString(
+ PrefManager.getAnimeDownloadPreferences().getString(
animeDownloadTask.getTaskName(),
""
) ?: "",
false
)
- context.getSharedPreferences(
- getString(context, R.string.anime_downloads),
- Context.MODE_PRIVATE
- ).edit()
+ PrefManager.getAnimeDownloadPreferences().edit()
.remove(animeDownloadTask.getTaskName())
.apply()
downloadsManger.removeDownload(
diff --git a/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt b/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt
index 53e917487f..f3baeb3f4d 100644
--- a/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt
+++ b/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt
@@ -2,7 +2,6 @@ package ani.dantotsu.home
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
-import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -28,13 +27,13 @@ import ani.dantotsu.connections.anilist.AnilistAnimeViewModel
import ani.dantotsu.connections.anilist.SearchResults
import ani.dantotsu.connections.anilist.getUserId
import ani.dantotsu.databinding.FragmentAnimeBinding
-import ani.dantotsu.loadData
import ani.dantotsu.media.MediaAdaptor
import ani.dantotsu.media.ProgressAdapter
import ani.dantotsu.media.SearchActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.px
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import ani.dantotsu.statusBarHeight
import kotlinx.coroutines.Dispatchers
@@ -50,9 +49,6 @@ class AnimeFragment : Fragment() {
private val binding get() = _binding!!
private lateinit var animePageAdapter: AnimePageAdapter
- private var uiSettings: UserInterfaceSettings =
- loadData("ui_settings") ?: UserInterfaceSettings()
-
val model: AnilistAnimeViewModel by activityViewModels()
override fun onCreateView(
@@ -217,7 +213,7 @@ class AnimeFragment : Fragment() {
if (it != null) {
animePageAdapter.updateTrending(
MediaAdaptor(
- if (uiSettings.smallView) 3 else 2,
+ if (PrefManager.getVal(PrefName.SmallView)) 3 else 2,
it,
requireActivity(),
viewPager = animePageAdapter.trendingViewPager
@@ -268,8 +264,11 @@ class AnimeFragment : Fragment() {
model.loaded = true
model.loadTrending(1)
model.loadUpdated()
- model.loadPopular("ANIME", sort = Anilist.sortBy[1], onList = requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- .getBoolean("popular_list", false))
+ model.loadPopular(
+ "ANIME", sort = Anilist.sortBy[1], onList = PrefManager.getVal(
+ PrefName.PopularAnimeList
+ )
+ )
}
live.postValue(false)
_binding?.animeRefresh?.isRefreshing = false
diff --git a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt
index 69f02ab041..295b49b27e 100644
--- a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt
@@ -1,6 +1,5 @@
package ani.dantotsu.home
-import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
@@ -22,7 +21,6 @@ import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.currContext
import ani.dantotsu.databinding.ItemAnimePageBinding
-import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.media.CalendarActivity
import ani.dantotsu.media.GenreActivity
@@ -33,7 +31,8 @@ import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.setSlideIn
import ani.dantotsu.setSlideUp
import ani.dantotsu.settings.SettingsDialogFragment
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight
import com.google.android.material.card.MaterialCardView
import com.google.android.material.textfield.TextInputLayout
@@ -44,8 +43,6 @@ class AnimePageAdapter : RecyclerView.Adapter {
+ if (PrefManager.getVal(PrefName.SmallView)) binding.animeTrendingContainer.updateLayoutParams {
bottomMargin = (-108f).px
}
@@ -133,14 +125,12 @@ class AnimePageAdapter : RecyclerView.Adapter
onIncludeListClick.invoke(isChecked)
- currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
- ?.putBoolean("popular_list", isChecked)?.apply()
+ PrefManager.setVal(PrefName.PopularAnimeList, isChecked)
}
if (ready.value == false)
ready.postValue(true)
@@ -179,12 +169,12 @@ class AnimePageAdapter : RecyclerView.Adapter("ui_settings") ?: UserInterfaceSettings()
fun load() {
if (activity != null && _binding != null) lifecycleScope.launch(Dispatchers.Main) {
binding.homeUserName.text = Anilist.username
binding.homeUserEpisodesWatched.text = Anilist.episodesWatched.toString()
binding.homeUserChaptersRead.text = Anilist.chapterRead.toString()
binding.homeUserAvatar.loadImage(Anilist.avatar)
- if (!uiSettings.bannerAnimations) binding.homeUserBg.pause()
+ if (!(PrefManager.getVal(PrefName.BannerAnimations) as Boolean)) binding.homeUserBg.pause()
binding.homeUserBg.loadImage(Anilist.bg)
binding.homeUserDataProgressBar.visibility = View.GONE
@@ -98,14 +97,14 @@ class HomeFragment : Fragment() {
)
}
- binding.homeUserAvatarContainer.startAnimation(setSlideUp(uiSettings))
+ binding.homeUserAvatarContainer.startAnimation(setSlideUp())
binding.homeUserDataContainer.visibility = View.VISIBLE
binding.homeUserDataContainer.layoutAnimation =
- LayoutAnimationController(setSlideUp(uiSettings), 0.25f)
+ LayoutAnimationController(setSlideUp(), 0.25f)
binding.homeAnimeList.visibility = View.VISIBLE
binding.homeMangaList.visibility = View.VISIBLE
binding.homeListContainer.layoutAnimation =
- LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
+ LayoutAnimationController(setSlideIn(), 0.25f)
}
else {
snackString(currContext()?.getString(R.string.please_reload))
@@ -127,7 +126,7 @@ class HomeFragment : Fragment() {
binding.homeTopContainer.updatePadding(top = statusBarHeight)
var reached = false
- val duration = (uiSettings.animationSpeed * 200).toLong()
+ val duration = ((PrefManager.getVal(PrefName.AnimationSpeed) as Float) * 200).toLong()
binding.homeScroll.setOnScrollChangeListener { _, _, _, _, _ ->
if (!binding.homeScroll.canScrollVertically(1)) {
reached = true
@@ -206,13 +205,13 @@ class HomeFragment : Fragment() {
)
recyclerView.visibility = View.VISIBLE
recyclerView.layoutAnimation =
- LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
+ LayoutAnimationController(setSlideIn(), 0.25f)
} else {
empty.visibility = View.VISIBLE
}
title.visibility = View.VISIBLE
- title.startAnimation(setSlideUp(uiSettings))
+ title.startAnimation(setSlideUp())
progress.visibility = View.GONE
}
}
@@ -295,12 +294,12 @@ class HomeFragment : Fragment() {
binding.homeRecommended
)
- binding.homeUserAvatarContainer.startAnimation(setSlideUp(uiSettings))
+ binding.homeUserAvatarContainer.startAnimation(setSlideUp())
model.empty.observe(viewLifecycleOwner) {
binding.homeDantotsuContainer.visibility = if (it == true) View.VISIBLE else View.GONE
(binding.homeDantotsuIcon.drawable as Animatable).start()
- binding.homeDantotsuContainer.startAnimation(setSlideUp(uiSettings))
+ binding.homeDantotsuContainer.startAnimation(setSlideUp())
binding.homeDantotsuIcon.setSafeOnClickListener {
(binding.homeDantotsuIcon.drawable as Animatable).start()
}
@@ -330,8 +329,6 @@ class HomeFragment : Fragment() {
live.observe(viewLifecycleOwner) {
if (it) {
scope.launch {
- uiSettings =
- loadData("ui_settings") ?: UserInterfaceSettings()
withContext(Dispatchers.IO) {
//Get userData First
getUserId(requireContext()) {
@@ -340,8 +337,10 @@ class HomeFragment : Fragment() {
model.loaded = true
model.setListImages()
var empty = true
+ val homeLayoutShow: List =
+ PrefManager.getVal(PrefName.HomeLayoutShow)
(array.indices).forEach { i ->
- if (uiSettings.homeLayoutShow[i]) {
+ if (homeLayoutShow.elementAt(i)) {
array[i].run()
empty = false
} else withContext(Dispatchers.Main) {
diff --git a/app/src/main/java/ani/dantotsu/home/LoginFragment.kt b/app/src/main/java/ani/dantotsu/home/LoginFragment.kt
index 58bb2fc905..8efa8ac391 100644
--- a/app/src/main/java/ani/dantotsu/home/LoginFragment.kt
+++ b/app/src/main/java/ani/dantotsu/home/LoginFragment.kt
@@ -1,14 +1,23 @@
package ani.dantotsu.home
+import android.app.AlertDialog
+import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.TextView
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.databinding.FragmentLoginBinding
import ani.dantotsu.openLinkInBrowser
+import ani.dantotsu.settings.saving.internal.PreferenceKeystore
+import ani.dantotsu.settings.saving.internal.PreferencePackager
+import ani.dantotsu.toast
+import com.google.android.material.textfield.TextInputEditText
class LoginFragment : Fragment() {
@@ -29,5 +38,99 @@ class LoginFragment : Fragment() {
binding.loginDiscord.setOnClickListener { openLinkInBrowser(getString(R.string.discord)) }
binding.loginGithub.setOnClickListener { openLinkInBrowser(getString(R.string.github)) }
binding.loginTelegram.setOnClickListener { openLinkInBrowser(getString(R.string.telegram)) }
+
+ val openDocumentLauncher =
+ registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
+ if (uri != null) {
+ try {
+ val jsonString =
+ requireActivity().contentResolver.openInputStream(uri)?.readBytes()
+ ?: throw Exception("Error reading file")
+ val name =
+ DocumentFile.fromSingleUri(requireActivity(), uri)?.name ?: "settings"
+ //.sani is encrypted, .ani is not
+ if (name.endsWith(".sani")) {
+ passwordAlertDialog() { password ->
+ if (password != null) {
+ val salt = jsonString.copyOfRange(0, 16)
+ val encrypted = jsonString.copyOfRange(16, jsonString.size)
+ val decryptedJson = try {
+ PreferenceKeystore.decryptWithPassword(
+ password,
+ encrypted,
+ salt
+ )
+ } catch (e: Exception) {
+ toast("Incorrect password")
+ return@passwordAlertDialog
+ }
+ if (PreferencePackager.unpack(decryptedJson))
+ restartApp()
+ } else {
+ toast("Password cannot be empty")
+ }
+ }
+ } else if (name.endsWith(".ani")) {
+ val decryptedJson = jsonString.toString(Charsets.UTF_8)
+ if (PreferencePackager.unpack(decryptedJson))
+ restartApp()
+ } else {
+ toast("Invalid file type")
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ toast("Error importing settings")
+ }
+ }
+ }
+
+ binding.importSettingsButton.setOnClickListener {
+ openDocumentLauncher.launch(arrayOf("*/*"))
+ }
}
+
+ private fun passwordAlertDialog(callback: (CharArray?) -> Unit) {
+ val password = CharArray(16).apply { fill('0') }
+
+ // Inflate the dialog layout
+ val dialogView =
+ LayoutInflater.from(requireActivity()).inflate(R.layout.dialog_user_agent, null)
+ dialogView.findViewById(R.id.userAgentTextBox)?.hint = "Password"
+ val subtitleTextView = dialogView.findViewById(R.id.subtitle)
+ subtitleTextView?.visibility = View.VISIBLE
+ subtitleTextView?.text = "Enter your password to decrypt the file"
+
+ val dialog = AlertDialog.Builder(requireActivity(), R.style.MyPopup)
+ .setTitle("Enter Password")
+ .setView(dialogView)
+ .setPositiveButton("OK", null)
+ .setNegativeButton("Cancel") { dialog, _ ->
+ password.fill('0')
+ dialog.dismiss()
+ callback(null)
+ }
+ .create()
+
+ dialog.window?.setDimAmount(0.8f)
+ dialog.show()
+
+ // Override the positive button here
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
+ val editText = dialog.findViewById(R.id.userAgentTextBox)
+ if (editText?.text?.isNotBlank() == true) {
+ editText.text?.toString()?.trim()?.toCharArray(password)
+ dialog.dismiss()
+ callback(password)
+ } else {
+ toast("Password cannot be empty")
+ }
+ }
+ }
+
+ private fun restartApp() {
+ val intent = Intent(requireActivity(), requireActivity().javaClass)
+ requireActivity().finish()
+ startActivity(intent)
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/home/MangaFragment.kt b/app/src/main/java/ani/dantotsu/home/MangaFragment.kt
index 8fc18f9c8c..efe39ed62c 100644
--- a/app/src/main/java/ani/dantotsu/home/MangaFragment.kt
+++ b/app/src/main/java/ani/dantotsu/home/MangaFragment.kt
@@ -2,7 +2,6 @@ package ani.dantotsu.home
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
-import android.content.Context
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
@@ -26,12 +25,12 @@ import ani.dantotsu.connections.anilist.AnilistMangaViewModel
import ani.dantotsu.connections.anilist.SearchResults
import ani.dantotsu.connections.anilist.getUserId
import ani.dantotsu.databinding.FragmentMangaBinding
-import ani.dantotsu.loadData
import ani.dantotsu.media.MediaAdaptor
import ani.dantotsu.media.ProgressAdapter
import ani.dantotsu.navBarHeight
import ani.dantotsu.px
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import ani.dantotsu.statusBarHeight
import kotlinx.coroutines.Dispatchers
@@ -46,9 +45,6 @@ class MangaFragment : Fragment() {
private val binding get() = _binding!!
private lateinit var mangaPageAdapter: MangaPageAdapter
- private var uiSettings: UserInterfaceSettings =
- loadData("ui_settings") ?: UserInterfaceSettings()
-
val model: AnilistMangaViewModel by activityViewModels()
override fun onCreateView(
@@ -175,7 +171,7 @@ class MangaFragment : Fragment() {
if (it != null) {
mangaPageAdapter.updateTrending(
MediaAdaptor(
- if (uiSettings.smallView) 3 else 2,
+ if (PrefManager.getVal(PrefName.SmallView)) 3 else 2,
it,
requireActivity(),
viewPager = mangaPageAdapter.trendingViewPager
@@ -242,8 +238,11 @@ class MangaFragment : Fragment() {
model.loaded = true
model.loadTrending()
model.loadTrendingNovel()
- model.loadPopular("MANGA", sort = Anilist.sortBy[1], onList = requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- .getBoolean("popular_list", false) )
+ model.loadPopular(
+ "MANGA", sort = Anilist.sortBy[1], onList = PrefManager.getVal(
+ PrefName.PopularMangaList
+ )
+ )
}
live.postValue(false)
_binding?.mangaRefresh?.isRefreshing = false
diff --git a/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt b/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt
index ec4a50342b..6cedbb8071 100644
--- a/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt
@@ -1,6 +1,5 @@
package ani.dantotsu.home
-import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
@@ -22,7 +21,6 @@ import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.currContext
import ani.dantotsu.databinding.ItemMangaPageBinding
-import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.media.GenreActivity
import ani.dantotsu.media.MediaAdaptor
@@ -32,7 +30,8 @@ import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.setSlideIn
import ani.dantotsu.setSlideUp
import ani.dantotsu.settings.SettingsDialogFragment
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight
import com.google.android.material.card.MaterialCardView
import com.google.android.material.textfield.TextInputLayout
@@ -43,8 +42,6 @@ class MangaPageAdapter : RecyclerView.Adapter {
+ if (PrefManager.getVal(PrefName.SmallView)) binding.mangaTrendingContainer.updateLayoutParams {
bottomMargin = (-108f).px
}
@@ -126,14 +118,12 @@ class MangaPageAdapter : RecyclerView.Adapter
onIncludeListClick.invoke(isChecked)
- currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
- ?.putBoolean("popular_list", isChecked)?.apply()
+ PrefManager.setVal(PrefName.PopularMangaList, isChecked)
}
if (ready.value == false)
ready.postValue(true)
@@ -169,10 +159,10 @@ class MangaPageAdapter : RecyclerView.Adapter(R.id.navbar)
+ val bottomBar = findViewById(R.id.navbar)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- val backgroundDrawable = _bottomBar.background as GradientDrawable
+ val backgroundDrawable = bottomBar.background as GradientDrawable
val currentColor = backgroundDrawable.color?.defaultColor ?: 0
val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xE8000000.toInt()
backgroundDrawable.setColor(semiTransparentColor)
- _bottomBar.background = backgroundDrawable
+ bottomBar.background = backgroundDrawable
}
- val colorOverflow = this.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- .getBoolean("colorOverflow", false)
- if (!colorOverflow) {
- _bottomBar.background = ContextCompat.getDrawable(this, R.drawable.bottom_nav_gray)
+ bottomBar.background = ContextCompat.getDrawable(this, R.drawable.bottom_nav_gray)
- }
var doubleBackToExitPressedOnce = false
onBackPressedDispatcher.addCallback(this) {
@@ -76,8 +68,7 @@ class NoInternet : AppCompatActivity() {
binding.root.doOnAttach {
initActivity(this)
- uiSettings = loadData("ui_settings") ?: uiSettings
- selectedOption = uiSettings.defaultStartUpTab
+ selectedOption = PrefManager.getVal(PrefName.DefaultStartUpTab)
binding.includedNavbar.navbarContainer.updateLayoutParams {
bottomMargin = navBarHeight
@@ -89,7 +80,7 @@ class NoInternet : AppCompatActivity() {
val mainViewPager = binding.viewpager
mainViewPager.isUserInputEnabled = false
mainViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle)
- mainViewPager.setPageTransformer(ZoomOutPageTransformer(uiSettings))
+ mainViewPager.setPageTransformer(ZoomOutPageTransformer())
navbar.setOnTabSelectListener(object :
AnimatedBottomBar.OnTabSelectListener {
override fun onTabSelected(
diff --git a/app/src/main/java/ani/dantotsu/media/AuthorActivity.kt b/app/src/main/java/ani/dantotsu/media/AuthorActivity.kt
index 9188b540f0..22bc54f17b 100644
--- a/app/src/main/java/ani/dantotsu/media/AuthorActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/AuthorActivity.kt
@@ -18,7 +18,6 @@ import ani.dantotsu.Refresh
import ani.dantotsu.databinding.ActivityAuthorBinding
import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
-import ani.dantotsu.others.LangSet
import ani.dantotsu.others.getSerialized
import ani.dantotsu.px
import ani.dantotsu.statusBarHeight
@@ -36,7 +35,7 @@ class AuthorActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
binding = ActivityAuthorBinding.inflate(layoutInflater)
setContentView(binding.root)
diff --git a/app/src/main/java/ani/dantotsu/media/CalendarActivity.kt b/app/src/main/java/ani/dantotsu/media/CalendarActivity.kt
index 77035416c4..e6bc8b3b3f 100644
--- a/app/src/main/java/ani/dantotsu/media/CalendarActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/CalendarActivity.kt
@@ -16,11 +16,10 @@ import androidx.lifecycle.lifecycleScope
import ani.dantotsu.R
import ani.dantotsu.Refresh
import ani.dantotsu.databinding.ActivityListBinding
-import ani.dantotsu.loadData
import ani.dantotsu.media.user.ListViewPagerAdapter
import ani.dantotsu.navBarHeight
-import ani.dantotsu.others.LangSet
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import com.google.android.material.tabs.TabLayout
@@ -38,7 +37,7 @@ class CalendarActivity : AppCompatActivity() {
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
binding = ActivityListBinding.inflate(layoutInflater)
@@ -67,8 +66,7 @@ class CalendarActivity : AppCompatActivity() {
binding.listTitle.setTextColor(primaryTextColor)
binding.listTabLayout.setTabTextColors(secondaryTextColor, primaryTextColor)
binding.listTabLayout.setSelectedTabIndicatorColor(primaryTextColor)
- val uiSettings = loadData("ui_settings") ?: UserInterfaceSettings()
- if (!uiSettings.immersiveMode) {
+ if (!(PrefManager.getVal(PrefName.ImmersiveMode) as Boolean)) {
this.window.statusBarColor =
ContextCompat.getColor(this, R.color.nav_bg_inv)
binding.root.fitsSystemWindows = true
diff --git a/app/src/main/java/ani/dantotsu/media/CharacterAdapter.kt b/app/src/main/java/ani/dantotsu/media/CharacterAdapter.kt
index 632230230a..d8c4961ce9 100644
--- a/app/src/main/java/ani/dantotsu/media/CharacterAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/CharacterAdapter.kt
@@ -11,10 +11,8 @@ import androidx.core.util.Pair
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.databinding.ItemCharacterBinding
-import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.setAnimation
-import ani.dantotsu.settings.UserInterfaceSettings
import java.io.Serializable
class CharacterAdapter(
@@ -26,13 +24,10 @@ class CharacterAdapter(
return CharacterViewHolder(binding)
}
- private val uiSettings =
- loadData("ui_settings") ?: UserInterfaceSettings()
-
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: CharacterViewHolder, position: Int) {
val binding = holder.binding
- setAnimation(binding.root.context, holder.binding.root, uiSettings)
+ setAnimation(binding.root.context, holder.binding.root)
val character = characterList[position]
binding.itemCompactRelation.text = character.role + " "
binding.itemCompactImage.loadImage(character.image)
diff --git a/app/src/main/java/ani/dantotsu/media/CharacterDetailsActivity.kt b/app/src/main/java/ani/dantotsu/media/CharacterDetailsActivity.kt
index 3ccba6e1fe..6c987096a3 100644
--- a/app/src/main/java/ani/dantotsu/media/CharacterDetailsActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/CharacterDetailsActivity.kt
@@ -17,14 +17,13 @@ import ani.dantotsu.R
import ani.dantotsu.Refresh
import ani.dantotsu.databinding.ActivityCharacterBinding
import ani.dantotsu.initActivity
-import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.navBarHeight
import ani.dantotsu.others.ImageViewDialog
-import ani.dantotsu.others.LangSet
import ani.dantotsu.others.getSerialized
import ani.dantotsu.px
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import com.google.android.material.appbar.AppBarLayout
@@ -38,22 +37,21 @@ class CharacterDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChang
private val model: OtherDetailsViewModel by viewModels()
private lateinit var character: Character
private var loaded = false
- val uiSettings = loadData("ui_settings") ?: UserInterfaceSettings()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
binding = ActivityCharacterBinding.inflate(layoutInflater)
setContentView(binding.root)
initActivity(this)
screenWidth = resources.displayMetrics.run { widthPixels / density }
- if (uiSettings.immersiveMode) this.window.statusBarColor =
+ if (PrefManager.getVal(PrefName.ImmersiveMode)) this.window.statusBarColor =
ContextCompat.getColor(this, R.color.status)
val banner =
- if (uiSettings.bannerAnimations) binding.characterBanner else binding.characterBannerNoKen
+ if (PrefManager.getVal(PrefName.BannerAnimations)) binding.characterBanner else binding.characterBannerNoKen
banner.updateLayoutParams { height += statusBarHeight }
binding.characterClose.updateLayoutParams { topMargin += statusBarHeight }
@@ -136,16 +134,16 @@ class CharacterDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChang
binding.characterCover.visibility =
if (binding.characterCover.scaleX == 0f) View.GONE else View.VISIBLE
-
+ val immersiveMode: Boolean = PrefManager.getVal(PrefName.ImmersiveMode)
if (percentage >= percent && !isCollapsed) {
isCollapsed = true
- if (uiSettings.immersiveMode) this.window.statusBarColor =
+ if (immersiveMode) this.window.statusBarColor =
ContextCompat.getColor(this, R.color.nav_bg)
binding.characterAppBar.setBackgroundResource(R.color.nav_bg)
}
if (percentage <= percent && isCollapsed) {
isCollapsed = false
- if (uiSettings.immersiveMode) this.window.statusBarColor =
+ if (immersiveMode) this.window.statusBarColor =
ContextCompat.getColor(this, R.color.status)
binding.characterAppBar.setBackgroundResource(R.color.bg)
}
diff --git a/app/src/main/java/ani/dantotsu/media/GenreActivity.kt b/app/src/main/java/ani/dantotsu/media/GenreActivity.kt
index e5960502f4..0c49ec11d6 100644
--- a/app/src/main/java/ani/dantotsu/media/GenreActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/GenreActivity.kt
@@ -12,9 +12,9 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.GenresViewModel
import ani.dantotsu.databinding.ActivityGenreBinding
import ani.dantotsu.initActivity
-import ani.dantotsu.loadData
import ani.dantotsu.navBarHeight
-import ani.dantotsu.others.LangSet
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import kotlinx.coroutines.Dispatchers
@@ -27,7 +27,7 @@ class GenreActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
binding = ActivityGenreBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -54,7 +54,9 @@ class GenreActivity : AppCompatActivity() {
GridLayoutManager(this, (screenWidth / 156f).toInt())
lifecycleScope.launch(Dispatchers.IO) {
- model.loadGenres(Anilist.genres ?: loadData("genres_list") ?: arrayListOf()) {
+ model.loadGenres(
+ Anilist.genres ?: loadLocalGenres() ?: arrayListOf()
+ ) {
MainScope().launch {
adapter.addGenre(it)
}
@@ -62,4 +64,14 @@ class GenreActivity : AppCompatActivity() {
}
}
}
+
+ private fun loadLocalGenres(): ArrayList? {
+ val genres = PrefManager.getVal>(PrefName.GenresList)
+ .toMutableList() as ArrayList?
+ return if (genres.isNullOrEmpty()) {
+ null
+ } else {
+ genres
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/media/MediaAdaptor.kt b/app/src/main/java/ani/dantotsu/media/MediaAdaptor.kt
index e3cd6618d8..c17a5ec1cc 100644
--- a/app/src/main/java/ani/dantotsu/media/MediaAdaptor.kt
+++ b/app/src/main/java/ani/dantotsu/media/MediaAdaptor.kt
@@ -26,7 +26,8 @@ import ani.dantotsu.databinding.ItemMediaCompactBinding
import ani.dantotsu.databinding.ItemMediaLargeBinding
import ani.dantotsu.databinding.ItemMediaPageBinding
import ani.dantotsu.databinding.ItemMediaPageSmallBinding
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.model.GlideUrl
@@ -44,9 +45,6 @@ class MediaAdaptor(
private val viewPager: ViewPager2? = null,
) : RecyclerView.Adapter() {
- private val uiSettings =
- loadData("ui_settings") ?: UserInterfaceSettings()
-
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (type) {
0 -> MediaViewHolder(
@@ -91,7 +89,7 @@ class MediaAdaptor(
when (type) {
0 -> {
val b = (holder as MediaViewHolder).binding
- setAnimation(activity, b.root, uiSettings)
+ setAnimation(activity, b.root)
val media = mediaList?.getOrNull(position)
if (media != null) {
b.itemCompactImage.loadImage(media.cover)
@@ -135,7 +133,7 @@ class MediaAdaptor(
1 -> {
val b = (holder as MediaLargeViewHolder).binding
- setAnimation(activity, b.root, uiSettings)
+ setAnimation(activity, b.root)
val media = mediaList?.get(position)
if (media != null) {
b.itemCompactImage.loadImage(media.cover)
@@ -178,16 +176,17 @@ class MediaAdaptor(
val b = (holder as MediaPageViewHolder).binding
val media = mediaList?.get(position)
if (media != null) {
+ val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
b.itemCompactImage.loadImage(media.cover)
- if (uiSettings.bannerAnimations)
+ if (bannerAnimations)
b.itemCompactBanner.setTransitionGenerator(
RandomTransitionGenerator(
- (10000 + 15000 * (uiSettings.animationSpeed)).toLong(),
+ (10000 + 15000 * ((PrefManager.getVal(PrefName.AnimationSpeed)) as Float)).toLong(),
AccelerateDecelerateInterpolator()
)
)
val banner =
- if (uiSettings.bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
+ if (bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
val context = b.itemCompactBanner.context
if (!(context as Activity).isDestroyed)
Glide.with(context as Context)
@@ -234,16 +233,17 @@ class MediaAdaptor(
val b = (holder as MediaPageSmallViewHolder).binding
val media = mediaList?.get(position)
if (media != null) {
+ val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
b.itemCompactImage.loadImage(media.cover)
- if (uiSettings.bannerAnimations)
+ if (bannerAnimations)
b.itemCompactBanner.setTransitionGenerator(
RandomTransitionGenerator(
- (10000 + 15000 * (uiSettings.animationSpeed)).toLong(),
+ (10000 + 15000 * ((PrefManager.getVal(PrefName.AnimationSpeed) as Float))).toLong(),
AccelerateDecelerateInterpolator()
)
)
val banner =
- if (uiSettings.bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
+ if (bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
val context = b.itemCompactBanner.context
if (!(context as Activity).isDestroyed)
Glide.with(context as Context)
@@ -396,10 +396,8 @@ class MediaAdaptor(
if (itemCompactImage != null) {
ActivityOptionsCompat.makeSceneTransitionAnimation(
activity,
- Pair.create(
- itemCompactImage,
- ViewCompat.getTransitionName(activity.findViewById(R.id.itemCompactImage))!!
- ),
+ itemCompactImage,
+ ViewCompat.getTransitionName(itemCompactImage)!!
).toBundle()
} else {
null
diff --git a/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt b/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt
index 432daa5e4e..77224111aa 100644
--- a/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt
@@ -2,7 +2,6 @@ package ani.dantotsu.media
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
-import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.SpannableStringBuilder
@@ -35,7 +34,6 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.copyToClipboard
import ani.dantotsu.databinding.ActivityMediaBinding
import ani.dantotsu.initActivity
-import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.media.anime.AnimeWatchFragment
import ani.dantotsu.media.manga.MangaReadFragment
@@ -44,8 +42,8 @@ import ani.dantotsu.navBarHeight
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.others.getSerialized
-import ani.dantotsu.saveData
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
@@ -65,7 +63,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
private val scope = lifecycleScope
private val model: MediaDetailsViewModel by viewModels()
private lateinit var tabLayout: NavigationBarView
- private lateinit var uiSettings: UserInterfaceSettings
var selected = 0
var anime = true
private var adult = false
@@ -90,7 +87,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
//Ui init
initActivity(this)
- uiSettings = loadData("ui_settings") ?: UserInterfaceSettings()
binding.mediaBanner.updateLayoutParams { height += statusBarHeight }
binding.mediaBannerNoKen.updateLayoutParams { height += statusBarHeight }
@@ -111,20 +107,21 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
onBackPressedDispatcher.onBackPressed()
}
- if (uiSettings.bannerAnimations) {
+ val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
+ if (bannerAnimations) {
val adi = AccelerateDecelerateInterpolator()
val generator = RandomTransitionGenerator(
- (10000 + 15000 * (uiSettings.animationSpeed)).toLong(),
+ (10000 + 15000 * ((PrefManager.getVal(PrefName.AnimationSpeed) as Float))).toLong(),
adi
)
binding.mediaBanner.setTransitionGenerator(generator)
}
val banner =
- if (uiSettings.bannerAnimations) binding.mediaBanner else binding.mediaBannerNoKen
+ if (bannerAnimations) binding.mediaBanner else binding.mediaBannerNoKen
val viewPager = binding.mediaViewPager
tabLayout = binding.mediaTab as NavigationBarView
viewPager.isUserInputEnabled = false
- viewPager.setPageTransformer(ZoomOutPageTransformer(uiSettings))
+ viewPager.setPageTransformer(ZoomOutPageTransformer())
val isDownload = intent.getBooleanExtra("download", false)
@@ -141,7 +138,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
banner.loadImage(media.banner ?: media.cover, 400)
val gestureDetector = GestureDetector(this, object : GesturesListener() {
override fun onDoubleClick(event: MotionEvent) {
- if (!uiSettings.bannerAnimations)
+ if (!(PrefManager.getVal(PrefName.BannerAnimations) as Boolean))
snackString(getString(R.string.enable_banner_animations))
else {
binding.mediaBanner.restart()
@@ -159,11 +156,10 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
}
})
banner.setOnTouchListener { _, motionEvent -> gestureDetector.onTouchEvent(motionEvent);true }
- if (this.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- .getBoolean("incognito", false)) {
+ if (PrefManager.getVal(PrefName.Incognito)) {
binding.mediaTitle.text = " ${media.userPreferredName}"
binding.incognito.visibility = View.VISIBLE
- }else {
+ } else {
binding.mediaTitle.text = media.userPreferredName
}
binding.mediaTitle.setOnLongClickListener {
@@ -284,7 +280,10 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
} else snackString(getString(R.string.please_login_anilist))
}
binding.mediaAddToList.setOnLongClickListener {
- saveData("${media.id}_progressDialog", true)
+ PrefManager.setCustomVal(
+ "${media.id}_progressDialog",
+ true,
+ )
snackString(getString(R.string.auto_update_reset))
true
}
@@ -345,7 +344,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
viewPager.setCurrentItem(selected, false)
val sel = model.loadSelected(media, isDownload)
sel.window = selected
- model.saveSelected(media.id, sel, this)
+ model.saveSelected(media.id, sel)
true
}
@@ -354,7 +353,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
viewPager.setCurrentItem(selected, false)
if (model.continueMedia == null && media.cameFromContinue) {
- model.continueMedia = loadData("continue_media") ?: true
+ model.continueMedia = PrefManager.getVal(PrefName.ContinueMedia)
selected = 1
}
@@ -395,7 +394,9 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
}
override fun onResume() {
- tabLayout.selectedItemId = idFromSelect()
+ if (this::tabLayout.isInitialized) {
+ tabLayout.selectedItemId = idFromSelect()
+ }
super.onResume()
}
@@ -437,7 +438,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
binding.mediaCover.visibility =
if (binding.mediaCover.scaleX == 0f) View.GONE else View.VISIBLE
- val duration = (200 * uiSettings.animationSpeed).toLong()
+ val duration = (200 * (PrefManager.getVal(PrefName.AnimationSpeed) as Float)).toLong()
val typedValue = TypedValue()
this@MediaDetailsActivity.theme.resolveAttribute(
com.google.android.material.R.attr.colorSecondary,
@@ -467,7 +468,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
.start()
ObjectAnimator.ofFloat(binding.mediaCollapseContainer, "translationX", 0f)
.setDuration(duration).start()
- if (uiSettings.bannerAnimations) binding.mediaBanner.resume()
+ if (PrefManager.getVal(PrefName.BannerAnimations)) binding.mediaBanner.resume()
}
if (percentage == 1 && model.scrolledToTop.value != false) model.scrolledToTop.postValue(
false
@@ -491,10 +492,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
init {
enabled(true)
- scope.launch {
- delay(100) //TODO: a listener would be better
- clicked()
- }
image.setOnClickListener {
if (pressable && !disabled) {
pressable = false
diff --git a/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt b/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt
index cb5304726a..8f10863fb0 100644
--- a/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt
+++ b/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt
@@ -1,7 +1,5 @@
package ani.dantotsu.media
-import android.app.Activity
-import android.content.SharedPreferences
import android.os.Handler
import android.os.Looper
import androidx.fragment.app.FragmentManager
@@ -11,7 +9,6 @@ import androidx.lifecycle.ViewModel
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.currContext
-import ani.dantotsu.loadData
import ani.dantotsu.logger
import ani.dantotsu.media.anime.Episode
import ani.dantotsu.media.anime.SelectorDialogFragment
@@ -28,35 +25,32 @@ import ani.dantotsu.parsers.NovelSources
import ani.dantotsu.parsers.ShowResponse
import ani.dantotsu.parsers.VideoExtractor
import ani.dantotsu.parsers.WatchSources
-import ani.dantotsu.saveData
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import ani.dantotsu.tryWithSuspend
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
class MediaDetailsViewModel : ViewModel() {
val scrolledToTop = MutableLiveData(true)
- fun saveSelected(id: Int, data: Selected, activity: Activity? = null) {
- saveData("$id-select", data, activity)
+ fun saveSelected(id: Int, data: Selected) {
+ PrefManager.setCustomVal("Selected-$id", data)
}
fun loadSelected(media: Media, isDownload: Boolean = false): Selected {
- val sharedPreferences = Injekt.get()
- val data = loadData("${media.id}-select") ?: Selected().let {
- it.sourceIndex = if (media.isAdult) 0 else when (media.anime != null) {
- true -> sharedPreferences.getInt("settings_def_anime_source_s_r", 0)
- else -> sharedPreferences.getInt(("settings_def_manga_source_s_r"), 0)
- }
- it.preferDub = loadData("settings_prefer_dub") ?: false
- saveSelected(media.id, it)
- it
- }
+ val data =
+ PrefManager.getNullableCustomVal("Selected-${media.id}", null, Selected::class.java)
+ ?: Selected().let {
+ it.sourceIndex = 0
+ it.preferDub = PrefManager.getVal(PrefName.SettingsPreferDub)
+ saveSelected(media.id, it)
+ it
+ }
if (isDownload) {
data.sourceIndex = if (media.anime != null) {
AnimeSources.list.size - 1
diff --git a/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt b/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt
index c3c290aa60..e3d4a36376 100644
--- a/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt
@@ -2,7 +2,6 @@ package ani.dantotsu.media
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
-import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -26,6 +25,8 @@ import ani.dantotsu.*
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.GenresViewModel
import ani.dantotsu.databinding.*
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin
import kotlinx.coroutines.Dispatchers
@@ -60,7 +61,7 @@ class MediaInfoFragment : Fragment() {
@SuppressLint("SetJavaScriptEnabled")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val model: MediaDetailsViewModel by activityViewModels()
- val offline = requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("offlineMode", false) || !isOnline(requireContext())
+ val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode)
binding.mediaInfoProgressBar.visibility = if (!loaded) View.VISIBLE else View.GONE
binding.mediaInfoContainer.visibility = if (loaded) View.VISIBLE else View.GONE
binding.mediaInfoContainer.updateLayoutParams { bottomMargin += 128f.px + navBarHeight }
@@ -94,8 +95,31 @@ class MediaInfoFragment : Fragment() {
binding.mediaInfoStart.text = media.startDate?.toString() ?: "??"
binding.mediaInfoEnd.text = media.endDate?.toString() ?: "??"
if (media.anime != null) {
- binding.mediaInfoDuration.text =
- if (media.anime.episodeDuration != null) media.anime.episodeDuration.toString() else "??"
+ val episodeDuration = media.anime.episodeDuration
+
+ binding.mediaInfoDuration.text = when {
+ episodeDuration != null -> {
+ val hours = episodeDuration / 60
+ val minutes = episodeDuration % 60
+
+ val formattedDuration = buildString {
+ if (hours > 0) {
+ append("$hours hour")
+ if (hours > 1) append("s")
+ }
+
+ if (minutes > 0) {
+ if (hours > 0) append(", ")
+ append("$minutes min")
+ if (minutes > 1) append("s")
+ }
+ }
+
+ formattedDuration
+ }
+
+ else -> "??"
+ }
binding.mediaInfoDurationContainer.visibility = View.VISIBLE
binding.mediaInfoSeasonContainer.visibility = View.VISIBLE
binding.mediaInfoSeason.text =
@@ -464,7 +488,7 @@ class MediaInfoFragment : Fragment() {
parent.addView(bindi.root)
}
- if (!media.recommendations.isNullOrEmpty() && !offline ) {
+ if (!media.recommendations.isNullOrEmpty() && !offline) {
val bind = ItemTitleRecyclerBinding.inflate(
LayoutInflater.from(context),
parent,
diff --git a/app/src/main/java/ani/dantotsu/media/SearchActivity.kt b/app/src/main/java/ani/dantotsu/media/SearchActivity.kt
index 4c71664b5d..2962a5275a 100644
--- a/app/src/main/java/ani/dantotsu/media/SearchActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/SearchActivity.kt
@@ -16,7 +16,8 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistSearch
import ani.dantotsu.connections.anilist.SearchResults
import ani.dantotsu.databinding.ActivitySearchBinding
-import ani.dantotsu.others.LangSet
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.themes.ThemeManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -33,13 +34,14 @@ class SearchActivity : AppCompatActivity() {
private lateinit var mediaAdaptor: MediaAdaptor
private lateinit var progressAdapter: ProgressAdapter
private lateinit var concatAdapter: ConcatAdapter
+ private lateinit var headerAdaptor: SearchAdapter
lateinit var result: SearchResults
lateinit var updateChips: (() -> Unit)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
binding = ActivitySearchBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -51,7 +53,7 @@ class SearchActivity : AppCompatActivity() {
bottom = navBarHeight + 80f.px
)
- style = loadData("searchStyle") ?: 0
+ style = PrefManager.getVal(PrefName.SearchStyle)
var listOnly: Boolean? = intent.getBooleanExtra("listOnly", false)
if (!listOnly!!) listOnly = null
@@ -76,7 +78,7 @@ class SearchActivity : AppCompatActivity() {
progressAdapter = ProgressAdapter(searched = model.searched)
mediaAdaptor = MediaAdaptor(style, model.searchResults.results, this, matchParent = true)
- val headerAdaptor = SearchAdapter(this)
+ headerAdaptor = SearchAdapter(this, model.searchResults.type)
val gridSize = (screenWidth / 120f).toInt()
val gridLayoutManager = GridLayoutManager(this, gridSize)
@@ -154,9 +156,18 @@ class SearchActivity : AppCompatActivity() {
}
}
+ fun emptyMediaAdapter() {
+ searchTimer.cancel()
+ searchTimer.purge()
+ mediaAdaptor.notifyItemRangeRemoved(0, model.searchResults.results.size)
+ model.searchResults.results.clear()
+ progressAdapter.bar?.visibility = View.GONE
+ }
+
private var searchTimer = Timer()
private var loading = false
fun search() {
+ headerAdaptor.setHistoryVisibility(false)
val size = model.searchResults.results.size
model.searchResults.results.clear()
binding.searchRecyclerView.post {
@@ -188,6 +199,7 @@ class SearchActivity : AppCompatActivity() {
var state: Parcelable? = null
override fun onPause() {
+ headerAdaptor.addHistory()
super.onPause()
state = binding.searchRecyclerView.layoutManager?.onSaveInstanceState()
}
diff --git a/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt b/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt
index 18fe0a4c2c..ced90bc4ae 100644
--- a/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt
@@ -1,7 +1,6 @@
package ani.dantotsu.media
import android.annotation.SuppressLint
-import android.content.Context
import android.graphics.drawable.Drawable
import android.text.Editable
import android.text.TextWatcher
@@ -9,6 +8,8 @@ import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
+import android.view.animation.AlphaAnimation
+import android.view.animation.Animation
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity
@@ -19,19 +20,25 @@ import androidx.recyclerview.widget.RecyclerView.HORIZONTAL
import ani.dantotsu.App.Companion.context
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
-import ani.dantotsu.currContext
import ani.dantotsu.databinding.ItemChipBinding
import ani.dantotsu.databinding.ItemSearchHeaderBinding
-import ani.dantotsu.saveData
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import com.google.android.material.checkbox.MaterialCheckBox.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
-class SearchAdapter(private val activity: SearchActivity) :
+class SearchAdapter(private val activity: SearchActivity, private val type: String) :
RecyclerView.Adapter() {
private val itemViewType = 6969
var search: Runnable? = null
var requestFocus: Runnable? = null
private var textWatcher: TextWatcher? = null
+ private lateinit var searchHistoryAdapter: SearchHistoryAdapter
+ private lateinit var binding: ItemSearchHeaderBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHeaderViewHolder {
val binding =
@@ -41,8 +48,13 @@ class SearchAdapter(private val activity: SearchActivity) :
@SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: SearchHeaderViewHolder, position: Int) {
- val binding = holder.binding
+ binding = holder.binding
+ searchHistoryAdapter = SearchHistoryAdapter(type) {
+ binding.searchBarText.setText(it)
+ }
+ binding.searchHistoryList.layoutManager = LinearLayoutManager(binding.root.context)
+ binding.searchHistoryList.adapter = searchHistoryAdapter
val imm: InputMethodManager =
activity.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
@@ -60,8 +72,7 @@ class SearchAdapter(private val activity: SearchActivity) :
}
binding.searchBar.hint = activity.result.type
- if (currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getBoolean("incognito", false ) == true){
+ if (PrefManager.getVal(PrefName.Incognito)) {
val startIconDrawableRes = R.drawable.ic_incognito_24
val startIconDrawable: Drawable? =
context?.let { AppCompatResources.getDrawable(it, startIconDrawableRes) }
@@ -103,7 +114,18 @@ class SearchAdapter(private val activity: SearchActivity) :
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
- searchTitle()
+ if (s.toString().isBlank()) {
+ activity.emptyMediaAdapter()
+ CoroutineScope(Dispatchers.IO).launch {
+ delay(200)
+ activity.runOnUiThread {
+ setHistoryVisibility(true)
+ }
+ }
+ } else {
+ setHistoryVisibility(false)
+ searchTitle()
+ }
}
}
binding.searchBarText.addTextChangedListener(textWatcher)
@@ -126,14 +148,14 @@ class SearchAdapter(private val activity: SearchActivity) :
it.alpha = 1f
binding.searchResultList.alpha = 0.33f
activity.style = 0
- saveData("searchStyle", 0)
+ PrefManager.setVal(PrefName.SearchStyle, 0)
activity.recycler()
}
binding.searchResultList.setOnClickListener {
it.alpha = 1f
binding.searchResultGrid.alpha = 0.33f
activity.style = 1
- saveData("searchStyle", 1)
+ PrefManager.setVal(PrefName.SearchStyle, 1)
activity.recycler()
}
@@ -176,6 +198,40 @@ class SearchAdapter(private val activity: SearchActivity) :
requestFocus = Runnable { binding.searchBarText.requestFocus() }
}
+ fun setHistoryVisibility(visible: Boolean) {
+ if (visible) {
+ binding.searchResultLayout.startAnimation(fadeOutAnimation())
+ binding.searchHistoryList.startAnimation(fadeInAnimation())
+ binding.searchResultLayout.visibility = View.GONE
+ binding.searchHistoryList.visibility = View.VISIBLE
+ } else {
+ if (binding.searchResultLayout.visibility != View.VISIBLE) {
+ binding.searchResultLayout.startAnimation(fadeInAnimation())
+ binding.searchHistoryList.startAnimation(fadeOutAnimation())
+ }
+ binding.searchResultLayout.visibility = View.VISIBLE
+ binding.searchHistoryList.visibility = View.GONE
+ }
+ }
+
+ private fun fadeInAnimation(): Animation {
+ return AlphaAnimation(0f, 1f).apply {
+ duration = 150
+ fillAfter = true
+ }
+ }
+
+ private fun fadeOutAnimation(): Animation {
+ return AlphaAnimation(1f, 0f).apply {
+ duration = 150
+ fillAfter = true
+ }
+ }
+
+
+ fun addHistory() {
+ searchHistoryAdapter.add(binding.searchBarText.text.toString())
+ }
override fun getItemCount(): Int = 1
diff --git a/app/src/main/java/ani/dantotsu/media/SearchFilterBottomDialog.kt b/app/src/main/java/ani/dantotsu/media/SearchFilterBottomDialog.kt
index 79fd87126a..297f82894c 100644
--- a/app/src/main/java/ani/dantotsu/media/SearchFilterBottomDialog.kt
+++ b/app/src/main/java/ani/dantotsu/media/SearchFilterBottomDialog.kt
@@ -17,6 +17,7 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.databinding.BottomSheetSearchFilterBinding
import ani.dantotsu.databinding.ItemChipBinding
import com.google.android.material.chip.Chip
+import java.util.Calendar
class SearchFilterBottomDialog : BottomSheetDialogFragment() {
private var _binding: BottomSheetSearchFilterBinding? = null
@@ -103,7 +104,8 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
ArrayAdapter(
binding.root.context,
R.layout.item_dropdown,
- (1970 until 2025).map { it.toString() }.reversed().toTypedArray()
+ (1970 until Calendar.getInstance().get(Calendar.YEAR) + 2).map { it.toString() }
+ .reversed().toTypedArray()
)
)
}
diff --git a/app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt b/app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt
new file mode 100644
index 0000000000..f1519e2b62
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt
@@ -0,0 +1,100 @@
+package ani.dantotsu.media
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import ani.dantotsu.R
+import ani.dantotsu.databinding.ItemSearchHistoryBinding
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefManager.asLiveStringSet
+import ani.dantotsu.settings.saving.PrefName
+import ani.dantotsu.settings.saving.SharedPreferenceStringSetLiveData
+import java.util.Locale
+
+class SearchHistoryAdapter(private val type: String, private val searchClicked: (String) -> Unit) :
+ ListAdapter(
+ DIFF_CALLBACK_INSTALLED
+ ) {
+ private var searchHistoryLiveData: SharedPreferenceStringSetLiveData? = null
+ private var searchHistory: MutableSet? = null
+ private var historyType: PrefName = when (type.lowercase(Locale.ROOT)) {
+ "anime" -> PrefName.AnimeSearchHistory
+ "manga" -> PrefName.MangaSearchHistory
+ else -> throw IllegalArgumentException("Invalid type")
+ }
+
+ init {
+ searchHistoryLiveData =
+ PrefManager.getLiveVal(historyType, mutableSetOf()).asLiveStringSet()
+ searchHistoryLiveData?.observeForever {
+ searchHistory = it.toMutableSet()
+ submitList(searchHistory?.toList())
+ }
+ }
+
+ fun remove(item: String) {
+ searchHistory?.remove(item)
+ PrefManager.setVal(historyType, searchHistory)
+ submitList(searchHistory?.toList())
+ }
+
+ fun add(item: String) {
+ if (searchHistory?.contains(item) == true || item.isBlank()) return
+ if (PrefManager.getVal(PrefName.Incognito)) return
+ searchHistory?.add(item)
+ submitList(searchHistory?.toList())
+ PrefManager.setVal(historyType, searchHistory)
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): SearchHistoryAdapter.SearchHistoryViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_search_history, parent, false)
+ return SearchHistoryViewHolder(view)
+ }
+
+ override fun onBindViewHolder(
+ holder: SearchHistoryAdapter.SearchHistoryViewHolder,
+ position: Int
+ ) {
+ val item = getItem(position)
+ holder.binding.searchHistoryTextView.text = item
+ holder.binding.closeTextView.setOnClickListener {
+ val currentPosition = holder.bindingAdapterPosition
+ if (currentPosition >= itemCount || currentPosition < 0) return@setOnClickListener
+ remove(getItem(currentPosition))
+ }
+ holder.binding.searchHistoryTextView.setOnClickListener {
+ val currentPosition = holder.bindingAdapterPosition
+ if (currentPosition >= itemCount || currentPosition < 0) return@setOnClickListener
+ searchClicked(getItem(currentPosition))
+ }
+ }
+
+ inner class SearchHistoryViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val binding = ItemSearchHistoryBinding.bind(view)
+ }
+
+ companion object {
+ val DIFF_CALLBACK_INSTALLED = object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: String,
+ newItem: String
+ ): Boolean {
+ return oldItem == newItem
+ }
+
+ override fun areContentsTheSame(
+ oldItem: String,
+ newItem: String
+ ): Boolean {
+ return oldItem == newItem
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ani/dantotsu/media/StudioActivity.kt b/app/src/main/java/ani/dantotsu/media/StudioActivity.kt
index 53e04a37c3..ac28cd5f1e 100644
--- a/app/src/main/java/ani/dantotsu/media/StudioActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/StudioActivity.kt
@@ -18,7 +18,6 @@ import ani.dantotsu.Refresh
import ani.dantotsu.databinding.ActivityStudioBinding
import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
-import ani.dantotsu.others.LangSet
import ani.dantotsu.others.getSerialized
import ani.dantotsu.px
import ani.dantotsu.statusBarHeight
@@ -36,7 +35,7 @@ class StudioActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
binding = ActivityStudioBinding.inflate(layoutInflater)
setContentView(binding.root)
diff --git a/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt b/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt
index d74a89294d..e5ebd81b02 100644
--- a/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt
+++ b/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt
@@ -45,9 +45,18 @@ class SubtitleDownloader {
}
//actually downloads lol
- suspend fun downloadSubtitle(context: Context, url: String, downloadedType: DownloadedType) {
+ suspend fun downloadSubtitle(
+ context: Context,
+ url: String,
+ downloadedType: DownloadedType
+ ) {
try {
- val directory = DownloadsManager.getDirectory(context, downloadedType.type, downloadedType.title, downloadedType.chapter)
+ val directory = DownloadsManager.getDirectory(
+ context,
+ downloadedType.type,
+ downloadedType.title,
+ downloadedType.chapter
+ )
if (!directory.exists()) { //just in case
directory.mkdirs()
}
diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt
index f072a549b5..a30da85fdf 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt
@@ -1,5 +1,6 @@
package ani.dantotsu.media.anime
+import java.util.Locale
import java.util.regex.Matcher
import java.util.regex.Pattern
@@ -9,7 +10,57 @@ class AnimeNameAdapter {
"(episode|ep|e)[\\s:.\\-]*([\\d]+\\.?[\\d]*)[\\s:.\\-]*\\(?\\s*(sub|subbed|dub|dubbed)*\\s*\\)?\\s*"
const val failedEpisodeNumberRegex =
"(? "sub"
+ SubDubType.DUB -> "dub"
+ SubDubType.NULL -> ""
+ }
+ val toggledCasePreserved =
+ if (subdub?.get(0)?.isUpperCase() == true || soft?.get(0)
+ ?.isUpperCase() == true
+ ) toggled.replaceFirstChar {
+ if (it.isLowerCase()) it.titlecase(
+ Locale.ROOT
+ ) else it.toString()
+ } else toggled
+
+ subdubMatcher.replaceFirst(toggledCasePreserved + bed)
+ } else {
+ null
+ }
+ }
+
+ fun getSubDub(text: String): SubDubType {
+ val subdubPattern: Pattern = Pattern.compile(subdubRegex, Pattern.CASE_INSENSITIVE)
+ val subdubMatcher: Matcher = subdubPattern.matcher(text)
+
+ return if (subdubMatcher.find()) {
+ val subdub = subdubMatcher.group(2)?.lowercase(Locale.ROOT)
+ when (subdub) {
+ "sub" -> SubDubType.SUB
+ "dub" -> SubDubType.DUB
+ else -> SubDubType.NULL
+ }
+ } else {
+ SubDubType.NULL
+ }
+ }
+
+ enum class SubDubType {
+ SUB, DUB, NULL
+ }
fun findSeasonNumber(text: String): Int? {
val seasonPattern: Pattern = Pattern.compile(seasonRegex, Pattern.CASE_INSENSITIVE)
diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt
index 2f70a2a9c8..0ad6ecafe3 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt
@@ -1,7 +1,6 @@
package ani.dantotsu.media.anime
import android.annotation.SuppressLint
-import android.content.Context
import android.content.Intent
import android.net.Uri
import android.view.LayoutInflater
@@ -27,6 +26,8 @@ import ani.dantotsu.others.webview.CookieCatcher
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.DynamicAnimeParser
import ani.dantotsu.parsers.WatchSources
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.subcriptions.Notifications.Companion.openSettings
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
import com.google.android.material.chip.Chip
@@ -59,7 +60,7 @@ class AnimeWatchAdapter(
_binding = binding
//Youtube
- if (media.anime!!.youtube != null && fragment.uiSettings.showYtButton) {
+ if (media.anime!!.youtube != null && PrefManager.getVal(PrefName.ShowYtButton)) {
binding.animeSourceYT.visibility = View.VISIBLE
binding.animeSourceYT.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(media.anime.youtube))
@@ -90,11 +91,9 @@ class AnimeWatchAdapter(
null
)
}
- val offline = if (!isOnline(binding.root.context) || currContext()?.getSharedPreferences(
- "Dantotsu",
- Context.MODE_PRIVATE
+ val offline = if (!isOnline(binding.root.context) || PrefManager.getVal(
+ PrefName.OfflineMode
)
- ?.getBoolean("offlineMode", false) == true
) View.GONE else View.VISIBLE
binding.animeSourceNameContainer.visibility = offline
@@ -113,7 +112,7 @@ class AnimeWatchAdapter(
binding.animeSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } }
binding.animeSourceDubbedCont.visibility =
- if (isDubAvailableSeparately) View.VISIBLE else View.GONE
+ if (isDubAvailableSeparately()) View.VISIBLE else View.GONE
}
}
@@ -133,7 +132,7 @@ class AnimeWatchAdapter(
binding.animeSourceDubbed.isChecked = selectDub
changing = false
binding.animeSourceDubbedCont.visibility =
- if (isDubAvailableSeparately) View.VISIBLE else View.GONE
+ if (isDubAvailableSeparately()) View.VISIBLE else View.GONE
source = i
setLanguageList(0, i)
}
@@ -154,7 +153,7 @@ class AnimeWatchAdapter(
binding.animeSourceDubbed.isChecked = selectDub
changing = false
binding.animeSourceDubbedCont.visibility =
- if (isDubAvailableSeparately) View.VISIBLE else View.GONE
+ if (isDubAvailableSeparately()) View.VISIBLE else View.GONE
setLanguageList(i, source)
}
subscribeButton(false)
@@ -199,7 +198,8 @@ class AnimeWatchAdapter(
var refresh = false
var run = false
var reversed = media.selected!!.recyclerReversed
- var style = media.selected!!.recyclerStyle ?: fragment.uiSettings.animeDefaultView
+ var style =
+ media.selected!!.recyclerStyle ?: PrefManager.getVal(PrefName.AnimeDefaultView)
dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down"
dialogBinding.animeSourceTop.setOnClickListener {
@@ -357,7 +357,9 @@ class AnimeWatchAdapter(
val episodes = media.anime.episodes!!.keys.toTypedArray()
val anilistEp = (media.userProgress ?: 0).plus(1)
- val appEp = loadData("${media.id}_current_ep")?.toIntOrNull() ?: 1
+ val appEp =
+ PrefManager.getCustomVal("${media.id}_current_ep", "")?.toIntOrNull()
+ ?: 1
var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString()
if (episodes.contains(continueEp)) {
@@ -369,7 +371,10 @@ class AnimeWatchAdapter(
media.id,
continueEp
)
- if ((binding.itemEpisodeProgress.layoutParams as LinearLayout.LayoutParams).weight > fragment.playerSettings.watchPercentage) {
+ if ((binding.itemEpisodeProgress.layoutParams as LinearLayout.LayoutParams).weight > PrefManager.getVal(
+ PrefName.WatchPercentage
+ )
+ ) {
val e = episodes.indexOf(continueEp)
if (e != -1 && e + 1 < episodes.size) {
continueEp = episodes[e + 1]
@@ -396,7 +401,10 @@ class AnimeWatchAdapter(
fragment.onEpisodeClick(continueEp)
}
if (fragment.continueEp) {
- if ((binding.itemEpisodeProgress.layoutParams as LinearLayout.LayoutParams).weight < fragment.playerSettings.watchPercentage) {
+ if ((binding.itemEpisodeProgress.layoutParams as LinearLayout.LayoutParams).weight < PrefManager.getVal(
+ PrefName.WatchPercentage
+ )
+ ) {
binding.animeSourceContinue.performClick()
fragment.continueEp = false
}
diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt
index 487bf20718..9d720020fa 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt
@@ -25,6 +25,7 @@ import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.DownloadService
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.*
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
@@ -39,9 +40,9 @@ import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.parsers.AnimeParser
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.HAnimeSources
-import ani.dantotsu.settings.PlayerSettings
-import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.subcriptions.Notifications
import ani.dantotsu.subcriptions.Notifications.Group.ANIME_GROUP
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
@@ -84,8 +85,6 @@ class AnimeWatchFragment : Fragment() {
var continueEp: Boolean = false
var loaded = false
- lateinit var playerSettings: PlayerSettings
- lateinit var uiSettings: UserInterfaceSettings
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -118,12 +117,6 @@ class AnimeWatchFragment : Fragment() {
var maxGridSize = (screenWidth / 100f).roundToInt()
maxGridSize = max(4, maxGridSize - (maxGridSize % 2))
- playerSettings =
- loadData("player_settings", toast = false)
- ?: PlayerSettings().apply { saveData("player_settings", this) }
- uiSettings = loadData("ui_settings", toast = false)
- ?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
-
val gridLayoutManager = GridLayoutManager(requireContext(), maxGridSize)
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
@@ -144,6 +137,23 @@ class AnimeWatchFragment : Fragment() {
binding.animeSourceRecycler.layoutManager = gridLayoutManager
+ binding.ScrollTop.setOnClickListener {
+ binding.animeSourceRecycler.scrollToPosition(10)
+ binding.animeSourceRecycler.smoothScrollToPosition(0)
+ }
+ binding.animeSourceRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ super.onScrolled(recyclerView, dx, dy)
+
+ val position = gridLayoutManager.findFirstVisibleItemPosition()
+ if (position > 2) {
+ binding.ScrollTop.translationY = -navBarHeight.toFloat()
+ binding.ScrollTop.visibility = View.VISIBLE
+ } else {
+ binding.ScrollTop.visibility = View.GONE
+ }
+ }
+ })
model.scrolledToTop.observe(viewLifecycleOwner) {
if (it) binding.animeSourceRecycler.scrollToPosition(0)
}
@@ -155,7 +165,7 @@ class AnimeWatchFragment : Fragment() {
media.selected = model.loadSelected(media)
subscribed =
- SubscriptionHelper.getSubscriptions(requireContext()).containsKey(media.id)
+ SubscriptionHelper.getSubscriptions().containsKey(media.id)
style = media.selected!!.recyclerStyle
reverse = media.selected!!.recyclerReversed
@@ -172,7 +182,7 @@ class AnimeWatchFragment : Fragment() {
headerAdapter = AnimeWatchAdapter(it, this, model.watchSources!!)
episodeAdapter =
EpisodeAdapter(
- style ?: uiSettings.animeDefaultView,
+ style ?: PrefManager.getVal(PrefName.AnimeDefaultView),
media,
this,
offlineMode = offlineMode
@@ -273,7 +283,7 @@ class AnimeWatchFragment : Fragment() {
model.watchSources?.get(selected.sourceIndex)?.showUserTextListener = null
selected.sourceIndex = i
selected.server = null
- model.saveSelected(media.id, selected, requireActivity())
+ model.saveSelected(media.id, selected)
media.selected = selected
return model.watchSources?.get(i)!!
}
@@ -281,7 +291,7 @@ class AnimeWatchFragment : Fragment() {
fun onLangChange(i: Int) {
val selected = model.loadSelected(media)
selected.langIndex = i
- model.saveSelected(media.id, selected, requireActivity())
+ model.saveSelected(media.id, selected)
media.selected = selected
}
@@ -289,7 +299,7 @@ class AnimeWatchFragment : Fragment() {
val selected = model.loadSelected(media)
model.watchSources?.get(selected.sourceIndex)?.selectDub = checked
selected.preferDub = checked
- model.saveSelected(media.id, selected, requireActivity())
+ model.saveSelected(media.id, selected)
media.selected = selected
lifecycleScope.launch(Dispatchers.IO) {
model.forceLoadEpisode(
@@ -308,7 +318,7 @@ class AnimeWatchFragment : Fragment() {
reverse = rev
media.selected!!.recyclerStyle = style
media.selected!!.recyclerReversed = reverse
- model.saveSelected(media.id, media.selected!!, requireActivity())
+ model.saveSelected(media.id, media.selected!!)
reload()
}
@@ -316,7 +326,7 @@ class AnimeWatchFragment : Fragment() {
media.selected!!.chip = i
start = s
end = e
- model.saveSelected(media.id, media.selected!!, requireActivity())
+ model.saveSelected(media.id, media.selected!!)
reload()
}
@@ -364,12 +374,10 @@ class AnimeWatchFragment : Fragment() {
if (allSettings.size > 1) {
val names =
allSettings.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
- var selectedIndex = 0
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
.setTitle("Select a Source")
- .setSingleChoiceItems(names, selectedIndex) { dialog, which ->
- selectedIndex = which
- selectedSetting = allSettings[selectedIndex]
+ .setSingleChoiceItems(names, -1) { dialog, which ->
+ selectedSetting = allSettings[which]
itemSelected = true
dialog.dismiss()
@@ -419,7 +427,7 @@ class AnimeWatchFragment : Fragment() {
fun onEpisodeClick(i: String) {
model.continueMedia = false
- model.saveSelected(media.id, media.selected!!, requireActivity())
+ model.saveSelected(media.id, media.selected!!)
model.onEpisodeClick(media, i, requireActivity().supportFragmentManager)
}
@@ -458,17 +466,11 @@ class AnimeWatchFragment : Fragment() {
)
)
val taskName = AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), i)
- val id = requireContext().getSharedPreferences(
- ContextCompat.getString(requireContext(), R.string.anime_downloads),
- Context.MODE_PRIVATE
- ).getString(
+ val id = PrefManager.getAnimeDownloadPreferences().getString(
taskName,
""
) ?: ""
- requireContext().getSharedPreferences(
- ContextCompat.getString(requireContext(), R.string.anime_downloads),
- Context.MODE_PRIVATE
- ).edit().remove(taskName).apply()
+ PrefManager.getAnimeDownloadPreferences().edit().remove(taskName).apply()
DownloadService.sendRemoveDownload(
requireContext(),
ExoplayerDownloadService::class.java,
@@ -520,7 +522,7 @@ class AnimeWatchFragment : Fragment() {
selected.latest =
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
- model.saveSelected(media.id, selected, requireActivity())
+ model.saveSelected(media.id, selected)
headerAdapter.handleEpisodes()
val isDownloaded = model.watchSources!!.isDownloadedSource(media.selected!!.sourceIndex)
episodeAdapter.offlineMode = isDownloaded
@@ -536,7 +538,7 @@ class AnimeWatchFragment : Fragment() {
arr = (arr.reversed() as? ArrayList) ?: arr
}
episodeAdapter.arr = arr
- episodeAdapter.updateType(style ?: uiSettings.animeDefaultView)
+ episodeAdapter.updateType(style ?: PrefManager.getVal(PrefName.AnimeDefaultView))
episodeAdapter.notifyItemRangeInserted(0, arr.size)
for (download in downloadManager.animeDownloadedTypes) {
if (download.title == media.mainName()) {
diff --git a/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt b/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt
index 980f9d0084..78f4fbef7d 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt
@@ -2,14 +2,12 @@ package ani.dantotsu.media.anime
import android.annotation.SuppressLint
import android.app.AlertDialog
-import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.LinearLayout
import androidx.annotation.OptIn
-import androidx.core.content.ContextCompat
import androidx.lifecycle.coroutineScope
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.DownloadIndex
@@ -22,6 +20,7 @@ import ani.dantotsu.databinding.ItemEpisodeListBinding
import ani.dantotsu.download.anime.AnimeDownloaderService
import ani.dantotsu.download.video.Helper
import ani.dantotsu.media.Media
+import ani.dantotsu.settings.saving.PrefManager
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import kotlinx.coroutines.delay
@@ -30,8 +29,8 @@ import kotlin.math.ln
import kotlin.math.pow
fun handleProgress(cont: LinearLayout, bar: View, empty: View, mediaId: Int, ep: String) {
- val curr = loadData("${mediaId}_${ep}")
- val max = loadData("${mediaId}_${ep}_max")
+ val curr = PrefManager.getNullableCustomVal("${mediaId}_${ep}", null, Long::class.java)
+ val max = PrefManager.getNullableCustomVal("${mediaId}_${ep}_max", null, Long::class.java)
if (curr != null && max != null) {
cont.visibility = View.VISIBLE
val div = curr.toFloat() / max.toFloat()
@@ -110,7 +109,7 @@ class EpisodeAdapter(
when (holder) {
is EpisodeListViewHolder -> {
val binding = holder.binding
- setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
+ setAnimation(fragment.requireContext(), holder.binding.root)
val thumb =
ep.thumb?.let { if (it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
@@ -129,7 +128,7 @@ class EpisodeAdapter(
binding.itemEpisodeDesc.visibility =
if (ep.desc != null && ep.desc?.trim(' ') != "") View.VISIBLE else View.GONE
binding.itemEpisodeDesc.text = ep.desc ?: ""
- holder.bind(ep.number, ep.downloadProgress , ep.desc)
+ holder.bind(ep.number, ep.downloadProgress, ep.desc)
if (media.userProgress != null) {
if ((ep.number.toFloatOrNull() ?: 9999f) <= media.userProgress!!.toFloat()) {
@@ -159,7 +158,7 @@ class EpisodeAdapter(
is EpisodeGridViewHolder -> {
val binding = holder.binding
- setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
+ setAnimation(fragment.requireContext(), holder.binding.root)
val thumb =
ep.thumb?.let { if (it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
@@ -202,7 +201,7 @@ class EpisodeAdapter(
is EpisodeCompactViewHolder -> {
val binding = holder.binding
- setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
+ setAnimation(fragment.requireContext(), holder.binding.root)
binding.itemEpisodeNumber.text = ep.number
binding.itemEpisodeFillerView.visibility =
if (ep.filler) View.VISIBLE else View.GONE
@@ -253,10 +252,7 @@ class EpisodeAdapter(
media.mainName(),
episodeNumber
)
- val id = fragment.requireContext().getSharedPreferences(
- ContextCompat.getString(fragment.requireContext(), R.string.anime_downloads),
- Context.MODE_PRIVATE
- ).getString(
+ val id = PrefManager.getAnimeDownloadPreferences().getString(
taskName,
""
) ?: ""
@@ -391,9 +387,10 @@ class EpisodeAdapter(
}, 1000)
} else {
binding.itemDownloadStatus.visibility = View.GONE
- binding.itemEpisodeDesc.visibility = if (desc != null && desc.trim(' ') != "") View.VISIBLE else View.GONE
+ binding.itemEpisodeDesc.visibility =
+ if (desc != null && desc.trim(' ') != "") View.VISIBLE else View.GONE
// Show download icon
- binding.itemDownload.setImageResource(R.drawable.ic_circle_add)
+ binding.itemDownload.setImageResource(R.drawable.ic_download_24)
binding.itemDownload.rotation = 0f
}
diff --git a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
index 29aae29fb7..cbe39704ca 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
@@ -65,6 +65,7 @@ import androidx.mediarouter.app.MediaRouteButton
import ani.dantotsu.*
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.connections.discord.Discord
import ani.dantotsu.connections.discord.DiscordService
import ani.dantotsu.connections.discord.DiscordServiceRunningSingleton
@@ -77,13 +78,12 @@ import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.SubtitleDownloader
import ani.dantotsu.others.AniSkip
import ani.dantotsu.others.AniSkip.getType
-import ani.dantotsu.others.LangSet
import ani.dantotsu.others.ResettableTimer
import ani.dantotsu.others.getSerialized
import ani.dantotsu.parsers.*
-import ani.dantotsu.settings.PlayerSettings
import ani.dantotsu.settings.PlayerSettingsActivity
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.themes.ThemeManager
import com.bumptech.glide.Glide
import com.google.android.gms.cast.framework.CastButtonFactory
@@ -91,7 +91,6 @@ import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.material.slider.Slider
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.lagradost.nicehttp.ignoreAllSSLErrors
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -99,6 +98,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import okhttp3.internal.immutableListOf
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
import java.util.*
import java.util.concurrent.*
import kotlin.math.max
@@ -132,7 +133,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
private lateinit var exoSubtitle: ImageButton
private lateinit var exoSubtitleView: SubtitleView
private lateinit var exoRotate: ImageButton
- private lateinit var exoQuality: ImageButton
private lateinit var exoSpeed: ImageButton
private lateinit var exoScreen: ImageButton
private lateinit var exoNext: ImageButton
@@ -148,9 +148,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
private lateinit var skipTimeText: TextView
private lateinit var timeStampText: TextView
private lateinit var animeTitle: TextView
- private lateinit var videoName: TextView
private lateinit var videoInfo: TextView
- private lateinit var serverInfo: TextView
private lateinit var episodeTitle: Spinner
private var orientationListener: OrientationEventListener? = null
@@ -187,9 +185,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
private var pipEnabled = false
private var aspectRatio = Rational(16, 9)
- var settings = PlayerSettings()
- private var uiSettings = UserInterfaceSettings()
-
private val handler = Handler(Looper.getMainLooper())
val model: MediaDetailsViewModel by viewModels()
@@ -243,8 +238,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
- private fun setupSubFormatting(playerView: PlayerView, settings: PlayerSettings) {
- val primaryColor = when (settings.primaryColor) {
+ private fun setupSubFormatting(playerView: PlayerView) {
+ val primaryColor = when (PrefManager.getVal(PrefName.PrimaryColor)) {
0 -> Color.BLACK
1 -> Color.DKGRAY
2 -> Color.GRAY
@@ -259,7 +254,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
11 -> Color.TRANSPARENT
else -> Color.WHITE
}
- val secondaryColor = when (settings.secondaryColor) {
+ val secondaryColor = when (PrefManager.getVal(PrefName.SecondaryColor)) {
0 -> Color.BLACK
1 -> Color.DKGRAY
2 -> Color.GRAY
@@ -274,14 +269,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
11 -> Color.TRANSPARENT
else -> Color.BLACK
}
- val outline = when (settings.outline) {
+ val outline = when (PrefManager.getVal(PrefName.Outline)) {
0 -> EDGE_TYPE_OUTLINE // Normal
1 -> EDGE_TYPE_DEPRESSED // Shine
2 -> EDGE_TYPE_DROP_SHADOW // Drop shadow
3 -> EDGE_TYPE_NONE // No outline
else -> EDGE_TYPE_OUTLINE // Normal
}
- val subBackground = when (settings.subBackground) {
+ val subBackground = when (PrefManager.getVal(PrefName.SubBackground)) {
0 -> Color.TRANSPARENT
1 -> Color.BLACK
2 -> Color.DKGRAY
@@ -296,7 +291,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
11 -> Color.MAGENTA
else -> Color.TRANSPARENT
}
- val subWindow = when (settings.subWindow) {
+ val subWindow = when (PrefManager.getVal(PrefName.SubWindow)) {
0 -> Color.TRANSPARENT
1 -> Color.BLACK
2 -> Color.DKGRAY
@@ -311,7 +306,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
11 -> Color.MAGENTA
else -> Color.TRANSPARENT
}
- val font = when (settings.font) {
+ val font = when (PrefManager.getVal(PrefName.Font)) {
0 -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
1 -> ResourcesCompat.getFont(this, R.font.poppins_bold)
2 -> ResourcesCompat.getFont(this, R.font.poppins)
@@ -334,7 +329,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
binding = ActivityExoplayerBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -357,21 +352,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
finishAndRemoveTask()
}
- settings = loadData("player_settings") ?: PlayerSettings().apply {
- saveData(
- "player_settings",
- this
- )
- }
- uiSettings = loadData("ui_settings") ?: UserInterfaceSettings().apply {
- saveData(
- "ui_settings",
- this
- )
- }
-
playerView = findViewById(R.id.player_view)
- exoQuality = playerView.findViewById(R.id.exo_quality)
exoPlay = playerView.findViewById(androidx.media3.ui.R.id.exo_play)
exoSource = playerView.findViewById(R.id.exo_source)
exoSettings = playerView.findViewById(R.id.exo_settings)
@@ -406,21 +387,23 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}, AUDIO_CONTENT_TYPE_MOVIE, AUDIOFOCUS_GAIN)
if (System.getInt(contentResolver, System.ACCELEROMETER_ROTATION, 0) != 1) {
- orientationListener =
- object : OrientationEventListener(this, SensorManager.SENSOR_DELAY_UI) {
- override fun onOrientationChanged(orientation: Int) {
- if (orientation in 45..135) {
- if (rotation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) exoRotate.visibility =
- View.VISIBLE
- rotation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
- } else if (orientation in 225..315) {
- if (rotation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) exoRotate.visibility =
- View.VISIBLE
- rotation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ if (PrefManager.getVal(PrefName.RotationPlayer)) {
+ orientationListener =
+ object : OrientationEventListener(this, SensorManager.SENSOR_DELAY_UI) {
+ override fun onOrientationChanged(orientation: Int) {
+ if (orientation in 45..135) {
+ if (rotation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) exoRotate.visibility =
+ View.VISIBLE
+ rotation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
+ } else if (orientation in 225..315) {
+ if (rotation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) exoRotate.visibility =
+ View.VISIBLE
+ rotation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ }
}
}
- }
- orientationListener?.enable()
+ orientationListener?.enable()
+ }
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
exoRotate.setOnClickListener {
@@ -429,14 +412,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
- setupSubFormatting(playerView, settings)
+ setupSubFormatting(playerView)
- playerView.subtitleView?.alpha = when (settings.subtitles) {
+ playerView.subtitleView?.alpha = when (PrefManager.getVal(PrefName.Subtitles)) {
true -> 1f
false -> 0f
}
- val fontSize = settings.fontSize.toFloat()
+ val fontSize = PrefManager.getVal(PrefName.FontSize).toFloat()
playerView.subtitleView?.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize)
if (savedInstanceState != null) {
@@ -469,17 +452,16 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
} else View.GONE
}
- exoSkipOpEd.alpha = if (settings.autoSkipOPED) 1f else 0.3f
+ exoSkipOpEd.alpha = if (PrefManager.getVal(PrefName.AutoSkipOPED)) 1f else 0.3f
exoSkipOpEd.setOnClickListener {
- settings.autoSkipOPED = if (settings.autoSkipOPED) {
+ if (PrefManager.getVal(PrefName.AutoSkipOPED)) {
snackString(getString(R.string.disabled_auto_skip))
- false
+ PrefManager.setVal(PrefName.AutoSkipOPED, false)
} else {
snackString(getString(R.string.auto_skip))
- true
+ PrefManager.setVal(PrefName.AutoSkipOPED, true)
}
- saveData("player_settings", settings)
- exoSkipOpEd.alpha = if (settings.autoSkipOPED) 1f else 0.3f
+ exoSkipOpEd.alpha = if (PrefManager.getVal(PrefName.AutoSkipOPED)) 1f else 0.3f
}
//Play Pause
@@ -506,7 +488,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
// Picture-in-picture
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pipEnabled =
- packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && settings.pip
+ packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && PrefManager.getVal(
+ PrefName.Pip
+ )
if (pipEnabled) {
exoPip.visibility = View.VISIBLE
exoPip.setOnClickListener {
@@ -539,14 +523,15 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
//Skip Time Button
- if (settings.skipTime > 0) {
- exoSkip.findViewById(R.id.exo_skip_time).text = settings.skipTime.toString()
+ var skipTime = PrefManager.getVal(PrefName.SkipTime)
+ if (skipTime > 0) {
+ exoSkip.findViewById(R.id.exo_skip_time).text = skipTime.toString()
exoSkip.setOnClickListener {
if (isInitialized)
- exoPlayer.seekTo(exoPlayer.currentPosition + settings.skipTime * 1000)
+ exoPlayer.seekTo(exoPlayer.currentPosition + skipTime * 1000)
}
exoSkip.setOnLongClickListener {
- val dialog = Dialog(this, R.style.DialogTheme)
+ val dialog = Dialog(this, R.style.MyPopup)
dialog.setContentView(R.layout.item_seekbar_dialog)
dialog.setCancelable(true)
dialog.setCanceledOnTouchOutside(true)
@@ -554,18 +539,19 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
- if (settings.skipTime <= 120) {
- dialog.findViewById(R.id.seekbar).value = settings.skipTime.toFloat()
+ if (skipTime <= 120) {
+ dialog.findViewById(R.id.seekbar).value = skipTime.toFloat()
} else {
dialog.findViewById(R.id.seekbar).value = 120f
}
dialog.findViewById(R.id.seekbar).addOnChangeListener { _, value, _ ->
- settings.skipTime = value.toInt()
- saveData(player, settings)
+ skipTime = value.toInt()
+ //saveData(player, settings)
+ PrefManager.setVal(PrefName.SkipTime, skipTime)
playerView.findViewById(R.id.exo_skip_time).text =
- settings.skipTime.toString()
+ skipTime.toString()
dialog.findViewById(R.id.seekbar_value).text =
- settings.skipTime.toString()
+ skipTime.toString()
}
dialog.findViewById(R.id.seekbar)
.addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
@@ -577,7 +563,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
dialog.findViewById(R.id.seekbar_title).text =
getString(R.string.skip_time)
dialog.findViewById(R.id.seekbar_value).text =
- settings.skipTime.toString()
+ skipTime.toString()
@Suppress("DEPRECATION")
dialog.window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
dialog.show()
@@ -587,7 +573,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
exoSkip.visibility = View.GONE
}
- val gestureSpeed = (300 * uiSettings.animationSpeed).toLong()
+ val gestureSpeed = (300 * PrefManager.getVal(PrefName.AnimationSpeed)).toLong()
//Player UI Visibility Handler
val brightnessRunnable = Runnable {
if (exoBrightnessCont.alpha == 1f)
@@ -617,7 +603,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
})
val overshoot = AnimationUtils.loadInterpolator(this, R.anim.over_shoot)
- val controllerDuration = (uiSettings.animationSpeed * 200).toLong()
+ val controllerDuration = (300 * PrefManager.getVal(PrefName.AnimationSpeed)).toLong()
fun handleController() {
if (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) !isInPictureInPictureMode else true) {
if (playerView.isControllerFullyVisible) {
@@ -702,13 +688,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
var seekTimesR = 0
fun seek(forward: Boolean, event: MotionEvent? = null) {
+ val seekTime = PrefManager.getVal(PrefName.SeekTime)
val (card, text) = if (forward) {
- forwardText.text = "+${settings.seekTime * ++seekTimesF}"
- handler.post { exoPlayer.seekTo(exoPlayer.currentPosition + settings.seekTime * 1000) }
+ forwardText.text = "+${seekTime * ++seekTimesF}"
+ handler.post { exoPlayer.seekTo(exoPlayer.currentPosition + seekTime * 1000) }
fastForwardCard to forwardText
} else {
- rewindText.text = "-${settings.seekTime * ++seekTimesR}"
- handler.post { exoPlayer.seekTo(exoPlayer.currentPosition - settings.seekTime * 1000) }
+ rewindText.text = "-${seekTime * ++seekTimesR}"
+ handler.post { exoPlayer.seekTo(exoPlayer.currentPosition - seekTime * 1000) }
fastRewindCard to rewindText
}
@@ -763,7 +750,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
- if (!settings.doubleTap) {
+ if (!PrefManager.getVal(PrefName.DoubleTap)) {
playerView.findViewById(R.id.exo_fast_forward_button_cont).visibility =
View.VISIBLE
playerView.findViewById(R.id.exo_fast_rewind_button_cont).visibility =
@@ -784,10 +771,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
keyMap[KEYCODE_DPAD_LEFT] = { seek(false) }
//Screen Gestures
- if (settings.gestures || settings.doubleTap) {
+ if (PrefManager.getVal(PrefName.Gestures) || PrefManager.getVal(PrefName.DoubleTap)) {
fun doubleTap(forward: Boolean, event: MotionEvent) {
- if (!locked && isInitialized && settings.doubleTap) {
+ if (!locked && isInitialized && PrefManager.getVal(PrefName.DoubleTap)) {
seek(forward, event)
}
}
@@ -844,7 +831,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
isFastForwarding = true
exoPlayer.setPlaybackSpeed(exoPlayer.playbackParameters.speed * 2)
fastForward.visibility = View.VISIBLE
- fastForward.text = ("${exoPlayer.playbackParameters.speed}x")
+ fastForward.text = "${exoPlayer.playbackParameters.speed}x"
}
fun stopFastForward() {
@@ -858,7 +845,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
//FastRewind (Left Panel)
val fastRewindDetector = GestureDetector(this, object : GesturesListener() {
override fun onLongClick(event: MotionEvent) {
- if (settings.fastforward) fastForward()
+ if (PrefManager.getVal(PrefName.FastForward)) fastForward()
}
override fun onDoubleClick(event: MotionEvent) {
@@ -866,7 +853,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
override fun onScrollYClick(y: Float) {
- if (!locked && settings.gestures) {
+ if (!locked && PrefManager.getVal(PrefName.Gestures)) {
exoBrightness.value = clamp(exoBrightness.value + y / 100, 0f, 10f)
if (exoBrightnessCont.visibility != View.VISIBLE) {
exoBrightnessCont.visibility = View.VISIBLE
@@ -890,7 +877,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
//FastForward (Right Panel)
val fastForwardDetector = GestureDetector(this, object : GesturesListener() {
override fun onLongClick(event: MotionEvent) {
- if (settings.fastforward) fastForward()
+ if (PrefManager.getVal(PrefName.FastForward)) fastForward()
}
override fun onDoubleClick(event: MotionEvent) {
@@ -898,7 +885,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
override fun onScrollYClick(y: Float) {
- if (!locked && settings.gestures) {
+ if (!locked && PrefManager.getVal(PrefName.Gestures)) {
exoVolume.value = clamp(exoVolume.value + y / 100, 0f, 10f)
if (exoVolumeCont.visibility != View.VISIBLE) {
exoVolumeCont.visibility = View.VISIBLE
@@ -926,21 +913,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
title = media.userPreferredName
episodes = media.anime?.episodes ?: return startMainActivity(this)
- videoName = playerView.findViewById(R.id.exo_video_name)
videoInfo = playerView.findViewById(R.id.exo_video_info)
- serverInfo = playerView.findViewById(R.id.exo_server_info)
-
- if (!settings.videoInfo) {
- videoName.visibility = View.GONE
- videoInfo.visibility = View.GONE
- serverInfo.visibility = View.GONE
- } else {
- videoName.isSelected = true
- }
model.watchSources = if (media.isAdult) HAnimeSources else AnimeSources
- serverInfo.text = model.watchSources!!.names.getOrNull(media.selected!!.sourceIndex)
- ?: model.watchSources!!.names[0]
model.epChanged.observe(this) {
epChanging = !it
@@ -964,10 +939,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
fun change(index: Int) {
if (isInitialized) {
changingServer = false
- saveData(
+ PrefManager.setCustomVal(
"${media.id}_${episodeArr[currentEpisodeIndex]}",
- exoPlayer.currentPosition,
- this
+ exoPlayer.currentPosition
)
exoPlayer.seekTo(0)
val prev = episodeArr[currentEpisodeIndex]
@@ -1024,14 +998,16 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
currentEpisodeIndex = episodeArr.indexOf(ep.number)
episodeTitle.setSelection(currentEpisodeIndex)
if (isInitialized) releasePlayer()
- playbackPosition = loadData("${media.id}_${ep.number}", this) ?: 0
+ playbackPosition = PrefManager.getCustomVal(
+ "${media.id}_${ep.number}",
+ 0
+ )
initPlayer()
preloading = false
val context = this
-
- val incognito = baseContext.getSharedPreferences("Dantotsu", MODE_PRIVATE)
- .getBoolean("incognito", false)
- if (isOnline(context) && Discord.token != null && !incognito) {
+ val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode)
+ val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
+ if ((isOnline(context) && !offline) && Discord.token != null && !incognito) {
lifecycleScope.launch {
val presence = RPC.createPresence(RPC.Companion.RPCData(
applicationId = Discord.application_Id,
@@ -1075,7 +1051,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
//FullScreen
- isFullscreen = loadData("${media.id}_fullscreenInt", this) ?: isFullscreen
+ isFullscreen = PrefManager.getCustomVal("${media.id}_fullscreenInt", isFullscreen)
playerView.resizeMode = when (isFullscreen) {
0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
@@ -1099,11 +1075,11 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
else -> "Original"
}
)
- saveData("${media.id}_fullscreenInt", isFullscreen, this)
+ PrefManager.setCustomVal("${media.id}_fullscreenInt", isFullscreen)
}
//Cast
- if (settings.cast) {
+ if (PrefManager.getVal(PrefName.Cast)) {
playerView.findViewById(R.id.exo_cast).apply {
visibility = View.VISIBLE
try {
@@ -1121,10 +1097,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
//Settings
exoSettings.setOnClickListener {
- saveData(
+ PrefManager.setCustomVal(
"${media.id}_${media.anime!!.selectedEpisode}",
- exoPlayer.currentPosition,
- this
+ exoPlayer.currentPosition
)
val intent = Intent(this, PlayerSettingsActivity::class.java).apply {
putExtra("subtitle", subtitle)
@@ -1135,7 +1110,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
//Speed
val speeds =
- if (settings.cursedSpeeds)
+ if (PrefManager.getVal(PrefName.CursedSpeeds))
arrayOf(1f, 1.25f, 1.5f, 1.75f, 2f, 2.5f, 3f, 4f, 5f, 10f, 25f, 50f)
else
arrayOf(
@@ -1155,16 +1130,20 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
)
val speedsName = speeds.map { "${it}x" }.toTypedArray()
- var curSpeed = loadData("${media.id}_speed", this) ?: settings.defaultSpeed
+ //var curSpeed = loadData("${media.id}_speed", this) ?: settings.defaultSpeed
+ var curSpeed = PrefManager.getCustomVal(
+ "${media.id}_speed",
+ PrefManager.getVal(PrefName.DefaultSpeed)
+ )
playbackParameters = PlaybackParameters(speeds[curSpeed])
var speed: Float
val speedDialog =
- AlertDialog.Builder(this, R.style.DialogTheme).setTitle(getString(R.string.speed))
+ AlertDialog.Builder(this, R.style.MyPopup).setTitle(getString(R.string.speed))
exoSpeed.setOnClickListener {
val dialog = speedDialog.setSingleChoiceItems(speedsName, curSpeed) { dialog, i ->
if (isInitialized) {
- saveData("${media.id}_speed", i, this)
+ PrefManager.setCustomVal("${media.id}_speed", i)
speed = speeds[i]
curSpeed = i
playbackParameters = PlaybackParameters(speed)
@@ -1177,7 +1156,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
speedDialog.setOnCancelListener { hideSystemBars() }
- if (settings.autoPlay) {
+ if (PrefManager.getVal(PrefName.AutoPlay)) {
var touchTimer = Timer()
fun touched() {
interacted = true
@@ -1198,8 +1177,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
- isFullscreen = settings.resize
- playerView.resizeMode = when (settings.resize) {
+ isFullscreen = PrefManager.getVal(PrefName.Resize)
+ playerView.resizeMode = when (isFullscreen) {
0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
2 -> AspectRatioFrameLayout.RESIZE_MODE_FILL
@@ -1207,39 +1186,52 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
preloading = false
- val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getBoolean("incognito", false) ?: false
+ val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
val showProgressDialog =
- if (settings.askIndividual) loadData("${media.id}_progressDialog")
- ?: true else false
- if (showProgressDialog && Anilist.userid != null && if (media.isAdult) settings.updateForH else true)
+ if (PrefManager.getVal(PrefName.AskIndividualPlayer)) PrefManager.getCustomVal(
+ "${media.id}_ProgressDialog",
+ true
+ ) else false
+ if (!incognito && showProgressDialog && Anilist.userid != null && if (media.isAdult) PrefManager.getVal(
+ PrefName.UpdateForHPlayer
+ ) else true
+ ) {
AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.auto_update, media.userPreferredName))
.apply {
- if (incognito) {
- setMessage(getString(R.string.incognito_will_not_update))
- }
setOnCancelListener { hideSystemBars() }
setCancelable(false)
setPositiveButton(getString(R.string.yes)) { dialog, _ ->
- saveData("${media.id}_progressDialog", false)
- saveData("${media.id}_save_progress", true)
+ PrefManager.setCustomVal(
+ "${media.id}_ProgressDialog",
+ false
+ )
+ PrefManager.setCustomVal(
+ "${media.id}_save_progress",
+ true
+ )
dialog.dismiss()
model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
}
setNegativeButton(getString(R.string.no)) { dialog, _ ->
- saveData("${media.id}_progressDialog", false)
- saveData("${media.id}_save_progress", false)
+ PrefManager.setCustomVal(
+ "${media.id}_ProgressDialog",
+ false
+ )
+ PrefManager.setCustomVal(
+ "${media.id}_save_progress",
+ false
+ )
toast(getString(R.string.reset_auto_update))
dialog.dismiss()
model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
}
show()
}
- else model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
+ } else model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
//Start the recursive Fun
- if (settings.timeStampsEnabled)
+ if (PrefManager.getVal(PrefName.TimeStampsEnabled))
updateTimeStamp()
}
@@ -1247,12 +1239,15 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
private fun initPlayer() {
checkNotch()
- saveData("${media.id}_current_ep", media.anime!!.selectedEpisode!!, this)
+ PrefManager.setCustomVal(
+ "${media.id}_current_ep",
+ media.anime!!.selectedEpisode!!
+ )
- val set = loadData>("continue_ANIME", this) ?: mutableSetOf()
- if (set.contains(media.id)) set.remove(media.id)
- set.add(media.id)
- saveData("continue_ANIME", set, this)
+ val list = PrefManager.getVal>(PrefName.ContinuedAnime).toMutableList()
+ if (list.contains(media.id)) list.remove(media.id)
+ list.add(media.id)
+ PrefManager.setVal(PrefName.ContinuedAnime, list.toList())
lifecycleScope.launch(Dispatchers.IO) {
extractor?.onVideoStopped(video)
@@ -1263,7 +1258,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
video = ext.videos.getOrNull(episode.selectedVideo) ?: return
subtitle = intent.getSerialized("subtitle")
- ?: when (val subLang: String? = loadData("subLang_${media.id}", this)) {
+ ?: when (val subLang: String? =
+ PrefManager.getCustomVal("subLang_${media.id}", null as String?)) {
null -> {
when (episode.selectedSubtitle) {
null -> null
@@ -1305,7 +1301,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
println("sub: $sub")
} else {
- val subUri = Uri.parse((subtitle!!.file.url))
+ val subUri = Uri.parse(subtitle!!.file.url)
sub = MediaItem.SubtitleConfiguration
.Builder(subUri)
.setSelectionFlags(C.SELECTION_FLAG_FORCED)
@@ -1342,7 +1338,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
dataSource
}
- val dafuckDataSourceFactory = DefaultDataSourceFactory(this, Util.getUserAgent(this, R.string.app_name.toString()))
+ val dafuckDataSourceFactory =
+ DefaultDataSourceFactory(this, Util.getUserAgent(this, R.string.app_name.toString()))
cacheFactory = CacheDataSource.Factory().apply {
setCache(Helper.getSimpleCache(this@ExoplayerView))
if (ext.server.offline) {
@@ -1361,7 +1358,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
val downloadedMediaItem = if (ext.server.offline) {
val key = ext.server.name
- downloadId = getSharedPreferences(getString(R.string.anime_downloads), MODE_PRIVATE)
+ downloadId = PrefManager.getAnimeDownloadPreferences()
.getString(key, null)
if (downloadId != null) {
Helper.downloadManager(this)
@@ -1413,16 +1410,12 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
.setRendererDisabled(TRACK_TYPE_VIDEO, false)
.setRendererDisabled(C.TRACK_TYPE_AUDIO, false)
.setRendererDisabled(C.TRACK_TYPE_TEXT, false)
- .setMinVideoSize(
- loadData("maxWidth", this) ?: 720,
- loadData("maxHeight", this) ?: 480
- )
.setMaxVideoSize(1, 1)
//.setOverrideForType(
// TrackSelectionOverride(trackSelector, 2))
)
- if (playbackPosition != 0L && !changingServer && !settings.alwaysContinue) {
+ if (playbackPosition != 0L && !changingServer && !PrefManager.getVal(PrefName.AlwaysContinue)) {
val time = String.format(
"%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(playbackPosition),
TimeUnit.MILLISECONDS.toMinutes(playbackPosition) - TimeUnit.HOURS.toMinutes(
@@ -1436,7 +1429,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
)
)
)
- val dialog = AlertDialog.Builder(this, R.style.DialogTheme)
+ val dialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.continue_from, time)).apply {
setCancelable(false)
setPositiveButton(getString(R.string.yes)) { d, _ ->
@@ -1464,9 +1457,12 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
this.playbackParameters = this@ExoplayerView.playbackParameters
setMediaItem(mediaItem)
prepare()
- loadData("${media.id}_${media.anime!!.selectedEpisode}_max")?.apply {
- if (this <= playbackPosition) playbackPosition = max(0, this - 5)
- }
+ PrefManager.getCustomVal(
+ "${media.id}_${media.anime!!.selectedEpisode}_max",
+ Long.MAX_VALUE
+ )
+ .takeIf { it != Long.MAX_VALUE }
+ ?.let { if (it <= playbackPosition) playbackPosition = max(0, it - 5) }
seekTo(playbackPosition)
}
playerView.player = exoPlayer
@@ -1539,8 +1535,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
changingServer = true
media.selected!!.server = null
- saveData("${media.id}_${media.anime!!.selectedEpisode}", exoPlayer.currentPosition, this)
- model.saveSelected(media.id, media.selected!!, this)
+ PrefManager.setCustomVal(
+ "${media.id}_${media.anime!!.selectedEpisode}", exoPlayer.currentPosition
+ )
+ model.saveSelected(media.id, media.selected!!)
model.onEpisodeClick(
media, episode.number, this.supportFragmentManager,
launch = false
@@ -1548,8 +1546,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
private fun subClick() {
- saveData("${media.id}_${media.anime!!.selectedEpisode}", exoPlayer.currentPosition, this)
- model.saveSelected(media.id, media.selected!!, this)
+ PrefManager.setCustomVal(
+ "${media.id}_${media.anime!!.selectedEpisode}", exoPlayer.currentPosition
+ )
+ model.saveSelected(media.id, media.selected!!)
SubtitleDialogFragment().show(supportFragmentManager, "dialog")
}
@@ -1561,10 +1561,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
playerView.player?.pause()
}
if (exoPlayer.currentPosition > 5000) {
- saveData(
+ PrefManager.setCustomVal(
"${media.id}_${media.anime!!.selectedEpisode}",
- exoPlayer.currentPosition,
- this
+ exoPlayer.currentPosition
)
}
}
@@ -1572,7 +1571,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
override fun onResume() {
super.onResume()
- LangSet.setLocale(this)
orientationListener?.enable()
hideSystemBars()
if (isInitialized) {
@@ -1590,7 +1588,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
private var wasPlaying = false
override fun onWindowFocusChanged(hasFocus: Boolean) {
- if (settings.focusPause && !epChanging) {
+ if (PrefManager.getVal(PrefName.FocusPause) && !epChanging) {
if (isInitialized && !hasFocus) wasPlaying = exoPlayer.isPlaying
if (hasFocus) {
if (isInitialized && wasPlaying) exoPlayer.play()
@@ -1614,19 +1612,16 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
override fun onRenderedFirstFrame() {
super.onRenderedFirstFrame()
- saveData("${media.id}_${media.anime!!.selectedEpisode}_max", exoPlayer.duration, this)
+ PrefManager.setCustomVal(
+ "${media.id}_${media.anime!!.selectedEpisode}_max",
+ exoPlayer.duration
+ )
val height = (exoPlayer.videoFormat ?: return).height
val width = (exoPlayer.videoFormat ?: return).width
- if (video?.format != VideoType.CONTAINER) {
- saveData("maxHeight", height)
- saveData("maxWidth", width)
- }
-
aspectRatio = Rational(width, height)
- videoName.text = episode.selectedExtractor
- videoInfo.text = "$width x $height"
+ videoInfo.text = "Quality: ${height}p"
if (exoPlayer.duration < playbackPosition)
exoPlayer.seekTo(0)
@@ -1637,14 +1632,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
exoPlayer.seekTo(0)
}
- if (!isTimeStampsLoaded && settings.timeStampsEnabled) {
+ if (!isTimeStampsLoaded && PrefManager.getVal(PrefName.TimeStampsEnabled)) {
val dur = exoPlayer.duration
lifecycleScope.launch(Dispatchers.IO) {
model.loadTimeStamps(
media.idMAL,
media.anime?.selectedEpisode?.trim()?.toIntOrNull(),
dur / 1000,
- settings.useProxyForTimeStamps
+ PrefManager.getVal(PrefName.UseProxyForTimeStamps)
)
}
}
@@ -1654,7 +1649,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
private var preloading = false
private fun updateProgress() {
if (isInitialized) {
- if (exoPlayer.currentPosition.toFloat() / exoPlayer.duration > settings.watchPercentage) {
+ if (exoPlayer.currentPosition.toFloat() / exoPlayer.duration > PrefManager.getVal(
+ PrefName.WatchPercentage
+ )
+ ) {
preloading = true
nextEpisode(false) { i ->
val ep = episodes[episodeArr[currentEpisodeIndex + i]] ?: return@nextEpisode
@@ -1685,7 +1683,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
val new = currentTimeStamp
timeStampText.text = if (new != null) {
- if (settings.showTimeStampButton) {
+ if (PrefManager.getVal(PrefName.ShowTimeStampButton)) {
skipTimeButton.visibility = View.VISIBLE
exoSkip.visibility = View.GONE
skipTimeText.text = new.skipType.getType()
@@ -1693,7 +1691,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
}
}
- if (settings.autoSkipOPED && (new.skipType == "op" || new.skipType == "ed") && !skippedTimeStamps.contains(
+ if (PrefManager.getVal(PrefName.AutoSkipOPED) && (new.skipType == "op" || new.skipType == "ed") && !skippedTimeStamps.contains(
new
)
) {
@@ -1703,7 +1701,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
new.skipType.getType()
} else {
skipTimeButton.visibility = View.GONE
- if (settings.skipTime > 0) exoSkip.visibility = View.VISIBLE
+ if (PrefManager.getVal(PrefName.SkipTime) > 0) exoSkip.visibility =
+ View.VISIBLE
""
}
}
@@ -1736,13 +1735,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
println("Track: ${tracks.groups.size}")
- if (tracks.groups.size <= 2) exoQuality.visibility = View.GONE
- else {
- exoQuality.visibility = View.VISIBLE
- exoQuality.setOnClickListener {
- initPopupQuality().show()
- }
- }
}
override fun onPlayerError(error: PlaybackException) {
@@ -1757,7 +1749,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
else
-> {
toast("Player Error ${error.errorCode} (${error.errorCodeName}) : ${error.message}")
- FirebaseCrashlytics.getInstance().recordException(error)
+ Injekt.get().logException(error)
}
}
}
@@ -1772,7 +1764,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
isBuffering = playbackState == Player.STATE_BUFFERING
- if (playbackState == Player.STATE_ENDED && settings.autoPlay) {
+ if (playbackState == Player.STATE_ENDED && PrefManager.getVal(PrefName.AutoPlay)) {
if (interacted) exoNext.performClick()
else toast(getString(R.string.autoplay_cancelled))
}
@@ -1780,8 +1772,16 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
private fun updateAniProgress() {
- if (exoPlayer.currentPosition / episodeLength > settings.watchPercentage && Anilist.userid != null)
- if (loadData("${media.id}_save_progress") != false && if (media.isAdult) settings.updateForH else true) {
+ val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
+ if (!incognito && exoPlayer.currentPosition / episodeLength > PrefManager.getVal(
+ PrefName.WatchPercentage
+ ) && Anilist.userid != null
+ )
+ if (PrefManager.getCustomVal(
+ "${media.id}_save_progress",
+ true
+ ) && (if (media.isAdult) PrefManager.getVal(PrefName.UpdateForHPlayer) else true)
+ ) {
media.anime!!.selectedEpisode?.apply {
updateProgress(media, this)
}
@@ -1794,7 +1794,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
while (isFiller) {
if (episodeArr.size > currentEpisodeIndex + i) {
isFiller =
- if (settings.autoSkipFiller) episodes[episodeArr[currentEpisodeIndex + i]]?.filler
+ if (PrefManager.getVal(PrefName.AutoSkipFiller)) episodes[episodeArr[currentEpisodeIndex + i]]?.filler
?: false else false
if (!isFiller) runnable.invoke(i)
i++
@@ -1830,20 +1830,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
finishAndRemoveTask()
}
- // QUALITY SELECTOR
- private fun initPopupQuality(): Dialog {
-
- val trackSelectionDialogBuilder =
- TrackSelectionDialogBuilder(this, "Available Qualities", exoPlayer, TRACK_TYPE_VIDEO)
- trackSelectionDialogBuilder.setTheme(R.style.DialogTheme)
- trackSelectionDialogBuilder.setTrackNameProvider {
- if (it.frameRate > 0f) it.height.toString() + "p" else it.height.toString() + "p (fps : N/A)"
- }
- val trackDialog = trackSelectionDialogBuilder.build()
- trackDialog.setOnDismissListener { hideSystemBars() }
- return trackDialog
- }
-
// Cast
private fun cast() {
val videoURL = video?.file?.url ?: return
@@ -1905,7 +1891,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
orientationListener?.enable()
}
if (isInitialized) {
- saveData("${media.id}_${episode.number}", exoPlayer.currentPosition, this)
+ PrefManager.setCustomVal(
+ "${media.id}_${episode.number}",
+ exoPlayer.currentPosition
+ )
if (wasPlaying) exoPlayer.play()
}
}
@@ -1988,7 +1977,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
override fun onCastSessionAvailable() {
- if (isCastApiAvailable) {
+ if (isCastApiAvailable && !this.isDestroyed) {
startCastPlayer()
}
}
diff --git a/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt
index 688222c585..a00253eae0 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt
@@ -8,7 +8,6 @@ import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.util.TypedValue
-import android.view.HapticFeedbackConstants
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -18,6 +17,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.*
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.databinding.BottomSheetSelectorBinding
import ani.dantotsu.databinding.ItemStreamBinding
import ani.dantotsu.databinding.ItemUrlBinding
@@ -28,11 +28,14 @@ import ani.dantotsu.others.Download.download
import ani.dantotsu.parsers.Subtitle
import ani.dantotsu.parsers.VideoExtractor
import ani.dantotsu.parsers.VideoType
-import com.google.firebase.crashlytics.FirebaseCrashlytics
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
import java.text.DecimalFormat
@@ -93,7 +96,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
binding.selectorAutoText.text = selected
binding.selectorCancel.setOnClickListener {
media!!.selected!!.server = null
- model.saveSelected(media!!.id, media!!.selected!!, requireActivity())
+ model.saveSelected(media!!.id, media!!.selected!!)
tryWith {
dismiss()
}
@@ -142,11 +145,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
binding.selectorRecyclerView.adapter = null
binding.selectorProgressBar.visibility = View.VISIBLE
- makeDefault = loadData("make_default") ?: true
+ makeDefault = PrefManager.getVal(PrefName.MakeDefault)
binding.selectorMakeDefault.isChecked = makeDefault
binding.selectorMakeDefault.setOnClickListener {
makeDefault = binding.selectorMakeDefault.isChecked
- saveData("make_default", makeDefault)
+ PrefManager.setVal(PrefName.MakeDefault, makeDefault)
}
binding.selectorRecyclerView.layoutManager =
LinearLayoutManager(
@@ -265,7 +268,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedVideo = 0
startExoplayer(media!!)
} catch (e: Exception) {
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
}
}
@@ -300,96 +303,88 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
extractor.server.name
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedVideo =
position
- binding.urlDownload.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
- val episode = media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!
- val selectedVideo =
- if (extractor.videos.size > episode.selectedVideo) extractor.videos[episode.selectedVideo] else null
-
- val subtitles = extractor.subtitles
- val subtitleNames = subtitles.map { it.language }
- var subtitleToDownload: Subtitle? = null
- if (subtitles.isNotEmpty()) {
- val alertDialog = AlertDialog.Builder(context, R.style.MyPopup)
- .setTitle("Download Subtitle")
- .setSingleChoiceItems(
- subtitleNames.toTypedArray(),
- -1
- ) { dialog, which ->
- subtitleToDownload = subtitles[which]
- }
- .setPositiveButton("Download") { _, _ ->
- dialog?.dismiss()
- if (selectedVideo != null) {
- Helper.startAnimeDownloadService(
- currActivity()!!,
- media!!.mainName(),
- episode.number,
- selectedVideo,
- subtitleToDownload,
- media,
- episode.thumb?.url ?: media!!.banner ?: media!!.cover
- )
- } else {
- snackString("No Video Selected")
- }
- }
- .setNegativeButton("Skip") { dialog, _ ->
- subtitleToDownload = null
- if (selectedVideo != null) {
- Helper.startAnimeDownloadService(
- currActivity()!!,
- media!!.mainName(),
- episode.number,
- selectedVideo,
- subtitleToDownload,
- media,
- episode.thumb?.url ?: media!!.banner ?: media!!.cover
- )
- } else {
- snackString("No Video Selected")
- }
- dialog.dismiss()
- }
- .setNeutralButton("Cancel") { dialog, _ ->
- subtitleToDownload = null
- dialog.dismiss()
- }
- .show()
- alertDialog.window?.setDimAmount(0.8f)
-
- } else {
- if (selectedVideo != null) {
- Helper.startAnimeDownloadService(
- requireActivity(),
- media!!.mainName(),
- episode.number,
- selectedVideo,
- subtitleToDownload,
- media,
- episode.thumb?.url ?: media!!.banner ?: media!!.cover
- )
- } else {
- snackString("No Video Selected")
- }
- }
- dismiss()
- }
- binding.urlDownload.setOnLongClickListener {
- binding.urlDownload.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
- if ((loadData("settings_download_manager") ?: 0) != 0) {
- media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedExtractor =
- extractor.server.name
- media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedVideo =
- position
+ if ((PrefManager.getVal(PrefName.DownloadManager) as Int) != 0) {
download(
requireActivity(),
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!,
media!!.userPreferredName
)
} else {
- snackString("No Download Manager Selected")
+ val episode = media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!
+ val selectedVideo =
+ if (extractor.videos.size > episode.selectedVideo) extractor.videos[episode.selectedVideo] else null
+ val subtitles = extractor.subtitles
+ val subtitleNames = subtitles.map { it.language }
+ var subtitleToDownload: Subtitle? = null
+ if (subtitles.isNotEmpty()) {
+ val alertDialog = AlertDialog.Builder(context, R.style.MyPopup)
+ .setTitle("Download Subtitle")
+ .setSingleChoiceItems(
+ subtitleNames.toTypedArray(),
+ -1
+ ) { dialog, which ->
+ subtitleToDownload = subtitles[which]
+ }
+ .setPositiveButton("Download") { _, _ ->
+ dialog?.dismiss()
+ if (selectedVideo != null) {
+ Helper.startAnimeDownloadService(
+ currActivity()!!,
+ media!!.mainName(),
+ episode.number,
+ selectedVideo,
+ subtitleToDownload,
+ media,
+ episode.thumb?.url ?: media!!.banner ?: media!!.cover
+ )
+ broadcastDownloadStarted(episode.number)
+ } else {
+ snackString("No Video Selected")
+ }
+ }
+ .setNegativeButton("Skip") { dialog, _ ->
+ subtitleToDownload = null
+ if (selectedVideo != null) {
+ Helper.startAnimeDownloadService(
+ currActivity()!!,
+ media!!.mainName(),
+ episode.number,
+ selectedVideo,
+ subtitleToDownload,
+ media,
+ episode.thumb?.url ?: media!!.banner ?: media!!.cover
+ )
+ broadcastDownloadStarted(episode.number)
+ } else {
+ snackString("No Video Selected")
+ }
+ dialog.dismiss()
+ }
+ .setNeutralButton("Cancel") { dialog, _ ->
+ subtitleToDownload = null
+ dialog.dismiss()
+ }
+ .show()
+ alertDialog.window?.setDimAmount(0.8f)
+
+ } else {
+ if (selectedVideo != null) {
+ Helper.startAnimeDownloadService(
+ requireActivity(),
+ media!!.mainName(),
+ episode.number,
+ selectedVideo,
+ subtitleToDownload,
+ media,
+ episode.thumb?.url ?: media!!.banner ?: media!!.cover
+ )
+ broadcastDownloadStarted(episode.number)
+ } else {
+ snackString("No Video Selected")
+ }
+ }
}
- true
+ dismiss()
}
if (video.format == VideoType.CONTAINER) {
binding.urlSize.visibility = if (video.size != null) View.VISIBLE else View.GONE
@@ -404,6 +399,13 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
binding.urlQuality.text = extractor.server.name
}
+ private fun broadcastDownloadStarted(episodeNumber: String) {
+ val intent = Intent(AnimeWatchFragment.ACTION_DOWNLOAD_STARTED).apply {
+ putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, episodeNumber)
+ }
+ requireActivity().sendBroadcast(intent)
+ }
+
override fun getItemCount(): Int = extractor.videos.size
private inner class UrlViewHolder(val binding: ItemUrlBinding) :
@@ -422,7 +424,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
if (makeDefault) {
media!!.selected!!.server = extractor.server.name
media!!.selected!!.video = bindingAdapterPosition
- model.saveSelected(media!!.id, media!!.selected!!, requireActivity())
+ model.saveSelected(media!!.id, media!!.selected!!)
}
startExoplayer(media!!)
}
diff --git a/app/src/main/java/ani/dantotsu/media/anime/SubtitleDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/SubtitleDialogFragment.kt
index 6cc971574f..3fae8c7981 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/SubtitleDialogFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/SubtitleDialogFragment.kt
@@ -15,10 +15,9 @@ import ani.dantotsu.BottomSheetDialogFragment
import ani.dantotsu.R
import ani.dantotsu.databinding.BottomSheetSubtitlesBinding
import ani.dantotsu.databinding.ItemSubtitleTextBinding
-import ani.dantotsu.loadData
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.parsers.Subtitle
-import ani.dantotsu.saveData
+import ani.dantotsu.settings.saving.PrefManager
class SubtitleDialogFragment : BottomSheetDialogFragment() {
private var _binding: BottomSheetSubtitlesBinding? = null
@@ -69,7 +68,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
binding.subtitleTitle.setText(R.string.none)
model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id
- val selSubs: String? = loadData("subLang_${mediaID}", activity)
+ val selSubs = PrefManager.getCustomVal("subLang_${mediaID}", null)
if (episode.selectedSubtitle != null && selSubs != "None") {
binding.root.setCardBackgroundColor(TRANSPARENT)
}
@@ -79,7 +78,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
model.setEpisode(episode, "Subtitle")
model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id
- saveData("subLang_${mediaID}", "None", activity)
+ PrefManager.setCustomVal("subLang_${mediaID}", "None")
}
dismiss()
}
@@ -108,7 +107,8 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
}
model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id
- val selSubs: String? = loadData("subLang_${mediaID}", activity)
+ val selSubs: String? =
+ PrefManager.getCustomVal("subLang_${mediaID}", null)
if (episode.selectedSubtitle != position - 1 && selSubs != subtitles[position - 1].language) {
binding.root.setCardBackgroundColor(TRANSPARENT)
}
@@ -119,7 +119,10 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
model.setEpisode(episode, "Subtitle")
model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id
- saveData("subLang_${mediaID}", subtitles[position - 1].language, activity)
+ PrefManager.setCustomVal(
+ "subLang_${mediaID}",
+ subtitles[position - 1].language
+ )
}
dismiss()
}
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt
index 6d0b36779b..5467676512 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt
@@ -166,7 +166,7 @@ class MangaChapterAdapter(
}, 1000)
} else {
// Show download icon
- binding.itemDownload.setImageResource(R.drawable.ic_circle_add)
+ binding.itemDownload.setImageResource(R.drawable.ic_download_24)
binding.itemDownload.rotation = 0f
}
@@ -261,7 +261,7 @@ class MangaChapterAdapter(
when (holder) {
is ChapterCompactViewHolder -> {
val binding = holder.binding
- setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
+ setAnimation(fragment.requireContext(), holder.binding.root)
val ep = arr[position]
val parsedNumber = MangaNameAdapter.findChapterNumber(ep.number)?.toInt()
binding.itemEpisodeNumber.text = parsedNumber?.toString() ?: ep.number
@@ -287,7 +287,7 @@ class MangaChapterAdapter(
val binding = holder.binding
val ep = arr[position]
holder.bind(ep.number, ep.progress)
- setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
+ setAnimation(fragment.requireContext(), holder.binding.root)
binding.itemChapterNumber.text = ep.number
if (ep.progress.isNullOrEmpty()) {
binding.itemChapterTitle.visibility = View.GONE
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
index da0866b26c..e990f94e1f 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
@@ -2,7 +2,6 @@ package ani.dantotsu.media.manga
import android.annotation.SuppressLint
import android.app.AlertDialog
-import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
@@ -28,6 +27,8 @@ import ani.dantotsu.others.webview.CookieCatcher
import ani.dantotsu.parsers.DynamicMangaParser
import ani.dantotsu.parsers.MangaReadSources
import ani.dantotsu.parsers.MangaSources
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.subcriptions.Notifications.Companion.openSettings
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
import com.google.android.material.chip.Chip
@@ -69,12 +70,9 @@ class MangaReadAdapter(
null
)
}
- val offline = if (!isOnline(binding.root.context) || currContext()?.getSharedPreferences(
- "Dantotsu",
- Context.MODE_PRIVATE
- )
- ?.getBoolean("offlineMode", false) == true
- ) View.GONE else View.VISIBLE
+ val offline =
+ if (!isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode)
+ ) View.GONE else View.VISIBLE
binding.animeSourceNameContainer.visibility = offline
binding.animeSourceSettings.visibility = offline
@@ -163,7 +161,8 @@ class MangaReadAdapter(
var refresh = false
var run = false
var reversed = media.selected!!.recyclerReversed
- var style = media.selected!!.recyclerStyle ?: fragment.uiSettings.mangaDefaultView
+ var style =
+ media.selected!!.recyclerStyle ?: PrefManager.getVal(PrefName.MangaDefaultView)
dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down"
dialogBinding.animeSourceTop.setOnClickListener {
@@ -392,7 +391,8 @@ class MangaReadAdapter(
if (media.manga?.chapters != null) {
val chapters = media.manga.chapters!!.keys.toTypedArray()
val anilistEp = (media.userProgress ?: 0).plus(1)
- val appEp = loadData("${media.id}_current_chp")?.toIntOrNull() ?: 1
+ val appEp = PrefManager.getCustomVal("${media.id}_current_chp", null)
+ ?.toIntOrNull() ?: 1
var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString()
val filteredChapters = chapters.filter { chapterKey ->
val chapter = media.manga.chapters!![chapterKey]!!
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
index 3153bb6a72..e21d7fc799 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
@@ -26,6 +26,7 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.*
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
@@ -42,8 +43,9 @@ import ani.dantotsu.parsers.DynamicMangaParser
import ani.dantotsu.parsers.HMangaSources
import ani.dantotsu.parsers.MangaParser
import ani.dantotsu.parsers.MangaSources
-import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.settings.extensionprefs.MangaSourcePreferencesFragment
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.subcriptions.Notifications
import ani.dantotsu.subcriptions.Notifications.Group.MANGA_GROUP
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
@@ -86,9 +88,6 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
var continueEp: Boolean = false
var loaded = false
- val uiSettings = loadData("ui_settings", toast = false)
- ?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
-
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -139,6 +138,23 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
binding.animeSourceRecycler.layoutManager = gridLayoutManager
+ binding.ScrollTop.setOnClickListener {
+ binding.animeSourceRecycler.scrollToPosition(10)
+ binding.animeSourceRecycler.smoothScrollToPosition(0)
+ }
+ binding.animeSourceRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ super.onScrolled(recyclerView, dx, dy)
+
+ val position = gridLayoutManager.findFirstVisibleItemPosition()
+ if (position > 2) {
+ binding.ScrollTop.translationY = -navBarHeight.toFloat()
+ binding.ScrollTop.visibility = View.VISIBLE
+ } else {
+ binding.ScrollTop.visibility = View.GONE
+ }
+ }
+ })
model.scrolledToTop.observe(viewLifecycleOwner) {
if (it) binding.animeSourceRecycler.scrollToPosition(0)
}
@@ -154,7 +170,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
media.selected = model.loadSelected(media)
subscribed =
- SubscriptionHelper.getSubscriptions(requireContext()).containsKey(media.id)
+ SubscriptionHelper.getSubscriptions().containsKey(media.id)
style = media.selected!!.recyclerStyle
reverse = media.selected!!.recyclerReversed
@@ -165,10 +181,14 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
headerAdapter = MangaReadAdapter(it, this, model.mangaReadSources!!)
headerAdapter.scanlatorSelectionListener = this
chapterAdapter =
- MangaChapterAdapter(style ?: uiSettings.mangaDefaultView, media, this)
+ MangaChapterAdapter(
+ style ?: PrefManager.getVal(PrefName.MangaDefaultView), media, this
+ )
for (download in downloadManager.mangaDownloadedTypes) {
- chapterAdapter.stopDownload(download.chapter)
+ if (download.title == media.mainName()) {
+ chapterAdapter.stopDownload(download.chapter)
+ }
}
binding.animeSourceRecycler.adapter =
@@ -284,7 +304,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
model.mangaReadSources?.get(selected.sourceIndex)?.showUserTextListener = null
selected.sourceIndex = i
selected.server = null
- model.saveSelected(media.id, selected, requireActivity())
+ model.saveSelected(media.id, selected)
media.selected = selected
return model.mangaReadSources?.get(i)!!
}
@@ -292,14 +312,14 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
fun onLangChange(i: Int) {
val selected = model.loadSelected(media)
selected.langIndex = i
- model.saveSelected(media.id, selected, requireActivity())
+ model.saveSelected(media.id, selected)
media.selected = selected
}
fun onScanlatorChange(list: List) {
val selected = model.loadSelected(media)
selected.scanlators = list
- model.saveSelected(media.id, selected, requireActivity())
+ model.saveSelected(media.id, selected)
media.selected = selected
}
@@ -312,7 +332,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
reverse = rev
media.selected!!.recyclerStyle = style
media.selected!!.recyclerReversed = reverse
- model.saveSelected(media.id, media.selected!!, requireActivity())
+ model.saveSelected(media.id, media.selected!!)
reload()
}
@@ -320,7 +340,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
media.selected!!.chip = i
start = s
end = e
- model.saveSelected(media.id, media.selected!!, requireActivity())
+ model.saveSelected(media.id, media.selected!!)
reload()
}
@@ -368,12 +388,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
if (allSettings.size > 1) {
val names =
allSettings.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
- var selectedIndex = 0
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
.setTitle("Select a Source")
- .setSingleChoiceItems(names, selectedIndex) { dialog, which ->
- selectedIndex = which
- selectedSetting = allSettings[selectedIndex]
+ .setSingleChoiceItems(names, -1) { dialog, which ->
+ selectedSetting = allSettings[which]
itemSelected = true
dialog.dismiss()
@@ -420,7 +438,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
model.continueMedia = false
media.manga?.chapters?.get(i)?.let {
media.manga?.selectedChapter = i
- model.saveSelected(media.id, media.selected!!, requireActivity())
+ model.saveSelected(media.id, media.selected!!)
ChapterLoaderDialog.newInstance(it, true)
.show(requireActivity().supportFragmentManager, "dialog")
}
@@ -558,7 +576,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
selected.latest =
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
- model.saveSelected(media.id, selected, requireActivity())
+ model.saveSelected(media.id, selected)
headerAdapter.handleChapters()
chapterAdapter.notifyItemRangeRemoved(0, chapterAdapter.arr.size)
var arr: ArrayList = arrayListOf()
@@ -572,7 +590,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
arr = (arr.reversed() as? ArrayList) ?: arr
}
chapterAdapter.arr = arr
- chapterAdapter.updateType(style ?: uiSettings.mangaDefaultView)
+ chapterAdapter.updateType(style ?: PrefManager.getVal(PrefName.MangaDefaultView))
chapterAdapter.notifyItemRangeInserted(0, arr.size)
}
diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt
index 952db6195c..c313d71dfe 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt
@@ -33,8 +33,7 @@ abstract class BaseImageAdapter(
val activity: MangaReaderActivity,
chapter: MangaChapter
) : RecyclerView.Adapter() {
- val settings = activity.settings.default
- val uiSettings = activity.uiSettings
+ val settings = activity.defaultSettings
val images = chapter.images()
@SuppressLint("ClickableViewAccessibility")
diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt
index 58b48038a8..92beb46926 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt
@@ -14,6 +14,8 @@ import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.settings.CurrentReaderSettings.Directions.LEFT_TO_RIGHT
import ani.dantotsu.settings.CurrentReaderSettings.Directions.RIGHT_TO_LEFT
import ani.dantotsu.settings.CurrentReaderSettings.Layouts.PAGED
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
@@ -83,7 +85,7 @@ open class ImageAdapter(
imageView.minScale = scale
ObjectAnimator.ofFloat(parent, "alpha", 0f, 1f)
- .setDuration((400 * uiSettings.animationSpeed).toLong())
+ .setDuration((400 * PrefManager.getVal(PrefName.AnimationSpeed)).toLong())
.start()
progress.visibility = View.GONE
diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt
index c396088de2..8e59713e89 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt
@@ -28,6 +28,7 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.*
import ani.dantotsu.connections.anilist.Anilist
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.connections.discord.Discord
import ani.dantotsu.connections.discord.DiscordService
import ani.dantotsu.connections.discord.DiscordServiceRunningSingleton
@@ -41,27 +42,29 @@ import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.media.manga.MangaNameAdapter
import ani.dantotsu.others.ImageViewDialog
-import ani.dantotsu.others.LangSet
import ani.dantotsu.parsers.HMangaSources
import ani.dantotsu.parsers.MangaImage
import ani.dantotsu.parsers.MangaSources
+import ani.dantotsu.settings.CurrentReaderSettings
import ani.dantotsu.settings.CurrentReaderSettings.Companion.applyWebtoon
import ani.dantotsu.settings.CurrentReaderSettings.Directions.*
import ani.dantotsu.settings.CurrentReaderSettings.DualPageModes.*
import ani.dantotsu.settings.CurrentReaderSettings.Layouts.*
-import ani.dantotsu.settings.ReaderSettings
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.themes.ThemeManager
import com.alexvasilkov.gestures.views.GestureFrameLayout
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
-import com.google.firebase.crashlytics.ktx.crashlytics
-import com.google.firebase.ktx.Firebase
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.io.ObjectInputStream
+import java.io.ObjectOutputStream
import java.util.*
import kotlin.math.min
import kotlin.properties.Delegates
@@ -74,6 +77,8 @@ class MangaReaderActivity : AppCompatActivity() {
private val model: MediaDetailsViewModel by viewModels()
private val scope = lifecycleScope
+ var defaultSettings = CurrentReaderSettings()
+
private lateinit var media: Media
private lateinit var chapter: MangaChapter
private lateinit var chapters: MutableMap
@@ -84,13 +89,9 @@ class MangaReaderActivity : AppCompatActivity() {
private var isContVisible = false
private var showProgressDialog = true
- //private var progressDialog: AlertDialog.Builder? = null
private var maxChapterPage = 0L
private var currentChapterPage = 0L
- lateinit var settings: ReaderSettings
- lateinit var uiSettings: UserInterfaceSettings
-
private var notchHeight: Int? = null
private var imageAdapter: BaseImageAdapter? = null
@@ -98,10 +99,8 @@ class MangaReaderActivity : AppCompatActivity() {
var sliding = false
var isAnimating = false
- private var rpc: RPC? = null
-
override fun onAttachedToWindow() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !settings.showSystemBars) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !PrefManager.getVal(PrefName.ShowSystemBars)) {
val displayCutout = window.decorView.rootWindowInsets.displayCutout
if (displayCutout != null) {
if (displayCutout.boundingRects.size > 0) {
@@ -123,7 +122,7 @@ class MangaReaderActivity : AppCompatActivity() {
}
private fun hideBars() {
- if (!settings.showSystemBars) hideSystemBars()
+ if (PrefManager.getVal(PrefName.ShowSystemBars)) hideSystemBars()
}
override fun onDestroy() {
@@ -138,28 +137,20 @@ class MangaReaderActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
ThemeManager(this).applyTheme()
binding = ActivityMangaReaderBinding.inflate(layoutInflater)
setContentView(binding.root)
-
binding.mangaReaderBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
+ defaultSettings = loadReaderSettings("reader_settings") ?: defaultSettings
+
onBackPressedDispatcher.addCallback(this) {
progress { finish() }
}
- settings = loadData("reader_settings", this)
- ?: ReaderSettings().apply { saveData("reader_settings", this) }
- uiSettings = loadData("ui_settings", this) ?: UserInterfaceSettings().apply {
- saveData(
- "ui_settings",
- this
- )
- }
- controllerDuration = (uiSettings.animationSpeed * 200).toLong()
+ controllerDuration = (PrefManager.getVal(PrefName.AnimationSpeed) * 200).toLong()
hideBars()
@@ -182,7 +173,7 @@ class MangaReaderActivity : AppCompatActivity() {
binding.mangaReaderSlider.addOnChangeListener { _, value, fromUser ->
if (fromUser) {
sliding = true
- if (settings.default.layout != PAGED)
+ if (defaultSettings.layout != PAGED)
binding.mangaReaderRecycler.scrollToPosition((value.toInt() - 1) / (dualPage { 2 }
?: 1))
else
@@ -205,34 +196,30 @@ class MangaReaderActivity : AppCompatActivity() {
else model.getMedia().value ?: return
model.setMedia(media)
- if (settings.autoDetectWebtoon && media.countryOfOrigin != "JP") applyWebtoon(settings.default)
- settings.default = loadData("${media.id}_current_settings") ?: settings.default
+ if (PrefManager.getVal(PrefName.AutoDetectWebtoon) && media.countryOfOrigin != "JP") applyWebtoon(
+ defaultSettings
+ )
+ defaultSettings = loadReaderSettings("${media.id}_current_settings") ?: defaultSettings
chapters = media.manga?.chapters ?: return
chapter = chapters[media.manga!!.selectedChapter] ?: return
model.mangaReadSources = if (media.isAdult) HMangaSources else MangaSources
- binding.mangaReaderSource.visibility = if (settings.showSource) View.VISIBLE else View.GONE
+ binding.mangaReaderSource.visibility =
+ if (PrefManager.getVal(PrefName.ShowSource)) View.VISIBLE else View.GONE
if (model.mangaReadSources!!.names.isEmpty()) {
//try to reload sources
try {
- if (media.isAdult) {
- val mangaSources = MangaSources
- val scope = lifecycleScope
- scope.launch(Dispatchers.IO) {
- mangaSources.init(Injekt.get().installedExtensionsFlow, this@MangaReaderActivity)
- }
- model.mangaReadSources = mangaSources
- } else {
- val mangaSources = HMangaSources
- val scope = lifecycleScope
- scope.launch(Dispatchers.IO) {
- mangaSources.init(Injekt.get().installedExtensionsFlow)
- }
- model.mangaReadSources = mangaSources
+ val mangaSources = MangaSources
+ val scope = lifecycleScope
+ scope.launch(Dispatchers.IO) {
+ mangaSources.init(
+ Injekt.get().installedExtensionsFlow
+ )
}
+ model.mangaReadSources = mangaSources
} catch (e: Exception) {
- Firebase.crashlytics.recordException(e)
+ Injekt.get().logException(e)
logError(e)
}
}
@@ -255,13 +242,18 @@ class MangaReaderActivity : AppCompatActivity() {
}
showProgressDialog =
- if (settings.askIndividual) loadData("${media.id}_progressDialog")
- ?: true else false
+ if (PrefManager.getVal(PrefName.AskIndividualReader)) PrefManager.getCustomVal(
+ "${media.id}_progressDialog",
+ true
+ ) else false
//Chapter Change
fun change(index: Int) {
mangaCache.clear()
- saveData("${media.id}_${chaptersArr[currentChapterIndex]}", currentChapterPage, this)
+ PrefManager.setCustomVal(
+ "${media.id}_${chaptersArr[currentChapterIndex]}",
+ currentChapterPage
+ )
ChapterLoaderDialog.newInstance(chapters[chaptersArr[index]]!!)
.show(supportFragmentManager, "dialog")
}
@@ -310,7 +302,7 @@ class MangaReaderActivity : AppCompatActivity() {
chapter = chap
media.manga!!.selectedChapter = chapter.number
media.selected = model.loadSelected(media)
- saveData("${media.id}_current_chp", chap.number, this)
+ PrefManager.setCustomVal("${media.id}_current_chp", chap.number)
currentChapterIndex = chaptersArr.indexOf(chap.number)
binding.mangaReaderChapterSelect.setSelection(currentChapterIndex)
binding.mangaReaderNextChap.text =
@@ -319,9 +311,9 @@ class MangaReaderActivity : AppCompatActivity() {
chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: ""
applySettings()
val context = this
- val incognito = context.getSharedPreferences("Dantotsu", 0)
- ?.getBoolean("incognito", false) ?: false
- if (isOnline(context) && Discord.token != null && !incognito) {
+ val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode)
+ val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
+ if ((isOnline(context) && !offline) && Discord.token != null && !incognito) {
lifecycleScope.launch {
val presence = RPC.createPresence(
RPC.Companion.RPCData(
@@ -369,7 +361,7 @@ class MangaReaderActivity : AppCompatActivity() {
private val snapHelper = PagerSnapHelper()
fun dualPage(callback: () -> T): T? {
- return when (settings.default.dualPageMode) {
+ return when (defaultSettings.dualPageMode) {
No -> null
Automatic -> {
val orientation = resources.configuration.orientation
@@ -384,29 +376,29 @@ class MangaReaderActivity : AppCompatActivity() {
@SuppressLint("ClickableViewAccessibility")
fun applySettings() {
- saveData("${media.id}_current_settings", settings.default)
+ saveReaderSettings("${media.id}_current_settings", defaultSettings)
hideBars()
//true colors
SubsamplingScaleImageView.setPreferredBitmapConfig(
- if (settings.default.trueColors) Bitmap.Config.ARGB_8888
+ if (defaultSettings.trueColors) Bitmap.Config.ARGB_8888
else Bitmap.Config.RGB_565
)
//keep screen On
- if (settings.default.keepScreenOn) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ if (defaultSettings.keepScreenOn) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
binding.mangaReaderPager.unregisterOnPageChangeCallback(pageChangeCallback)
- currentChapterPage = loadData("${media.id}_${chapter.number}", this) ?: 1
+ currentChapterPage = PrefManager.getCustomVal("${media.id}_${chapter.number}", 1L)
val chapImages = chapter.images()
maxChapterPage = 0
if (chapImages.isNotEmpty()) {
maxChapterPage = chapImages.size.toLong()
- saveData("${media.id}_${chapter.number}_max", maxChapterPage)
+ PrefManager.setCustomVal("${media.id}_${chapter.number}_max", maxChapterPage)
imageAdapter =
dualPage { DualPageAdapter(this, chapter) } ?: ImageAdapter(this, chapter)
@@ -421,15 +413,15 @@ class MangaReaderActivity : AppCompatActivity() {
binding.mangaReaderSlider.visibility = View.GONE
}
binding.mangaReaderPageNumber.text =
- if (settings.default.hidePageNumbers) "" else "${currentChapterPage}/$maxChapterPage"
+ if (defaultSettings.hidePageNumbers) "" else "${currentChapterPage}/$maxChapterPage"
}
val currentPage = currentChapterPage.toInt()
- if ((settings.default.direction == TOP_TO_BOTTOM || settings.default.direction == BOTTOM_TO_TOP)) {
+ if ((defaultSettings.direction == TOP_TO_BOTTOM || defaultSettings.direction == BOTTOM_TO_TOP)) {
binding.mangaReaderSwipy.vertical = true
- if (settings.default.direction == TOP_TO_BOTTOM) {
+ if (defaultSettings.direction == TOP_TO_BOTTOM) {
binding.BottomSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1)
?: getString(R.string.no_chapter)
binding.TopSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1)
@@ -466,7 +458,7 @@ class MangaReaderActivity : AppCompatActivity() {
}
} else {
binding.mangaReaderSwipy.vertical = false
- if (settings.default.direction == RIGHT_TO_LEFT) {
+ if (defaultSettings.direction == RIGHT_TO_LEFT) {
binding.LeftSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1)
?: getString(R.string.no_chapter)
binding.RightSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1)
@@ -503,11 +495,11 @@ class MangaReaderActivity : AppCompatActivity() {
}
}
- if (settings.default.layout != PAGED) {
+ if (defaultSettings.layout != PAGED) {
binding.mangaReaderRecyclerContainer.visibility = View.VISIBLE
binding.mangaReaderRecyclerContainer.controller.settings.isRotationEnabled =
- settings.default.rotation
+ defaultSettings.rotation
val detector = GestureDetectorCompat(this, object : GesturesListener() {
override fun onLongPress(e: MotionEvent) {
@@ -530,7 +522,7 @@ class MangaReaderActivity : AppCompatActivity() {
val page =
chapter.dualPages().getOrNull(pos) ?: return@dualPage false
val nextPage = page.second
- if (settings.default.direction != LEFT_TO_RIGHT && nextPage != null)
+ if (defaultSettings.direction != LEFT_TO_RIGHT && nextPage != null)
onImageLongClicked(pos * 2, nextPage, page.first, callback)
else
onImageLongClicked(pos * 2, page.first, nextPage, callback)
@@ -552,11 +544,11 @@ class MangaReaderActivity : AppCompatActivity() {
val manager = PreloadLinearLayoutManager(
this,
- if (settings.default.direction == TOP_TO_BOTTOM || settings.default.direction == BOTTOM_TO_TOP)
+ if (defaultSettings.direction == TOP_TO_BOTTOM || defaultSettings.direction == BOTTOM_TO_TOP)
RecyclerView.VERTICAL
else
RecyclerView.HORIZONTAL,
- !(settings.default.direction == TOP_TO_BOTTOM || settings.default.direction == LEFT_TO_RIGHT)
+ !(defaultSettings.direction == TOP_TO_BOTTOM || defaultSettings.direction == LEFT_TO_RIGHT)
)
manager.preloadItemCount = 5
@@ -575,7 +567,7 @@ class MangaReaderActivity : AppCompatActivity() {
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
- settings.default.apply {
+ defaultSettings.apply {
if (
((direction == TOP_TO_BOTTOM || direction == BOTTOM_TO_TOP)
&& (!v.canScrollVertically(-1) || !v.canScrollVertically(1)))
@@ -594,25 +586,25 @@ class MangaReaderActivity : AppCompatActivity() {
super.onScrolled(v, dx, dy)
}
})
- if ((settings.default.direction == TOP_TO_BOTTOM || settings.default.direction == BOTTOM_TO_TOP))
+ if ((defaultSettings.direction == TOP_TO_BOTTOM || defaultSettings.direction == BOTTOM_TO_TOP))
updatePadding(0, 128f.px, 0, 128f.px)
else
updatePadding(128f.px, 0, 128f.px, 0)
snapHelper.attachToRecyclerView(
- if (settings.default.layout == CONTINUOUS_PAGED) this
+ if (defaultSettings.layout == CONTINUOUS_PAGED) this
else null
)
onVolumeUp = {
- if ((settings.default.direction == TOP_TO_BOTTOM || settings.default.direction == BOTTOM_TO_TOP))
+ if ((defaultSettings.direction == TOP_TO_BOTTOM || defaultSettings.direction == BOTTOM_TO_TOP))
smoothScrollBy(0, -500)
else
smoothScrollBy(-500, 0)
}
onVolumeDown = {
- if ((settings.default.direction == TOP_TO_BOTTOM || settings.default.direction == BOTTOM_TO_TOP))
+ if ((defaultSettings.direction == TOP_TO_BOTTOM || defaultSettings.direction == BOTTOM_TO_TOP))
smoothScrollBy(0, 500)
else
smoothScrollBy(500, 0)
@@ -627,11 +619,11 @@ class MangaReaderActivity : AppCompatActivity() {
visibility = View.VISIBLE
adapter = imageAdapter
layoutDirection =
- if (settings.default.direction == BOTTOM_TO_TOP || settings.default.direction == RIGHT_TO_LEFT)
+ if (defaultSettings.direction == BOTTOM_TO_TOP || defaultSettings.direction == RIGHT_TO_LEFT)
View.LAYOUT_DIRECTION_RTL
else View.LAYOUT_DIRECTION_LTR
orientation =
- if (settings.default.direction == LEFT_TO_RIGHT || settings.default.direction == RIGHT_TO_LEFT)
+ if (defaultSettings.direction == LEFT_TO_RIGHT || defaultSettings.direction == RIGHT_TO_LEFT)
ViewPager2.ORIENTATION_HORIZONTAL
else ViewPager2.ORIENTATION_VERTICAL
registerOnPageChangeCallback(pageChangeCallback)
@@ -654,7 +646,7 @@ class MangaReaderActivity : AppCompatActivity() {
return when (event.keyCode) {
KEYCODE_VOLUME_UP, KEYCODE_DPAD_UP, KEYCODE_PAGE_UP -> {
if (event.keyCode == KEYCODE_VOLUME_UP)
- if (!settings.default.volumeButtons)
+ if (!defaultSettings.volumeButtons)
return false
if (event.action == ACTION_DOWN) {
onVolumeUp?.invoke()
@@ -664,7 +656,7 @@ class MangaReaderActivity : AppCompatActivity() {
KEYCODE_VOLUME_DOWN, KEYCODE_DPAD_DOWN, KEYCODE_PAGE_DOWN -> {
if (event.keyCode == KEYCODE_VOLUME_DOWN)
- if (!settings.default.volumeButtons)
+ if (!defaultSettings.volumeButtons)
return false
if (event.action == ACTION_DOWN) {
onVolumeDown?.invoke()
@@ -711,14 +703,14 @@ class MangaReaderActivity : AppCompatActivity() {
fun handleController(shouldShow: Boolean? = null, event: MotionEvent? = null) {
var pressLocation = pressPos.CENTER
if (!sliding) {
- if (event != null && settings.default.layout == PAGED) {
+ if (event != null && defaultSettings.layout == PAGED) {
if (event.action != MotionEvent.ACTION_UP) return
val x = event.rawX.toInt()
val y = event.rawY.toInt()
val screenWidth = Resources.getSystem().displayMetrics.widthPixels
//if in the 1st 1/5th of the screen width, left and lower than 1/5th of the screen height, left
- if (screenWidth / 5 in (x + 1).. screenWidth - screenWidth / 5 && y > screenWidth / 5) {
- pressLocation = if (settings.default.direction == RIGHT_TO_LEFT) {
+ pressLocation = if (defaultSettings.direction == RIGHT_TO_LEFT) {
pressPos.LEFT
} else {
pressPos.RIGHT
@@ -758,12 +750,12 @@ class MangaReaderActivity : AppCompatActivity() {
}
}
- if (!settings.showSystemBars) {
+ if (!PrefManager.getVal(PrefName.ShowSystemBars)) {
hideBars()
checkNotch()
}
//horizontal scrollbar
- if (settings.default.horizontalScrollBar) {
+ if (defaultSettings.horizontalScrollBar) {
binding.mangaReaderSliderContainer.updateLayoutParams {
height = ViewGroup.LayoutParams.WRAP_CONTENT
width = ViewGroup.LayoutParams.WRAP_CONTENT
@@ -790,7 +782,7 @@ class MangaReaderActivity : AppCompatActivity() {
}
}
binding.mangaReaderSlider.layoutDirection =
- if (settings.default.direction == RIGHT_TO_LEFT || settings.default.direction == BOTTOM_TO_TOP)
+ if (defaultSettings.direction == RIGHT_TO_LEFT || defaultSettings.direction == BOTTOM_TO_TOP)
View.LAYOUT_DIRECTION_RTL
else View.LAYOUT_DIRECTION_LTR
shouldShow?.apply { isContVisible = !this }
@@ -828,9 +820,9 @@ class MangaReaderActivity : AppCompatActivity() {
fun updatePageNumber(page: Long) {
if (currentChapterPage != page) {
currentChapterPage = page
- saveData("${media.id}_${chapter.number}", page, this)
+ PrefManager.setCustomVal("${media.id}_${chapter.number}", page)
binding.mangaReaderPageNumber.text =
- if (settings.default.hidePageNumbers) "" else "${currentChapterPage}/$maxChapterPage"
+ if (defaultSettings.hidePageNumbers) "" else "${currentChapterPage}/$maxChapterPage"
if (!sliding) binding.mangaReaderSlider.apply {
value = clamp(currentChapterPage.toFloat(), 1f, valueTo)
}
@@ -851,31 +843,27 @@ class MangaReaderActivity : AppCompatActivity() {
private fun progress(runnable: Runnable) {
if (maxChapterPage - currentChapterPage <= 1 && Anilist.userid != null) {
showProgressDialog =
- if (settings.askIndividual) loadData("${media.id}_progressDialog")
- ?: true else false
- if (showProgressDialog) {
+ if (PrefManager.getVal(PrefName.AskIndividualReader)) PrefManager.getCustomVal(
+ "${media.id}_progressDialog",
+ true
+ )
+ else false
+ val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
+ if (showProgressDialog && !incognito) {
val dialogView = layoutInflater.inflate(R.layout.item_custom_dialog, null)
val checkbox = dialogView.findViewById(R.id.dialog_checkbox)
checkbox.text = getString(R.string.dont_ask_again, media.userPreferredName)
checkbox.setOnCheckedChangeListener { _, isChecked ->
- saveData("${media.id}_progressDialog", !isChecked)
+ PrefManager.setCustomVal("${media.id}_progressDialog", !isChecked)
showProgressDialog = !isChecked
}
- val incognito =
- currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getBoolean("incognito", false) ?: false
AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.title_update_progress))
- .apply {
- if (incognito) {
- setMessage(getString(R.string.incognito_will_not_update))
- }
- }
.setView(dialogView)
.setCancelable(false)
.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
- saveData("${media.id}_save_progress", true)
+ PrefManager.setCustomVal("${media.id}_save_progress", true)
updateProgress(
media,
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
@@ -885,7 +873,7 @@ class MangaReaderActivity : AppCompatActivity() {
runnable.run()
}
.setNegativeButton(getString(R.string.no)) { dialog, _ ->
- saveData("${media.id}_save_progress", false)
+ PrefManager.setCustomVal("${media.id}_save_progress", false)
dialog.dismiss()
runnable.run()
}
@@ -893,7 +881,11 @@ class MangaReaderActivity : AppCompatActivity() {
.create()
.show()
} else {
- if (loadData("${media.id}_save_progress") != false && if (media.isAdult) settings.updateForH else true)
+ if (!incognito && PrefManager.getCustomVal(
+ "${media.id}_save_progress",
+ true
+ ) && if (media.isAdult) PrefManager.getVal(PrefName.UpdateForHReader) else true
+ )
updateProgress(
media,
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
@@ -906,6 +898,51 @@ class MangaReaderActivity : AppCompatActivity() {
}
}
+
+ @Suppress("UNCHECKED_CAST")
+ private fun loadReaderSettings(
+ fileName: String,
+ context: Context? = null,
+ toast: Boolean = true
+ ): T? {
+ val a = context ?: currContext()
+ try {
+ if (a?.fileList() != null)
+ if (fileName in a.fileList()) {
+ val fileIS: FileInputStream = a.openFileInput(fileName)
+ val objIS = ObjectInputStream(fileIS)
+ val data = objIS.readObject() as T
+ objIS.close()
+ fileIS.close()
+ return data
+ }
+ } catch (e: Exception) {
+ if (toast) snackString(a?.getString(R.string.error_loading_data, fileName))
+ //try to delete the file
+ try {
+ a?.deleteFile(fileName)
+ } catch (e: Exception) {
+ Injekt.get().log("Failed to delete file $fileName")
+ Injekt.get().logException(e)
+ }
+ e.printStackTrace()
+ }
+ return null
+ }
+
+ private fun saveReaderSettings(fileName: String, data: Any?, context: Context? = null) {
+ tryWith {
+ val a = context ?: currContext()
+ if (a != null) {
+ val fos: FileOutputStream = a.openFileOutput(fileName, Context.MODE_PRIVATE)
+ val os = ObjectOutputStream(fos)
+ os.writeObject(data)
+ os.close()
+ fos.close()
+ }
+ }
+ }
+
fun getTransformation(mangaImage: MangaImage): BitmapTransformation? {
return model.loadTransformation(mangaImage, media.selected!!.sourceIndex)
}
@@ -916,7 +953,7 @@ class MangaReaderActivity : AppCompatActivity() {
img2: MangaImage?,
callback: ((ImageViewDialog) -> Unit)? = null
): Boolean {
- if (!settings.default.longClickImage) return false
+ if (!defaultSettings.longClickImage) return false
val title = "(Page ${pos + 1}${if (img2 != null) "-${pos + 2}" else ""}) ${
chaptersTitleArr.getOrNull(currentChapterIndex)?.replace(" : ", " - ") ?: ""
} [${media.userPreferredName}]"
@@ -930,8 +967,8 @@ class MangaReaderActivity : AppCompatActivity() {
val parserTransformation2 = getTransformation(img2)
if (parserTransformation2 != null) transforms2.add(parserTransformation2)
}
- val threshold = settings.default.cropBorderThreshold
- if (settings.default.cropBorders) {
+ val threshold = defaultSettings.cropBorderThreshold
+ if (defaultSettings.cropBorders) {
transforms1.add(RemoveBordersTransformation(true, threshold))
transforms1.add(RemoveBordersTransformation(false, threshold))
if (img2 != null) {
diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ReaderSettingsDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ReaderSettingsDialogFragment.kt
index fc399e19ce..c9c42aab02 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ReaderSettingsDialogFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ReaderSettingsDialogFragment.kt
@@ -26,7 +26,7 @@ class ReaderSettingsDialogFragment : BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val activity = requireActivity() as MangaReaderActivity
- val settings = activity.settings.default
+ val settings = activity.defaultSettings
binding.readerDirectionText.text =
resources.getStringArray(R.array.manga_directions)[settings.direction.ordinal]
diff --git a/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt b/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt
index 0c654c16c4..369f446e7d 100644
--- a/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt
@@ -26,14 +26,11 @@ import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.novel.NovelDownloaderService
import ani.dantotsu.download.novel.NovelServiceDataSingleton
-import ani.dantotsu.loadData
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.novel.novelreader.NovelReaderActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.parsers.ShowResponse
-import ani.dantotsu.saveData
-import ani.dantotsu.settings.UserInterfaceSettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -61,9 +58,6 @@ class NovelReadFragment : Fragment(),
private var continueEp: Boolean = false
var loaded = false
- val uiSettings = loadData("ui_settings", toast = false)
- ?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
-
override fun downloadTrigger(novelDownloadPackage: NovelDownloadPackage) {
Log.e("downloadTrigger", novelDownloadPackage.link)
val downloadTask = NovelDownloaderService.DownloadTask(
@@ -253,7 +247,7 @@ class NovelReadFragment : Fragment(),
if (save) {
val selected = model.loadSelected(media)
selected.server = query
- model.saveSelected(media.id, selected, requireActivity())
+ model.saveSelected(media.id, selected)
}
}
}
@@ -263,7 +257,7 @@ class NovelReadFragment : Fragment(),
selected.sourceIndex = i
source = i
selected.server = null
- model.saveSelected(media.id, selected, requireActivity())
+ model.saveSelected(media.id, selected)
media.selected = selected
}
diff --git a/app/src/main/java/ani/dantotsu/media/novel/NovelResponseAdapter.kt b/app/src/main/java/ani/dantotsu/media/novel/NovelResponseAdapter.kt
index 6d57037456..0ab80e14cb 100644
--- a/app/src/main/java/ani/dantotsu/media/novel/NovelResponseAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/novel/NovelResponseAdapter.kt
@@ -35,7 +35,7 @@ class NovelResponseAdapter(
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val binding = holder.binding
val novel = list[position]
- setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
+ setAnimation(fragment.requireContext(), holder.binding.root)
val cover = GlideUrl(novel.coverUrl.url) { novel.coverUrl.headers }
Glide.with(binding.itemEpisodeImage).load(cover).override(400, 0)
@@ -104,7 +104,7 @@ class NovelResponseAdapter(
binding.root.setOnLongClickListener {
val builder = androidx.appcompat.app.AlertDialog.Builder(
fragment.requireContext(),
- R.style.DialogTheme
+ R.style.MyPopup
)
builder.setTitle("Delete ${novel.name}?")
builder.setMessage("Are you sure you want to delete ${novel.name}?")
diff --git a/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderActivity.kt b/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderActivity.kt
index b62032fae1..b77cdc7783 100644
--- a/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderActivity.kt
@@ -2,6 +2,7 @@ package ani.dantotsu.media.novel.novelreader
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
+import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.graphics.Color
@@ -28,17 +29,16 @@ import androidx.webkit.WebViewCompat
import ani.dantotsu.GesturesListener
import ani.dantotsu.NoPaddingArrayAdapter
import ani.dantotsu.R
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
+import ani.dantotsu.currContext
import ani.dantotsu.databinding.ActivityNovelReaderBinding
import ani.dantotsu.hideSystemBars
-import ani.dantotsu.loadData
import ani.dantotsu.others.ImageViewDialog
-import ani.dantotsu.others.LangSet
-import ani.dantotsu.saveData
import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.settings.CurrentNovelReaderSettings
import ani.dantotsu.settings.CurrentReaderSettings
-import ani.dantotsu.settings.ReaderSettings
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.tryWith
@@ -52,8 +52,13 @@ import com.vipulog.ebookreader.RelocationInfo
import com.vipulog.ebookreader.TocItem
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
import java.io.File
+import java.io.FileInputStream
import java.io.FileOutputStream
+import java.io.ObjectInputStream
+import java.io.ObjectOutputStream
import java.util.*
import kotlin.math.min
import kotlin.properties.Delegates
@@ -63,9 +68,6 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
private lateinit var binding: ActivityNovelReaderBinding
private val scope = lifecycleScope
- lateinit var settings: ReaderSettings
- private lateinit var uiSettings: UserInterfaceSettings
-
private var notchHeight: Int? = null
var loaded = false
@@ -78,6 +80,8 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
val themes = ArrayList()
+ var defaultSettings = CurrentNovelReaderSettings()
+
init {
val forestTheme = ReaderTheme(
@@ -171,16 +175,12 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
return
}
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
binding = ActivityNovelReaderBinding.inflate(layoutInflater)
setContentView(binding.root)
- settings = loadData("reader_settings", this)
- ?: ReaderSettings().apply { saveData("reader_settings", this) }
- uiSettings = loadData("ui_settings", this)
- ?: UserInterfaceSettings().also { saveData("ui_settings", it) }
- controllerDuration = (uiSettings.animationSpeed * 200).toLong()
+ controllerDuration = (PrefManager.getVal(PrefName.AnimationSpeed) * 200).toLong()
setupViews()
setupBackPressedHandler()
@@ -286,12 +286,12 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
binding.bookReader.getAppearance {
currentTheme = it
themes.add(0, it)
- settings.defaultLN =
- loadData("${sanitizedBookId}_current_settings") ?: settings.defaultLN
+ defaultSettings =
+ loadReaderSettings("${sanitizedBookId}_current_settings") ?: defaultSettings
applySettings()
}
- val cfi = loadData("${sanitizedBookId}_progress")
+ val cfi = PrefManager.getCustomVal("${sanitizedBookId}_progress", null as String?)
cfi?.let { binding.bookReader.goto(it) }
binding.progress.visibility = View.GONE
@@ -304,7 +304,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
binding.novelReaderSlider.value = info.fraction.toFloat()
val pos = info.tocItem?.let { item -> toc.indexOfFirst { it == item } }
if (pos != null) binding.novelReaderChapterSelect.setSelection(pos)
- saveData("${sanitizedBookId}_progress", info.cfi)
+ PrefManager.setCustomVal("${sanitizedBookId}_progress", info.cfi)
}
@@ -339,7 +339,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
return when (event.keyCode) {
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_PAGE_UP -> {
if (event.keyCode == KeyEvent.KEYCODE_VOLUME_UP)
- if (!settings.defaultLN.volumeButtons)
+ if (!defaultSettings.volumeButtons)
return false
if (event.action == KeyEvent.ACTION_DOWN) {
onVolumeUp?.invoke()
@@ -349,7 +349,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_PAGE_DOWN -> {
if (event.keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
- if (!settings.defaultLN.volumeButtons)
+ if (!defaultSettings.volumeButtons)
return false
if (event.action == KeyEvent.ACTION_DOWN) {
onVolumeDown?.invoke()
@@ -365,18 +365,18 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
fun applySettings() {
- saveData("${sanitizedBookId}_current_settings", settings.defaultLN)
+ saveReaderSettings("${sanitizedBookId}_current_settings", defaultSettings)
hideBars()
- if (settings.defaultLN.useOledTheme) {
+ if (defaultSettings.useOledTheme) {
themes.forEach { theme ->
theme.darkBg = Color.parseColor("#000000")
}
}
currentTheme =
- themes.first { it.name.equals(settings.defaultLN.currentThemeName, ignoreCase = true) }
+ themes.first { it.name.equals(defaultSettings.currentThemeName, ignoreCase = true) }
- when (settings.defaultLN.layout) {
+ when (defaultSettings.layout) {
CurrentNovelReaderSettings.Layouts.PAGED -> {
currentTheme?.flow = ReaderFlow.PAGINATED
}
@@ -387,22 +387,22 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
}
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
- when (settings.defaultLN.dualPageMode) {
+ when (defaultSettings.dualPageMode) {
CurrentReaderSettings.DualPageModes.No -> currentTheme?.maxColumnCount = 1
CurrentReaderSettings.DualPageModes.Automatic -> currentTheme?.maxColumnCount = 2
CurrentReaderSettings.DualPageModes.Force -> requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
}
- currentTheme?.lineHeight = settings.defaultLN.lineHeight
- currentTheme?.gap = settings.defaultLN.margin
- currentTheme?.maxInlineSize = settings.defaultLN.maxInlineSize
- currentTheme?.maxBlockSize = settings.defaultLN.maxBlockSize
- currentTheme?.useDark = settings.defaultLN.useDarkTheme
+ currentTheme?.lineHeight = defaultSettings.lineHeight
+ currentTheme?.gap = defaultSettings.margin
+ currentTheme?.maxInlineSize = defaultSettings.maxInlineSize
+ currentTheme?.maxBlockSize = defaultSettings.maxBlockSize
+ currentTheme?.useDark = defaultSettings.useDarkTheme
currentTheme?.let { binding.bookReader.setAppearance(it) }
- if (settings.defaultLN.keepScreenOn) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ if (defaultSettings.keepScreenOn) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
@@ -432,7 +432,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
fun handleController(shouldShow: Boolean? = null) {
if (!loaded) return
- if (!settings.showSystemBars) {
+ if (!PrefManager.getVal(PrefName.ShowSystemBars)) {
hideBars()
applyNotchMargin()
}
@@ -465,7 +465,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
private fun checkNotch() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !settings.showSystemBars) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !PrefManager.getVal(PrefName.ShowSystemBars)) {
val displayCutout = window.decorView.rootWindowInsets.displayCutout
if (displayCutout != null) {
if (displayCutout.boundingRects.size > 0) {
@@ -486,8 +486,53 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
}
}
+ @Suppress("UNCHECKED_CAST")
+ private fun loadReaderSettings(
+ fileName: String,
+ context: Context? = null,
+ toast: Boolean = true
+ ): T? {
+ val a = context ?: currContext()
+ try {
+ if (a?.fileList() != null)
+ if (fileName in a.fileList()) {
+ val fileIS: FileInputStream = a.openFileInput(fileName)
+ val objIS = ObjectInputStream(fileIS)
+ val data = objIS.readObject() as T
+ objIS.close()
+ fileIS.close()
+ return data
+ }
+ } catch (e: Exception) {
+ if (toast) snackString(a?.getString(R.string.error_loading_data, fileName))
+ //try to delete the file
+ try {
+ a?.deleteFile(fileName)
+ } catch (e: Exception) {
+ Injekt.get().log("Failed to delete file $fileName")
+ Injekt.get().logException(e)
+ }
+ e.printStackTrace()
+ }
+ return null
+ }
+
+ private fun saveReaderSettings(fileName: String, data: Any?, context: Context? = null) {
+ tryWith {
+ val a = context ?: currContext()
+ if (a != null) {
+ val fos: FileOutputStream = a.openFileOutput(fileName, Context.MODE_PRIVATE)
+ val os = ObjectOutputStream(fos)
+ os.writeObject(data)
+ os.close()
+ fos.close()
+ }
+ }
+ }
private fun hideBars() {
- if (!settings.showSystemBars) hideSystemBars()
+ if (!PrefManager.getVal(PrefName.ShowSystemBars)) {
+ hideSystemBars()
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderSettingsDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderSettingsDialogFragment.kt
index 760c7eb3d8..67fc2ff3c2 100644
--- a/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderSettingsDialogFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderSettingsDialogFragment.kt
@@ -30,7 +30,7 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val activity = requireActivity() as NovelReaderActivity
- val settings = activity.settings.defaultLN
+ val settings = activity.defaultSettings
val themeLabels = activity.themes.map { it.name }
binding.themeSelect.adapter =
NoPaddingArrayAdapter(activity, R.layout.item_dropdown, themeLabels)
diff --git a/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt b/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt
index a6d044e18f..7db5f057bd 100644
--- a/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt
@@ -1,7 +1,6 @@
package ani.dantotsu.media.user
import android.annotation.SuppressLint
-import android.content.Context
import android.os.Bundle
import android.util.TypedValue
import android.view.View
@@ -17,12 +16,10 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import ani.dantotsu.R
import ani.dantotsu.Refresh
-import ani.dantotsu.currContext
import ani.dantotsu.databinding.ActivityListBinding
-import ani.dantotsu.loadData
import ani.dantotsu.navBarHeight
-import ani.dantotsu.others.LangSet
-import ani.dantotsu.settings.UserInterfaceSettings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import com.google.android.material.tabs.TabLayout
@@ -39,7 +36,7 @@ class ListActivity : AppCompatActivity() {
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
ThemeManager(this).applyTheme()
binding = ActivityListBinding.inflate(layoutInflater)
@@ -67,8 +64,7 @@ class ListActivity : AppCompatActivity() {
binding.listTitle.setTextColor(primaryTextColor)
binding.listTabLayout.setTabTextColors(secondaryTextColor, primaryTextColor)
binding.listTabLayout.setSelectedTabIndicatorColor(primaryTextColor)
- val uiSettings = loadData("ui_settings") ?: UserInterfaceSettings()
- if (!uiSettings.immersiveMode) {
+ if (!PrefManager.getVal(PrefName.ImmersiveMode)) {
this.window.statusBarColor =
ContextCompat.getColor(this, R.color.nav_bg_inv)
binding.root.fitsSystemWindows = true
@@ -154,8 +150,10 @@ class ListActivity : AppCompatActivity() {
R.id.release -> "release"
else -> null
}
- currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
- ?.putString("sort_order", sort)?.apply()
+ PrefManager.setVal(
+ if (anime) PrefName.AnimeListSortOrder else PrefName.MangaListSortOrder,
+ sort ?: ""
+ )
binding.listProgressBar.visibility = View.VISIBLE
binding.listViewPager.adapter = null
scope.launch {
diff --git a/app/src/main/java/ani/dantotsu/media/user/ListViewModel.kt b/app/src/main/java/ani/dantotsu/media/user/ListViewModel.kt
index 87071791f3..06fd29a9fc 100644
--- a/app/src/main/java/ani/dantotsu/media/user/ListViewModel.kt
+++ b/app/src/main/java/ani/dantotsu/media/user/ListViewModel.kt
@@ -4,12 +4,13 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import ani.dantotsu.connections.anilist.Anilist
-import ani.dantotsu.loadData
import ani.dantotsu.media.Media
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.tryWithSuspend
class ListViewModel : ViewModel() {
- var grid = MutableLiveData(loadData("listGrid") ?: true)
+ var grid = MutableLiveData(PrefManager.getVal(PrefName.ListGrid))
private val lists = MutableLiveData>>()
fun getLists(): LiveData>> = lists
diff --git a/app/src/main/java/ani/dantotsu/offline/OfflineFragment.kt b/app/src/main/java/ani/dantotsu/offline/OfflineFragment.kt
index 02779ab28d..5807cb4886 100644
--- a/app/src/main/java/ani/dantotsu/offline/OfflineFragment.kt
+++ b/app/src/main/java/ani/dantotsu/offline/OfflineFragment.kt
@@ -1,6 +1,5 @@
package ani.dantotsu.offline
-import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -11,6 +10,8 @@ import ani.dantotsu.R
import ani.dantotsu.databinding.FragmentOfflineBinding
import ani.dantotsu.isOnline
import ani.dantotsu.navBarHeight
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.startMainActivity
import ani.dantotsu.statusBarHeight
@@ -26,8 +27,7 @@ class OfflineFragment : Fragment() {
topMargin = statusBarHeight
bottomMargin = navBarHeight
}
- offline = requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getBoolean("offlineMode", false) ?: false
+ offline = PrefManager.getVal(PrefName.OfflineMode)
binding.noInternet.text =
if (offline) "Offline Mode" else getString(R.string.no_internet)
binding.refreshButton.visibility = if (offline) View.GONE else View.VISIBLE
@@ -41,7 +41,6 @@ class OfflineFragment : Fragment() {
override fun onResume() {
super.onResume()
- offline = requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getBoolean("offlineMode", false) ?: false
+ offline = PrefManager.getVal(PrefName.OfflineMode)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/others/CustomBottomDialog.kt b/app/src/main/java/ani/dantotsu/others/CustomBottomDialog.kt
index d95d56cb41..d5e794bd65 100644
--- a/app/src/main/java/ani/dantotsu/others/CustomBottomDialog.kt
+++ b/app/src/main/java/ani/dantotsu/others/CustomBottomDialog.kt
@@ -2,12 +2,10 @@ package ani.dantotsu.others
import android.graphics.Color
import android.os.Bundle
-import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import ani.dantotsu.BottomSheetDialogFragment
-import ani.dantotsu.R
import ani.dantotsu.databinding.BottomSheetCustomBinding
open class CustomBottomDialog : BottomSheetDialogFragment() {
diff --git a/app/src/main/java/ani/dantotsu/others/DisabledReports.kt b/app/src/main/java/ani/dantotsu/others/DisabledReports.kt
index ed8f5813f9..c25956f07f 100644
--- a/app/src/main/java/ani/dantotsu/others/DisabledReports.kt
+++ b/app/src/main/java/ani/dantotsu/others/DisabledReports.kt
@@ -1,5 +1,5 @@
package ani.dantotsu.others
const val DisabledReports = false
-//Setting this to false, will allow sending crash reports to Dantotsu's Firebase Crashlytics
+//Setting this to false, will allow sending crash reports to Dantotsu's Firebase CrashlyticsInterface
//if you want a custom build without crash reporting, set this to true
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/others/Download.kt b/app/src/main/java/ani/dantotsu/others/Download.kt
index 8a8b496ab9..3a266af44f 100644
--- a/app/src/main/java/ani/dantotsu/others/Download.kt
+++ b/app/src/main/java/ani/dantotsu/others/Download.kt
@@ -14,9 +14,10 @@ import ani.dantotsu.FileUrl
import ani.dantotsu.R
import ani.dantotsu.currContext
import ani.dantotsu.defaultHeaders
-import ani.dantotsu.loadData
import ani.dantotsu.media.anime.Episode
import ani.dantotsu.parsers.Book
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -36,7 +37,7 @@ object Download {
private fun getDownloadDir(context: Context): File {
val direct: File
- if (loadData("sd_dl") == true) {
+ if (PrefManager.getVal(PrefName.SdDl)) {
val arrayOfFiles = ContextCompat.getExternalFilesDirs(context, null)
val parentDirectory = arrayOfFiles[1].toString()
direct = File(parentDirectory)
@@ -92,7 +93,7 @@ object Download {
if (!file.url.startsWith("http"))
toast(context.getString(R.string.invalid_url))
else
- when (loadData("settings_download_manager", context, false) ?: 0) {
+ when (PrefManager.getVal(PrefName.DownloadManager) as Int) {
1 -> oneDM(context, file, notif ?: fileName)
2 -> adm(context, file, fileName, folder)
else -> defaultDownload(context, file, fileName, folder, notif ?: fileName)
@@ -117,7 +118,7 @@ object Download {
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
val arrayOfFiles = ContextCompat.getExternalFilesDirs(context, null)
- if (loadData("sd_dl") == true && arrayOfFiles.size > 1 && arrayOfFiles[0] != null && arrayOfFiles[1] != null) {
+ if (PrefManager.getVal(PrefName.SdDl) && arrayOfFiles.size > 1 && arrayOfFiles[0] != null && arrayOfFiles[1] != null) {
val parentDirectory = arrayOfFiles[1].toString() + folder
val direct = File(parentDirectory)
if (!direct.exists()) direct.mkdirs()
diff --git a/app/src/main/java/ani/dantotsu/others/ImageViewDialog.kt b/app/src/main/java/ani/dantotsu/others/ImageViewDialog.kt
index aa792d9ffb..e11715c739 100644
--- a/app/src/main/java/ani/dantotsu/others/ImageViewDialog.kt
+++ b/app/src/main/java/ani/dantotsu/others/ImageViewDialog.kt
@@ -5,12 +5,14 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import ani.dantotsu.BottomSheetDialogFragment
import ani.dantotsu.FileUrl
import ani.dantotsu.R
import ani.dantotsu.databinding.BottomSheetImageBinding
+import ani.dantotsu.downloadsPermission
import ani.dantotsu.media.manga.mangareader.BaseImageAdapter.Companion.loadBitmap
import ani.dantotsu.media.manga.mangareader.BaseImageAdapter.Companion.loadBitmap_old
import ani.dantotsu.media.manga.mangareader.BaseImageAdapter.Companion.mergeBitmap
@@ -98,7 +100,8 @@ class ImageViewDialog : BottomSheetDialogFragment() {
binding.bottomImageShare.isEnabled = true
binding.bottomImageSave.isEnabled = true
binding.bottomImageSave.setOnClickListener {
- saveImageToDownloads(title, bitmap, requireActivity())
+ if (downloadsPermission(context as AppCompatActivity))
+ saveImageToDownloads(title, bitmap, requireActivity())
}
binding.bottomImageShare.setOnClickListener {
shareImage(title, bitmap, requireContext())
diff --git a/app/src/main/java/ani/dantotsu/others/Kitsu.kt b/app/src/main/java/ani/dantotsu/others/Kitsu.kt
index bee4beeea7..374f0dd0eb 100644
--- a/app/src/main/java/ani/dantotsu/others/Kitsu.kt
+++ b/app/src/main/java/ani/dantotsu/others/Kitsu.kt
@@ -96,6 +96,7 @@ query {
}
}
}
+
else -> {
res?.body?.string()
}
diff --git a/app/src/main/java/ani/dantotsu/others/LangSet.kt b/app/src/main/java/ani/dantotsu/others/LangSet.kt
deleted file mode 100644
index 3e7857dade..0000000000
--- a/app/src/main/java/ani/dantotsu/others/LangSet.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ani.dantotsu.others
-
-import android.app.Activity
-import android.content.res.Configuration
-import android.content.res.Resources
-import java.util.Locale
-
-
-class LangSet {
- companion object {
- fun setLocale(activity: Activity) {
- val useCursedLang = activity.getSharedPreferences("Dantotsu", Activity.MODE_PRIVATE)
- .getBoolean("use_cursed_lang", false)
- val locale = if (useCursedLang) Locale("en", "DW") else Locale("en", "US")
- Locale.setDefault(locale)
- val resources: Resources = activity.resources
- val config: Configuration = resources.configuration
- config.setLocale(locale)
- resources.updateConfiguration(config, resources.displayMetrics)
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/others/MalScraper.kt b/app/src/main/java/ani/dantotsu/others/MalScraper.kt
index 648f3a4a31..9ae38ccfe2 100644
--- a/app/src/main/java/ani/dantotsu/others/MalScraper.kt
+++ b/app/src/main/java/ani/dantotsu/others/MalScraper.kt
@@ -1,11 +1,7 @@
package ani.dantotsu.others
-import ani.dantotsu.R
import ani.dantotsu.client
-import ani.dantotsu.currContext
import ani.dantotsu.media.Media
-import ani.dantotsu.snackString
-import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.withTimeout
object MalScraper {
@@ -50,7 +46,7 @@ object MalScraper {
}
}
} catch (e: Exception) {
- // if (e is TimeoutCancellationException) snackString(currContext()?.getString(R.string.error_loading_mal_data))
+ // if (e is TimeoutCancellationException) snackString(currContext()?.getString(R.string.error_loading_mal_data))
}
}
}
diff --git a/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchActivity.kt b/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchActivity.kt
index 282c17fc40..4f70998af7 100644
--- a/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchActivity.kt
+++ b/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchActivity.kt
@@ -19,7 +19,6 @@ import ani.dantotsu.databinding.ActivityImageSearchBinding
import ani.dantotsu.initActivity
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight
-import ani.dantotsu.others.LangSet
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.toast
import kotlinx.coroutines.Dispatchers
@@ -52,7 +51,7 @@ class ImageSearchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- LangSet.setLocale(this)
+
initActivity(this)
ThemeManager(this).applyTheme()
binding = ActivityImageSearchBinding.inflate(layoutInflater)
diff --git a/app/src/main/java/ani/dantotsu/parsers/AnimeParser.kt b/app/src/main/java/ani/dantotsu/parsers/AnimeParser.kt
index 0687b69f7c..8914c44038 100644
--- a/app/src/main/java/ani/dantotsu/parsers/AnimeParser.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/AnimeParser.kt
@@ -5,13 +5,11 @@ import ani.dantotsu.FileUrl
import ani.dantotsu.R
import ani.dantotsu.asyncMap
import ani.dantotsu.currContext
-import ani.dantotsu.loadData
import ani.dantotsu.others.MalSyncBackup
-import ani.dantotsu.saveData
+import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.tryWithSuspend
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
-import kotlin.properties.Delegates
/**
* An abstract class for creating a new Source
@@ -163,7 +161,7 @@ abstract class AnimeParser : BaseParser() {
*
* **NOTE : do not forget to override `search` if the site does not support only dub search**
* **/
- open val isDubAvailableSeparately by Delegates.notNull()
+ open fun isDubAvailableSeparately(sourceLang: Int? = null): Boolean = false
/**
* The app changes this, depending on user's choice.
@@ -182,8 +180,12 @@ abstract class AnimeParser : BaseParser() {
* **/
override suspend fun loadSavedShowResponse(mediaId: Int): ShowResponse? {
checkIfVariablesAreEmpty()
- val dub = if (isDubAvailableSeparately) "_${if (selectDub) "dub" else "sub"}" else ""
- var loaded = loadData("${saveName}${dub}_$mediaId")
+ val dub = if (isDubAvailableSeparately()) "_${if (selectDub) "dub" else "sub"}" else ""
+ var loaded = PrefManager.getNullableCustomVal(
+ "${saveName}${dub}_$mediaId",
+ null,
+ ShowResponse::class.java
+ )
if (loaded == null && malSyncBackupName.isNotEmpty())
loaded = MalSyncBackup.get(mediaId, malSyncBackupName, selectDub)
?.also { saveShowResponse(mediaId, it, true) }
@@ -200,8 +202,8 @@ abstract class AnimeParser : BaseParser() {
)
} : ${response.name}"
)
- val dub = if (isDubAvailableSeparately) "_${if (selectDub) "dub" else "sub"}" else ""
- saveData("${saveName}${dub}_$mediaId", response)
+ val dub = if (isDubAvailableSeparately()) "_${if (selectDub) "dub" else "sub"}" else ""
+ PrefManager.setCustomVal("${saveName}${dub}_$mediaId", response)
}
}
}
@@ -209,8 +211,6 @@ abstract class AnimeParser : BaseParser() {
class EmptyAnimeParser : AnimeParser() {
override val name: String = "None"
override val saveName: String = "None"
-
- override val isDubAvailableSeparately: Boolean = false
override suspend fun loadEpisodes(
animeLink: String,
extra: Map?,
diff --git a/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt b/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt
index 11a69c3101..9806767aee 100644
--- a/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt
@@ -1,19 +1,21 @@
package ani.dantotsu.parsers
-import android.content.Context
import ani.dantotsu.Lazier
import ani.dantotsu.lazyList
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
object AnimeSources : WatchSources() {
override var list: List> = emptyList()
- var pinnedAnimeSources: Set = emptySet()
+ var pinnedAnimeSources: List = emptyList()
- suspend fun init(fromExtensions: StateFlow>, context: Context) {
- val sharedPrefs = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- pinnedAnimeSources = sharedPrefs.getStringSet("pinned_anime_sources", emptySet()) ?: emptySet()
+ suspend fun init(fromExtensions: StateFlow>) {
+ pinnedAnimeSources =
+ PrefManager.getNullableVal>(PrefName.AnimeSourcesOrder, null)
+ ?: emptyList()
// Initialize with the first value from StateFlow
val initialExtensions = fromExtensions.first()
@@ -24,7 +26,10 @@ object AnimeSources : WatchSources() {
// Update as StateFlow emits new values
fromExtensions.collect { extensions ->
- list = sortPinnedAnimeSources(createParsersFromExtensions(extensions), pinnedAnimeSources) + Lazier(
+ list = sortPinnedAnimeSources(
+ createParsersFromExtensions(extensions),
+ pinnedAnimeSources
+ ) + Lazier(
{ OfflineAnimeParser() },
"Downloaded"
)
@@ -34,7 +39,7 @@ object AnimeSources : WatchSources() {
fun performReorderAnimeSources() {
//remove the downloaded source from the list to avoid duplicates
list = list.filter { it.name != "Downloaded" }
- list = sortPinnedAnimeSources(list, pinnedAnimeSources) + Lazier(
+ list = sortPinnedAnimeSources(list, pinnedAnimeSources) + Lazier(
{ OfflineAnimeParser() },
"Downloaded"
)
@@ -47,13 +52,17 @@ object AnimeSources : WatchSources() {
}
}
- private fun sortPinnedAnimeSources(Sources: List>, pinnedAnimeSources: Set): List> {
- //find the pinned sources
- val pinnedSources = Sources.filter { pinnedAnimeSources.contains(it.name) }
- //find the unpinned sources
- val unpinnedSources = Sources.filter { !pinnedAnimeSources.contains(it.name) }
- //put the pinned sources at the top of the list
- return pinnedSources + unpinnedSources
+ private fun sortPinnedAnimeSources(
+ sources: List>,
+ pinnedAnimeSources: List
+ ): List> {
+ val pinnedSourcesMap = sources.filter { pinnedAnimeSources.contains(it.name) }
+ .associateBy { it.name }
+ val orderedPinnedSources = pinnedAnimeSources.mapNotNull { name ->
+ pinnedSourcesMap[name]
+ }
+ val unpinnedSources = sources.filterNot { pinnedAnimeSources.contains(it.name) }
+ return orderedPinnedSources + unpinnedSources
}
}
diff --git a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
index 4ef6aee20c..761bd94948 100644
--- a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
@@ -10,12 +10,14 @@ import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import ani.dantotsu.FileUrl
+import ani.dantotsu.currContext
import ani.dantotsu.logger
import ani.dantotsu.media.anime.AnimeNameAdapter
import ani.dantotsu.media.manga.ImageData
import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.snackString
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
+import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
@@ -26,6 +28,7 @@ import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.interceptor.CloudflareBypassException
+import eu.kanade.tachiyomi.source.anime.getPreferenceKey
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
@@ -71,8 +74,78 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
override val name = extension.name
override val saveName = extension.name
override val hostUrl = extension.sources.first().name
- override val isDubAvailableSeparately = false
override val isNSFW = extension.isNsfw
+ override var selectDub: Boolean
+ get() = getDub()
+ set(value) {
+ setDub(value)
+ }
+
+ private fun getDub(): Boolean {
+ val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource
+ ?: return false
+ currContext()?.let { context ->
+ val sharedPreferences =
+ context.getSharedPreferences(
+ configurableSource.getPreferenceKey(),
+ Context.MODE_PRIVATE
+ )
+ sharedPreferences.all.filterValues { AnimeNameAdapter.getSubDub(it.toString()) != AnimeNameAdapter.Companion.SubDubType.NULL }
+ .forEach { value ->
+ return when (AnimeNameAdapter.getSubDub(value.value.toString())) {
+ AnimeNameAdapter.Companion.SubDubType.SUB -> false
+ AnimeNameAdapter.Companion.SubDubType.DUB -> true
+ AnimeNameAdapter.Companion.SubDubType.NULL -> false
+ }
+ }
+ }
+ return false
+ }
+
+ fun setDub(setDub: Boolean) {
+ val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource
+ ?: return
+ val type = when (setDub) {
+ true -> AnimeNameAdapter.Companion.SubDubType.DUB
+ false -> AnimeNameAdapter.Companion.SubDubType.SUB
+ }
+ currContext()?.let { context ->
+ val sharedPreferences =
+ context.getSharedPreferences(
+ configurableSource.getPreferenceKey(),
+ Context.MODE_PRIVATE
+ )
+ sharedPreferences.all.filterValues { AnimeNameAdapter.getSubDub(it.toString()) != AnimeNameAdapter.Companion.SubDubType.NULL }
+ .forEach { value ->
+ val setValue = AnimeNameAdapter.setSubDub(value.value.toString(), type)
+ if (setValue != null) {
+ sharedPreferences.edit().putString(value.key, setValue).apply()
+ }
+ }
+ }
+ }
+
+ override fun isDubAvailableSeparately(sourceLang: Int?): Boolean {
+ val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource
+ ?: return false
+ currContext()?.let { context ->
+ logger("isDubAvailableSeparately: ${configurableSource.getPreferenceKey()}")
+ val sharedPreferences =
+ context.getSharedPreferences(
+ configurableSource.getPreferenceKey(),
+ Context.MODE_PRIVATE
+ )
+ sharedPreferences.all.filterValues {
+ AnimeNameAdapter.setSubDub(
+ it.toString(),
+ AnimeNameAdapter.Companion.SubDubType.NULL
+ ) != null
+ }
+ .forEach { _ -> return true }
+ }
+ return false
+ }
+
override suspend fun loadEpisodes(
animeLink: String,
extra: Map?,
@@ -106,6 +179,8 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
}
it
}
+ } else if (episodesAreIncrementing(res)) {
+ res.sortedBy { it.episode_number }
} else {
var episodeCounter = 1f
// Group by season, sort within each season, and then renumber while keeping episode number 0 as is
@@ -113,20 +188,20 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
res.groupBy { AnimeNameAdapter.findSeasonNumber(it.name) ?: 0 }
seasonGroups.keys.sortedBy { it.toInt() }
.flatMap { season ->
- seasonGroups[season]?.sortedBy { it.episode_number }?.map { episode ->
- if (episode.episode_number != 0f) { // Skip renumbering for episode number 0
- val potentialNumber =
- AnimeNameAdapter.findEpisodeNumber(episode.name)
- if (potentialNumber != null) {
- episode.episode_number = potentialNumber
- } else {
- episode.episode_number = episodeCounter
+ seasonGroups[season]?.sortedBy { it.episode_number }?.map { episode ->
+ if (episode.episode_number != 0f) { // Skip renumbering for episode number 0
+ val potentialNumber =
+ AnimeNameAdapter.findEpisodeNumber(episode.name)
+ if (potentialNumber != null) {
+ episode.episode_number = potentialNumber
+ } else {
+ episode.episode_number = episodeCounter
+ }
+ episodeCounter++
}
- episodeCounter++
- }
- episode
- } ?: emptyList()
- }
+ episode
+ } ?: emptyList()
+ }
}
return sortedEpisodes.map { SEpisodeToEpisode(it) }
} catch (e: Exception) {
@@ -135,6 +210,19 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
return emptyList()
}
+ private fun episodesAreIncrementing(episodes: List): Boolean {
+ val sortedEpisodes = episodes.sortedBy { it.episode_number }
+ val takenNumbers = mutableListOf()
+ sortedEpisodes.forEach {
+ if (it.episode_number !in takenNumbers) {
+ takenNumbers.add(it.episode_number)
+ } else {
+ return false
+ }
+ }
+ return true
+ }
+
override suspend fun loadVideoServers(
episodeLink: String,
extra: Map?,
@@ -177,7 +265,7 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
} catch (e: CloudflareBypassException) {
logger("Exception in search: $e")
withContext(Dispatchers.Main) {
- snackString( "Failed to bypass Cloudflare")
+ snackString("Failed to bypass Cloudflare")
}
emptyList()
} catch (e: Exception) {
@@ -626,8 +714,6 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
// If the format is still undetermined, log an error
if (format == null) {
logger("Unknown video format: $videoUrl")
- //FirebaseCrashlytics.getInstance()
- // .recordException(Exception("Unknown video format: $videoUrl"))
format = VideoType.CONTAINER
}
val headersMap: Map =
diff --git a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt
index 40ce4601ea..e61c68901f 100644
--- a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt
@@ -3,10 +3,9 @@ package ani.dantotsu.parsers
import ani.dantotsu.FileUrl
import ani.dantotsu.R
import ani.dantotsu.currContext
-import ani.dantotsu.loadData
import ani.dantotsu.logger
import ani.dantotsu.media.Media
-import ani.dantotsu.saveData
+import ani.dantotsu.settings.saving.PrefManager
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.source.model.SManga
import me.xdrop.fuzzywuzzy.FuzzySearch
@@ -135,7 +134,11 @@ abstract class BaseParser {
* **/
open suspend fun loadSavedShowResponse(mediaId: Int): ShowResponse? {
checkIfVariablesAreEmpty()
- return loadData("${saveName}_$mediaId")
+ return PrefManager.getNullableCustomVal(
+ "${saveName}_$mediaId",
+ null,
+ ShowResponse::class.java
+ )
}
/**
@@ -151,7 +154,7 @@ abstract class BaseParser {
)
} : ${response.name}"
)
- saveData("${saveName}_$mediaId", response)
+ PrefManager.setCustomVal("${saveName}_$mediaId", response)
}
}
diff --git a/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt b/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt
index a6da1540bc..34c3e0a00d 100644
--- a/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt
@@ -1,19 +1,21 @@
package ani.dantotsu.parsers
-import android.content.Context
import ani.dantotsu.Lazier
import ani.dantotsu.lazyList
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
object MangaSources : MangaReadSources() {
override var list: List> = emptyList()
- var pinnedMangaSources: Set = emptySet()
+ var pinnedMangaSources: List = emptyList()
- suspend fun init(fromExtensions: StateFlow>, context: Context) {
- val sharedPrefs = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- pinnedMangaSources = sharedPrefs.getStringSet("pinned_manga_sources", emptySet()) ?: emptySet()
+ suspend fun init(fromExtensions: StateFlow>) {
+ pinnedMangaSources =
+ PrefManager.getNullableVal>(PrefName.MangaSourcesOrder, null)
+ ?: emptyList()
// Initialize with the first value from StateFlow
val initialExtensions = fromExtensions.first()
@@ -24,7 +26,10 @@ object MangaSources : MangaReadSources() {
// Update as StateFlow emits new values
fromExtensions.collect { extensions ->
- list = sortPinnedMangaSources(createParsersFromExtensions(extensions), pinnedMangaSources) + Lazier(
+ list = sortPinnedMangaSources(
+ createParsersFromExtensions(extensions),
+ pinnedMangaSources
+ ) + Lazier(
{ OfflineMangaParser() },
"Downloaded"
)
@@ -47,21 +52,21 @@ object MangaSources : MangaReadSources() {
}
}
- private fun sortPinnedMangaSources(Sources: List>, pinnedMangaSources: Set): List> {
- //find the pinned sources
- val pinnedSources = Sources.filter { pinnedMangaSources.contains(it.name) }
- //find the unpinned sources
- val unpinnedSources = Sources.filter { !pinnedMangaSources.contains(it.name) }
- //put the pinned sources at the top of the list
- return pinnedSources + unpinnedSources
+ private fun sortPinnedMangaSources(
+ sources: List>,
+ pinnedMangaSources: List
+ ): List> {
+ val pinnedSourcesMap = sources.filter { pinnedMangaSources.contains(it.name) }
+ .associateBy { it.name }
+ val orderedPinnedSources = pinnedMangaSources.mapNotNull { name ->
+ pinnedSourcesMap[name]
+ }
+ val unpinnedSources = sources.filterNot { pinnedMangaSources.contains(it.name) }
+ return orderedPinnedSources + unpinnedSources
}
}
object HMangaSources : MangaReadSources() {
- val aList: List> = lazyList()
- suspend fun init(fromExtensions: StateFlow>) {
- //todo
- }
-
+ private val aList: List> = lazyList()
override val list = listOf(aList, MangaSources.list).flatten()
}
diff --git a/app/src/main/java/ani/dantotsu/parsers/NovelSources.kt b/app/src/main/java/ani/dantotsu/parsers/NovelSources.kt
index e5298bceb7..8fb66650d4 100644
--- a/app/src/main/java/ani/dantotsu/parsers/NovelSources.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/NovelSources.kt
@@ -4,13 +4,20 @@ import android.util.Log
import ani.dantotsu.Lazier
import ani.dantotsu.parsers.novel.DynamicNovelParser
import ani.dantotsu.parsers.novel.NovelExtension
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
object NovelSources : NovelReadSources() {
override var list: List> = emptyList()
+ var pinnedNovelSources: List = emptyList()
suspend fun init(fromExtensions: StateFlow>) {
+ pinnedNovelSources =
+ PrefManager.getNullableVal>(PrefName.NovelSourcesOrder, null)
+ ?: emptyList()
+
// Initialize with the first value from StateFlow
val initialExtensions = fromExtensions.first()
list = createParsersFromExtensions(initialExtensions) + Lazier(
@@ -20,13 +27,25 @@ object NovelSources : NovelReadSources() {
// Update as StateFlow emits new values
fromExtensions.collect { extensions ->
- list = createParsersFromExtensions(extensions) + Lazier(
+ list = sortPinnedNovelSources(
+ createParsersFromExtensions(extensions),
+ pinnedNovelSources
+ ) + Lazier(
{ OfflineNovelParser() },
"Downloaded"
)
}
}
+ fun performReorderNovelSources() {
+ //remove the downloaded source from the list to avoid duplicates
+ list = list.filter { it.name != "Downloaded" }
+ list = sortPinnedNovelSources(list, pinnedNovelSources) + Lazier(
+ { OfflineNovelParser() },
+ "Downloaded"
+ )
+ }
+
private fun createParsersFromExtensions(extensions: List): List> {
Log.d("NovelSources", "createParsersFromExtensions")
Log.d("NovelSources", extensions.toString())
@@ -35,4 +54,17 @@ object NovelSources : NovelReadSources() {
Lazier({ DynamicNovelParser(extension) }, name)
}
}
+
+ private fun sortPinnedNovelSources(
+ parsers: List>,
+ pinnedSources: List
+ ): List> {
+ val pinnedSourcesMap = parsers.filter { pinnedSources.contains(it.name) }
+ .associateBy { it.name }
+ val orderedPinnedSources = pinnedSources.mapNotNull { name ->
+ pinnedSourcesMap[name]
+ }
+ val unpinnedSources = parsers.filterNot { pinnedSources.contains(it.name) }
+ return orderedPinnedSources + unpinnedSources
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt b/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt
index 0f8c79243e..1c9c3b6c7a 100644
--- a/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt
@@ -21,7 +21,6 @@ class OfflineAnimeParser : AnimeParser() {
override val name = "Offline"
override val saveName = "Offline"
override val hostUrl = "Offline"
- override val isDubAvailableSeparately = false
override val isNSFW = false
override suspend fun loadEpisodes(
@@ -119,7 +118,7 @@ class OfflineVideoExtractor(val videoServer: VideoServer) : VideoExtractor() {
val sublist = getSubtitle(
videoServer.extraData?.get("title") ?: "",
videoServer.extraData?.get("episode") ?: ""
- )?: emptyList()
+ ) ?: emptyList()
//we need to return a "fake" video so that the app doesn't crash
val video = Video(
null,
diff --git a/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt b/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt
index 582a7cb4bf..ef164b3404 100644
--- a/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt
@@ -62,6 +62,7 @@ data class VideoServer(
) : Serializable {
constructor(name: String, embedUrl: String, extraData: Map? = null)
: this(name, FileUrl(embedUrl), extraData)
+
constructor(name: String, offline: Boolean, extraData: Map?)
: this(name, FileUrl(""), extraData, null, offline)
diff --git a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionGithubApi.kt b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionGithubApi.kt
index ea23fbeb58..4381ee2792 100644
--- a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionGithubApi.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionGithubApi.kt
@@ -2,8 +2,9 @@ package ani.dantotsu.parsers.novel
import android.content.Context
-import ani.dantotsu.currContext
import ani.dantotsu.logger
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import eu.kanade.tachiyomi.extension.anime.model.AnimeLoadResult
@@ -26,9 +27,7 @@ class NovelExtensionGithubApi {
private val novelExtensionManager: NovelExtensionManager by injectLazy()
private val json: Json by injectLazy()
- private val lastExtCheck: Long =
- currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getLong("last_ext_check", 0) ?: 0
+ private val lastExtCheck: Long = PrefManager.getVal(PrefName.NovelLastExtCheck)
private var requiresFallbackSource = false
@@ -86,8 +85,7 @@ class NovelExtensionGithubApi {
novelExtensionManager.availableExtensionsFlow.value
} else {
findExtensions().also {
- context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
- ?.putLong("last_ext_check", Date().time)?.apply()
+ PrefManager.setVal(PrefName.NovelLastExtCheck, Date().time)
}
}
diff --git a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionLoader.kt b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionLoader.kt
index 3c26530cb5..94574e205b 100644
--- a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionLoader.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionLoader.kt
@@ -6,12 +6,14 @@ import android.content.pm.PackageManager.GET_SIGNATURES
import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
import android.os.Build
import android.util.Log
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.logger
import ani.dantotsu.parsers.NovelInterface
import ani.dantotsu.snackString
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import dalvik.system.PathClassLoader
import eu.kanade.tachiyomi.util.lang.Hash
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
import java.io.File
import java.util.Locale
@@ -141,7 +143,7 @@ internal object NovelExtensionLoader {
listOfNotNull(novelInterfaceInstance)
} catch (e: Exception) {
e.printStackTrace()
- FirebaseCrashlytics.getInstance().recordException(e)
+ Injekt.get().logException(e)
emptyList()
}
}
diff --git a/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt
index e46b1d711e..a6d6c39e11 100644
--- a/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt
+++ b/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt
@@ -12,13 +12,13 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.R
+import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding
import ani.dantotsu.settings.paging.AnimeExtensionAdapter
import ani.dantotsu.settings.paging.AnimeExtensionsViewModel
import ani.dantotsu.settings.paging.AnimeExtensionsViewModelFactory
import ani.dantotsu.settings.paging.OnAnimeInstallClickListener
import ani.dantotsu.snackString
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
@@ -71,6 +71,10 @@ class AnimeExtensionsFragment : Fragment(),
viewModel.setSearchQuery(query ?: "")
}
+ override fun notifyDataChanged() {
+ viewModel.invalidatePager()
+ }
+
override fun onInstallClick(pkg: AnimeExtension.Available) {
val context = requireContext()
if (isAdded) {
@@ -93,7 +97,7 @@ class AnimeExtensionsFragment : Fragment(),
notificationManager.notify(1, builder.build())
},
{ error ->
- FirebaseCrashlytics.getInstance().recordException(error)
+ Injekt.get().logException(error)
val builder = NotificationCompat.Builder(
context,
Notifications.CHANNEL_DOWNLOADER_ERROR
@@ -110,7 +114,7 @@ class AnimeExtensionsFragment : Fragment(),
context,
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
- .setSmallIcon(R.drawable.ic_round_download_24)
+ .setSmallIcon(R.drawable.ic_download_24)
.setContentTitle("Installation complete")
.setContentText("The extension has been successfully installed.")
.setPriority(NotificationCompat.PRIORITY_LOW)
diff --git a/app/src/main/java/ani/dantotsu/settings/CurrentNovelReaderSettings.kt b/app/src/main/java/ani/dantotsu/settings/CurrentNovelReaderSettings.kt
index b1cd429bda..eae49c8d37 100644
--- a/app/src/main/java/ani/dantotsu/settings/CurrentNovelReaderSettings.kt
+++ b/app/src/main/java/ani/dantotsu/settings/CurrentNovelReaderSettings.kt
@@ -1,23 +1,29 @@
package ani.dantotsu.settings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import java.io.Serializable
data class CurrentNovelReaderSettings(
- var currentThemeName: String = "Default",
- var layout: Layouts = Layouts.PAGED,
- var dualPageMode: CurrentReaderSettings.DualPageModes = CurrentReaderSettings.DualPageModes.Automatic,
- var lineHeight: Float = 1.4f,
- var margin: Float = 0.06f,
- var justify: Boolean = true,
- var hyphenation: Boolean = true,
- var useDarkTheme: Boolean = false,
- var useOledTheme: Boolean = false,
- var invert: Boolean = false,
- var maxInlineSize: Int = 720,
- var maxBlockSize: Int = 1440,
- var horizontalScrollBar: Boolean = true,
- var keepScreenOn: Boolean = false,
- var volumeButtons: Boolean = false,
+ var currentThemeName: String = PrefManager.getVal(PrefName.CurrentThemeName),
+ var layout: Layouts = Layouts[PrefManager.getVal(PrefName.LayoutNovel)]
+ ?: Layouts.PAGED,
+ var dualPageMode: CurrentReaderSettings.DualPageModes = CurrentReaderSettings.DualPageModes[PrefManager.getVal(
+ PrefName.DualPageModeNovel
+ )]
+ ?: CurrentReaderSettings.DualPageModes.Automatic,
+ var lineHeight: Float = PrefManager.getVal(PrefName.LineHeight),
+ var margin: Float = PrefManager.getVal(PrefName.Margin),
+ var justify: Boolean = PrefManager.getVal(PrefName.Justify),
+ var hyphenation: Boolean = PrefManager.getVal(PrefName.Hyphenation),
+ var useDarkTheme: Boolean = PrefManager.getVal(PrefName.UseDarkThemeNovel),
+ var useOledTheme: Boolean = PrefManager.getVal(PrefName.UseOledThemeNovel),
+ var invert: Boolean = PrefManager.getVal(PrefName.Invert),
+ var maxInlineSize: Int = PrefManager.getVal(PrefName.MaxInlineSize),
+ var maxBlockSize: Int = PrefManager.getVal(PrefName.MaxBlockSize),
+ var horizontalScrollBar: Boolean = PrefManager.getVal(PrefName.HorizontalScrollBarNovel),
+ var keepScreenOn: Boolean = PrefManager.getVal(PrefName.KeepScreenOnNovel),
+ var volumeButtons: Boolean = PrefManager.getVal(PrefName.VolumeButtonsNovel)
) : Serializable {
enum class Layouts(val string: String) {
diff --git a/app/src/main/java/ani/dantotsu/settings/CurrentReaderSettings.kt b/app/src/main/java/ani/dantotsu/settings/CurrentReaderSettings.kt
index bfa2b4d835..2aca1b3e93 100644
--- a/app/src/main/java/ani/dantotsu/settings/CurrentReaderSettings.kt
+++ b/app/src/main/java/ani/dantotsu/settings/CurrentReaderSettings.kt
@@ -1,23 +1,28 @@
package ani.dantotsu.settings
+import ani.dantotsu.settings.saving.PrefManager
+import ani.dantotsu.settings.saving.PrefName
import java.io.Serializable
data class CurrentReaderSettings(
- var direction: Directions = Directions.TOP_TO_BOTTOM,
- var layout: Layouts = Layouts.CONTINUOUS,
- var dualPageMode: DualPageModes = DualPageModes.Automatic,
- var overScrollMode: Boolean = true,
- var trueColors: Boolean = false,
- var rotation: Boolean = true,
- var padding: Boolean = true,
- var hidePageNumbers: Boolean = false,
- var horizontalScrollBar: Boolean = true,
- var keepScreenOn: Boolean = false,
- var volumeButtons: Boolean = false,
- var wrapImages: Boolean = false,
- var longClickImage: Boolean = true,
- var cropBorders: Boolean = false,
- var cropBorderThreshold: Int = 10,
+ var direction: Directions = Directions[PrefManager.getVal(PrefName.Direction)]
+ ?: Directions.TOP_TO_BOTTOM,
+ var layout: Layouts = Layouts[PrefManager.getVal(PrefName.LayoutReader)]
+ ?: Layouts.CONTINUOUS,
+ var dualPageMode: DualPageModes = DualPageModes[PrefManager.getVal(PrefName.DualPageModeReader)]
+ ?: DualPageModes.Automatic,
+ var overScrollMode: Boolean = PrefManager.getVal(PrefName.OverScrollMode),
+ var trueColors: Boolean = PrefManager.getVal(PrefName.TrueColors),
+ var rotation: Boolean = PrefManager.getVal(PrefName.Rotation),
+ var padding: Boolean = PrefManager.getVal(PrefName.Padding),
+ var hidePageNumbers: Boolean = PrefManager.getVal(PrefName.HidePageNumbers),
+ var horizontalScrollBar: Boolean = PrefManager.getVal(PrefName.HorizontalScrollBar),
+ var keepScreenOn: Boolean = PrefManager.getVal(PrefName.KeepScreenOn),
+ var volumeButtons: Boolean = PrefManager.getVal(PrefName.VolumeButtonsReader),
+ var wrapImages: Boolean = PrefManager.getVal(PrefName.WrapImages),
+ var longClickImage: Boolean = PrefManager.getVal(PrefName.LongClickImage),
+ var cropBorders: Boolean = PrefManager.getVal(PrefName.CropBorders),
+ var cropBorderThreshold: Int = PrefManager.getVal(PrefName.CropBorderThreshold)
) : Serializable {
enum class Directions {
diff --git a/app/src/main/java/ani/dantotsu/settings/DevelopersAdapter.kt b/app/src/main/java/ani/dantotsu/settings/DevelopersAdapter.kt
index 7375d101e8..7f42017d47 100644
--- a/app/src/main/java/ani/dantotsu/settings/DevelopersAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/settings/DevelopersAdapter.kt
@@ -4,15 +4,12 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.databinding.ItemDeveloperBinding
-import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.setAnimation
class DevelopersAdapter(private val developers: Array