commit
de1788950f
410 changed files with 7912 additions and 5835 deletions
86
.github/workflows/beta.yml
vendored
86
.github/workflows/beta.yml
vendored
|
@ -16,12 +16,50 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v3
|
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
|
- name: Set variables
|
||||||
run: |
|
run: |
|
||||||
VER=$(grep -E -o "versionName \".*\"" app/build.gradle | sed -e 's/versionName //g' | tr -d '"')
|
VER=$(grep -E -o "versionName \".*\"" app/build.gradle | sed -e 's/versionName //g' | tr -d '"')
|
||||||
SHA=${{ github.sha }}
|
SHA=${{ github.sha }}
|
||||||
VERSION="$VER.${SHA:0:7}"
|
VERSION="$VER+${SHA:0:7}"
|
||||||
echo "Version $VERSION"
|
echo "Version $VERSION"
|
||||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
@ -31,30 +69,54 @@ jobs:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
cache: gradle
|
cache: gradle
|
||||||
|
|
||||||
- name: Decode Keystore File
|
- name: Decode Keystore File
|
||||||
run: echo "${{ secrets.KEYSTORE_FILE }}" | base64 -d > $GITHUB_WORKSPACE/key.keystore
|
run: echo "${{ secrets.KEYSTORE_FILE }}" | base64 -d > $GITHUB_WORKSPACE/key.keystore
|
||||||
|
|
||||||
- name: List files in the directory
|
- name: List files in the directory
|
||||||
run: ls -l
|
run: ls -l
|
||||||
|
|
||||||
- name: Make gradlew executable
|
- name: Make gradlew executable
|
||||||
run: chmod +x ./gradlew
|
run: chmod +x ./gradlew
|
||||||
|
|
||||||
- name: Build with Gradle
|
- 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
|
- name: Upload a Build Artifact
|
||||||
uses: actions/upload-artifact@v3.0.0
|
uses: actions/upload-artifact@v3.0.0
|
||||||
with:
|
with:
|
||||||
name: Dantotsu
|
name: Dantotsu
|
||||||
path: "app/build/outputs/apk/debug/app-debug.apk"
|
path: "app/build/outputs/apk/google/alpha/app-google-alpha.apk"
|
||||||
|
|
||||||
- name: Upload APK to Discord
|
- name: Upload APK to Discord and Telegram
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
contentbody=$( jq -Rsa . <<< "${{ github.event.head_commit.message }}" )
|
#Discord
|
||||||
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 }}
|
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
|
- name: Delete Old Pre-Releases
|
||||||
id: delete-pre-releases
|
id: delete-pre-releases
|
||||||
|
|
4
app/.gitignore
vendored
4
app/.gitignore
vendored
|
@ -1,4 +1,8 @@
|
||||||
/build
|
/build
|
||||||
/debug
|
/debug
|
||||||
/debug/output-metadata.json
|
/debug/output-metadata.json
|
||||||
|
/alpha
|
||||||
|
/alpha/output-metadata.json
|
||||||
|
/google/*
|
||||||
|
/fdroid/*
|
||||||
/release
|
/release
|
|
@ -1,12 +1,9 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
id 'com.google.gms.google-services'
|
|
||||||
id 'com.google.firebase.crashlytics'
|
|
||||||
id 'kotlin-android'
|
id 'kotlin-android'
|
||||||
id 'kotlinx-serialization'
|
id 'kotlinx-serialization'
|
||||||
id 'org.jetbrains.kotlin.android'
|
id 'org.jetbrains.kotlin.android'
|
||||||
id 'com.google.devtools.ksp'
|
id 'com.google.devtools.ksp'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def gitCommitHash = providers.exec {
|
def gitCommitHash = providers.exec {
|
||||||
|
@ -20,14 +17,39 @@ android {
|
||||||
applicationId "ani.dantotsu"
|
applicationId "ani.dantotsu"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode ((System.currentTimeMillis() / 60000).toInteger())
|
versionCode((System.currentTimeMillis() / 60000).toInteger())
|
||||||
versionName "2.0.0-beta01-iv1"
|
versionName "2.1.0"
|
||||||
|
versionCode 210000000
|
||||||
signingConfig signingConfigs.debug
|
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 {
|
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 {
|
debug {
|
||||||
applicationIdSuffix ".beta"
|
applicationIdSuffix ".beta"
|
||||||
|
versionNameSuffix "-beta01"
|
||||||
manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher_beta", icon_placeholder_round: "@mipmap/ic_launcher_beta_round"]
|
manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher_beta", icon_placeholder_round: "@mipmap/ic_launcher_beta_round"]
|
||||||
debuggable System.getenv("CI") == null
|
debuggable System.getenv("CI") == null
|
||||||
}
|
}
|
||||||
|
@ -39,6 +61,7 @@ android {
|
||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
|
buildConfig true
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_17
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
|
@ -52,6 +75,11 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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
|
// Core
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
implementation 'androidx.browser:browser:1.7.0'
|
implementation 'androidx.browser:browser:1.7.0'
|
||||||
|
@ -67,7 +95,7 @@ dependencies {
|
||||||
implementation 'com.github.Blatzar:NiceHttp:0.4.4'
|
implementation 'com.github.Blatzar:NiceHttp:0.4.4'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2'
|
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
implementation 'androidx.webkit:webkit:1.9.0'
|
implementation 'androidx.webkit:webkit:1.10.0'
|
||||||
|
|
||||||
// Glide
|
// Glide
|
||||||
ext.glide_version = '4.16.0'
|
ext.glide_version = '4.16.0'
|
||||||
|
@ -77,13 +105,8 @@ dependencies {
|
||||||
implementation "com.github.bumptech.glide:okhttp3-integration:$glide_version"
|
implementation "com.github.bumptech.glide:okhttp3-integration:$glide_version"
|
||||||
implementation 'jp.wasabeef:glide-transformations:4.3.0'
|
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
|
// Exoplayer
|
||||||
ext.exo_version = '1.2.0'
|
ext.exo_version = '1.2.1'
|
||||||
implementation "androidx.media3:media3-exoplayer:$exo_version"
|
implementation "androidx.media3:media3-exoplayer:$exo_version"
|
||||||
implementation "androidx.media3:media3-ui:$exo_version"
|
implementation "androidx.media3:media3-ui:$exo_version"
|
||||||
implementation "androidx.media3:media3-exoplayer-hls:$exo_version"
|
implementation "androidx.media3:media3-exoplayer-hls:$exo_version"
|
||||||
|
|
|
@ -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": {
|
"client_info": {
|
||||||
"mobilesdk_app_id": "1:1039200814590:android:40e14720ee97917e1aacaf",
|
"mobilesdk_app_id": "1:1039200814590:android:40e14720ee97917e1aacaf",
|
||||||
|
|
376
app/src/alpha/res/drawable/anim_splash.xml
Normal file
376
app/src/alpha/res/drawable/anim_splash.xml
Normal file
|
@ -0,0 +1,376 @@
|
||||||
|
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt">
|
||||||
|
<aapt:attr name="android:drawable">
|
||||||
|
<vector
|
||||||
|
android:name="vector"
|
||||||
|
android:width="768dp"
|
||||||
|
android:height="768dp"
|
||||||
|
android:viewportWidth="768"
|
||||||
|
android:viewportHeight="768">
|
||||||
|
<group
|
||||||
|
android:name="wrapper"
|
||||||
|
android:pivotX="384"
|
||||||
|
android:pivotY="384">
|
||||||
|
<clip-path
|
||||||
|
android:name="clippath"
|
||||||
|
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" />
|
||||||
|
<group android:name="group">
|
||||||
|
<group android:name="group_1">
|
||||||
|
<path
|
||||||
|
android:name="path"
|
||||||
|
android:fillColor="#ED0021"
|
||||||
|
android:pathData="M 128 128 L 640 128 L 640 639.96 L 128 639.96 Z"
|
||||||
|
android:strokeWidth="1" />
|
||||||
|
<group
|
||||||
|
android:name="group_12"
|
||||||
|
android:pivotX="384"
|
||||||
|
android:pivotY="384">
|
||||||
|
<path
|
||||||
|
android:name="path_2"
|
||||||
|
android:fillColor="#D40037"
|
||||||
|
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" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group android:name="group_2">
|
||||||
|
<group android:name="group_7">
|
||||||
|
<group android:name="group_10">
|
||||||
|
<group
|
||||||
|
android:name="group_11"
|
||||||
|
android:pivotX="94"
|
||||||
|
android:pivotY="440"
|
||||||
|
android:rotation="-90">
|
||||||
|
<path
|
||||||
|
android:name="path_1"
|
||||||
|
android:fillColor="#A70060"
|
||||||
|
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" />
|
||||||
|
<clip-path
|
||||||
|
android:name="mask_2"
|
||||||
|
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" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group
|
||||||
|
android:name="group_13"
|
||||||
|
android:pivotX="384"
|
||||||
|
android:pivotY="384">
|
||||||
|
<clip-path
|
||||||
|
android:name="mask_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" />
|
||||||
|
<group
|
||||||
|
android:name="group_9"
|
||||||
|
android:pivotX="94"
|
||||||
|
android:pivotY="440"
|
||||||
|
android:rotation="-90">
|
||||||
|
<path
|
||||||
|
android:name="path_3"
|
||||||
|
android:fillColor="#BF005E"
|
||||||
|
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" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group
|
||||||
|
android:name="group_6"
|
||||||
|
android:pivotX="94"
|
||||||
|
android:pivotY="440"
|
||||||
|
android:rotation="-5"
|
||||||
|
android:scaleX="1.2"
|
||||||
|
android:scaleY="1.2" />
|
||||||
|
</group>
|
||||||
|
<group
|
||||||
|
android:name="group_8"
|
||||||
|
android:pivotX="94"
|
||||||
|
android:pivotY="440"
|
||||||
|
android:rotation="-90">
|
||||||
|
<group
|
||||||
|
android:name="group_14"
|
||||||
|
android:pivotX="94"
|
||||||
|
android:pivotY="440">
|
||||||
|
<path
|
||||||
|
android:name="path_4"
|
||||||
|
android:fillColor="#C70051"
|
||||||
|
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" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group
|
||||||
|
android:name="group_3"
|
||||||
|
android:translateX="-360">
|
||||||
|
<path
|
||||||
|
android:name="path_6"
|
||||||
|
android:fillColor="#251528"
|
||||||
|
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" />
|
||||||
|
</group>
|
||||||
|
<group
|
||||||
|
android:name="group_4"
|
||||||
|
android:pivotX="384"
|
||||||
|
android:pivotY="384"
|
||||||
|
android:scaleX="1.5"
|
||||||
|
android:scaleY="1.5">
|
||||||
|
<path
|
||||||
|
android:name="path_5"
|
||||||
|
android:fillColor="#251528"
|
||||||
|
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" />
|
||||||
|
</group>
|
||||||
|
<group
|
||||||
|
android:name="group_5"
|
||||||
|
android:pivotX="384"
|
||||||
|
android:pivotY="384"
|
||||||
|
android:rotation="-15"
|
||||||
|
android:scaleX="3"
|
||||||
|
android:scaleY="3">
|
||||||
|
<path
|
||||||
|
android:name="path_7"
|
||||||
|
android:fillAlpha="0"
|
||||||
|
android:fillColor="#FFD8DF"
|
||||||
|
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" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
|
</aapt:attr>
|
||||||
|
<target android:name="wrapper">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<set>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="500"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="scaleX"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="500"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="scaleY"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</set>
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="group_6">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<set>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="rotation"
|
||||||
|
android:startOffset="350"
|
||||||
|
android:valueFrom="-10"
|
||||||
|
android:valueTo="0"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="300"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="scaleX"
|
||||||
|
android:startOffset="350"
|
||||||
|
android:valueFrom="1.2"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="300"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="scaleY"
|
||||||
|
android:startOffset="350"
|
||||||
|
android:valueFrom="1.2"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</set>
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="group_3">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="400"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="translateX"
|
||||||
|
android:startOffset="250"
|
||||||
|
android:valueFrom="-360"
|
||||||
|
android:valueTo="0"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="group_4">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<set>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="scaleX"
|
||||||
|
android:startOffset="400"
|
||||||
|
android:valueFrom="1.5"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="scaleY"
|
||||||
|
android:startOffset="400"
|
||||||
|
android:valueFrom="1.5"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</set>
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="path_7">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="fillAlpha"
|
||||||
|
android:startOffset="350"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="group_5">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<set>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:propertyName="rotation"
|
||||||
|
android:startOffset="350"
|
||||||
|
android:valueFrom="-45"
|
||||||
|
android:valueTo="0"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="scaleX"
|
||||||
|
android:startOffset="350"
|
||||||
|
android:valueFrom="3"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="scaleY"
|
||||||
|
android:startOffset="350"
|
||||||
|
android:valueFrom="3"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</set>
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="group_8">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="rotation"
|
||||||
|
android:startOffset="100"
|
||||||
|
android:valueFrom="-90"
|
||||||
|
android:valueTo="0"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="group_9">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<set>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="rotation"
|
||||||
|
android:startOffset="100"
|
||||||
|
android:valueFrom="-90"
|
||||||
|
android:valueTo="0"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="scaleX"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="scaleY"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</set>
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="group_11">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="rotation"
|
||||||
|
android:startOffset="100"
|
||||||
|
android:valueFrom="-90"
|
||||||
|
android:valueTo="0"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="group_12">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<set>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="scaleX"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="scaleY"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</set>
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="group_13">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<set>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="scaleX"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="scaleY"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="1"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</set>
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
<target android:name="group_14">
|
||||||
|
<aapt:attr name="android:animation">
|
||||||
|
<set>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="200"
|
||||||
|
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
|
||||||
|
android:propertyName="rotation"
|
||||||
|
android:startOffset="350"
|
||||||
|
android:valueFrom="5"
|
||||||
|
android:valueTo="0"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="100"
|
||||||
|
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
|
||||||
|
android:propertyName="rotation"
|
||||||
|
android:startOffset="250"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="5"
|
||||||
|
android:valueType="floatType" />
|
||||||
|
</set>
|
||||||
|
</aapt:attr>
|
||||||
|
</target>
|
||||||
|
</animated-vector>
|
4
app/src/alpha/res/values/strings.xml
Normal file
4
app/src/alpha/res/values/strings.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Dantotsu α</string>
|
||||||
|
</resources>
|
|
@ -1,5 +1,4 @@
|
||||||
<animated-vector
|
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt">
|
xmlns:aapt="http://schemas.android.com/aapt">
|
||||||
<aapt:attr name="android:drawable">
|
<aapt:attr name="android:drawable">
|
||||||
<vector
|
<vector
|
||||||
|
@ -14,23 +13,23 @@
|
||||||
android:pivotY="384">
|
android:pivotY="384">
|
||||||
<clip-path
|
<clip-path
|
||||||
android:name="clippath"
|
android:name="clippath"
|
||||||
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 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" />
|
||||||
<group android:name="group">
|
<group android:name="group">
|
||||||
<group android:name="group_1">
|
<group android:name="group_1">
|
||||||
<path
|
<path
|
||||||
android:name="path"
|
android:name="path"
|
||||||
android:pathData="M 128 128 L 640 128 L 640 639.96 L 128 639.96 Z"
|
|
||||||
android:fillColor="#6901fd"
|
android:fillColor="#6901fd"
|
||||||
android:strokeWidth="1"/>
|
android:pathData="M 128 128 L 640 128 L 640 639.96 L 128 639.96 Z"
|
||||||
|
android:strokeWidth="1" />
|
||||||
<group
|
<group
|
||||||
android:name="group_12"
|
android:name="group_12"
|
||||||
android:pivotX="384"
|
android:pivotX="384"
|
||||||
android:pivotY="384">
|
android:pivotY="384">
|
||||||
<path
|
<path
|
||||||
android:name="path_2"
|
android:name="path_2"
|
||||||
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:fillColor="#4800e5"
|
android:fillColor="#4800e5"
|
||||||
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" />
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group android:name="group_2">
|
<group android:name="group_2">
|
||||||
|
@ -43,12 +42,12 @@
|
||||||
android:rotation="-90">
|
android:rotation="-90">
|
||||||
<path
|
<path
|
||||||
android:name="path_1"
|
android:name="path_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:fillColor="#2000bd"
|
android:fillColor="#2000bd"
|
||||||
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:strokeWidth="1" />
|
||||||
<clip-path
|
<clip-path
|
||||||
android:name="mask_2"
|
android:name="mask_2"
|
||||||
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 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" />
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group
|
<group
|
||||||
|
@ -57,7 +56,7 @@
|
||||||
android:pivotY="384">
|
android:pivotY="384">
|
||||||
<clip-path
|
<clip-path
|
||||||
android:name="mask_1"
|
android:name="mask_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: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" />
|
||||||
<group
|
<group
|
||||||
android:name="group_9"
|
android:name="group_9"
|
||||||
android:pivotX="94"
|
android:pivotX="94"
|
||||||
|
@ -65,18 +64,18 @@
|
||||||
android:rotation="-90">
|
android:rotation="-90">
|
||||||
<path
|
<path
|
||||||
android:name="path_3"
|
android:name="path_3"
|
||||||
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:fillColor="#1e00d1"
|
android:fillColor="#1e00d1"
|
||||||
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:strokeWidth="1" />
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group
|
<group
|
||||||
android:name="group_6"
|
android:name="group_6"
|
||||||
android:pivotX="94"
|
android:pivotX="94"
|
||||||
android:pivotY="440"
|
android:pivotY="440"
|
||||||
|
android:rotation="-5"
|
||||||
android:scaleX="1.2"
|
android:scaleX="1.2"
|
||||||
android:scaleY="1.2"
|
android:scaleY="1.2" />
|
||||||
android:rotation="-5"/>
|
|
||||||
</group>
|
</group>
|
||||||
<group
|
<group
|
||||||
android:name="group_8"
|
android:name="group_8"
|
||||||
|
@ -89,9 +88,9 @@
|
||||||
android:pivotY="440">
|
android:pivotY="440">
|
||||||
<path
|
<path
|
||||||
android:name="path_4"
|
android:name="path_4"
|
||||||
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:fillColor="#2900da"
|
android:fillColor="#2900da"
|
||||||
android:strokeWidth="1"/>
|
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" />
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
@ -100,9 +99,9 @@
|
||||||
android:translateX="-360">
|
android:translateX="-360">
|
||||||
<path
|
<path
|
||||||
android:name="path_6"
|
android:name="path_6"
|
||||||
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:fillColor="#1f1f30"
|
android:fillColor="#1f1f30"
|
||||||
android:strokeWidth="1"/>
|
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" />
|
||||||
</group>
|
</group>
|
||||||
<group
|
<group
|
||||||
android:name="group_4"
|
android:name="group_4"
|
||||||
|
@ -112,23 +111,23 @@
|
||||||
android:scaleY="1.5">
|
android:scaleY="1.5">
|
||||||
<path
|
<path
|
||||||
android:name="path_5"
|
android:name="path_5"
|
||||||
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:fillColor="#1f1f30"
|
android:fillColor="#1f1f30"
|
||||||
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" />
|
||||||
</group>
|
</group>
|
||||||
<group
|
<group
|
||||||
android:name="group_5"
|
android:name="group_5"
|
||||||
android:pivotX="384"
|
android:pivotX="384"
|
||||||
android:pivotY="384"
|
android:pivotY="384"
|
||||||
|
android:rotation="-15"
|
||||||
android:scaleX="3"
|
android:scaleX="3"
|
||||||
android:scaleY="3"
|
android:scaleY="3">
|
||||||
android:rotation="-15">
|
|
||||||
<path
|
<path
|
||||||
android:name="path_7"
|
android:name="path_7"
|
||||||
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:fillColor="#efe7ff"
|
|
||||||
android:fillAlpha="0"
|
android:fillAlpha="0"
|
||||||
android:strokeWidth="1"/>
|
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" />
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
@ -138,19 +137,19 @@
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<set>
|
<set>
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="500"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
android:propertyName="scaleX"
|
android:propertyName="scaleX"
|
||||||
android:duration="500"
|
|
||||||
android:valueFrom="0"
|
android:valueFrom="0"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/overshoot_interpolator"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
android:propertyName="scaleY"
|
|
||||||
android:duration="500"
|
android:duration="500"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="scaleY"
|
||||||
android:valueFrom="0"
|
android:valueFrom="0"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/overshoot_interpolator"/>
|
|
||||||
</set>
|
</set>
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
|
@ -158,177 +157,177 @@
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<set>
|
<set>
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
android:propertyName="rotation"
|
android:propertyName="rotation"
|
||||||
android:startOffset="350"
|
android:startOffset="350"
|
||||||
android:duration="550"
|
|
||||||
android:valueFrom="-10"
|
android:valueFrom="-10"
|
||||||
android:valueTo="0"
|
android:valueTo="0"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/overshoot_interpolator"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="300"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="scaleX"
|
android:propertyName="scaleX"
|
||||||
android:startOffset="350"
|
android:startOffset="350"
|
||||||
android:duration="300"
|
|
||||||
android:valueFrom="1.2"
|
android:valueFrom="1.2"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="300"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="scaleY"
|
android:propertyName="scaleY"
|
||||||
android:startOffset="350"
|
android:startOffset="350"
|
||||||
android:duration="300"
|
|
||||||
android:valueFrom="1.2"
|
android:valueFrom="1.2"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
</set>
|
</set>
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
<target android:name="group_3">
|
<target android:name="group_3">
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="400"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
android:propertyName="translateX"
|
android:propertyName="translateX"
|
||||||
android:startOffset="250"
|
android:startOffset="250"
|
||||||
android:duration="400"
|
|
||||||
android:valueFrom="-360"
|
android:valueFrom="-360"
|
||||||
android:valueTo="0"
|
android:valueTo="0"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/overshoot_interpolator"/>
|
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
<target android:name="group_4">
|
<target android:name="group_4">
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<set>
|
<set>
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="scaleX"
|
android:propertyName="scaleX"
|
||||||
android:startOffset="400"
|
android:startOffset="400"
|
||||||
android:duration="350"
|
|
||||||
android:valueFrom="1.5"
|
android:valueFrom="1.5"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="scaleY"
|
android:propertyName="scaleY"
|
||||||
android:startOffset="400"
|
android:startOffset="400"
|
||||||
android:duration="350"
|
|
||||||
android:valueFrom="1.5"
|
android:valueFrom="1.5"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
</set>
|
</set>
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
<target android:name="path_7">
|
<target android:name="path_7">
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="fillAlpha"
|
android:propertyName="fillAlpha"
|
||||||
android:startOffset="350"
|
android:startOffset="350"
|
||||||
android:duration="550"
|
|
||||||
android:valueFrom="0"
|
android:valueFrom="0"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
<target android:name="group_5">
|
<target android:name="group_5">
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<set>
|
<set>
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
android:propertyName="rotation"
|
android:propertyName="rotation"
|
||||||
android:startOffset="350"
|
android:startOffset="350"
|
||||||
android:duration="550"
|
|
||||||
android:valueFrom="-45"
|
android:valueFrom="-45"
|
||||||
android:valueTo="0"
|
android:valueTo="0"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="scaleX"
|
android:propertyName="scaleX"
|
||||||
android:startOffset="350"
|
android:startOffset="350"
|
||||||
android:duration="550"
|
|
||||||
android:valueFrom="3"
|
android:valueFrom="3"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="scaleY"
|
android:propertyName="scaleY"
|
||||||
android:startOffset="350"
|
android:startOffset="350"
|
||||||
android:duration="550"
|
|
||||||
android:valueFrom="3"
|
android:valueFrom="3"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
</set>
|
</set>
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
<target android:name="group_8">
|
<target android:name="group_8">
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="rotation"
|
android:propertyName="rotation"
|
||||||
android:startOffset="100"
|
android:startOffset="100"
|
||||||
android:duration="350"
|
|
||||||
android:valueFrom="-90"
|
android:valueFrom="-90"
|
||||||
android:valueTo="0"
|
android:valueTo="0"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
<target android:name="group_9">
|
<target android:name="group_9">
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<set>
|
<set>
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="rotation"
|
android:propertyName="rotation"
|
||||||
android:startOffset="100"
|
android:startOffset="100"
|
||||||
android:duration="350"
|
|
||||||
android:valueFrom="-90"
|
android:valueFrom="-90"
|
||||||
android:valueTo="0"
|
android:valueTo="0"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="scaleX"
|
android:propertyName="scaleX"
|
||||||
android:duration="350"
|
|
||||||
android:valueFrom="0"
|
android:valueFrom="0"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
android:propertyName="scaleY"
|
|
||||||
android:duration="350"
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
|
android:propertyName="scaleY"
|
||||||
android:valueFrom="0"
|
android:valueFrom="0"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
</set>
|
</set>
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
<target android:name="group_11">
|
<target android:name="group_11">
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="350"
|
||||||
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:propertyName="rotation"
|
android:propertyName="rotation"
|
||||||
android:startOffset="100"
|
android:startOffset="100"
|
||||||
android:duration="350"
|
|
||||||
android:valueFrom="-90"
|
android:valueFrom="-90"
|
||||||
android:valueTo="0"
|
android:valueTo="0"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
<target android:name="group_12">
|
<target android:name="group_12">
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<set>
|
<set>
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
android:propertyName="scaleX"
|
android:propertyName="scaleX"
|
||||||
android:duration="550"
|
|
||||||
android:valueFrom="0"
|
android:valueFrom="0"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/overshoot_interpolator"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
android:propertyName="scaleY"
|
|
||||||
android:duration="550"
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="scaleY"
|
||||||
android:valueFrom="0"
|
android:valueFrom="0"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/overshoot_interpolator"/>
|
|
||||||
</set>
|
</set>
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
|
@ -336,19 +335,19 @@
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<set>
|
<set>
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
android:propertyName="scaleX"
|
android:propertyName="scaleX"
|
||||||
android:duration="550"
|
|
||||||
android:valueFrom="0"
|
android:valueFrom="0"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/overshoot_interpolator"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
android:propertyName="scaleY"
|
|
||||||
android:duration="550"
|
android:duration="550"
|
||||||
|
android:interpolator="@android:anim/overshoot_interpolator"
|
||||||
|
android:propertyName="scaleY"
|
||||||
android:valueFrom="0"
|
android:valueFrom="0"
|
||||||
android:valueTo="1"
|
android:valueTo="1"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/overshoot_interpolator"/>
|
|
||||||
</set>
|
</set>
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
||||||
|
@ -356,21 +355,21 @@
|
||||||
<aapt:attr name="android:animation">
|
<aapt:attr name="android:animation">
|
||||||
<set>
|
<set>
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="200"
|
||||||
|
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
|
||||||
android:propertyName="rotation"
|
android:propertyName="rotation"
|
||||||
android:startOffset="350"
|
android:startOffset="350"
|
||||||
android:duration="200"
|
|
||||||
android:valueFrom="5"
|
android:valueFrom="5"
|
||||||
android:valueTo="0"
|
android:valueTo="0"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/accelerate_decelerate_interpolator"/>
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
|
android:duration="100"
|
||||||
|
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
|
||||||
android:propertyName="rotation"
|
android:propertyName="rotation"
|
||||||
android:startOffset="250"
|
android:startOffset="250"
|
||||||
android:duration="100"
|
|
||||||
android:valueFrom="0"
|
android:valueFrom="0"
|
||||||
android:valueTo="5"
|
android:valueTo="5"
|
||||||
android:valueType="floatType"
|
android:valueType="floatType" />
|
||||||
android:interpolator="@android:anim/accelerate_decelerate_interpolator"/>
|
|
||||||
</set>
|
</set>
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</target>
|
</target>
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ani.dantotsu.connections.crashlytics
|
||||||
|
|
||||||
|
class CrashlyticsFactory {
|
||||||
|
companion object {
|
||||||
|
fun createCrashlytics(): CrashlyticsInterface {
|
||||||
|
return CrashlyticsStub()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
app/src/fdroid/java/ani/dantotsu/others/AppUpdater.kt
Normal file
9
app/src/fdroid/java/ani/dantotsu/others/AppUpdater.kt
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ani.dantotsu.connections.crashlytics
|
||||||
|
|
||||||
|
class CrashlyticsFactory {
|
||||||
|
companion object {
|
||||||
|
fun createCrashlytics(): CrashlyticsInterface {
|
||||||
|
return FirebaseCrashlytics()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import androidx.core.content.FileProvider
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import ani.dantotsu.*
|
import ani.dantotsu.*
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -50,7 +51,7 @@ object AppUpdater {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger("Git Version : $version")
|
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 {
|
if (compareVersion(version) && !dontShow && !activity.isDestroyed) activity.runOnUiThread {
|
||||||
CustomBottomDialog.newInstance().apply {
|
CustomBottomDialog.newInstance().apply {
|
||||||
setTitleText(
|
setTitleText(
|
||||||
|
@ -71,7 +72,7 @@ object AppUpdater {
|
||||||
false
|
false
|
||||||
) { isChecked ->
|
) { isChecked ->
|
||||||
if (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)) {
|
setPositiveButton(currContext()!!.getString(R.string.lets_go)) {
|
||||||
|
@ -186,7 +187,7 @@ object AppUpdater {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openApk(context: Context, uri: Uri) {
|
private fun openApk(context: Context, uri: Uri) {
|
||||||
try {
|
try {
|
||||||
uri.path?.let {
|
uri.path?.let {
|
||||||
val contentUri = FileProvider.getUriForFile(
|
val contentUri = FileProvider.getUriForFile(
|
|
@ -10,7 +10,8 @@
|
||||||
android:required="false" />
|
android:required="false" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"
|
<uses-permission
|
||||||
|
android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"
|
||||||
tools:ignore="LeanbackUsesWifi" />
|
tools:ignore="LeanbackUsesWifi" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
@ -51,7 +52,7 @@
|
||||||
android:icon="${icon_placeholder}"
|
android:icon="${icon_placeholder}"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:largeHeap="true"
|
android:largeHeap="true"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="false"
|
||||||
android:roundIcon="${icon_placeholder_round}"
|
android:roundIcon="${icon_placeholder_round}"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Dantotsu"
|
android:theme="@style/Theme.Dantotsu"
|
||||||
|
@ -289,13 +290,13 @@
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".widgets.CurrentlyAiringRemoteViewsService"
|
android:name=".widgets.CurrentlyAiringRemoteViewsService"
|
||||||
android:permission="android.permission.BIND_REMOTEVIEWS"
|
android:exported="true"
|
||||||
android:exported="true" />
|
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||||
<service
|
<service
|
||||||
android:name=".download.video.ExoplayerDownloadService"
|
android:name=".download.video.ExoplayerDownloadService"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="dataSync">
|
android:foregroundServiceType="dataSync">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART" />
|
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
@ -317,19 +318,22 @@
|
||||||
android:name=".download.novel.NovelDownloaderService"
|
android:name=".download.novel.NovelDownloaderService"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="dataSync" />
|
android:foregroundServiceType="dataSync" />
|
||||||
<service android:name=".download.anime.AnimeDownloaderService"
|
<service
|
||||||
|
android:name=".download.anime.AnimeDownloaderService"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="dataSync" />
|
android:foregroundServiceType="dataSync" />
|
||||||
<service
|
<service
|
||||||
android:name=".connections.discord.DiscordService"
|
android:name=".connections.discord.DiscordService"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="dataSync" />
|
android:foregroundServiceType="dataSync" />
|
||||||
<service android:name="androidx.media3.exoplayer.scheduler.PlatformScheduler$PlatformSchedulerService"
|
<service
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
android:name="androidx.media3.exoplayer.scheduler.PlatformScheduler$PlatformSchedulerService"
|
||||||
android:exported="true"/>
|
android:exported="true"
|
||||||
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
|
|
||||||
<meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
<meta-data
|
||||||
android:value="androidx.media3.cast.DefaultCastOptionsProvider"/>
|
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||||
|
android:value="androidx.media3.cast.DefaultCastOptionsProvider" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
BIN
app/src/main/ic_launcher_alpha-playstore.png
Normal file
BIN
app/src/main/ic_launcher_alpha-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -8,16 +8,16 @@ import androidx.multidex.MultiDex
|
||||||
import androidx.multidex.MultiDexApplication
|
import androidx.multidex.MultiDexApplication
|
||||||
import ani.dantotsu.aniyomi.anime.custom.AppModule
|
import ani.dantotsu.aniyomi.anime.custom.AppModule
|
||||||
import ani.dantotsu.aniyomi.anime.custom.PreferenceModule
|
import ani.dantotsu.aniyomi.anime.custom.PreferenceModule
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.others.DisabledReports
|
import ani.dantotsu.others.DisabledReports
|
||||||
import ani.dantotsu.parsers.AnimeSources
|
import ani.dantotsu.parsers.AnimeSources
|
||||||
import ani.dantotsu.parsers.MangaSources
|
import ani.dantotsu.parsers.MangaSources
|
||||||
import ani.dantotsu.parsers.NovelSources
|
import ani.dantotsu.parsers.NovelSources
|
||||||
import ani.dantotsu.parsers.novel.NovelExtensionManager
|
import ani.dantotsu.parsers.novel.NovelExtensionManager
|
||||||
import ani.dantotsu.settings.SettingsActivity
|
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.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.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
|
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
|
||||||
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
||||||
|
@ -51,36 +51,35 @@ class App : MultiDexApplication() {
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.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<CrashlyticsInterface>()
|
||||||
|
crashlytics.initialize(this)
|
||||||
|
|
||||||
|
val useMaterialYou: Boolean = PrefManager.getVal(PrefName.UseMaterialYou)
|
||||||
if (useMaterialYou) {
|
if (useMaterialYou) {
|
||||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||||
//TODO: HarmonizedColors
|
|
||||||
}
|
}
|
||||||
registerActivityLifecycleCallbacks(mFTActivityLifecycleCallbacks)
|
registerActivityLifecycleCallbacks(mFTActivityLifecycleCallbacks)
|
||||||
|
|
||||||
Firebase.crashlytics.setCrashlyticsCollectionEnabled(!DisabledReports)
|
crashlytics.setCrashlyticsCollectionEnabled(!DisabledReports)
|
||||||
getSharedPreferences(
|
(PrefManager.getVal(PrefName.SharedUserID) as Boolean).let {
|
||||||
getString(R.string.preference_file_key),
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).getBoolean("shared_user_id", true).let {
|
|
||||||
if (!it) return@let
|
if (!it) return@let
|
||||||
val dUsername = getSharedPreferences(
|
val dUsername = PrefManager.getVal(PrefName.DiscordUserName, null as String?)
|
||||||
getString(R.string.preference_file_key),
|
val aUsername = PrefManager.getVal(PrefName.AnilistUserName, null as String?)
|
||||||
Context.MODE_PRIVATE
|
if (dUsername != null) {
|
||||||
).getString("discord_username", null)
|
crashlytics.setCustomKey("dUsername", dUsername)
|
||||||
val aUsername = getSharedPreferences(
|
}
|
||||||
getString(R.string.preference_file_key),
|
if (aUsername != null) {
|
||||||
Context.MODE_PRIVATE
|
crashlytics.setCustomKey("aUsername", aUsername)
|
||||||
).getString("anilist_username", null)
|
|
||||||
if (dUsername != null || aUsername != null) {
|
|
||||||
Firebase.crashlytics.setUserId("$dUsername - $aUsername")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FirebaseCrashlytics.getInstance().setCustomKey("device Info", SettingsActivity.getDeviceInfo())
|
crashlytics.setCustomKey("device Info", SettingsActivity.getDeviceInfo())
|
||||||
|
|
||||||
|
|
||||||
Injekt.importModule(AppModule(this))
|
|
||||||
Injekt.importModule(PreferenceModule(this))
|
|
||||||
|
|
||||||
initializeNetwork(baseContext)
|
initializeNetwork(baseContext)
|
||||||
|
|
||||||
|
@ -97,13 +96,13 @@ class App : MultiDexApplication() {
|
||||||
animeScope.launch {
|
animeScope.launch {
|
||||||
animeExtensionManager.findAvailableExtensions()
|
animeExtensionManager.findAvailableExtensions()
|
||||||
logger("Anime Extensions: ${animeExtensionManager.installedExtensionsFlow.first()}")
|
logger("Anime Extensions: ${animeExtensionManager.installedExtensionsFlow.first()}")
|
||||||
AnimeSources.init(animeExtensionManager.installedExtensionsFlow, this@App)
|
AnimeSources.init(animeExtensionManager.installedExtensionsFlow)
|
||||||
}
|
}
|
||||||
val mangaScope = CoroutineScope(Dispatchers.Default)
|
val mangaScope = CoroutineScope(Dispatchers.Default)
|
||||||
mangaScope.launch {
|
mangaScope.launch {
|
||||||
mangaExtensionManager.findAvailableExtensions()
|
mangaExtensionManager.findAvailableExtensions()
|
||||||
logger("Manga Extensions: ${mangaExtensionManager.installedExtensionsFlow.first()}")
|
logger("Manga Extensions: ${mangaExtensionManager.installedExtensionsFlow.first()}")
|
||||||
MangaSources.init(mangaExtensionManager.installedExtensionsFlow, this@App)
|
MangaSources.init(mangaExtensionManager.installedExtensionsFlow)
|
||||||
}
|
}
|
||||||
val novelScope = CoroutineScope(Dispatchers.Default)
|
val novelScope = CoroutineScope(Dispatchers.Default)
|
||||||
novelScope.launch {
|
novelScope.launch {
|
||||||
|
|
|
@ -11,11 +11,12 @@ import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.content.res.Resources.getSystem
|
import android.content.res.Resources.getSystem
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.Manifest
|
||||||
import android.media.MediaScannerConnection
|
import android.media.MediaScannerConnection
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkCapabilities.*
|
import android.net.NetworkCapabilities.*
|
||||||
|
@ -31,8 +32,11 @@ import android.view.*
|
||||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
import android.view.animation.*
|
import android.view.animation.*
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.ContextCompat.getSystemService
|
import androidx.core.content.ContextCompat.getSystemService
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.math.MathUtils.clamp
|
import androidx.core.math.MathUtils.clamp
|
||||||
|
@ -45,12 +49,15 @@ import androidx.viewpager2.widget.ViewPager2
|
||||||
import ani.dantotsu.BuildConfig.APPLICATION_ID
|
import ani.dantotsu.BuildConfig.APPLICATION_ID
|
||||||
import ani.dantotsu.connections.anilist.Genre
|
import ani.dantotsu.connections.anilist.Genre
|
||||||
import ani.dantotsu.connections.anilist.api.FuzzyDate
|
import ani.dantotsu.connections.anilist.api.FuzzyDate
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.databinding.ItemCountDownBinding
|
import ani.dantotsu.databinding.ItemCountDownBinding
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.parsers.ShowResponse
|
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.subcriptions.NotificationClickReceiver
|
||||||
import ani.dantotsu.themes.ThemeManager
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.model.GlideUrl
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade
|
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.bottomsheet.BottomSheetDialogFragment
|
||||||
import com.google.android.material.internal.ViewUtils
|
import com.google.android.material.internal.ViewUtils
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import nl.joery.animatedbottombar.AnimatedBottomBar
|
import nl.joery.animatedbottombar.AnimatedBottomBar
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.lang.Runnable
|
import java.lang.Runnable
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
@ -105,63 +113,22 @@ fun logger(e: Any?, print: Boolean = true) {
|
||||||
println(e)
|
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 <T> 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) {
|
fun initActivity(a: Activity) {
|
||||||
val window = a.window
|
val window = a.window
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
val uiSettings = loadData<UserInterfaceSettings>("ui_settings", toast = false)
|
val darkMode = PrefManager.getVal<Int>(PrefName.DarkMode)
|
||||||
?: UserInterfaceSettings().apply {
|
val immersiveMode: Boolean = PrefManager.getVal(PrefName.ImmersiveMode)
|
||||||
saveData("ui_settings", this)
|
darkMode.apply {
|
||||||
}
|
|
||||||
uiSettings.darkMode.apply {
|
|
||||||
AppCompatDelegate.setDefaultNightMode(
|
AppCompatDelegate.setDefaultNightMode(
|
||||||
when (this) {
|
when (this) {
|
||||||
true -> AppCompatDelegate.MODE_NIGHT_YES
|
2 -> AppCompatDelegate.MODE_NIGHT_YES
|
||||||
false -> AppCompatDelegate.MODE_NIGHT_NO
|
1 -> AppCompatDelegate.MODE_NIGHT_NO
|
||||||
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (uiSettings.immersiveMode) {
|
if (immersiveMode) {
|
||||||
if (navBarHeight == 0) {
|
if (navBarHeight == 0) {
|
||||||
ViewCompat.getRootWindowInsets(window.decorView.findViewById(android.R.id.content))
|
ViewCompat.getRootWindowInsets(window.decorView.findViewById(android.R.id.content))
|
||||||
?.apply {
|
?.apply {
|
||||||
|
@ -216,7 +183,11 @@ open class BottomSheetDialogFragment : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
val typedValue = TypedValue()
|
val typedValue = TypedValue()
|
||||||
val theme = requireContext().theme
|
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
|
window.navigationBarColor = typedValue.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,20 +296,20 @@ class InputFilterMinMax(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ZoomOutPageTransformer(private val uiSettings: UserInterfaceSettings) :
|
class ZoomOutPageTransformer() :
|
||||||
ViewPager2.PageTransformer {
|
ViewPager2.PageTransformer {
|
||||||
override fun transformPage(view: View, position: Float) {
|
override fun transformPage(view: View, position: Float) {
|
||||||
if (position == 0.0f && uiSettings.layoutAnimations) {
|
if (position == 0.0f && PrefManager.getVal(PrefName.LayoutAnimations)) {
|
||||||
setAnimation(
|
setAnimation(
|
||||||
view.context,
|
view.context,
|
||||||
view,
|
view,
|
||||||
uiSettings,
|
|
||||||
300,
|
300,
|
||||||
floatArrayOf(1.3f, 1f, 1.3f, 1f),
|
floatArrayOf(1.3f, 1f, 1.3f, 1f),
|
||||||
0.5f to 0f
|
0.5f to 0f
|
||||||
)
|
)
|
||||||
ObjectAnimator.ofFloat(view, "alpha", 0f, 1.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(
|
fun setAnimation(
|
||||||
context: Context,
|
context: Context,
|
||||||
viewToAnimate: View,
|
viewToAnimate: View,
|
||||||
uiSettings: UserInterfaceSettings,
|
|
||||||
duration: Long = 150,
|
duration: Long = 150,
|
||||||
list: FloatArray = floatArrayOf(0.0f, 1.0f, 0.0f, 1.0f),
|
list: FloatArray = floatArrayOf(0.0f, 1.0f, 0.0f, 1.0f),
|
||||||
pivot: Pair<Float, Float> = 0.5f to 0.5f
|
pivot: Pair<Float, Float> = 0.5f to 0.5f
|
||||||
) {
|
) {
|
||||||
if (uiSettings.layoutAnimations) {
|
if (PrefManager.getVal(PrefName.LayoutAnimations)) {
|
||||||
val anim = ScaleAnimation(
|
val anim = ScaleAnimation(
|
||||||
list[0],
|
list[0],
|
||||||
list[1],
|
list[1],
|
||||||
|
@ -362,7 +332,7 @@ fun setAnimation(
|
||||||
Animation.RELATIVE_TO_SELF,
|
Animation.RELATIVE_TO_SELF,
|
||||||
pivot.second
|
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)
|
anim.setInterpolator(context, R.anim.over_shoot)
|
||||||
viewToAnimate.startAnimation(anim)
|
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(
|
FileProvider.getUriForFile(
|
||||||
context,
|
context,
|
||||||
"$APPLICATION_ID.provider",
|
"$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) {
|
fun shareImage(title: String, bitmap: Bitmap, context: Context) {
|
||||||
|
|
||||||
val contentUri = FileProvider.getUriForFile(
|
val contentUri = FileProvider.getUriForFile(
|
||||||
|
@ -743,10 +812,11 @@ fun MutableMap<String, Genre>.checkGenreTime(genre: String): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSlideIn(uiSettings: UserInterfaceSettings) = AnimationSet(false).apply {
|
fun setSlideIn() = AnimationSet(false).apply {
|
||||||
if (uiSettings.layoutAnimations) {
|
if (PrefManager.getVal(PrefName.LayoutAnimations)) {
|
||||||
var animation: Animation = AlphaAnimation(0.0f, 1.0f)
|
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()
|
animation.interpolator = AccelerateDecelerateInterpolator()
|
||||||
addAnimation(animation)
|
addAnimation(animation)
|
||||||
|
|
||||||
|
@ -757,16 +827,17 @@ fun setSlideIn(uiSettings: UserInterfaceSettings) = AnimationSet(false).apply {
|
||||||
Animation.RELATIVE_TO_SELF, 0f
|
Animation.RELATIVE_TO_SELF, 0f
|
||||||
)
|
)
|
||||||
|
|
||||||
animation.duration = (750 * uiSettings.animationSpeed).toLong()
|
animation.duration = (750 * animationSpeed).toLong()
|
||||||
animation.interpolator = OvershootInterpolator(1.1f)
|
animation.interpolator = OvershootInterpolator(1.1f)
|
||||||
addAnimation(animation)
|
addAnimation(animation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSlideUp(uiSettings: UserInterfaceSettings) = AnimationSet(false).apply {
|
fun setSlideUp() = AnimationSet(false).apply {
|
||||||
if (uiSettings.layoutAnimations) {
|
if (PrefManager.getVal(PrefName.LayoutAnimations)) {
|
||||||
var animation: Animation = AlphaAnimation(0.0f, 1.0f)
|
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()
|
animation.interpolator = AccelerateDecelerateInterpolator()
|
||||||
addAnimation(animation)
|
addAnimation(animation)
|
||||||
|
|
||||||
|
@ -777,7 +848,7 @@ fun setSlideUp(uiSettings: UserInterfaceSettings) = AnimationSet(false).apply {
|
||||||
Animation.RELATIVE_TO_SELF, 0f
|
Animation.RELATIVE_TO_SELF, 0f
|
||||||
)
|
)
|
||||||
|
|
||||||
animation.duration = (750 * uiSettings.animationSpeed).toLong()
|
animation.duration = (750 * animationSpeed).toLong()
|
||||||
animation.interpolator = OvershootInterpolator(1.1f)
|
animation.interpolator = OvershootInterpolator(1.1f)
|
||||||
addAnimation(animation)
|
addAnimation(animation)
|
||||||
}
|
}
|
||||||
|
@ -839,7 +910,7 @@ fun snackString(s: String?, activity: Activity? = null, clipboard: String? = nul
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger(e.stackTraceToString())
|
logger(e.stackTraceToString())
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -964,8 +1035,7 @@ const val INCOGNITO_CHANNEL_ID = 26
|
||||||
fun incognitoNotification(context: Context) {
|
fun incognitoNotification(context: Context) {
|
||||||
val notificationManager =
|
val notificationManager =
|
||||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
val incognito = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
|
||||||
.getBoolean("incognito", false)
|
|
||||||
if (incognito) {
|
if (incognito) {
|
||||||
val intent = Intent(context, NotificationClickReceiver::class.java)
|
val intent = Intent(context, NotificationClickReceiver::class.java)
|
||||||
val pendingIntent = PendingIntent.getBroadcast(
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
|
|
|
@ -2,7 +2,6 @@ package ani.dantotsu
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.drawable.Animatable
|
import android.graphics.drawable.Animatable
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
|
@ -45,15 +44,13 @@ import ani.dantotsu.home.MangaFragment
|
||||||
import ani.dantotsu.home.NoInternet
|
import ani.dantotsu.home.NoInternet
|
||||||
import ani.dantotsu.media.MediaDetailsActivity
|
import ani.dantotsu.media.MediaDetailsActivity
|
||||||
import ani.dantotsu.others.CustomBottomDialog
|
import ani.dantotsu.others.CustomBottomDialog
|
||||||
import ani.dantotsu.others.LangSet
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.others.SharedPreferenceBooleanLiveData
|
import ani.dantotsu.settings.saving.PrefManager.asLiveBool
|
||||||
import ani.dantotsu.parsers.novel.NovelExtensionManager
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.settings.UserInterfaceSettings
|
import ani.dantotsu.settings.saving.SharedPreferenceBooleanLiveData
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription
|
import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
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.Markwon
|
||||||
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -73,18 +70,16 @@ class MainActivity : AppCompatActivity() {
|
||||||
private val scope = lifecycleScope
|
private val scope = lifecycleScope
|
||||||
private var load = false
|
private var load = false
|
||||||
|
|
||||||
private var uiSettings = UserInterfaceSettings()
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("InternalInsetResource", "DiscouragedApi")
|
@SuppressLint("InternalInsetResource", "DiscouragedApi")
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
LangSet.setLocale(this)
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
//get FRAGMENT_CLASS_NAME from intent
|
//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)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
@ -98,12 +93,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
backgroundDrawable.setColor(semiTransparentColor)
|
backgroundDrawable.setColor(semiTransparentColor)
|
||||||
_bottomBar.background = backgroundDrawable
|
_bottomBar.background = backgroundDrawable
|
||||||
}
|
}
|
||||||
val sharedPreferences = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
_bottomBar.background = ContextCompat.getDrawable(this, R.drawable.bottom_nav_gray)
|
||||||
val colorOverflow = sharedPreferences.getBoolean("colorOverflow", false)
|
|
||||||
if (!colorOverflow) {
|
|
||||||
_bottomBar.background = ContextCompat.getDrawable(this, R.drawable.bottom_nav_gray)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
val offset = try {
|
val offset = try {
|
||||||
val statusBarHeightId = resources.getIdentifier("status_bar_height", "dimen", "android")
|
val statusBarHeightId = resources.getIdentifier("status_bar_height", "dimen", "android")
|
||||||
|
@ -114,11 +105,10 @@ class MainActivity : AppCompatActivity() {
|
||||||
val layoutParams = binding.incognito.layoutParams as ViewGroup.MarginLayoutParams
|
val layoutParams = binding.incognito.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
layoutParams.topMargin = 11 * offset / 12
|
layoutParams.topMargin = 11 * offset / 12
|
||||||
binding.incognito.layoutParams = layoutParams
|
binding.incognito.layoutParams = layoutParams
|
||||||
incognitoLiveData = SharedPreferenceBooleanLiveData(
|
incognitoLiveData = PrefManager.getLiveVal(
|
||||||
sharedPreferences,
|
PrefName.Incognito,
|
||||||
"incognito",
|
|
||||||
false
|
false
|
||||||
)
|
).asLiveBool()
|
||||||
incognitoLiveData.observe(this) {
|
incognitoLiveData.observe(this) {
|
||||||
if (it) {
|
if (it) {
|
||||||
val slideDownAnim = ObjectAnimator.ofFloat(
|
val slideDownAnim = ObjectAnimator.ofFloat(
|
||||||
|
@ -162,7 +152,9 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val preferences: SourcePreferences = Injekt.get()
|
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(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"You have extension updates available!",
|
"You have extension updates available!",
|
||||||
|
@ -213,24 +205,22 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
binding.root.doOnAttach {
|
binding.root.doOnAttach {
|
||||||
initActivity(this)
|
initActivity(this)
|
||||||
uiSettings = loadData("ui_settings") ?: uiSettings
|
selectedOption = if (fragment != null) {
|
||||||
selectedOption = if (FRAGMENT_CLASS_NAME != null) {
|
when (fragment) {
|
||||||
when (FRAGMENT_CLASS_NAME) {
|
|
||||||
AnimeFragment::class.java.name -> 0
|
AnimeFragment::class.java.name -> 0
|
||||||
HomeFragment::class.java.name -> 1
|
HomeFragment::class.java.name -> 1
|
||||||
MangaFragment::class.java.name -> 2
|
MangaFragment::class.java.name -> 2
|
||||||
else -> 1
|
else -> 1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uiSettings.defaultStartUpTab
|
PrefManager.getVal(PrefName.DefaultStartUpTab)
|
||||||
}
|
}
|
||||||
binding.includedNavbar.navbarContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
binding.includedNavbar.navbarContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
bottomMargin = navBarHeight
|
bottomMargin = navBarHeight
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val offlineMode = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
val offlineMode: Boolean = PrefManager.getVal(PrefName.OfflineMode)
|
||||||
.getBoolean("offlineMode", false)
|
|
||||||
if (!isOnline(this)) {
|
if (!isOnline(this)) {
|
||||||
snackString(this@MainActivity.getString(R.string.no_internet_connection))
|
snackString(this@MainActivity.getString(R.string.no_internet_connection))
|
||||||
startActivity(Intent(this, NoInternet::class.java))
|
startActivity(Intent(this, NoInternet::class.java))
|
||||||
|
@ -251,7 +241,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
mainViewPager.isUserInputEnabled = false
|
mainViewPager.isUserInputEnabled = false
|
||||||
mainViewPager.adapter =
|
mainViewPager.adapter =
|
||||||
ViewPagerAdapter(supportFragmentManager, lifecycle)
|
ViewPagerAdapter(supportFragmentManager, lifecycle)
|
||||||
mainViewPager.setPageTransformer(ZoomOutPageTransformer(uiSettings))
|
mainViewPager.setPageTransformer(ZoomOutPageTransformer())
|
||||||
navbar.setOnTabSelectListener(object :
|
navbar.setOnTabSelectListener(object :
|
||||||
AnimatedBottomBar.OnTabSelectListener {
|
AnimatedBottomBar.OnTabSelectListener {
|
||||||
override fun onTabSelected(
|
override fun onTabSelected(
|
||||||
|
@ -305,7 +295,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
if (loadData<Boolean>("allow_opening_links", this) != true) {
|
if (!(PrefManager.getVal(PrefName.AllowOpeningLinks) as Boolean)) {
|
||||||
CustomBottomDialog.newInstance().apply {
|
CustomBottomDialog.newInstance().apply {
|
||||||
title = "Allow Dantotsu to automatically open Anilist & MAL Links?"
|
title = "Allow Dantotsu to automatically open Anilist & MAL Links?"
|
||||||
val md = "Open settings & click +Add Links & select Anilist & Mal urls"
|
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)) {
|
setNegativeButton(this@MainActivity.getString(R.string.no)) {
|
||||||
saveData("allow_opening_links", true, this@MainActivity)
|
PrefManager.setVal(PrefName.AllowOpeningLinks, true)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
setPositiveButton(this@MainActivity.getString(R.string.yes)) {
|
setPositiveButton(this@MainActivity.getString(R.string.yes)) {
|
||||||
saveData("allow_opening_links", true, this@MainActivity)
|
PrefManager.setVal(PrefName.AllowOpeningLinks, true)
|
||||||
tryWith(true) {
|
tryWith(true) {
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS)
|
Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS)
|
||||||
.setData(Uri.parse("package:$packageName"))
|
.setData(Uri.parse("package:$packageName"))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
dismiss()
|
||||||
}
|
}
|
||||||
}.show(supportFragmentManager, "dialog")
|
}.show(supportFragmentManager, "dialog")
|
||||||
}
|
}
|
||||||
|
@ -342,7 +333,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
while (downloadCursor.moveToNext()) {
|
while (downloadCursor.moveToNext()) {
|
||||||
val download = downloadCursor.download
|
val download = downloadCursor.download
|
||||||
Log.e("Downloader", download.request.uri.toString())
|
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.mimeType.toString())
|
||||||
Log.e("Downloader", download.request.data.size.toString())
|
Log.e("Downloader", download.request.data.size.toString())
|
||||||
Log.e("Downloader", download.bytesDownloaded.toString())
|
Log.e("Downloader", download.bytesDownloaded.toString())
|
||||||
|
|
|
@ -2,11 +2,11 @@ package ani.dantotsu.aniyomi.anime.custom
|
||||||
|
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.database.StandaloneDatabaseProvider
|
import androidx.media3.database.StandaloneDatabaseProvider
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.media.manga.MangaCache
|
import ani.dantotsu.media.manga.MangaCache
|
||||||
import ani.dantotsu.parsers.novel.NovelExtensionManager
|
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.anime.AnimeExtensionManager
|
||||||
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
|
||||||
import eu.kanade.tachiyomi.source.anime.AndroidAnimeSourceManager
|
import eu.kanade.tachiyomi.source.anime.AndroidAnimeSourceManager
|
||||||
import eu.kanade.tachiyomi.source.manga.AndroidMangaSourceManager
|
import eu.kanade.tachiyomi.source.manga.AndroidMangaSourceManager
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
@ -36,7 +35,7 @@ class AppModule(val app: Application) : InjektModule {
|
||||||
|
|
||||||
addSingletonFactory { DownloadsManager(app) }
|
addSingletonFactory { DownloadsManager(app) }
|
||||||
|
|
||||||
addSingletonFactory { NetworkHelper(app, get()) }
|
addSingletonFactory { NetworkHelper(app) }
|
||||||
|
|
||||||
addSingletonFactory { AnimeExtensionManager(app) }
|
addSingletonFactory { AnimeExtensionManager(app) }
|
||||||
addSingletonFactory { MangaExtensionManager(app) }
|
addSingletonFactory { MangaExtensionManager(app) }
|
||||||
|
@ -45,9 +44,6 @@ class AppModule(val app: Application) : InjektModule {
|
||||||
addSingletonFactory<AnimeSourceManager> { AndroidAnimeSourceManager(app, get()) }
|
addSingletonFactory<AnimeSourceManager> { AndroidAnimeSourceManager(app, get()) }
|
||||||
addSingletonFactory<MangaSourceManager> { AndroidMangaSourceManager(app, get()) }
|
addSingletonFactory<MangaSourceManager> { AndroidMangaSourceManager(app, get()) }
|
||||||
|
|
||||||
val sharedPreferences = app.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
|
||||||
addSingleton(sharedPreferences)
|
|
||||||
|
|
||||||
addSingletonFactory {
|
addSingletonFactory {
|
||||||
Json {
|
Json {
|
||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
|
@ -57,6 +53,10 @@ class AppModule(val app: Application) : InjektModule {
|
||||||
|
|
||||||
addSingletonFactory { StandaloneDatabaseProvider(app) }
|
addSingletonFactory { StandaloneDatabaseProvider(app) }
|
||||||
|
|
||||||
|
addSingletonFactory<CrashlyticsInterface> {
|
||||||
|
ani.dantotsu.connections.crashlytics.CrashlyticsFactory.createCrashlytics()
|
||||||
|
}
|
||||||
|
|
||||||
addSingletonFactory { MangaCache() }
|
addSingletonFactory { MangaCache() }
|
||||||
|
|
||||||
ContextCompat.getMainExecutor(app).execute {
|
ContextCompat.getMainExecutor(app).execute {
|
||||||
|
@ -72,13 +72,6 @@ class PreferenceModule(val application: Application) : InjektModule {
|
||||||
AndroidPreferenceStore(application)
|
AndroidPreferenceStore(application)
|
||||||
}
|
}
|
||||||
|
|
||||||
addSingletonFactory {
|
|
||||||
NetworkPreferences(
|
|
||||||
preferenceStore = get(),
|
|
||||||
verboseLogging = false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
addSingletonFactory {
|
addSingletonFactory {
|
||||||
SourcePreferences(get())
|
SourcePreferences(get())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,15 @@ import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.mal.MAL
|
import ani.dantotsu.connections.mal.MAL
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.toast
|
import ani.dantotsu.toast
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
fun updateProgress(media: Media, number: String) {
|
fun updateProgress(media: Media, number: String) {
|
||||||
val incognito = currContext()?.getSharedPreferences("Dantotsu", 0)
|
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
|
||||||
?.getBoolean("incognito", false) ?: false
|
|
||||||
if (!incognito) {
|
if (!incognito) {
|
||||||
if (Anilist.userid != null) {
|
if (Anilist.userid != null) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
|
|
@ -8,8 +8,10 @@ import ani.dantotsu.R
|
||||||
import ani.dantotsu.client
|
import ani.dantotsu.client
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.openLinkInBrowser
|
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 ani.dantotsu.tryWithSuspend
|
||||||
import java.io.File
|
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
|
|
||||||
object Anilist {
|
object Anilist {
|
||||||
|
@ -28,6 +30,8 @@ object Anilist {
|
||||||
var genres: ArrayList<String>? = null
|
var genres: ArrayList<String>? = null
|
||||||
var tags: Map<Boolean, List<String>>? = null
|
var tags: Map<Boolean, List<String>>? = null
|
||||||
|
|
||||||
|
var rateLimitReset: Long = 0
|
||||||
|
|
||||||
val sortBy = listOf(
|
val sortBy = listOf(
|
||||||
"SCORE_DESC",
|
"SCORE_DESC",
|
||||||
"POPULARITY_DESC",
|
"POPULARITY_DESC",
|
||||||
|
@ -94,15 +98,12 @@ object Anilist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSavedToken(context: Context): Boolean {
|
fun getSavedToken(): Boolean {
|
||||||
if ("anilistToken" in context.fileList()) {
|
token = PrefManager.getVal(PrefName.AnilistToken, null as String?)
|
||||||
token = File(context.filesDir, "anilistToken").readText()
|
return !token.isNullOrEmpty()
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeSavedToken(context: Context) {
|
fun removeSavedToken() {
|
||||||
token = null
|
token = null
|
||||||
username = null
|
username = null
|
||||||
adult = false
|
adult = false
|
||||||
|
@ -111,9 +112,7 @@ object Anilist {
|
||||||
bg = null
|
bg = null
|
||||||
episodesWatched = null
|
episodesWatched = null
|
||||||
chapterRead = null
|
chapterRead = null
|
||||||
if ("anilistToken" in context.fileList()) {
|
PrefManager.removeVal(PrefName.AnilistToken)
|
||||||
File(context.filesDir, "anilistToken").delete()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend inline fun <reified T : Any> executeQuery(
|
suspend inline fun <reified T : Any> executeQuery(
|
||||||
|
@ -125,6 +124,11 @@ object Anilist {
|
||||||
cache: Int? = null
|
cache: Int? = null
|
||||||
): T? {
|
): T? {
|
||||||
return tryWithSuspend {
|
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(
|
val data = mapOf(
|
||||||
"query" to query,
|
"query" to query,
|
||||||
"variables" to variables
|
"variables" to variables
|
||||||
|
@ -143,6 +147,16 @@ object Anilist {
|
||||||
data = data,
|
data = data,
|
||||||
cacheTime = cache ?: 10
|
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 (!json.text.startsWith("{")) throw Exception(currContext()?.getString(R.string.anilist_down))
|
||||||
if (show) println("Response : ${json.text}")
|
if (show) println("Response : ${json.text}")
|
||||||
json.parsed()
|
json.parsed()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package ani.dantotsu.connections.anilist
|
package ani.dantotsu.connections.anilist
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.util.Base64
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.checkGenreTime
|
import ani.dantotsu.checkGenreTime
|
||||||
import ani.dantotsu.checkId
|
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.connections.anilist.api.Query
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.isOnline
|
import ani.dantotsu.isOnline
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.logError
|
import ani.dantotsu.logError
|
||||||
import ani.dantotsu.media.Author
|
import ani.dantotsu.media.Author
|
||||||
import ani.dantotsu.media.Character
|
import ani.dantotsu.media.Character
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.media.Studio
|
import ani.dantotsu.media.Studio
|
||||||
import ani.dantotsu.others.MalScraper
|
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 ani.dantotsu.snackString
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.runBlocking
|
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
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
class AnilistQueries {
|
class AnilistQueries {
|
||||||
|
@ -35,12 +40,7 @@ class AnilistQueries {
|
||||||
}.also { println("time : $it") }
|
}.also { println("time : $it") }
|
||||||
val user = response?.data?.user ?: return false
|
val user = response?.data?.user ?: return false
|
||||||
|
|
||||||
currContext()?.let {
|
PrefManager.setVal(PrefName.AnilistUserName, user.name)
|
||||||
it.getSharedPreferences(it.getString(R.string.preference_file_key), Context.MODE_PRIVATE)
|
|
||||||
.edit()
|
|
||||||
.putString("anilist_username", user.name)
|
|
||||||
.apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
Anilist.userid = user.id
|
Anilist.userid = user.id
|
||||||
Anilist.username = user.name
|
Anilist.username = user.name
|
||||||
|
@ -281,8 +281,8 @@ class AnilistQueries {
|
||||||
}
|
}
|
||||||
|
|
||||||
statuses.forEach { repeat(it) }
|
statuses.forEach { repeat(it) }
|
||||||
val set = loadData<MutableSet<Int>>("continue_$type")
|
val set = PrefManager.getCustomVal<Set<Int>>("continue_$type", setOf()).toMutableSet()
|
||||||
if (set != null) {
|
if (set.isNotEmpty()) {
|
||||||
set.reversed().forEach {
|
set.reversed().forEach {
|
||||||
if (map.containsKey(it)) returnArray.add(map[it]!!)
|
if (map.containsKey(it)) returnArray.add(map[it]!!)
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,11 @@ class AnilistQueries {
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun bannerImage(type: String): String? {
|
private suspend fun bannerImage(type: String): String? {
|
||||||
var image = loadData<BannerImage>("banner_$type")
|
//var image = loadData<BannerImage>("banner_$type")
|
||||||
|
val image: BannerImage? = BannerImage(
|
||||||
|
PrefManager.getCustomVal("banner_${type}_url", null),
|
||||||
|
PrefManager.getCustomVal("banner_${type}_time", 0L)
|
||||||
|
)
|
||||||
if (image == null || image.checkTime()) {
|
if (image == null || image.checkTime()) {
|
||||||
val response =
|
val response =
|
||||||
executeQuery<Query.MediaListCollection>("""{ MediaListCollection(userId: ${Anilist.userid}, type: $type, chunk:1,perChunk:25, sort: [SCORE_DESC,UPDATED_TIME_DESC]) { lists { entries{ media { id bannerImage } } } } } """)
|
executeQuery<Query.MediaListCollection>("""{ 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
|
else null
|
||||||
}
|
}
|
||||||
}?.flatten()?.randomOrNull() ?: return null
|
}?.flatten()?.randomOrNull() ?: return null
|
||||||
|
PrefManager.setCustomVal("banner_${type}_url", random)
|
||||||
image = BannerImage(
|
PrefManager.setCustomVal("banner_${type}_time", System.currentTimeMillis())
|
||||||
random,
|
return random
|
||||||
System.currentTimeMillis()
|
|
||||||
)
|
|
||||||
saveData("banner_$type", image)
|
|
||||||
return image.url
|
|
||||||
} else return image.url
|
} else return image.url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,11 +419,17 @@ class AnilistQueries {
|
||||||
|
|
||||||
sorted["Favourites"] = favMedia(anime)
|
sorted["Favourites"] = favMedia(anime)
|
||||||
sorted["Favourites"]?.sortWith(compareBy { it.userFavOrder })
|
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
|
sorted["All"] = all
|
||||||
val listsort = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
val listSort: String = if (anime) PrefManager.getVal(PrefName.AnimeListSortOrder)
|
||||||
?.getString("sort_order", "score")
|
else PrefManager.getVal(PrefName.MangaListSortOrder)
|
||||||
val sort = listsort ?: sortOrder ?: options?.rowOrder
|
val sort = listSort ?: sortOrder ?: options?.rowOrder
|
||||||
for (i in sorted.keys) {
|
for (i in sorted.keys) {
|
||||||
when (sort) {
|
when (sort) {
|
||||||
"score" -> sorted[i]?.sortWith { b, a ->
|
"score" -> sorted[i]?.sortWith { b, a ->
|
||||||
|
@ -444,11 +450,19 @@ class AnilistQueries {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun getGenresAndTags(activity: Activity): Boolean {
|
suspend fun getGenresAndTags(): Boolean {
|
||||||
var genres: ArrayList<String>? = loadData("genres_list", activity)
|
var genres: ArrayList<String>? = PrefManager.getVal<Set<String>>(PrefName.GenresList)
|
||||||
var tags: Map<Boolean, List<String>>? = loadData("tags_map", activity)
|
.toMutableList() as ArrayList<String>?
|
||||||
|
val adultTags = PrefManager.getVal<Set<String>>(PrefName.TagsListIsAdult).toMutableList()
|
||||||
|
val nonAdultTags =
|
||||||
|
PrefManager.getVal<Set<String>>(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<Query.GenreCollection>(
|
executeQuery<Query.GenreCollection>(
|
||||||
"""{GenreCollection}""",
|
"""{GenreCollection}""",
|
||||||
force = true,
|
force = true,
|
||||||
|
@ -458,7 +472,7 @@ class AnilistQueries {
|
||||||
forEach {
|
forEach {
|
||||||
genres?.add(it)
|
genres?.add(it)
|
||||||
}
|
}
|
||||||
saveData("genres_list", genres!!)
|
PrefManager.setVal(PrefName.GenresList, genres?.toSet())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tags == null) {
|
if (tags == null) {
|
||||||
|
@ -476,10 +490,11 @@ class AnilistQueries {
|
||||||
true to adult,
|
true to adult,
|
||||||
false to good
|
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.genres = genres
|
||||||
Anilist.tags = tags
|
Anilist.tags = tags
|
||||||
true
|
true
|
||||||
|
@ -496,8 +511,37 @@ class AnilistQueries {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun <K, V : Serializable> saveSerializableMap(prefKey: String, map: Map<K, V>) {
|
||||||
|
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 <K, V : Serializable> loadSerializableMap(prefKey: String): Map<K, V>? {
|
||||||
|
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<K, V>
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun getGenreThumbnail(genre: String): Genre? {
|
private suspend fun getGenreThumbnail(genre: String): Genre? {
|
||||||
val genres = loadData<MutableMap<String, Genre>>("genre_thumb") ?: mutableMapOf()
|
val genres: MutableMap<String, Genre> =
|
||||||
|
loadSerializableMap<String, Genre>("genre_thumb")?.toMutableMap()
|
||||||
|
?: mutableMapOf()
|
||||||
if (genres.checkGenreTime(genre)) {
|
if (genres.checkGenreTime(genre)) {
|
||||||
try {
|
try {
|
||||||
val genreQuery =
|
val genreQuery =
|
||||||
|
@ -510,7 +554,7 @@ class AnilistQueries {
|
||||||
it.bannerImage!!,
|
it.bannerImage!!,
|
||||||
System.currentTimeMillis()
|
System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
saveData("genre_thumb", genres)
|
saveSerializableMap("genre_thumb", genres)
|
||||||
return genres[genre]
|
return genres[genre]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -665,7 +709,7 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
|
||||||
page = pageInfo.currentPage.toString().toIntOrNull() ?: 0,
|
page = pageInfo.currentPage.toString().toIntOrNull() ?: 0,
|
||||||
hasNextPage = pageInfo.hasNextPage == true,
|
hasNextPage = pageInfo.hasNextPage == true,
|
||||||
)
|
)
|
||||||
} else snackString(currContext()?.getString(R.string.empty_response))
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -723,7 +767,7 @@ Page(page:$page,perPage:50) {
|
||||||
if (smaller) {
|
if (smaller) {
|
||||||
val response = execute()?.airingSchedules ?: return null
|
val response = execute()?.airingSchedules ?: return null
|
||||||
val idArr = mutableListOf<Int>()
|
val idArr = mutableListOf<Int>()
|
||||||
val listOnly = loadData("recently_list_only") ?: false
|
val listOnly: Boolean = PrefManager.getVal(PrefName.RecentlyListOnly)
|
||||||
return response.mapNotNull { i ->
|
return response.mapNotNull { i ->
|
||||||
i.media?.let {
|
i.media?.let {
|
||||||
if (!idArr.contains(it.id))
|
if (!idArr.contains(it.id))
|
||||||
|
|
|
@ -5,12 +5,14 @@ import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import ani.dantotsu.BuildConfig
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.discord.Discord
|
import ani.dantotsu.connections.discord.Discord
|
||||||
import ani.dantotsu.connections.mal.MAL
|
import ani.dantotsu.connections.mal.MAL
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.others.AppUpdater
|
import ani.dantotsu.others.AppUpdater
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.snackString
|
import ani.dantotsu.snackString
|
||||||
import ani.dantotsu.tryWithSuspend
|
import ani.dantotsu.tryWithSuspend
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -19,12 +21,8 @@ import kotlinx.coroutines.launch
|
||||||
|
|
||||||
suspend fun getUserId(context: Context, block: () -> Unit) {
|
suspend fun getUserId(context: Context, block: () -> Unit) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val sharedPref = context.getSharedPreferences(
|
val token = PrefManager.getVal(PrefName.DiscordToken, null as String?)
|
||||||
context.getString(R.string.preference_file_key),
|
val userid = PrefManager.getVal(PrefName.DiscordId, null as String?)
|
||||||
Context.MODE_PRIVATE
|
|
||||||
)
|
|
||||||
val token = sharedPref.getString("discord_token", null)
|
|
||||||
val userid = sharedPref.getString("discord_id", null)
|
|
||||||
if (userid == null && token != null) {
|
if (userid == null && token != null) {
|
||||||
/*if (!Discord.getUserData())
|
/*if (!Discord.getUserData())
|
||||||
snackString(context.getString(R.string.error_loading_discord_user_data))*/
|
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 setRecommendation() = recommendation.postValue(Anilist.query.recommendations())
|
||||||
|
|
||||||
suspend fun loadMain(context: FragmentActivity) {
|
suspend fun loadMain(context: FragmentActivity) {
|
||||||
Anilist.getSavedToken(context)
|
Anilist.getSavedToken()
|
||||||
MAL.getSavedToken(context)
|
MAL.getSavedToken(context)
|
||||||
Discord.getSavedToken(context)
|
Discord.getSavedToken(context)
|
||||||
if (loadData<Boolean>("check_update") != false) AppUpdater.check(context)
|
if (!BuildConfig.FLAVOR.contains("fdroid")) {
|
||||||
genres.postValue(Anilist.query.getGenresAndTags(context))
|
if (PrefManager.getVal(PrefName.CheckUpdate)) AppUpdater.check(context)
|
||||||
|
}
|
||||||
|
genres.postValue(Anilist.query.getGenresAndTags())
|
||||||
}
|
}
|
||||||
|
|
||||||
val empty = MutableLiveData<Boolean>(null)
|
val empty = MutableLiveData<Boolean>(null)
|
||||||
|
|
|
@ -1,29 +1,26 @@
|
||||||
package ani.dantotsu.connections.anilist
|
package ani.dantotsu.connections.anilist
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import ani.dantotsu.logError
|
import ani.dantotsu.logError
|
||||||
import ani.dantotsu.logger
|
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.startMainActivity
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
|
|
||||||
class Login : AppCompatActivity() {
|
class Login : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
val data: Uri? = intent?.data
|
val data: Uri? = intent?.data
|
||||||
logger(data.toString())
|
logger(data.toString())
|
||||||
try {
|
try {
|
||||||
Anilist.token =
|
Anilist.token =
|
||||||
Regex("""(?<=access_token=).+(?=&token_type)""").find(data.toString())!!.value
|
Regex("""(?<=access_token=).+(?=&token_type)""").find(data.toString())!!.value
|
||||||
val filename = "anilistToken"
|
PrefManager.setVal(PrefName.AnilistToken, Anilist.token ?: "")
|
||||||
this.openFileOutput(filename, Context.MODE_PRIVATE).use {
|
|
||||||
it.write(Anilist.token!!.toByteArray())
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logError(e)
|
logError(e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,13 @@ import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import ani.dantotsu.loadMedia
|
import ani.dantotsu.loadMedia
|
||||||
import ani.dantotsu.others.LangSet
|
|
||||||
import ani.dantotsu.startMainActivity
|
import ani.dantotsu.startMainActivity
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
|
|
||||||
class UrlMedia : Activity() {
|
class UrlMedia : Activity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
|
var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
|
||||||
var isMAL = false
|
var isMAL = false
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,9 +3,10 @@ package ani.dantotsu.connections.discord
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.content.edit
|
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.others.CustomBottomDialog
|
import ani.dantotsu.others.CustomBottomDialog
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.toast
|
import ani.dantotsu.toast
|
||||||
import ani.dantotsu.tryWith
|
import ani.dantotsu.tryWith
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
|
@ -18,37 +19,20 @@ object Discord {
|
||||||
var userid: String? = null
|
var userid: String? = null
|
||||||
var avatar: String? = null
|
var avatar: String? = null
|
||||||
|
|
||||||
const val TOKEN = "discord_token"
|
|
||||||
|
|
||||||
fun getSavedToken(context: Context): Boolean {
|
fun getSavedToken(context: Context): Boolean {
|
||||||
val sharedPref = context.getSharedPreferences(
|
token = PrefManager.getVal(
|
||||||
context.getString(R.string.preference_file_key),
|
PrefName.DiscordToken, null as String?
|
||||||
Context.MODE_PRIVATE
|
|
||||||
)
|
)
|
||||||
token = sharedPref.getString(TOKEN, null)
|
|
||||||
return token != null
|
return token != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveToken(context: Context, token: String) {
|
fun saveToken(context: Context, token: String) {
|
||||||
val sharedPref = context.getSharedPreferences(
|
PrefManager.setVal(PrefName.DiscordToken, token)
|
||||||
context.getString(R.string.preference_file_key),
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
)
|
|
||||||
sharedPref.edit {
|
|
||||||
putString(TOKEN, token)
|
|
||||||
commit()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeSavedToken(context: Context) {
|
fun removeSavedToken(context: Context) {
|
||||||
val sharedPref = context.getSharedPreferences(
|
PrefManager.removeVal(PrefName.DiscordToken)
|
||||||
context.getString(R.string.preference_file_key),
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
)
|
|
||||||
sharedPref.edit {
|
|
||||||
remove(TOKEN)
|
|
||||||
commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
tryWith(true) {
|
tryWith(true) {
|
||||||
val dir = File(context.filesDir?.parentFile, "app_webview")
|
val dir = File(context.filesDir?.parentFile, "app_webview")
|
||||||
|
|
|
@ -24,6 +24,8 @@ import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.discord.serializers.Presence
|
import ani.dantotsu.connections.discord.serializers.Presence
|
||||||
import ani.dantotsu.connections.discord.serializers.User
|
import ani.dantotsu.connections.discord.serializers.User
|
||||||
import ani.dantotsu.isOnline
|
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.JsonArray
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
|
@ -149,19 +151,11 @@ class DiscordService : Service() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveProfile(response: String) {
|
fun saveProfile(response: String) {
|
||||||
val sharedPref = baseContext.getSharedPreferences(
|
|
||||||
baseContext.getString(R.string.preference_file_key),
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
)
|
|
||||||
val user = json.decodeFromString<User.Response>(response).d.user
|
val user = json.decodeFromString<User.Response>(response).d.user
|
||||||
log("User data: $user")
|
log("User data: $user")
|
||||||
with(sharedPref.edit()) {
|
PrefManager.setVal(PrefName.DiscordUserName, user.username)
|
||||||
putString("discord_username", user.username)
|
PrefManager.setVal(PrefName.DiscordId, user.id)
|
||||||
putString("discord_id", user.id)
|
PrefManager.setVal(PrefName.DiscordAvatar, user.avatar)
|
||||||
putString("discord_avatar", user.avatar)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(p0: Intent?): IBinder? = null
|
override fun onBind(p0: Intent?): IBinder? = null
|
||||||
|
@ -318,17 +312,13 @@ class DiscordService : Service() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getToken(context: Context): String {
|
fun getToken(context: Context): String {
|
||||||
val sharedPref = context.getSharedPreferences(
|
val token = PrefManager.getVal(PrefName.DiscordToken, null as String?)
|
||||||
context.getString(R.string.preference_file_key),
|
return if (token == null) {
|
||||||
Context.MODE_PRIVATE
|
|
||||||
)
|
|
||||||
val token = sharedPref.getString(Discord.TOKEN, null)
|
|
||||||
if (token == null) {
|
|
||||||
log("WebSocket: Token not found")
|
log("WebSocket: Token not found")
|
||||||
errorNotification("Could not set the presence", "token not found")
|
errorNotification("Could not set the presence", "token not found")
|
||||||
return ""
|
""
|
||||||
} else {
|
} else {
|
||||||
return token
|
token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.discord.Discord.saveToken
|
import ani.dantotsu.connections.discord.Discord.saveToken
|
||||||
import ani.dantotsu.others.LangSet
|
|
||||||
import ani.dantotsu.startMainActivity
|
import ani.dantotsu.startMainActivity
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
|
|
||||||
|
@ -20,7 +19,7 @@ class Login : AppCompatActivity() {
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
val process = getProcessName()
|
val process = getProcessName()
|
||||||
|
|
|
@ -8,9 +8,9 @@ import ani.dantotsu.R
|
||||||
import ani.dantotsu.client
|
import ani.dantotsu.client
|
||||||
import ani.dantotsu.connections.mal.MAL.clientId
|
import ani.dantotsu.connections.mal.MAL.clientId
|
||||||
import ani.dantotsu.connections.mal.MAL.saveResponse
|
import ani.dantotsu.connections.mal.MAL.saveResponse
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.logError
|
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.snackString
|
||||||
import ani.dantotsu.startMainActivity
|
import ani.dantotsu.startMainActivity
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
|
@ -21,12 +21,12 @@ import kotlinx.coroutines.launch
|
||||||
class Login : AppCompatActivity() {
|
class Login : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
try {
|
try {
|
||||||
val data: Uri = intent?.data
|
val data: Uri = intent?.data
|
||||||
?: throw Exception(getString(R.string.mal_login_uri_not_found))
|
?: 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))
|
?: throw Exception(getString(R.string.mal_login_code_challenge_not_found))
|
||||||
val code = data.getQueryParameter("code")
|
val code = data.getQueryParameter("code")
|
||||||
?: throw Exception(getString(R.string.mal_login_code_not_present))
|
?: throw Exception(getString(R.string.mal_login_code_not_present))
|
||||||
|
|
|
@ -9,13 +9,12 @@ import androidx.fragment.app.FragmentActivity
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.client
|
import ani.dantotsu.client
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.openLinkInBrowser
|
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 ani.dantotsu.tryWithSuspend
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import java.io.File
|
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
|
||||||
object MAL {
|
object MAL {
|
||||||
|
@ -34,7 +33,7 @@ object MAL {
|
||||||
.replace("/", "_")
|
.replace("/", "_")
|
||||||
.replace("\n", "")
|
.replace("\n", "")
|
||||||
|
|
||||||
saveData("malCodeChallenge", codeChallenge, context)
|
PrefManager.setVal(PrefName.MALCodeChallenge, codeChallenge)
|
||||||
val request =
|
val request =
|
||||||
"https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id=$clientId&code_challenge=$codeChallenge"
|
"https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id=$clientId&code_challenge=$codeChallenge"
|
||||||
try {
|
try {
|
||||||
|
@ -47,11 +46,9 @@ object MAL {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val MAL_TOKEN = "malToken"
|
|
||||||
|
|
||||||
private suspend fun refreshToken(): ResponseToken? {
|
private suspend fun refreshToken(): ResponseToken? {
|
||||||
return tryWithSuspend {
|
return tryWithSuspend {
|
||||||
val token = loadData<ResponseToken>(MAL_TOKEN)
|
val token = PrefManager.getNullableVal<ResponseToken>(PrefName.MALToken, null)
|
||||||
?: throw Exception(currContext()?.getString(R.string.refresh_token_load_failed))
|
?: throw Exception(currContext()?.getString(R.string.refresh_token_load_failed))
|
||||||
val res = client.post(
|
val res = client.post(
|
||||||
"https://myanimelist.net/v1/oauth2/token",
|
"https://myanimelist.net/v1/oauth2/token",
|
||||||
|
@ -69,8 +66,9 @@ object MAL {
|
||||||
|
|
||||||
suspend fun getSavedToken(context: FragmentActivity): Boolean {
|
suspend fun getSavedToken(context: FragmentActivity): Boolean {
|
||||||
return tryWithSuspend(false) {
|
return tryWithSuspend(false) {
|
||||||
var res: ResponseToken = loadData(MAL_TOKEN, context)
|
var res: ResponseToken =
|
||||||
?: return@tryWithSuspend false
|
PrefManager.getNullableVal<ResponseToken>(PrefName.MALToken, null)
|
||||||
|
?: return@tryWithSuspend false
|
||||||
if (System.currentTimeMillis() > res.expiresIn)
|
if (System.currentTimeMillis() > res.expiresIn)
|
||||||
res = refreshToken()
|
res = refreshToken()
|
||||||
?: throw Exception(currContext()?.getString(R.string.refreshing_token_failed))
|
?: throw Exception(currContext()?.getString(R.string.refreshing_token_failed))
|
||||||
|
@ -84,14 +82,12 @@ object MAL {
|
||||||
username = null
|
username = null
|
||||||
userid = null
|
userid = null
|
||||||
avatar = null
|
avatar = null
|
||||||
if (MAL_TOKEN in context.fileList()) {
|
PrefManager.removeVal(PrefName.MALToken)
|
||||||
File(context.filesDir, MAL_TOKEN).delete()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveResponse(res: ResponseToken) {
|
fun saveResponse(res: ResponseToken) {
|
||||||
res.expiresIn += System.currentTimeMillis()
|
res.expiresIn += System.currentTimeMillis()
|
||||||
saveData(MAL_TOKEN, res)
|
PrefManager.setVal(PrefName.MALToken, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -100,6 +96,10 @@ object MAL {
|
||||||
@SerialName("expires_in") var expiresIn: Long,
|
@SerialName("expires_in") var expiresIn: Long,
|
||||||
@SerialName("access_token") val accessToken: String,
|
@SerialName("access_token") val accessToken: String,
|
||||||
@SerialName("refresh_token") val refreshToken: String,
|
@SerialName("refresh_token") val refreshToken: String,
|
||||||
) : java.io.Serializable
|
) : java.io.Serializable {
|
||||||
|
companion object {
|
||||||
|
private const val serialVersionUID = 1L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
package ani.dantotsu.download
|
package ani.dantotsu.download
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.widget.Toast
|
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.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
class DownloadsManager(private val context: Context) {
|
class DownloadsManager(private val context: Context) {
|
||||||
private val prefs: SharedPreferences =
|
|
||||||
context.getSharedPreferences("downloads_pref", Context.MODE_PRIVATE)
|
|
||||||
private val gson = Gson()
|
private val gson = Gson()
|
||||||
private val downloadsList = loadDownloads().toMutableList()
|
private val downloadsList = loadDownloads().toMutableList()
|
||||||
|
|
||||||
|
@ -24,11 +23,11 @@ class DownloadsManager(private val context: Context) {
|
||||||
|
|
||||||
private fun saveDownloads() {
|
private fun saveDownloads() {
|
||||||
val jsonString = gson.toJson(downloadsList)
|
val jsonString = gson.toJson(downloadsList)
|
||||||
prefs.edit().putString("downloads_key", jsonString).apply()
|
PrefManager.setVal(PrefName.DownloadsKeys, jsonString)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadDownloads(): List<DownloadedType> {
|
private fun loadDownloads(): List<DownloadedType> {
|
||||||
val jsonString = prefs.getString("downloads_key", null)
|
val jsonString = PrefManager.getVal(PrefName.DownloadsKeys, null as String?)
|
||||||
return if (jsonString != null) {
|
return if (jsonString != null) {
|
||||||
val type = object : TypeToken<List<DownloadedType>>() {}.type
|
val type = object : TypeToken<List<DownloadedType>>() {}.type
|
||||||
gson.fromJson(jsonString, type)
|
gson.fromJson(jsonString, type)
|
||||||
|
@ -75,9 +74,11 @@ class DownloadsManager(private val context: Context) {
|
||||||
DownloadedType.Type.MANGA -> {
|
DownloadedType.Type.MANGA -> {
|
||||||
downloadsList.removeAll { it.title == title && it.type == DownloadedType.Type.MANGA }
|
downloadsList.removeAll { it.title == title && it.type == DownloadedType.Type.MANGA }
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadedType.Type.ANIME -> {
|
DownloadedType.Type.ANIME -> {
|
||||||
downloadsList.removeAll { it.title == title && it.type == DownloadedType.Type.ANIME }
|
downloadsList.removeAll { it.title == title && it.type == DownloadedType.Type.ANIME }
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadedType.Type.NOVEL -> {
|
DownloadedType.Type.NOVEL -> {
|
||||||
downloadsList.removeAll { it.title == title && it.type == 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 mangaLocation = "Dantotsu/Manga"
|
||||||
const val animeLocation = "Dantotsu/Anime"
|
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) {
|
return if (type == DownloadedType.Type.MANGA) {
|
||||||
if (chapter != null) {
|
if (chapter != null) {
|
||||||
File(
|
File(
|
||||||
|
|
|
@ -21,6 +21,7 @@ import androidx.media3.exoplayer.offline.DownloadManager
|
||||||
import androidx.media3.exoplayer.offline.DownloadService
|
import androidx.media3.exoplayer.offline.DownloadService
|
||||||
import ani.dantotsu.FileUrl
|
import ani.dantotsu.FileUrl
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.currActivity
|
import ani.dantotsu.currActivity
|
||||||
import ani.dantotsu.download.DownloadedType
|
import ani.dantotsu.download.DownloadedType
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
|
@ -32,8 +33,8 @@ import ani.dantotsu.media.SubtitleDownloader
|
||||||
import ani.dantotsu.media.anime.AnimeWatchFragment
|
import ani.dantotsu.media.anime.AnimeWatchFragment
|
||||||
import ani.dantotsu.parsers.Subtitle
|
import ani.dantotsu.parsers.Subtitle
|
||||||
import ani.dantotsu.parsers.Video
|
import ani.dantotsu.parsers.Video
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.snackString
|
import ani.dantotsu.snackString
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.InstanceCreator
|
import com.google.gson.InstanceCreator
|
||||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
|
@ -84,7 +85,7 @@ class AnimeDownloaderService : Service() {
|
||||||
builder =
|
builder =
|
||||||
NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
|
NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
|
||||||
setContentTitle("Anime Download Progress")
|
setContentTitle("Anime Download Progress")
|
||||||
setSmallIcon(R.drawable.ic_round_download_24)
|
setSmallIcon(R.drawable.ic_download_24)
|
||||||
priority = NotificationCompat.PRIORITY_DEFAULT
|
priority = NotificationCompat.PRIORITY_DEFAULT
|
||||||
setOnlyAlertOnce(true)
|
setOnlyAlertOnce(true)
|
||||||
}
|
}
|
||||||
|
@ -224,8 +225,6 @@ class AnimeDownloaderService : Service() {
|
||||||
notificationManager.notify(NOTIFICATION_ID, builder.build())
|
notificationManager.notify(NOTIFICATION_ID, builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastDownloadStarted(task.episode)
|
|
||||||
|
|
||||||
currActivity()?.let {
|
currActivity()?.let {
|
||||||
Helper.downloadVideo(
|
Helper.downloadVideo(
|
||||||
it,
|
it,
|
||||||
|
@ -276,7 +275,7 @@ class AnimeDownloaderService : Service() {
|
||||||
DownloadedType.Type.ANIME,
|
DownloadedType.Type.ANIME,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
FirebaseCrashlytics.getInstance().recordException(
|
Injekt.get<CrashlyticsInterface>().logException(
|
||||||
Exception(
|
Exception(
|
||||||
"Anime Download failed:" +
|
"Anime Download failed:" +
|
||||||
" ${download.failureReason}" +
|
" ${download.failureReason}" +
|
||||||
|
@ -294,10 +293,7 @@ class AnimeDownloaderService : Service() {
|
||||||
builder.setContentText("${task.title} - ${task.episode} Download completed")
|
builder.setContentText("${task.title} - ${task.episode} Download completed")
|
||||||
notificationManager.notify(NOTIFICATION_ID, builder.build())
|
notificationManager.notify(NOTIFICATION_ID, builder.build())
|
||||||
snackString("${task.title} - ${task.episode} Download completed")
|
snackString("${task.title} - ${task.episode} Download completed")
|
||||||
getSharedPreferences(
|
PrefManager.getAnimeDownloadPreferences().edit().putString(
|
||||||
getString(R.string.anime_downloads),
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).edit().putString(
|
|
||||||
task.getTaskName(),
|
task.getTaskName(),
|
||||||
task.video.file.url
|
task.video.file.url
|
||||||
).apply()
|
).apply()
|
||||||
|
@ -335,7 +331,7 @@ class AnimeDownloaderService : Service() {
|
||||||
logger("Exception while downloading file: ${e.message}")
|
logger("Exception while downloading file: ${e.message}")
|
||||||
snackString("Exception while downloading file: ${e.message}")
|
snackString("Exception while downloading file: ${e.message}")
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
}
|
}
|
||||||
broadcastDownloadFailed(task.episode)
|
broadcastDownloadFailed(task.episode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.cardview.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
|
||||||
|
|
||||||
class OfflineAnimeAdapter(
|
class OfflineAnimeAdapter(
|
||||||
|
@ -22,8 +24,7 @@ class OfflineAnimeAdapter(
|
||||||
private val inflater: LayoutInflater =
|
private val inflater: LayoutInflater =
|
||||||
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||||
private var originalItems: List<OfflineAnimeModel> = items
|
private var originalItems: List<OfflineAnimeModel> = items
|
||||||
private var style =
|
private var style: Int = PrefManager.getVal(PrefName.OfflineView)
|
||||||
context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
|
|
||||||
|
|
||||||
override fun getCount(): Int {
|
override fun getCount(): Int {
|
||||||
return items.size
|
return items.size
|
||||||
|
@ -105,8 +106,7 @@ class OfflineAnimeAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyNewGrid() {
|
fun notifyNewGrid() {
|
||||||
style =
|
style = PrefManager.getVal(PrefName.OfflineView)
|
||||||
context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
|
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,8 @@
|
||||||
package ani.dantotsu.download.anime
|
package ani.dantotsu.download.anime
|
||||||
|
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
|
@ -16,7 +13,6 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.animation.AlphaAnimation
|
import android.view.animation.AlphaAnimation
|
||||||
import android.view.animation.LayoutAnimationController
|
import android.view.animation.LayoutAnimationController
|
||||||
import android.view.animation.OvershootInterpolator
|
|
||||||
import android.widget.AbsListView
|
import android.widget.AbsListView
|
||||||
import android.widget.AutoCompleteTextView
|
import android.widget.AutoCompleteTextView
|
||||||
import android.widget.GridView
|
import android.widget.GridView
|
||||||
|
@ -25,34 +21,30 @@ import android.widget.TextView
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.cardview.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
import androidx.core.app.ActivityOptionsCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.util.Pair
|
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import androidx.core.view.marginBottom
|
import androidx.core.view.marginBottom
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.bottomBar
|
import ani.dantotsu.bottomBar
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.currActivity
|
import ani.dantotsu.currActivity
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.download.DownloadedType
|
import ani.dantotsu.download.DownloadedType
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.logger
|
import ani.dantotsu.logger
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.media.MediaDetailsActivity
|
import ani.dantotsu.media.MediaDetailsActivity
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.setSafeOnClickListener
|
import ani.dantotsu.setSafeOnClickListener
|
||||||
import ani.dantotsu.settings.SettingsDialogFragment
|
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.snackString
|
||||||
import ani.dantotsu.statusBarHeight
|
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.google.android.material.imageview.ShapeableImageView
|
import com.google.android.material.imageview.ShapeableImageView
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.InstanceCreator
|
import com.google.gson.InstanceCreator
|
||||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
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.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.math.max
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
|
|
||||||
|
@ -73,9 +63,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
private var downloads: List<OfflineAnimeModel> = listOf()
|
private var downloads: List<OfflineAnimeModel> = listOf()
|
||||||
private lateinit var gridView: GridView
|
private lateinit var gridView: GridView
|
||||||
private lateinit var adapter: OfflineAnimeAdapter
|
private lateinit var adapter: OfflineAnimeAdapter
|
||||||
private lateinit var total : TextView
|
private lateinit var total: TextView
|
||||||
private var uiSettings: UserInterfaceSettings =
|
|
||||||
loadData("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -101,15 +89,12 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
SettingsDialogFragment.newInstance(SettingsDialogFragment.Companion.PageType.OfflineANIME)
|
SettingsDialogFragment.newInstance(SettingsDialogFragment.Companion.PageType.OfflineANIME)
|
||||||
dialogFragment.show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
|
dialogFragment.show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
|
||||||
}
|
}
|
||||||
if (!uiSettings.immersiveMode) {
|
if (!(PrefManager.getVal(PrefName.ImmersiveMode) as Boolean)) {
|
||||||
view.rootView.fitsSystemWindows = true
|
view.rootView.fitsSystemWindows = true
|
||||||
}
|
}
|
||||||
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
|
||||||
?.getBoolean("colorOverflow", false) ?: false
|
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000
|
||||||
if (!colorOverflow) {
|
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000)
|
||||||
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
|
|
||||||
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
val searchView = view.findViewById<AutoCompleteTextView>(R.id.animeSearchBarText)
|
val searchView = view.findViewById<AutoCompleteTextView>(R.id.animeSearchBarText)
|
||||||
searchView.addTextChangedListener(object : TextWatcher {
|
searchView.addTextChangedListener(object : TextWatcher {
|
||||||
|
@ -123,8 +108,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
onSearchQuery(s.toString())
|
onSearchQuery(s.toString())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
var style = context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
var style: Int = PrefManager.getVal(PrefName.OfflineView)
|
||||||
?.getInt("offline_view", 0)
|
|
||||||
val layoutList = view.findViewById<ImageView>(R.id.downloadedList)
|
val layoutList = view.findViewById<ImageView>(R.id.downloadedList)
|
||||||
val layoutcompact = view.findViewById<ImageView>(R.id.downloadedGrid)
|
val layoutcompact = view.findViewById<ImageView>(R.id.downloadedGrid)
|
||||||
var selected = when (style) {
|
var selected = when (style) {
|
||||||
|
@ -143,8 +127,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
layoutList.setOnClickListener {
|
layoutList.setOnClickListener {
|
||||||
selected(it as ImageView)
|
selected(it as ImageView)
|
||||||
style = 0
|
style = 0
|
||||||
context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
|
PrefManager.setVal(PrefName.OfflineView, style)
|
||||||
?.putInt("offline_view", style!!)?.apply()
|
|
||||||
gridView.visibility = View.GONE
|
gridView.visibility = View.GONE
|
||||||
gridView = view.findViewById(R.id.gridView)
|
gridView = view.findViewById(R.id.gridView)
|
||||||
adapter.notifyNewGrid()
|
adapter.notifyNewGrid()
|
||||||
|
@ -154,15 +137,15 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
layoutcompact.setOnClickListener {
|
layoutcompact.setOnClickListener {
|
||||||
selected(it as ImageView)
|
selected(it as ImageView)
|
||||||
style = 1
|
style = 1
|
||||||
context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
|
PrefManager.setVal(PrefName.OfflineView, style)
|
||||||
?.putInt("offline_view", style!!)?.apply()
|
|
||||||
gridView.visibility = View.GONE
|
gridView.visibility = View.GONE
|
||||||
gridView = view.findViewById(R.id.gridView1)
|
gridView = view.findViewById(R.id.gridView1)
|
||||||
adapter.notifyNewGrid()
|
adapter.notifyNewGrid()
|
||||||
grid()
|
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)
|
total = view.findViewById(R.id.total)
|
||||||
grid()
|
grid()
|
||||||
return view
|
return view
|
||||||
|
@ -195,13 +178,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
Intent(requireContext(), MediaDetailsActivity::class.java)
|
Intent(requireContext(), MediaDetailsActivity::class.java)
|
||||||
.putExtra("download", true),
|
.putExtra("download", true),
|
||||||
ActivityOptionsCompat.makeSceneTransitionAnimation(
|
null
|
||||||
requireActivity(),
|
|
||||||
Pair.create(
|
|
||||||
requireActivity().findViewById<ImageView>(R.id.itemCompactImage),
|
|
||||||
ViewCompat.getTransitionName(requireActivity().findViewById(R.id.itemCompactImage))
|
|
||||||
),
|
|
||||||
).toBundle()
|
|
||||||
)
|
)
|
||||||
} ?: run {
|
} ?: run {
|
||||||
snackString("no media found")
|
snackString("no media found")
|
||||||
|
@ -220,11 +197,9 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
builder.setMessage("Are you sure you want to delete ${item.title}?")
|
builder.setMessage("Are you sure you want to delete ${item.title}?")
|
||||||
builder.setPositiveButton("Yes") { _, _ ->
|
builder.setPositiveButton("Yes") { _, _ ->
|
||||||
downloadManager.removeMedia(item.title, type)
|
downloadManager.removeMedia(item.title, type)
|
||||||
val mediaIds = requireContext().getSharedPreferences(
|
val mediaIds =
|
||||||
getString(R.string.anime_downloads),
|
PrefManager.getAnimeDownloadPreferences().all?.filter { it.key.contains(item.title) }?.values
|
||||||
Context.MODE_PRIVATE
|
?: emptySet()
|
||||||
)
|
|
||||||
?.all?.filter { it.key.contains(item.title) }?.values ?: emptySet()
|
|
||||||
if (mediaIds.isEmpty()) {
|
if (mediaIds.isEmpty()) {
|
||||||
snackString("No media found") // if this happens, terrible things have happened
|
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?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
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<CardView>(R.id.mangaPageScrollTop)
|
val scrollTop = view.findViewById<CardView>(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 {
|
scrollTop.setOnClickListener {
|
||||||
gridView.smoothScrollToPositionFromTop(0, 0)
|
gridView.smoothScrollToPositionFromTop(0, 0)
|
||||||
}
|
}
|
||||||
|
@ -306,7 +247,9 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
totalItemCount: Int
|
totalItemCount: Int
|
||||||
) {
|
) {
|
||||||
val first = view.getChildAt(0)
|
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
|
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 animeTitles = downloadManager.animeDownloadedTypes.map { it.title }.distinct()
|
||||||
val newAnimeDownloads = mutableListOf<OfflineAnimeModel>()
|
val newAnimeDownloads = mutableListOf<OfflineAnimeModel>()
|
||||||
for (title in animeTitles) {
|
for (title in animeTitles) {
|
||||||
val _downloads = downloadManager.animeDownloadedTypes.filter { it.title == title }
|
val tDownloads = downloadManager.animeDownloadedTypes.filter { it.title == title }
|
||||||
val download = _downloads.first()
|
val download = tDownloads.first()
|
||||||
val offlineAnimeModel = loadOfflineAnimeModel(download)
|
val offlineAnimeModel = loadOfflineAnimeModel(download)
|
||||||
newAnimeDownloads += offlineAnimeModel
|
newAnimeDownloads += offlineAnimeModel
|
||||||
}
|
}
|
||||||
|
@ -349,12 +292,10 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMedia(downloadedType: DownloadedType): Media? {
|
private fun getMedia(downloadedType: DownloadedType): Media? {
|
||||||
val type = if (downloadedType.type == DownloadedType.Type.ANIME) {
|
val type = when (downloadedType.type) {
|
||||||
"Anime"
|
DownloadedType.Type.MANGA -> "Manga"
|
||||||
} else if (downloadedType.type == DownloadedType.Type.MANGA) {
|
DownloadedType.Type.ANIME -> "Anime"
|
||||||
"Manga"
|
else -> "Novel"
|
||||||
} else {
|
|
||||||
"Novel"
|
|
||||||
}
|
}
|
||||||
val directory = File(
|
val directory = File(
|
||||||
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
@ -379,18 +320,16 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger("Error loading media.json: ${e.message}")
|
logger("Error loading media.json: ${e.message}")
|
||||||
logger(e.printStackTrace())
|
logger(e.printStackTrace())
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadOfflineAnimeModel(downloadedType: DownloadedType): OfflineAnimeModel {
|
private fun loadOfflineAnimeModel(downloadedType: DownloadedType): OfflineAnimeModel {
|
||||||
val type = if (downloadedType.type == DownloadedType.Type.MANGA) {
|
val type = when (downloadedType.type) {
|
||||||
"Manga"
|
DownloadedType.Type.MANGA -> "Manga"
|
||||||
} else if (downloadedType.type == DownloadedType.Type.ANIME) {
|
DownloadedType.Type.ANIME -> "Anime"
|
||||||
"Anime"
|
else -> "Novel"
|
||||||
} else {
|
|
||||||
"Novel"
|
|
||||||
}
|
}
|
||||||
val directory = File(
|
val directory = File(
|
||||||
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
@ -398,8 +337,6 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
)
|
)
|
||||||
//load media.json and convert to media class with gson
|
//load media.json and convert to media class with gson
|
||||||
try {
|
try {
|
||||||
val media = File(directory, "media.json")
|
|
||||||
val mediaJson = media.readText()
|
|
||||||
val mediaModel = getMedia(downloadedType)!!
|
val mediaModel = getMedia(downloadedType)!!
|
||||||
val cover = File(directory, "cover.jpg")
|
val cover = File(directory, "cover.jpg")
|
||||||
val coverUri: Uri? = if (cover.exists()) {
|
val coverUri: Uri? = if (cover.exists()) {
|
||||||
|
@ -439,7 +376,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger("Error loading media.json: ${e.message}")
|
logger("Error loading media.json: ${e.message}")
|
||||||
logger(e.printStackTrace())
|
logger(e.printStackTrace())
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
return OfflineAnimeModel(
|
return OfflineAnimeModel(
|
||||||
"unknown",
|
"unknown",
|
||||||
"0",
|
"0",
|
||||||
|
@ -448,8 +385,8 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
"??",
|
"??",
|
||||||
"movie",
|
"movie",
|
||||||
"hmm",
|
"hmm",
|
||||||
false,
|
isOngoing = false,
|
||||||
false,
|
isUserScored = false,
|
||||||
null,
|
null,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.download.DownloadedType
|
import ani.dantotsu.download.DownloadedType
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.logger
|
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.ACTION_DOWNLOAD_STARTED
|
||||||
import ani.dantotsu.media.manga.MangaReadFragment.Companion.EXTRA_CHAPTER_NUMBER
|
import ani.dantotsu.media.manga.MangaReadFragment.Companion.EXTRA_CHAPTER_NUMBER
|
||||||
import ani.dantotsu.snackString
|
import ani.dantotsu.snackString
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.InstanceCreator
|
import com.google.gson.InstanceCreator
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_DOWNLOADER_PROGRESS
|
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_DOWNLOADER_PROGRESS
|
||||||
|
@ -76,7 +76,7 @@ class MangaDownloaderService : Service() {
|
||||||
notificationManager = NotificationManagerCompat.from(this)
|
notificationManager = NotificationManagerCompat.from(this)
|
||||||
builder = NotificationCompat.Builder(this, CHANNEL_DOWNLOADER_PROGRESS).apply {
|
builder = NotificationCompat.Builder(this, CHANNEL_DOWNLOADER_PROGRESS).apply {
|
||||||
setContentTitle("Manga Download Progress")
|
setContentTitle("Manga Download Progress")
|
||||||
setSmallIcon(R.drawable.ic_round_download_24)
|
setSmallIcon(R.drawable.ic_download_24)
|
||||||
priority = NotificationCompat.PRIORITY_DEFAULT
|
priority = NotificationCompat.PRIORITY_DEFAULT
|
||||||
setOnlyAlertOnce(true)
|
setOnlyAlertOnce(true)
|
||||||
setProgress(0, 0, false)
|
setProgress(0, 0, false)
|
||||||
|
@ -253,7 +253,7 @@ class MangaDownloaderService : Service() {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger("Exception while downloading file: ${e.message}")
|
logger("Exception while downloading file: ${e.message}")
|
||||||
snackString("Exception while downloading file: ${e.message}")
|
snackString("Exception while downloading file: ${e.message}")
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
broadcastDownloadFailed(task.chapter)
|
broadcastDownloadFailed(task.chapter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ class MangaDownloaderService : Service() {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println("Exception while saving image: ${e.message}")
|
println("Exception while saving image: ${e.message}")
|
||||||
snackString("Exception while saving image: ${e.message}")
|
snackString("Exception while saving image: ${e.message}")
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.cardview.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
|
||||||
|
|
||||||
class OfflineMangaAdapter(
|
class OfflineMangaAdapter(
|
||||||
|
@ -21,8 +23,7 @@ class OfflineMangaAdapter(
|
||||||
private val inflater: LayoutInflater =
|
private val inflater: LayoutInflater =
|
||||||
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||||
private var originalItems: List<OfflineMangaModel> = items
|
private var originalItems: List<OfflineMangaModel> = items
|
||||||
private var style =
|
private var style: Int = PrefManager.getVal(PrefName.OfflineView)
|
||||||
context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
|
|
||||||
|
|
||||||
override fun getCount(): Int {
|
override fun getCount(): Int {
|
||||||
return items.size
|
return items.size
|
||||||
|
@ -104,8 +105,7 @@ class OfflineMangaAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyNewGrid() {
|
fun notifyNewGrid() {
|
||||||
style =
|
style = PrefManager.getVal(PrefName.OfflineView)
|
||||||
context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
|
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,10 +1,7 @@
|
||||||
package ani.dantotsu.download.manga
|
package ani.dantotsu.download.manga
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
|
@ -15,7 +12,6 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.animation.AlphaAnimation
|
import android.view.animation.AlphaAnimation
|
||||||
import android.view.animation.LayoutAnimationController
|
import android.view.animation.LayoutAnimationController
|
||||||
import android.view.animation.OvershootInterpolator
|
|
||||||
import android.widget.AbsListView
|
import android.widget.AbsListView
|
||||||
import android.widget.AutoCompleteTextView
|
import android.widget.AutoCompleteTextView
|
||||||
import android.widget.GridView
|
import android.widget.GridView
|
||||||
|
@ -23,33 +19,29 @@ import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.cardview.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
import androidx.core.app.ActivityOptionsCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.util.Pair
|
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import androidx.core.view.marginBottom
|
import androidx.core.view.marginBottom
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.bottomBar
|
import ani.dantotsu.bottomBar
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.currActivity
|
import ani.dantotsu.currActivity
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.download.DownloadedType
|
import ani.dantotsu.download.DownloadedType
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.logger
|
import ani.dantotsu.logger
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.media.MediaDetailsActivity
|
import ani.dantotsu.media.MediaDetailsActivity
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.setSafeOnClickListener
|
import ani.dantotsu.setSafeOnClickListener
|
||||||
import ani.dantotsu.settings.SettingsDialogFragment
|
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.snackString
|
||||||
import ani.dantotsu.statusBarHeight
|
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.google.android.material.imageview.ShapeableImageView
|
import com.google.android.material.imageview.ShapeableImageView
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.InstanceCreator
|
import com.google.gson.InstanceCreator
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
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.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.math.max
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
|
|
||||||
|
@ -67,8 +57,6 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
private lateinit var gridView: GridView
|
private lateinit var gridView: GridView
|
||||||
private lateinit var adapter: OfflineMangaAdapter
|
private lateinit var adapter: OfflineMangaAdapter
|
||||||
private lateinit var total: TextView
|
private lateinit var total: TextView
|
||||||
private var uiSettings: UserInterfaceSettings =
|
|
||||||
loadData("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -94,15 +82,12 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
SettingsDialogFragment.newInstance(SettingsDialogFragment.Companion.PageType.OfflineMANGA)
|
SettingsDialogFragment.newInstance(SettingsDialogFragment.Companion.PageType.OfflineMANGA)
|
||||||
dialogFragment.show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
|
dialogFragment.show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
|
||||||
}
|
}
|
||||||
if (!uiSettings.immersiveMode) {
|
if (!(PrefManager.getVal(PrefName.ImmersiveMode) as Boolean)) {
|
||||||
view.rootView.fitsSystemWindows = true
|
view.rootView.fitsSystemWindows = true
|
||||||
}
|
}
|
||||||
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
|
||||||
?.getBoolean("colorOverflow", false) ?: false
|
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000
|
||||||
if (!colorOverflow) {
|
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000)
|
||||||
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
|
|
||||||
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
val searchView = view.findViewById<AutoCompleteTextView>(R.id.animeSearchBarText)
|
val searchView = view.findViewById<AutoCompleteTextView>(R.id.animeSearchBarText)
|
||||||
searchView.addTextChangedListener(object : TextWatcher {
|
searchView.addTextChangedListener(object : TextWatcher {
|
||||||
|
@ -116,8 +101,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
onSearchQuery(s.toString())
|
onSearchQuery(s.toString())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
var style = context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
var style: Int = PrefManager.getVal(PrefName.OfflineView)
|
||||||
?.getInt("offline_view", 0)
|
|
||||||
val layoutList = view.findViewById<ImageView>(R.id.downloadedList)
|
val layoutList = view.findViewById<ImageView>(R.id.downloadedList)
|
||||||
val layoutcompact = view.findViewById<ImageView>(R.id.downloadedGrid)
|
val layoutcompact = view.findViewById<ImageView>(R.id.downloadedGrid)
|
||||||
var selected = when (style) {
|
var selected = when (style) {
|
||||||
|
@ -136,8 +120,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
layoutList.setOnClickListener {
|
layoutList.setOnClickListener {
|
||||||
selected(it as ImageView)
|
selected(it as ImageView)
|
||||||
style = 0
|
style = 0
|
||||||
requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit()
|
PrefManager.setVal(PrefName.OfflineView, style)
|
||||||
.putInt("offline_view", style!!).apply()
|
|
||||||
gridView.visibility = View.GONE
|
gridView.visibility = View.GONE
|
||||||
gridView = view.findViewById(R.id.gridView)
|
gridView = view.findViewById(R.id.gridView)
|
||||||
adapter.notifyNewGrid()
|
adapter.notifyNewGrid()
|
||||||
|
@ -148,8 +131,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
layoutcompact.setOnClickListener {
|
layoutcompact.setOnClickListener {
|
||||||
selected(it as ImageView)
|
selected(it as ImageView)
|
||||||
style = 1
|
style = 1
|
||||||
requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit()
|
PrefManager.setVal(PrefName.OfflineView, style)
|
||||||
.putInt("offline_view", style!!).apply()
|
|
||||||
gridView.visibility = View.GONE
|
gridView.visibility = View.GONE
|
||||||
gridView = view.findViewById(R.id.gridView1)
|
gridView = view.findViewById(R.id.gridView1)
|
||||||
adapter.notifyNewGrid()
|
adapter.notifyNewGrid()
|
||||||
|
@ -180,19 +162,13 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
downloadManager.mangaDownloadedTypes.firstOrNull { it.title == item.title }
|
downloadManager.mangaDownloadedTypes.firstOrNull { it.title == item.title }
|
||||||
?: downloadManager.novelDownloadedTypes.firstOrNull { it.title == item.title }
|
?: downloadManager.novelDownloadedTypes.firstOrNull { it.title == item.title }
|
||||||
media?.let {
|
media?.let {
|
||||||
|
|
||||||
ContextCompat.startActivity(
|
ContextCompat.startActivity(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
Intent(requireContext(), MediaDetailsActivity::class.java)
|
Intent(requireContext(), MediaDetailsActivity::class.java)
|
||||||
.putExtra("media", getMedia(it))
|
.putExtra("media", getMedia(it))
|
||||||
.putExtra("download", true),
|
.putExtra("download", true),
|
||||||
ActivityOptionsCompat.makeSceneTransitionAnimation(
|
null
|
||||||
requireActivity(),
|
|
||||||
Pair.create(
|
|
||||||
gridView.getChildAt(position)
|
|
||||||
.findViewById<ImageView>(R.id.itemCompactImage),
|
|
||||||
ViewCompat.getTransitionName(requireActivity().findViewById(R.id.itemCompactImage))
|
|
||||||
)
|
|
||||||
).toBundle()
|
|
||||||
)
|
)
|
||||||
} ?: run {
|
} ?: run {
|
||||||
snackString("no media found")
|
snackString("no media found")
|
||||||
|
@ -236,41 +212,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
initActivity(requireActivity())
|
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<CardView>(R.id.mangaPageScrollTop)
|
val scrollTop = view.findViewById<CardView>(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 {
|
scrollTop.setOnClickListener {
|
||||||
gridView.smoothScrollToPositionFromTop(0, 0)
|
gridView.smoothScrollToPositionFromTop(0, 0)
|
||||||
}
|
}
|
||||||
|
@ -290,8 +233,10 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
totalItemCount: Int
|
totalItemCount: Int
|
||||||
) {
|
) {
|
||||||
val first = view.getChildAt(0)
|
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.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 mangaTitles = downloadManager.mangaDownloadedTypes.map { it.title }.distinct()
|
||||||
val newMangaDownloads = mutableListOf<OfflineMangaModel>()
|
val newMangaDownloads = mutableListOf<OfflineMangaModel>()
|
||||||
for (title in mangaTitles) {
|
for (title in mangaTitles) {
|
||||||
val _downloads = downloadManager.mangaDownloadedTypes.filter { it.title == title }
|
val tDownloads = downloadManager.mangaDownloadedTypes.filter { it.title == title }
|
||||||
val download = _downloads.first()
|
val download = tDownloads.first()
|
||||||
val offlineMangaModel = loadOfflineMangaModel(download)
|
val offlineMangaModel = loadOfflineMangaModel(download)
|
||||||
newMangaDownloads += offlineMangaModel
|
newMangaDownloads += offlineMangaModel
|
||||||
}
|
}
|
||||||
|
@ -333,8 +278,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
val novelTitles = downloadManager.novelDownloadedTypes.map { it.title }.distinct()
|
val novelTitles = downloadManager.novelDownloadedTypes.map { it.title }.distinct()
|
||||||
val newNovelDownloads = mutableListOf<OfflineMangaModel>()
|
val newNovelDownloads = mutableListOf<OfflineMangaModel>()
|
||||||
for (title in novelTitles) {
|
for (title in novelTitles) {
|
||||||
val _downloads = downloadManager.novelDownloadedTypes.filter { it.title == title }
|
val tDownloads = downloadManager.novelDownloadedTypes.filter { it.title == title }
|
||||||
val download = _downloads.first()
|
val download = tDownloads.first()
|
||||||
val offlineMangaModel = loadOfflineMangaModel(download)
|
val offlineMangaModel = loadOfflineMangaModel(download)
|
||||||
newNovelDownloads += offlineMangaModel
|
newNovelDownloads += offlineMangaModel
|
||||||
}
|
}
|
||||||
|
@ -343,12 +288,10 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMedia(downloadedType: DownloadedType): Media? {
|
private fun getMedia(downloadedType: DownloadedType): Media? {
|
||||||
val type = if (downloadedType.type == DownloadedType.Type.MANGA) {
|
val type = when (downloadedType.type) {
|
||||||
"Manga"
|
DownloadedType.Type.MANGA -> "Manga"
|
||||||
} else if (downloadedType.type == DownloadedType.Type.ANIME) {
|
DownloadedType.Type.ANIME -> "Anime"
|
||||||
"Anime"
|
else -> "Novel"
|
||||||
} else {
|
|
||||||
"Novel"
|
|
||||||
}
|
}
|
||||||
val directory = File(
|
val directory = File(
|
||||||
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
@ -367,18 +310,16 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger("Error loading media.json: ${e.message}")
|
logger("Error loading media.json: ${e.message}")
|
||||||
logger(e.printStackTrace())
|
logger(e.printStackTrace())
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadOfflineMangaModel(downloadedType: DownloadedType): OfflineMangaModel {
|
private fun loadOfflineMangaModel(downloadedType: DownloadedType): OfflineMangaModel {
|
||||||
val type = if (downloadedType.type == DownloadedType.Type.MANGA) {
|
val type = when (downloadedType.type) {
|
||||||
"Manga"
|
DownloadedType.Type.MANGA -> "Manga"
|
||||||
} else if (downloadedType.type == DownloadedType.Type.ANIME) {
|
DownloadedType.Type.ANIME -> "Anime"
|
||||||
"Anime"
|
else -> "Novel"
|
||||||
} else {
|
|
||||||
"Novel"
|
|
||||||
}
|
}
|
||||||
val directory = File(
|
val directory = File(
|
||||||
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
@ -386,8 +327,6 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
)
|
)
|
||||||
//load media.json and convert to media class with gson
|
//load media.json and convert to media class with gson
|
||||||
try {
|
try {
|
||||||
val media = File(directory, "media.json")
|
|
||||||
val mediaJson = media.readText()
|
|
||||||
val mediaModel = getMedia(downloadedType)!!
|
val mediaModel = getMedia(downloadedType)!!
|
||||||
val cover = File(directory, "cover.jpg")
|
val cover = File(directory, "cover.jpg")
|
||||||
val coverUri: Uri? = if (cover.exists()) {
|
val coverUri: Uri? = if (cover.exists()) {
|
||||||
|
@ -421,7 +360,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger("Error loading media.json: ${e.message}")
|
logger("Error loading media.json: ${e.message}")
|
||||||
logger(e.printStackTrace())
|
logger(e.printStackTrace())
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
return OfflineMangaModel(
|
return OfflineMangaModel(
|
||||||
"unknown",
|
"unknown",
|
||||||
"0",
|
"0",
|
||||||
|
@ -429,8 +368,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
"??",
|
"??",
|
||||||
"movie",
|
"movie",
|
||||||
"hmm",
|
"hmm",
|
||||||
false,
|
isOngoing = false,
|
||||||
false,
|
isUserScored = false,
|
||||||
null,
|
null,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,13 +17,13 @@ import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.download.DownloadedType
|
import ani.dantotsu.download.DownloadedType
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.logger
|
import ani.dantotsu.logger
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.media.novel.NovelReadFragment
|
import ani.dantotsu.media.novel.NovelReadFragment
|
||||||
import ani.dantotsu.snackString
|
import ani.dantotsu.snackString
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.InstanceCreator
|
import com.google.gson.InstanceCreator
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
|
@ -75,7 +75,7 @@ class NovelDownloaderService : Service() {
|
||||||
builder =
|
builder =
|
||||||
NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
|
NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
|
||||||
setContentTitle("Novel Download Progress")
|
setContentTitle("Novel Download Progress")
|
||||||
setSmallIcon(R.drawable.ic_round_download_24)
|
setSmallIcon(R.drawable.ic_download_24)
|
||||||
priority = NotificationCompat.PRIORITY_DEFAULT
|
priority = NotificationCompat.PRIORITY_DEFAULT
|
||||||
setOnlyAlertOnce(true)
|
setOnlyAlertOnce(true)
|
||||||
setProgress(0, 0, false)
|
setProgress(0, 0, false)
|
||||||
|
@ -342,7 +342,7 @@ class NovelDownloaderService : Service() {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger("Exception while downloading .epub: ${e.message}")
|
logger("Exception while downloading .epub: ${e.message}")
|
||||||
snackString("Exception while downloading .epub: ${e.message}")
|
snackString("Exception while downloading .epub: ${e.message}")
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
broadcastDownloadFailed(task.originalLink)
|
broadcastDownloadFailed(task.originalLink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,9 @@ import android.util.Log
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.ContextCompat.getString
|
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.MimeTypes
|
import androidx.media3.common.MimeTypes
|
||||||
import androidx.media3.common.TrackSelectionParameters
|
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.database.StandaloneDatabaseProvider
|
import androidx.media3.database.StandaloneDatabaseProvider
|
||||||
import androidx.media3.datasource.DataSource
|
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.DownloadManager
|
||||||
import androidx.media3.exoplayer.offline.DownloadService
|
import androidx.media3.exoplayer.offline.DownloadService
|
||||||
import androidx.media3.exoplayer.scheduler.Requirements
|
import androidx.media3.exoplayer.scheduler.Requirements
|
||||||
import androidx.media3.ui.TrackSelectionDialogBuilder
|
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.defaultHeaders
|
import ani.dantotsu.defaultHeaders
|
||||||
import ani.dantotsu.download.DownloadedType
|
import ani.dantotsu.download.DownloadedType
|
||||||
|
@ -45,6 +42,7 @@ import ani.dantotsu.parsers.Subtitle
|
||||||
import ani.dantotsu.parsers.SubtitleType
|
import ani.dantotsu.parsers.SubtitleType
|
||||||
import ani.dantotsu.parsers.Video
|
import ani.dantotsu.parsers.Video
|
||||||
import ani.dantotsu.parsers.VideoType
|
import ani.dantotsu.parsers.VideoType
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
@ -231,19 +229,13 @@ object Helper {
|
||||||
DownloadService.sendRemoveDownload(
|
DownloadService.sendRemoveDownload(
|
||||||
context,
|
context,
|
||||||
ExoplayerDownloadService::class.java,
|
ExoplayerDownloadService::class.java,
|
||||||
context.getSharedPreferences(
|
PrefManager.getAnimeDownloadPreferences().getString(
|
||||||
getString(context, R.string.anime_downloads),
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).getString(
|
|
||||||
animeDownloadTask.getTaskName(),
|
animeDownloadTask.getTaskName(),
|
||||||
""
|
""
|
||||||
) ?: "",
|
) ?: "",
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
context.getSharedPreferences(
|
PrefManager.getAnimeDownloadPreferences().edit()
|
||||||
getString(context, R.string.anime_downloads),
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).edit()
|
|
||||||
.remove(animeDownloadTask.getTaskName())
|
.remove(animeDownloadTask.getTaskName())
|
||||||
.apply()
|
.apply()
|
||||||
downloadsManger.removeDownload(
|
downloadsManger.removeDownload(
|
||||||
|
|
|
@ -2,7 +2,6 @@ package ani.dantotsu.home
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
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.SearchResults
|
||||||
import ani.dantotsu.connections.anilist.getUserId
|
import ani.dantotsu.connections.anilist.getUserId
|
||||||
import ani.dantotsu.databinding.FragmentAnimeBinding
|
import ani.dantotsu.databinding.FragmentAnimeBinding
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.media.MediaAdaptor
|
import ani.dantotsu.media.MediaAdaptor
|
||||||
import ani.dantotsu.media.ProgressAdapter
|
import ani.dantotsu.media.ProgressAdapter
|
||||||
import ani.dantotsu.media.SearchActivity
|
import ani.dantotsu.media.SearchActivity
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.px
|
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.snackString
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -50,9 +49,6 @@ class AnimeFragment : Fragment() {
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
private lateinit var animePageAdapter: AnimePageAdapter
|
private lateinit var animePageAdapter: AnimePageAdapter
|
||||||
|
|
||||||
private var uiSettings: UserInterfaceSettings =
|
|
||||||
loadData("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
|
|
||||||
val model: AnilistAnimeViewModel by activityViewModels()
|
val model: AnilistAnimeViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
@ -217,7 +213,7 @@ class AnimeFragment : Fragment() {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
animePageAdapter.updateTrending(
|
animePageAdapter.updateTrending(
|
||||||
MediaAdaptor(
|
MediaAdaptor(
|
||||||
if (uiSettings.smallView) 3 else 2,
|
if (PrefManager.getVal(PrefName.SmallView)) 3 else 2,
|
||||||
it,
|
it,
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
viewPager = animePageAdapter.trendingViewPager
|
viewPager = animePageAdapter.trendingViewPager
|
||||||
|
@ -268,8 +264,11 @@ class AnimeFragment : Fragment() {
|
||||||
model.loaded = true
|
model.loaded = true
|
||||||
model.loadTrending(1)
|
model.loadTrending(1)
|
||||||
model.loadUpdated()
|
model.loadUpdated()
|
||||||
model.loadPopular("ANIME", sort = Anilist.sortBy[1], onList = requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
model.loadPopular(
|
||||||
.getBoolean("popular_list", false))
|
"ANIME", sort = Anilist.sortBy[1], onList = PrefManager.getVal(
|
||||||
|
PrefName.PopularAnimeList
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
live.postValue(false)
|
live.postValue(false)
|
||||||
_binding?.animeRefresh?.isRefreshing = false
|
_binding?.animeRefresh?.isRefreshing = false
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package ani.dantotsu.home
|
package ani.dantotsu.home
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
@ -22,7 +21,6 @@ import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.databinding.ItemAnimePageBinding
|
import ani.dantotsu.databinding.ItemAnimePageBinding
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.loadImage
|
import ani.dantotsu.loadImage
|
||||||
import ani.dantotsu.media.CalendarActivity
|
import ani.dantotsu.media.CalendarActivity
|
||||||
import ani.dantotsu.media.GenreActivity
|
import ani.dantotsu.media.GenreActivity
|
||||||
|
@ -33,7 +31,8 @@ import ani.dantotsu.setSafeOnClickListener
|
||||||
import ani.dantotsu.setSlideIn
|
import ani.dantotsu.setSlideIn
|
||||||
import ani.dantotsu.setSlideUp
|
import ani.dantotsu.setSlideUp
|
||||||
import ani.dantotsu.settings.SettingsDialogFragment
|
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 ani.dantotsu.statusBarHeight
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
@ -44,8 +43,6 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
|
||||||
private var trendHandler: Handler? = null
|
private var trendHandler: Handler? = null
|
||||||
private lateinit var trendRun: Runnable
|
private lateinit var trendRun: Runnable
|
||||||
var trendingViewPager: ViewPager2? = null
|
var trendingViewPager: ViewPager2? = null
|
||||||
private var uiSettings: UserInterfaceSettings =
|
|
||||||
loadData("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnimePageViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnimePageViewHolder {
|
||||||
val binding =
|
val binding =
|
||||||
|
@ -68,17 +65,12 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
|
||||||
currContext()?.theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
|
currContext()?.theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
|
||||||
val color = typedValue.data
|
val color = typedValue.data
|
||||||
|
|
||||||
|
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000
|
||||||
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000)
|
||||||
?.getBoolean("colorOverflow", false) ?: false
|
|
||||||
if (!colorOverflow) {
|
|
||||||
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
|
|
||||||
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.animeTitleContainer.updatePadding(top = statusBarHeight)
|
binding.animeTitleContainer.updatePadding(top = statusBarHeight)
|
||||||
|
|
||||||
if (uiSettings.smallView) binding.animeTrendingContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
if (PrefManager.getVal(PrefName.SmallView)) binding.animeTrendingContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
bottomMargin = (-108f).px
|
bottomMargin = (-108f).px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,14 +125,12 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
|
||||||
binding.animeIncludeList.visibility =
|
binding.animeIncludeList.visibility =
|
||||||
if (Anilist.userid != null) View.VISIBLE else View.GONE
|
if (Anilist.userid != null) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
binding.animeIncludeList.isChecked = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
binding.animeIncludeList.isChecked = PrefManager.getVal(PrefName.PopularAnimeList)
|
||||||
?.getBoolean("popular_list", true) ?: true
|
|
||||||
|
|
||||||
binding.animeIncludeList.setOnCheckedChangeListener { _, isChecked ->
|
binding.animeIncludeList.setOnCheckedChangeListener { _, isChecked ->
|
||||||
onIncludeListClick.invoke(isChecked)
|
onIncludeListClick.invoke(isChecked)
|
||||||
|
|
||||||
currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
|
PrefManager.setVal(PrefName.PopularAnimeList, isChecked)
|
||||||
?.putBoolean("popular_list", isChecked)?.apply()
|
|
||||||
}
|
}
|
||||||
if (ready.value == false)
|
if (ready.value == false)
|
||||||
ready.postValue(true)
|
ready.postValue(true)
|
||||||
|
@ -179,12 +169,12 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.animeTrendingViewPager.layoutAnimation =
|
binding.animeTrendingViewPager.layoutAnimation =
|
||||||
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
|
LayoutAnimationController(setSlideIn(), 0.25f)
|
||||||
binding.animeTitleContainer.startAnimation(setSlideUp(uiSettings))
|
binding.animeTitleContainer.startAnimation(setSlideUp())
|
||||||
binding.animeListContainer.layoutAnimation =
|
binding.animeListContainer.layoutAnimation =
|
||||||
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
|
LayoutAnimationController(setSlideIn(), 0.25f)
|
||||||
binding.animeSeasonsCont.layoutAnimation =
|
binding.animeSeasonsCont.layoutAnimation =
|
||||||
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
|
LayoutAnimationController(setSlideIn(), 0.25f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateRecent(adaptor: MediaAdaptor) {
|
fun updateRecent(adaptor: MediaAdaptor) {
|
||||||
|
@ -199,11 +189,11 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
|
||||||
binding.animeUpdatedRecyclerView.visibility = View.VISIBLE
|
binding.animeUpdatedRecyclerView.visibility = View.VISIBLE
|
||||||
|
|
||||||
binding.animeRecently.visibility = View.VISIBLE
|
binding.animeRecently.visibility = View.VISIBLE
|
||||||
binding.animeRecently.startAnimation(setSlideUp(uiSettings))
|
binding.animeRecently.startAnimation(setSlideUp())
|
||||||
binding.animeUpdatedRecyclerView.layoutAnimation =
|
binding.animeUpdatedRecyclerView.layoutAnimation =
|
||||||
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
|
LayoutAnimationController(setSlideIn(), 0.25f)
|
||||||
binding.animePopular.visibility = View.VISIBLE
|
binding.animePopular.visibility = View.VISIBLE
|
||||||
binding.animePopular.startAnimation(setSlideUp(uiSettings))
|
binding.animePopular.startAnimation(setSlideUp())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateAvatar() {
|
fun updateAvatar() {
|
||||||
|
|
|
@ -27,7 +27,6 @@ import ani.dantotsu.connections.anilist.AnilistHomeViewModel
|
||||||
import ani.dantotsu.connections.anilist.getUserId
|
import ani.dantotsu.connections.anilist.getUserId
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.databinding.FragmentHomeBinding
|
import ani.dantotsu.databinding.FragmentHomeBinding
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.loadImage
|
import ani.dantotsu.loadImage
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.media.MediaAdaptor
|
import ani.dantotsu.media.MediaAdaptor
|
||||||
|
@ -37,7 +36,8 @@ import ani.dantotsu.setSafeOnClickListener
|
||||||
import ani.dantotsu.setSlideIn
|
import ani.dantotsu.setSlideIn
|
||||||
import ani.dantotsu.setSlideUp
|
import ani.dantotsu.setSlideUp
|
||||||
import ani.dantotsu.settings.SettingsDialogFragment
|
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.snackString
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -70,14 +70,13 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
val scope = lifecycleScope
|
val scope = lifecycleScope
|
||||||
var uiSettings = loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
fun load() {
|
fun load() {
|
||||||
if (activity != null && _binding != null) lifecycleScope.launch(Dispatchers.Main) {
|
if (activity != null && _binding != null) lifecycleScope.launch(Dispatchers.Main) {
|
||||||
binding.homeUserName.text = Anilist.username
|
binding.homeUserName.text = Anilist.username
|
||||||
binding.homeUserEpisodesWatched.text = Anilist.episodesWatched.toString()
|
binding.homeUserEpisodesWatched.text = Anilist.episodesWatched.toString()
|
||||||
binding.homeUserChaptersRead.text = Anilist.chapterRead.toString()
|
binding.homeUserChaptersRead.text = Anilist.chapterRead.toString()
|
||||||
binding.homeUserAvatar.loadImage(Anilist.avatar)
|
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.homeUserBg.loadImage(Anilist.bg)
|
||||||
binding.homeUserDataProgressBar.visibility = View.GONE
|
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.visibility = View.VISIBLE
|
||||||
binding.homeUserDataContainer.layoutAnimation =
|
binding.homeUserDataContainer.layoutAnimation =
|
||||||
LayoutAnimationController(setSlideUp(uiSettings), 0.25f)
|
LayoutAnimationController(setSlideUp(), 0.25f)
|
||||||
binding.homeAnimeList.visibility = View.VISIBLE
|
binding.homeAnimeList.visibility = View.VISIBLE
|
||||||
binding.homeMangaList.visibility = View.VISIBLE
|
binding.homeMangaList.visibility = View.VISIBLE
|
||||||
binding.homeListContainer.layoutAnimation =
|
binding.homeListContainer.layoutAnimation =
|
||||||
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
|
LayoutAnimationController(setSlideIn(), 0.25f)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
snackString(currContext()?.getString(R.string.please_reload))
|
snackString(currContext()?.getString(R.string.please_reload))
|
||||||
|
@ -127,7 +126,7 @@ class HomeFragment : Fragment() {
|
||||||
binding.homeTopContainer.updatePadding(top = statusBarHeight)
|
binding.homeTopContainer.updatePadding(top = statusBarHeight)
|
||||||
|
|
||||||
var reached = false
|
var reached = false
|
||||||
val duration = (uiSettings.animationSpeed * 200).toLong()
|
val duration = ((PrefManager.getVal(PrefName.AnimationSpeed) as Float) * 200).toLong()
|
||||||
binding.homeScroll.setOnScrollChangeListener { _, _, _, _, _ ->
|
binding.homeScroll.setOnScrollChangeListener { _, _, _, _, _ ->
|
||||||
if (!binding.homeScroll.canScrollVertically(1)) {
|
if (!binding.homeScroll.canScrollVertically(1)) {
|
||||||
reached = true
|
reached = true
|
||||||
|
@ -206,13 +205,13 @@ class HomeFragment : Fragment() {
|
||||||
)
|
)
|
||||||
recyclerView.visibility = View.VISIBLE
|
recyclerView.visibility = View.VISIBLE
|
||||||
recyclerView.layoutAnimation =
|
recyclerView.layoutAnimation =
|
||||||
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
|
LayoutAnimationController(setSlideIn(), 0.25f)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
empty.visibility = View.VISIBLE
|
empty.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
title.visibility = View.VISIBLE
|
title.visibility = View.VISIBLE
|
||||||
title.startAnimation(setSlideUp(uiSettings))
|
title.startAnimation(setSlideUp())
|
||||||
progress.visibility = View.GONE
|
progress.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,12 +294,12 @@ class HomeFragment : Fragment() {
|
||||||
binding.homeRecommended
|
binding.homeRecommended
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.homeUserAvatarContainer.startAnimation(setSlideUp(uiSettings))
|
binding.homeUserAvatarContainer.startAnimation(setSlideUp())
|
||||||
|
|
||||||
model.empty.observe(viewLifecycleOwner) {
|
model.empty.observe(viewLifecycleOwner) {
|
||||||
binding.homeDantotsuContainer.visibility = if (it == true) View.VISIBLE else View.GONE
|
binding.homeDantotsuContainer.visibility = if (it == true) View.VISIBLE else View.GONE
|
||||||
(binding.homeDantotsuIcon.drawable as Animatable).start()
|
(binding.homeDantotsuIcon.drawable as Animatable).start()
|
||||||
binding.homeDantotsuContainer.startAnimation(setSlideUp(uiSettings))
|
binding.homeDantotsuContainer.startAnimation(setSlideUp())
|
||||||
binding.homeDantotsuIcon.setSafeOnClickListener {
|
binding.homeDantotsuIcon.setSafeOnClickListener {
|
||||||
(binding.homeDantotsuIcon.drawable as Animatable).start()
|
(binding.homeDantotsuIcon.drawable as Animatable).start()
|
||||||
}
|
}
|
||||||
|
@ -330,8 +329,6 @@ class HomeFragment : Fragment() {
|
||||||
live.observe(viewLifecycleOwner) {
|
live.observe(viewLifecycleOwner) {
|
||||||
if (it) {
|
if (it) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
uiSettings =
|
|
||||||
loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
//Get userData First
|
//Get userData First
|
||||||
getUserId(requireContext()) {
|
getUserId(requireContext()) {
|
||||||
|
@ -340,8 +337,10 @@ class HomeFragment : Fragment() {
|
||||||
model.loaded = true
|
model.loaded = true
|
||||||
model.setListImages()
|
model.setListImages()
|
||||||
var empty = true
|
var empty = true
|
||||||
|
val homeLayoutShow: List<Boolean> =
|
||||||
|
PrefManager.getVal(PrefName.HomeLayoutShow)
|
||||||
(array.indices).forEach { i ->
|
(array.indices).forEach { i ->
|
||||||
if (uiSettings.homeLayoutShow[i]) {
|
if (homeLayoutShow.elementAt(i)) {
|
||||||
array[i].run()
|
array[i].run()
|
||||||
empty = false
|
empty = false
|
||||||
} else withContext(Dispatchers.Main) {
|
} else withContext(Dispatchers.Main) {
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
package ani.dantotsu.home
|
package ani.dantotsu.home
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
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 androidx.fragment.app.Fragment
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.databinding.FragmentLoginBinding
|
import ani.dantotsu.databinding.FragmentLoginBinding
|
||||||
import ani.dantotsu.openLinkInBrowser
|
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() {
|
class LoginFragment : Fragment() {
|
||||||
|
|
||||||
|
@ -29,5 +38,99 @@ class LoginFragment : Fragment() {
|
||||||
binding.loginDiscord.setOnClickListener { openLinkInBrowser(getString(R.string.discord)) }
|
binding.loginDiscord.setOnClickListener { openLinkInBrowser(getString(R.string.discord)) }
|
||||||
binding.loginGithub.setOnClickListener { openLinkInBrowser(getString(R.string.github)) }
|
binding.loginGithub.setOnClickListener { openLinkInBrowser(getString(R.string.github)) }
|
||||||
binding.loginTelegram.setOnClickListener { openLinkInBrowser(getString(R.string.telegram)) }
|
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<TextInputEditText>(R.id.userAgentTextBox)?.hint = "Password"
|
||||||
|
val subtitleTextView = dialogView.findViewById<TextView>(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<TextInputEditText>(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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,7 +2,6 @@ package ani.dantotsu.home
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
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.SearchResults
|
||||||
import ani.dantotsu.connections.anilist.getUserId
|
import ani.dantotsu.connections.anilist.getUserId
|
||||||
import ani.dantotsu.databinding.FragmentMangaBinding
|
import ani.dantotsu.databinding.FragmentMangaBinding
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.media.MediaAdaptor
|
import ani.dantotsu.media.MediaAdaptor
|
||||||
import ani.dantotsu.media.ProgressAdapter
|
import ani.dantotsu.media.ProgressAdapter
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.px
|
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.snackString
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -46,9 +45,6 @@ class MangaFragment : Fragment() {
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
private lateinit var mangaPageAdapter: MangaPageAdapter
|
private lateinit var mangaPageAdapter: MangaPageAdapter
|
||||||
|
|
||||||
private var uiSettings: UserInterfaceSettings =
|
|
||||||
loadData("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
|
|
||||||
val model: AnilistMangaViewModel by activityViewModels()
|
val model: AnilistMangaViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
@ -175,7 +171,7 @@ class MangaFragment : Fragment() {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
mangaPageAdapter.updateTrending(
|
mangaPageAdapter.updateTrending(
|
||||||
MediaAdaptor(
|
MediaAdaptor(
|
||||||
if (uiSettings.smallView) 3 else 2,
|
if (PrefManager.getVal(PrefName.SmallView)) 3 else 2,
|
||||||
it,
|
it,
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
viewPager = mangaPageAdapter.trendingViewPager
|
viewPager = mangaPageAdapter.trendingViewPager
|
||||||
|
@ -242,8 +238,11 @@ class MangaFragment : Fragment() {
|
||||||
model.loaded = true
|
model.loaded = true
|
||||||
model.loadTrending()
|
model.loadTrending()
|
||||||
model.loadTrendingNovel()
|
model.loadTrendingNovel()
|
||||||
model.loadPopular("MANGA", sort = Anilist.sortBy[1], onList = requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
model.loadPopular(
|
||||||
.getBoolean("popular_list", false) )
|
"MANGA", sort = Anilist.sortBy[1], onList = PrefManager.getVal(
|
||||||
|
PrefName.PopularMangaList
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
live.postValue(false)
|
live.postValue(false)
|
||||||
_binding?.mangaRefresh?.isRefreshing = false
|
_binding?.mangaRefresh?.isRefreshing = false
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package ani.dantotsu.home
|
package ani.dantotsu.home
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
@ -22,7 +21,6 @@ import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.databinding.ItemMangaPageBinding
|
import ani.dantotsu.databinding.ItemMangaPageBinding
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.loadImage
|
import ani.dantotsu.loadImage
|
||||||
import ani.dantotsu.media.GenreActivity
|
import ani.dantotsu.media.GenreActivity
|
||||||
import ani.dantotsu.media.MediaAdaptor
|
import ani.dantotsu.media.MediaAdaptor
|
||||||
|
@ -32,7 +30,8 @@ import ani.dantotsu.setSafeOnClickListener
|
||||||
import ani.dantotsu.setSlideIn
|
import ani.dantotsu.setSlideIn
|
||||||
import ani.dantotsu.setSlideUp
|
import ani.dantotsu.setSlideUp
|
||||||
import ani.dantotsu.settings.SettingsDialogFragment
|
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 ani.dantotsu.statusBarHeight
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
@ -43,8 +42,6 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
|
||||||
private var trendHandler: Handler? = null
|
private var trendHandler: Handler? = null
|
||||||
private lateinit var trendRun: Runnable
|
private lateinit var trendRun: Runnable
|
||||||
var trendingViewPager: ViewPager2? = null
|
var trendingViewPager: ViewPager2? = null
|
||||||
private var uiSettings: UserInterfaceSettings =
|
|
||||||
loadData("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MangaPageViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MangaPageViewHolder {
|
||||||
val binding =
|
val binding =
|
||||||
|
@ -67,17 +64,12 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
|
||||||
currContext()?.theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
|
currContext()?.theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
|
||||||
val color = typedValue.data
|
val color = typedValue.data
|
||||||
|
|
||||||
|
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
|
||||||
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
|
||||||
?.getBoolean("colorOverflow", false) ?: false
|
|
||||||
if (!colorOverflow) {
|
|
||||||
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
|
|
||||||
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.mangaTitleContainer.updatePadding(top = statusBarHeight)
|
binding.mangaTitleContainer.updatePadding(top = statusBarHeight)
|
||||||
|
|
||||||
if (uiSettings.smallView) binding.mangaTrendingContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
if (PrefManager.getVal(PrefName.SmallView)) binding.mangaTrendingContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
bottomMargin = (-108f).px
|
bottomMargin = (-108f).px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,14 +118,12 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
|
||||||
binding.mangaIncludeList.visibility =
|
binding.mangaIncludeList.visibility =
|
||||||
if (Anilist.userid != null) View.VISIBLE else View.GONE
|
if (Anilist.userid != null) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
binding.mangaIncludeList.isChecked = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
binding.mangaIncludeList.isChecked = PrefManager.getVal(PrefName.PopularMangaList)
|
||||||
?.getBoolean("popular_list", true) ?: true
|
|
||||||
|
|
||||||
binding.mangaIncludeList.setOnCheckedChangeListener { _, isChecked ->
|
binding.mangaIncludeList.setOnCheckedChangeListener { _, isChecked ->
|
||||||
onIncludeListClick.invoke(isChecked)
|
onIncludeListClick.invoke(isChecked)
|
||||||
|
|
||||||
currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
|
PrefManager.setVal(PrefName.PopularMangaList, isChecked)
|
||||||
?.putBoolean("popular_list", isChecked)?.apply()
|
|
||||||
}
|
}
|
||||||
if (ready.value == false)
|
if (ready.value == false)
|
||||||
ready.postValue(true)
|
ready.postValue(true)
|
||||||
|
@ -169,10 +159,10 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.mangaTrendingViewPager.layoutAnimation =
|
binding.mangaTrendingViewPager.layoutAnimation =
|
||||||
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
|
LayoutAnimationController(setSlideIn(), 0.25f)
|
||||||
binding.mangaTitleContainer.startAnimation(setSlideUp(uiSettings))
|
binding.mangaTitleContainer.startAnimation(setSlideUp())
|
||||||
binding.mangaListContainer.layoutAnimation =
|
binding.mangaListContainer.layoutAnimation =
|
||||||
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
|
LayoutAnimationController(setSlideIn(), 0.25f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateNovel(adaptor: MediaAdaptor) {
|
fun updateNovel(adaptor: MediaAdaptor) {
|
||||||
|
@ -187,11 +177,11 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
|
||||||
binding.mangaNovelRecyclerView.visibility = View.VISIBLE
|
binding.mangaNovelRecyclerView.visibility = View.VISIBLE
|
||||||
|
|
||||||
binding.mangaNovel.visibility = View.VISIBLE
|
binding.mangaNovel.visibility = View.VISIBLE
|
||||||
binding.mangaNovel.startAnimation(setSlideUp(uiSettings))
|
binding.mangaNovel.startAnimation(setSlideUp())
|
||||||
binding.mangaNovelRecyclerView.layoutAnimation =
|
binding.mangaNovelRecyclerView.layoutAnimation =
|
||||||
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
|
LayoutAnimationController(setSlideIn(), 0.25f)
|
||||||
binding.mangaPopular.visibility = View.VISIBLE
|
binding.mangaPopular.visibility = View.VISIBLE
|
||||||
binding.mangaPopular.startAnimation(setSlideUp(uiSettings))
|
binding.mangaPopular.startAnimation(setSlideUp())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateAvatar() {
|
fun updateAvatar() {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package ani.dantotsu.home
|
package ani.dantotsu.home
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -23,43 +22,36 @@ import ani.dantotsu.databinding.ActivityNoInternetBinding
|
||||||
import ani.dantotsu.download.anime.OfflineAnimeFragment
|
import ani.dantotsu.download.anime.OfflineAnimeFragment
|
||||||
import ani.dantotsu.download.manga.OfflineMangaFragment
|
import ani.dantotsu.download.manga.OfflineMangaFragment
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.offline.OfflineFragment
|
import ani.dantotsu.offline.OfflineFragment
|
||||||
import ani.dantotsu.others.LangSet
|
|
||||||
import ani.dantotsu.selectedOption
|
import ani.dantotsu.selectedOption
|
||||||
import ani.dantotsu.settings.UserInterfaceSettings
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.snackString
|
import ani.dantotsu.snackString
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import nl.joery.animatedbottombar.AnimatedBottomBar
|
import nl.joery.animatedbottombar.AnimatedBottomBar
|
||||||
|
|
||||||
class NoInternet : AppCompatActivity() {
|
class NoInternet : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivityNoInternetBinding
|
private lateinit var binding: ActivityNoInternetBinding
|
||||||
lateinit var bottomBar: AnimatedBottomBar
|
|
||||||
private var uiSettings = UserInterfaceSettings()
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
|
|
||||||
binding = ActivityNoInternetBinding.inflate(layoutInflater)
|
binding = ActivityNoInternetBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
val _bottomBar = findViewById<AnimatedBottomBar>(R.id.navbar)
|
val bottomBar = findViewById<AnimatedBottomBar>(R.id.navbar)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
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 currentColor = backgroundDrawable.color?.defaultColor ?: 0
|
||||||
val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xE8000000.toInt()
|
val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xE8000000.toInt()
|
||||||
backgroundDrawable.setColor(semiTransparentColor)
|
backgroundDrawable.setColor(semiTransparentColor)
|
||||||
_bottomBar.background = backgroundDrawable
|
bottomBar.background = backgroundDrawable
|
||||||
}
|
}
|
||||||
val colorOverflow = this.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
bottomBar.background = ContextCompat.getDrawable(this, R.drawable.bottom_nav_gray)
|
||||||
.getBoolean("colorOverflow", false)
|
|
||||||
if (!colorOverflow) {
|
|
||||||
_bottomBar.background = ContextCompat.getDrawable(this, R.drawable.bottom_nav_gray)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var doubleBackToExitPressedOnce = false
|
var doubleBackToExitPressedOnce = false
|
||||||
onBackPressedDispatcher.addCallback(this) {
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
|
@ -76,8 +68,7 @@ class NoInternet : AppCompatActivity() {
|
||||||
|
|
||||||
binding.root.doOnAttach {
|
binding.root.doOnAttach {
|
||||||
initActivity(this)
|
initActivity(this)
|
||||||
uiSettings = loadData("ui_settings") ?: uiSettings
|
selectedOption = PrefManager.getVal(PrefName.DefaultStartUpTab)
|
||||||
selectedOption = uiSettings.defaultStartUpTab
|
|
||||||
|
|
||||||
binding.includedNavbar.navbarContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
binding.includedNavbar.navbarContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
bottomMargin = navBarHeight
|
bottomMargin = navBarHeight
|
||||||
|
@ -89,7 +80,7 @@ class NoInternet : AppCompatActivity() {
|
||||||
val mainViewPager = binding.viewpager
|
val mainViewPager = binding.viewpager
|
||||||
mainViewPager.isUserInputEnabled = false
|
mainViewPager.isUserInputEnabled = false
|
||||||
mainViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle)
|
mainViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle)
|
||||||
mainViewPager.setPageTransformer(ZoomOutPageTransformer(uiSettings))
|
mainViewPager.setPageTransformer(ZoomOutPageTransformer())
|
||||||
navbar.setOnTabSelectListener(object :
|
navbar.setOnTabSelectListener(object :
|
||||||
AnimatedBottomBar.OnTabSelectListener {
|
AnimatedBottomBar.OnTabSelectListener {
|
||||||
override fun onTabSelected(
|
override fun onTabSelected(
|
||||||
|
|
|
@ -18,7 +18,6 @@ import ani.dantotsu.Refresh
|
||||||
import ani.dantotsu.databinding.ActivityAuthorBinding
|
import ani.dantotsu.databinding.ActivityAuthorBinding
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.others.LangSet
|
|
||||||
import ani.dantotsu.others.getSerialized
|
import ani.dantotsu.others.getSerialized
|
||||||
import ani.dantotsu.px
|
import ani.dantotsu.px
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
|
@ -36,7 +35,7 @@ class AuthorActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityAuthorBinding.inflate(layoutInflater)
|
binding = ActivityAuthorBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
|
@ -16,11 +16,10 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.Refresh
|
import ani.dantotsu.Refresh
|
||||||
import ani.dantotsu.databinding.ActivityListBinding
|
import ani.dantotsu.databinding.ActivityListBinding
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.media.user.ListViewPagerAdapter
|
import ani.dantotsu.media.user.ListViewPagerAdapter
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.others.LangSet
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.UserInterfaceSettings
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
|
@ -38,7 +37,7 @@ class CalendarActivity : AppCompatActivity() {
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityListBinding.inflate(layoutInflater)
|
binding = ActivityListBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
@ -67,8 +66,7 @@ class CalendarActivity : AppCompatActivity() {
|
||||||
binding.listTitle.setTextColor(primaryTextColor)
|
binding.listTitle.setTextColor(primaryTextColor)
|
||||||
binding.listTabLayout.setTabTextColors(secondaryTextColor, primaryTextColor)
|
binding.listTabLayout.setTabTextColors(secondaryTextColor, primaryTextColor)
|
||||||
binding.listTabLayout.setSelectedTabIndicatorColor(primaryTextColor)
|
binding.listTabLayout.setSelectedTabIndicatorColor(primaryTextColor)
|
||||||
val uiSettings = loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
|
if (!(PrefManager.getVal(PrefName.ImmersiveMode) as Boolean)) {
|
||||||
if (!uiSettings.immersiveMode) {
|
|
||||||
this.window.statusBarColor =
|
this.window.statusBarColor =
|
||||||
ContextCompat.getColor(this, R.color.nav_bg_inv)
|
ContextCompat.getColor(this, R.color.nav_bg_inv)
|
||||||
binding.root.fitsSystemWindows = true
|
binding.root.fitsSystemWindows = true
|
||||||
|
|
|
@ -11,10 +11,8 @@ import androidx.core.util.Pair
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import ani.dantotsu.databinding.ItemCharacterBinding
|
import ani.dantotsu.databinding.ItemCharacterBinding
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.loadImage
|
import ani.dantotsu.loadImage
|
||||||
import ani.dantotsu.setAnimation
|
import ani.dantotsu.setAnimation
|
||||||
import ani.dantotsu.settings.UserInterfaceSettings
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
class CharacterAdapter(
|
class CharacterAdapter(
|
||||||
|
@ -26,13 +24,10 @@ class CharacterAdapter(
|
||||||
return CharacterViewHolder(binding)
|
return CharacterViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val uiSettings =
|
|
||||||
loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onBindViewHolder(holder: CharacterViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: CharacterViewHolder, position: Int) {
|
||||||
val binding = holder.binding
|
val binding = holder.binding
|
||||||
setAnimation(binding.root.context, holder.binding.root, uiSettings)
|
setAnimation(binding.root.context, holder.binding.root)
|
||||||
val character = characterList[position]
|
val character = characterList[position]
|
||||||
binding.itemCompactRelation.text = character.role + " "
|
binding.itemCompactRelation.text = character.role + " "
|
||||||
binding.itemCompactImage.loadImage(character.image)
|
binding.itemCompactImage.loadImage(character.image)
|
||||||
|
|
|
@ -17,14 +17,13 @@ import ani.dantotsu.R
|
||||||
import ani.dantotsu.Refresh
|
import ani.dantotsu.Refresh
|
||||||
import ani.dantotsu.databinding.ActivityCharacterBinding
|
import ani.dantotsu.databinding.ActivityCharacterBinding
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.loadImage
|
import ani.dantotsu.loadImage
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.others.ImageViewDialog
|
import ani.dantotsu.others.ImageViewDialog
|
||||||
import ani.dantotsu.others.LangSet
|
|
||||||
import ani.dantotsu.others.getSerialized
|
import ani.dantotsu.others.getSerialized
|
||||||
import ani.dantotsu.px
|
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.statusBarHeight
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
@ -38,22 +37,21 @@ class CharacterDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChang
|
||||||
private val model: OtherDetailsViewModel by viewModels()
|
private val model: OtherDetailsViewModel by viewModels()
|
||||||
private lateinit var character: Character
|
private lateinit var character: Character
|
||||||
private var loaded = false
|
private var loaded = false
|
||||||
val uiSettings = loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityCharacterBinding.inflate(layoutInflater)
|
binding = ActivityCharacterBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
initActivity(this)
|
initActivity(this)
|
||||||
screenWidth = resources.displayMetrics.run { widthPixels / density }
|
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)
|
ContextCompat.getColor(this, R.color.status)
|
||||||
|
|
||||||
val banner =
|
val banner =
|
||||||
if (uiSettings.bannerAnimations) binding.characterBanner else binding.characterBannerNoKen
|
if (PrefManager.getVal(PrefName.BannerAnimations)) binding.characterBanner else binding.characterBannerNoKen
|
||||||
|
|
||||||
banner.updateLayoutParams { height += statusBarHeight }
|
banner.updateLayoutParams { height += statusBarHeight }
|
||||||
binding.characterClose.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
|
binding.characterClose.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
|
||||||
|
@ -136,16 +134,16 @@ class CharacterDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChang
|
||||||
|
|
||||||
binding.characterCover.visibility =
|
binding.characterCover.visibility =
|
||||||
if (binding.characterCover.scaleX == 0f) View.GONE else View.VISIBLE
|
if (binding.characterCover.scaleX == 0f) View.GONE else View.VISIBLE
|
||||||
|
val immersiveMode: Boolean = PrefManager.getVal(PrefName.ImmersiveMode)
|
||||||
if (percentage >= percent && !isCollapsed) {
|
if (percentage >= percent && !isCollapsed) {
|
||||||
isCollapsed = true
|
isCollapsed = true
|
||||||
if (uiSettings.immersiveMode) this.window.statusBarColor =
|
if (immersiveMode) this.window.statusBarColor =
|
||||||
ContextCompat.getColor(this, R.color.nav_bg)
|
ContextCompat.getColor(this, R.color.nav_bg)
|
||||||
binding.characterAppBar.setBackgroundResource(R.color.nav_bg)
|
binding.characterAppBar.setBackgroundResource(R.color.nav_bg)
|
||||||
}
|
}
|
||||||
if (percentage <= percent && isCollapsed) {
|
if (percentage <= percent && isCollapsed) {
|
||||||
isCollapsed = false
|
isCollapsed = false
|
||||||
if (uiSettings.immersiveMode) this.window.statusBarColor =
|
if (immersiveMode) this.window.statusBarColor =
|
||||||
ContextCompat.getColor(this, R.color.status)
|
ContextCompat.getColor(this, R.color.status)
|
||||||
binding.characterAppBar.setBackgroundResource(R.color.bg)
|
binding.characterAppBar.setBackgroundResource(R.color.bg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,9 @@ import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.anilist.GenresViewModel
|
import ani.dantotsu.connections.anilist.GenresViewModel
|
||||||
import ani.dantotsu.databinding.ActivityGenreBinding
|
import ani.dantotsu.databinding.ActivityGenreBinding
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.navBarHeight
|
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.statusBarHeight
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -27,7 +27,7 @@ class GenreActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityGenreBinding.inflate(layoutInflater)
|
binding = ActivityGenreBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
@ -54,7 +54,9 @@ class GenreActivity : AppCompatActivity() {
|
||||||
GridLayoutManager(this, (screenWidth / 156f).toInt())
|
GridLayoutManager(this, (screenWidth / 156f).toInt())
|
||||||
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
model.loadGenres(Anilist.genres ?: loadData("genres_list") ?: arrayListOf()) {
|
model.loadGenres(
|
||||||
|
Anilist.genres ?: loadLocalGenres() ?: arrayListOf()
|
||||||
|
) {
|
||||||
MainScope().launch {
|
MainScope().launch {
|
||||||
adapter.addGenre(it)
|
adapter.addGenre(it)
|
||||||
}
|
}
|
||||||
|
@ -62,4 +64,14 @@ class GenreActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadLocalGenres(): ArrayList<String>? {
|
||||||
|
val genres = PrefManager.getVal<Set<String>>(PrefName.GenresList)
|
||||||
|
.toMutableList() as ArrayList<String>?
|
||||||
|
return if (genres.isNullOrEmpty()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
genres
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -26,7 +26,8 @@ import ani.dantotsu.databinding.ItemMediaCompactBinding
|
||||||
import ani.dantotsu.databinding.ItemMediaLargeBinding
|
import ani.dantotsu.databinding.ItemMediaLargeBinding
|
||||||
import ani.dantotsu.databinding.ItemMediaPageBinding
|
import ani.dantotsu.databinding.ItemMediaPageBinding
|
||||||
import ani.dantotsu.databinding.ItemMediaPageSmallBinding
|
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.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.load.model.GlideUrl
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
|
@ -44,9 +45,6 @@ class MediaAdaptor(
|
||||||
private val viewPager: ViewPager2? = null,
|
private val viewPager: ViewPager2? = null,
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
private val uiSettings =
|
|
||||||
loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
0 -> MediaViewHolder(
|
0 -> MediaViewHolder(
|
||||||
|
@ -91,7 +89,7 @@ class MediaAdaptor(
|
||||||
when (type) {
|
when (type) {
|
||||||
0 -> {
|
0 -> {
|
||||||
val b = (holder as MediaViewHolder).binding
|
val b = (holder as MediaViewHolder).binding
|
||||||
setAnimation(activity, b.root, uiSettings)
|
setAnimation(activity, b.root)
|
||||||
val media = mediaList?.getOrNull(position)
|
val media = mediaList?.getOrNull(position)
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
b.itemCompactImage.loadImage(media.cover)
|
b.itemCompactImage.loadImage(media.cover)
|
||||||
|
@ -135,7 +133,7 @@ class MediaAdaptor(
|
||||||
|
|
||||||
1 -> {
|
1 -> {
|
||||||
val b = (holder as MediaLargeViewHolder).binding
|
val b = (holder as MediaLargeViewHolder).binding
|
||||||
setAnimation(activity, b.root, uiSettings)
|
setAnimation(activity, b.root)
|
||||||
val media = mediaList?.get(position)
|
val media = mediaList?.get(position)
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
b.itemCompactImage.loadImage(media.cover)
|
b.itemCompactImage.loadImage(media.cover)
|
||||||
|
@ -178,16 +176,17 @@ class MediaAdaptor(
|
||||||
val b = (holder as MediaPageViewHolder).binding
|
val b = (holder as MediaPageViewHolder).binding
|
||||||
val media = mediaList?.get(position)
|
val media = mediaList?.get(position)
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
|
val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
|
||||||
b.itemCompactImage.loadImage(media.cover)
|
b.itemCompactImage.loadImage(media.cover)
|
||||||
if (uiSettings.bannerAnimations)
|
if (bannerAnimations)
|
||||||
b.itemCompactBanner.setTransitionGenerator(
|
b.itemCompactBanner.setTransitionGenerator(
|
||||||
RandomTransitionGenerator(
|
RandomTransitionGenerator(
|
||||||
(10000 + 15000 * (uiSettings.animationSpeed)).toLong(),
|
(10000 + 15000 * ((PrefManager.getVal(PrefName.AnimationSpeed)) as Float)).toLong(),
|
||||||
AccelerateDecelerateInterpolator()
|
AccelerateDecelerateInterpolator()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val banner =
|
val banner =
|
||||||
if (uiSettings.bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
|
if (bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
|
||||||
val context = b.itemCompactBanner.context
|
val context = b.itemCompactBanner.context
|
||||||
if (!(context as Activity).isDestroyed)
|
if (!(context as Activity).isDestroyed)
|
||||||
Glide.with(context as Context)
|
Glide.with(context as Context)
|
||||||
|
@ -234,16 +233,17 @@ class MediaAdaptor(
|
||||||
val b = (holder as MediaPageSmallViewHolder).binding
|
val b = (holder as MediaPageSmallViewHolder).binding
|
||||||
val media = mediaList?.get(position)
|
val media = mediaList?.get(position)
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
|
val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
|
||||||
b.itemCompactImage.loadImage(media.cover)
|
b.itemCompactImage.loadImage(media.cover)
|
||||||
if (uiSettings.bannerAnimations)
|
if (bannerAnimations)
|
||||||
b.itemCompactBanner.setTransitionGenerator(
|
b.itemCompactBanner.setTransitionGenerator(
|
||||||
RandomTransitionGenerator(
|
RandomTransitionGenerator(
|
||||||
(10000 + 15000 * (uiSettings.animationSpeed)).toLong(),
|
(10000 + 15000 * ((PrefManager.getVal(PrefName.AnimationSpeed) as Float))).toLong(),
|
||||||
AccelerateDecelerateInterpolator()
|
AccelerateDecelerateInterpolator()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val banner =
|
val banner =
|
||||||
if (uiSettings.bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
|
if (bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
|
||||||
val context = b.itemCompactBanner.context
|
val context = b.itemCompactBanner.context
|
||||||
if (!(context as Activity).isDestroyed)
|
if (!(context as Activity).isDestroyed)
|
||||||
Glide.with(context as Context)
|
Glide.with(context as Context)
|
||||||
|
@ -396,10 +396,8 @@ class MediaAdaptor(
|
||||||
if (itemCompactImage != null) {
|
if (itemCompactImage != null) {
|
||||||
ActivityOptionsCompat.makeSceneTransitionAnimation(
|
ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||||
activity,
|
activity,
|
||||||
Pair.create(
|
itemCompactImage,
|
||||||
itemCompactImage,
|
ViewCompat.getTransitionName(itemCompactImage)!!
|
||||||
ViewCompat.getTransitionName(activity.findViewById(R.id.itemCompactImage))!!
|
|
||||||
),
|
|
||||||
).toBundle()
|
).toBundle()
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
|
@ -2,7 +2,6 @@ package ani.dantotsu.media
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
|
@ -35,7 +34,6 @@ import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.copyToClipboard
|
import ani.dantotsu.copyToClipboard
|
||||||
import ani.dantotsu.databinding.ActivityMediaBinding
|
import ani.dantotsu.databinding.ActivityMediaBinding
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.loadImage
|
import ani.dantotsu.loadImage
|
||||||
import ani.dantotsu.media.anime.AnimeWatchFragment
|
import ani.dantotsu.media.anime.AnimeWatchFragment
|
||||||
import ani.dantotsu.media.manga.MangaReadFragment
|
import ani.dantotsu.media.manga.MangaReadFragment
|
||||||
|
@ -44,8 +42,8 @@ import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.openLinkInBrowser
|
import ani.dantotsu.openLinkInBrowser
|
||||||
import ani.dantotsu.others.ImageViewDialog
|
import ani.dantotsu.others.ImageViewDialog
|
||||||
import ani.dantotsu.others.getSerialized
|
import ani.dantotsu.others.getSerialized
|
||||||
import ani.dantotsu.saveData
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.UserInterfaceSettings
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.snackString
|
import ani.dantotsu.snackString
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
|
@ -65,7 +63,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
private val scope = lifecycleScope
|
private val scope = lifecycleScope
|
||||||
private val model: MediaDetailsViewModel by viewModels()
|
private val model: MediaDetailsViewModel by viewModels()
|
||||||
private lateinit var tabLayout: NavigationBarView
|
private lateinit var tabLayout: NavigationBarView
|
||||||
private lateinit var uiSettings: UserInterfaceSettings
|
|
||||||
var selected = 0
|
var selected = 0
|
||||||
var anime = true
|
var anime = true
|
||||||
private var adult = false
|
private var adult = false
|
||||||
|
@ -90,7 +87,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
//Ui init
|
//Ui init
|
||||||
|
|
||||||
initActivity(this)
|
initActivity(this)
|
||||||
uiSettings = loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
|
|
||||||
|
|
||||||
binding.mediaBanner.updateLayoutParams { height += statusBarHeight }
|
binding.mediaBanner.updateLayoutParams { height += statusBarHeight }
|
||||||
binding.mediaBannerNoKen.updateLayoutParams { height += statusBarHeight }
|
binding.mediaBannerNoKen.updateLayoutParams { height += statusBarHeight }
|
||||||
|
@ -111,20 +107,21 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiSettings.bannerAnimations) {
|
val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
|
||||||
|
if (bannerAnimations) {
|
||||||
val adi = AccelerateDecelerateInterpolator()
|
val adi = AccelerateDecelerateInterpolator()
|
||||||
val generator = RandomTransitionGenerator(
|
val generator = RandomTransitionGenerator(
|
||||||
(10000 + 15000 * (uiSettings.animationSpeed)).toLong(),
|
(10000 + 15000 * ((PrefManager.getVal(PrefName.AnimationSpeed) as Float))).toLong(),
|
||||||
adi
|
adi
|
||||||
)
|
)
|
||||||
binding.mediaBanner.setTransitionGenerator(generator)
|
binding.mediaBanner.setTransitionGenerator(generator)
|
||||||
}
|
}
|
||||||
val banner =
|
val banner =
|
||||||
if (uiSettings.bannerAnimations) binding.mediaBanner else binding.mediaBannerNoKen
|
if (bannerAnimations) binding.mediaBanner else binding.mediaBannerNoKen
|
||||||
val viewPager = binding.mediaViewPager
|
val viewPager = binding.mediaViewPager
|
||||||
tabLayout = binding.mediaTab as NavigationBarView
|
tabLayout = binding.mediaTab as NavigationBarView
|
||||||
viewPager.isUserInputEnabled = false
|
viewPager.isUserInputEnabled = false
|
||||||
viewPager.setPageTransformer(ZoomOutPageTransformer(uiSettings))
|
viewPager.setPageTransformer(ZoomOutPageTransformer())
|
||||||
|
|
||||||
|
|
||||||
val isDownload = intent.getBooleanExtra("download", false)
|
val isDownload = intent.getBooleanExtra("download", false)
|
||||||
|
@ -141,7 +138,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
banner.loadImage(media.banner ?: media.cover, 400)
|
banner.loadImage(media.banner ?: media.cover, 400)
|
||||||
val gestureDetector = GestureDetector(this, object : GesturesListener() {
|
val gestureDetector = GestureDetector(this, object : GesturesListener() {
|
||||||
override fun onDoubleClick(event: MotionEvent) {
|
override fun onDoubleClick(event: MotionEvent) {
|
||||||
if (!uiSettings.bannerAnimations)
|
if (!(PrefManager.getVal(PrefName.BannerAnimations) as Boolean))
|
||||||
snackString(getString(R.string.enable_banner_animations))
|
snackString(getString(R.string.enable_banner_animations))
|
||||||
else {
|
else {
|
||||||
binding.mediaBanner.restart()
|
binding.mediaBanner.restart()
|
||||||
|
@ -159,11 +156,10 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
banner.setOnTouchListener { _, motionEvent -> gestureDetector.onTouchEvent(motionEvent);true }
|
banner.setOnTouchListener { _, motionEvent -> gestureDetector.onTouchEvent(motionEvent);true }
|
||||||
if (this.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
if (PrefManager.getVal(PrefName.Incognito)) {
|
||||||
.getBoolean("incognito", false)) {
|
|
||||||
binding.mediaTitle.text = " ${media.userPreferredName}"
|
binding.mediaTitle.text = " ${media.userPreferredName}"
|
||||||
binding.incognito.visibility = View.VISIBLE
|
binding.incognito.visibility = View.VISIBLE
|
||||||
}else {
|
} else {
|
||||||
binding.mediaTitle.text = media.userPreferredName
|
binding.mediaTitle.text = media.userPreferredName
|
||||||
}
|
}
|
||||||
binding.mediaTitle.setOnLongClickListener {
|
binding.mediaTitle.setOnLongClickListener {
|
||||||
|
@ -284,7 +280,10 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
} else snackString(getString(R.string.please_login_anilist))
|
} else snackString(getString(R.string.please_login_anilist))
|
||||||
}
|
}
|
||||||
binding.mediaAddToList.setOnLongClickListener {
|
binding.mediaAddToList.setOnLongClickListener {
|
||||||
saveData("${media.id}_progressDialog", true)
|
PrefManager.setCustomVal(
|
||||||
|
"${media.id}_progressDialog",
|
||||||
|
true,
|
||||||
|
)
|
||||||
snackString(getString(R.string.auto_update_reset))
|
snackString(getString(R.string.auto_update_reset))
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -345,7 +344,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
viewPager.setCurrentItem(selected, false)
|
viewPager.setCurrentItem(selected, false)
|
||||||
val sel = model.loadSelected(media, isDownload)
|
val sel = model.loadSelected(media, isDownload)
|
||||||
sel.window = selected
|
sel.window = selected
|
||||||
model.saveSelected(media.id, sel, this)
|
model.saveSelected(media.id, sel)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,7 +353,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
viewPager.setCurrentItem(selected, false)
|
viewPager.setCurrentItem(selected, false)
|
||||||
|
|
||||||
if (model.continueMedia == null && media.cameFromContinue) {
|
if (model.continueMedia == null && media.cameFromContinue) {
|
||||||
model.continueMedia = loadData("continue_media") ?: true
|
model.continueMedia = PrefManager.getVal(PrefName.ContinueMedia)
|
||||||
selected = 1
|
selected = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,7 +394,9 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
tabLayout.selectedItemId = idFromSelect()
|
if (this::tabLayout.isInitialized) {
|
||||||
|
tabLayout.selectedItemId = idFromSelect()
|
||||||
|
}
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,7 +438,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
|
|
||||||
binding.mediaCover.visibility =
|
binding.mediaCover.visibility =
|
||||||
if (binding.mediaCover.scaleX == 0f) View.GONE else View.VISIBLE
|
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()
|
val typedValue = TypedValue()
|
||||||
this@MediaDetailsActivity.theme.resolveAttribute(
|
this@MediaDetailsActivity.theme.resolveAttribute(
|
||||||
com.google.android.material.R.attr.colorSecondary,
|
com.google.android.material.R.attr.colorSecondary,
|
||||||
|
@ -467,7 +468,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
.start()
|
.start()
|
||||||
ObjectAnimator.ofFloat(binding.mediaCollapseContainer, "translationX", 0f)
|
ObjectAnimator.ofFloat(binding.mediaCollapseContainer, "translationX", 0f)
|
||||||
.setDuration(duration).start()
|
.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(
|
if (percentage == 1 && model.scrolledToTop.value != false) model.scrolledToTop.postValue(
|
||||||
false
|
false
|
||||||
|
@ -491,10 +492,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||||
|
|
||||||
init {
|
init {
|
||||||
enabled(true)
|
enabled(true)
|
||||||
scope.launch {
|
|
||||||
delay(100) //TODO: a listener would be better
|
|
||||||
clicked()
|
|
||||||
}
|
|
||||||
image.setOnClickListener {
|
image.setOnClickListener {
|
||||||
if (pressable && !disabled) {
|
if (pressable && !disabled) {
|
||||||
pressable = false
|
pressable = false
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package ani.dantotsu.media
|
package ani.dantotsu.media
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
|
@ -11,7 +9,6 @@ import androidx.lifecycle.ViewModel
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.logger
|
import ani.dantotsu.logger
|
||||||
import ani.dantotsu.media.anime.Episode
|
import ani.dantotsu.media.anime.Episode
|
||||||
import ani.dantotsu.media.anime.SelectorDialogFragment
|
import ani.dantotsu.media.anime.SelectorDialogFragment
|
||||||
|
@ -28,35 +25,32 @@ import ani.dantotsu.parsers.NovelSources
|
||||||
import ani.dantotsu.parsers.ShowResponse
|
import ani.dantotsu.parsers.ShowResponse
|
||||||
import ani.dantotsu.parsers.VideoExtractor
|
import ani.dantotsu.parsers.VideoExtractor
|
||||||
import ani.dantotsu.parsers.WatchSources
|
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.snackString
|
||||||
import ani.dantotsu.tryWithSuspend
|
import ani.dantotsu.tryWithSuspend
|
||||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
|
|
||||||
class MediaDetailsViewModel : ViewModel() {
|
class MediaDetailsViewModel : ViewModel() {
|
||||||
val scrolledToTop = MutableLiveData(true)
|
val scrolledToTop = MutableLiveData(true)
|
||||||
|
|
||||||
fun saveSelected(id: Int, data: Selected, activity: Activity? = null) {
|
fun saveSelected(id: Int, data: Selected) {
|
||||||
saveData("$id-select", data, activity)
|
PrefManager.setCustomVal("Selected-$id", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun loadSelected(media: Media, isDownload: Boolean = false): Selected {
|
fun loadSelected(media: Media, isDownload: Boolean = false): Selected {
|
||||||
val sharedPreferences = Injekt.get<SharedPreferences>()
|
val data =
|
||||||
val data = loadData<Selected>("${media.id}-select") ?: Selected().let {
|
PrefManager.getNullableCustomVal("Selected-${media.id}", null, Selected::class.java)
|
||||||
it.sourceIndex = if (media.isAdult) 0 else when (media.anime != null) {
|
?: Selected().let {
|
||||||
true -> sharedPreferences.getInt("settings_def_anime_source_s_r", 0)
|
it.sourceIndex = 0
|
||||||
else -> sharedPreferences.getInt(("settings_def_manga_source_s_r"), 0)
|
it.preferDub = PrefManager.getVal(PrefName.SettingsPreferDub)
|
||||||
}
|
saveSelected(media.id, it)
|
||||||
it.preferDub = loadData("settings_prefer_dub") ?: false
|
it
|
||||||
saveSelected(media.id, it)
|
}
|
||||||
it
|
|
||||||
}
|
|
||||||
if (isDownload) {
|
if (isDownload) {
|
||||||
data.sourceIndex = if (media.anime != null) {
|
data.sourceIndex = if (media.anime != null) {
|
||||||
AnimeSources.list.size - 1
|
AnimeSources.list.size - 1
|
||||||
|
|
|
@ -2,7 +2,6 @@ package ani.dantotsu.media
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -26,6 +25,8 @@ import ani.dantotsu.*
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.anilist.GenresViewModel
|
import ani.dantotsu.connections.anilist.GenresViewModel
|
||||||
import ani.dantotsu.databinding.*
|
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.Markwon
|
||||||
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -60,7 +61,7 @@ class MediaInfoFragment : Fragment() {
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
val model: MediaDetailsViewModel by activityViewModels()
|
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.mediaInfoProgressBar.visibility = if (!loaded) View.VISIBLE else View.GONE
|
||||||
binding.mediaInfoContainer.visibility = if (loaded) View.VISIBLE else View.GONE
|
binding.mediaInfoContainer.visibility = if (loaded) View.VISIBLE else View.GONE
|
||||||
binding.mediaInfoContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin += 128f.px + navBarHeight }
|
binding.mediaInfoContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin += 128f.px + navBarHeight }
|
||||||
|
@ -94,8 +95,31 @@ class MediaInfoFragment : Fragment() {
|
||||||
binding.mediaInfoStart.text = media.startDate?.toString() ?: "??"
|
binding.mediaInfoStart.text = media.startDate?.toString() ?: "??"
|
||||||
binding.mediaInfoEnd.text = media.endDate?.toString() ?: "??"
|
binding.mediaInfoEnd.text = media.endDate?.toString() ?: "??"
|
||||||
if (media.anime != null) {
|
if (media.anime != null) {
|
||||||
binding.mediaInfoDuration.text =
|
val episodeDuration = media.anime.episodeDuration
|
||||||
if (media.anime.episodeDuration != null) media.anime.episodeDuration.toString() else "??"
|
|
||||||
|
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.mediaInfoDurationContainer.visibility = View.VISIBLE
|
||||||
binding.mediaInfoSeasonContainer.visibility = View.VISIBLE
|
binding.mediaInfoSeasonContainer.visibility = View.VISIBLE
|
||||||
binding.mediaInfoSeason.text =
|
binding.mediaInfoSeason.text =
|
||||||
|
@ -464,7 +488,7 @@ class MediaInfoFragment : Fragment() {
|
||||||
parent.addView(bindi.root)
|
parent.addView(bindi.root)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!media.recommendations.isNullOrEmpty() && !offline ) {
|
if (!media.recommendations.isNullOrEmpty() && !offline) {
|
||||||
val bind = ItemTitleRecyclerBinding.inflate(
|
val bind = ItemTitleRecyclerBinding.inflate(
|
||||||
LayoutInflater.from(context),
|
LayoutInflater.from(context),
|
||||||
parent,
|
parent,
|
||||||
|
|
|
@ -16,7 +16,8 @@ import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.anilist.AnilistSearch
|
import ani.dantotsu.connections.anilist.AnilistSearch
|
||||||
import ani.dantotsu.connections.anilist.SearchResults
|
import ani.dantotsu.connections.anilist.SearchResults
|
||||||
import ani.dantotsu.databinding.ActivitySearchBinding
|
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 ani.dantotsu.themes.ThemeManager
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -33,13 +34,14 @@ class SearchActivity : AppCompatActivity() {
|
||||||
private lateinit var mediaAdaptor: MediaAdaptor
|
private lateinit var mediaAdaptor: MediaAdaptor
|
||||||
private lateinit var progressAdapter: ProgressAdapter
|
private lateinit var progressAdapter: ProgressAdapter
|
||||||
private lateinit var concatAdapter: ConcatAdapter
|
private lateinit var concatAdapter: ConcatAdapter
|
||||||
|
private lateinit var headerAdaptor: SearchAdapter
|
||||||
|
|
||||||
lateinit var result: SearchResults
|
lateinit var result: SearchResults
|
||||||
lateinit var updateChips: (() -> Unit)
|
lateinit var updateChips: (() -> Unit)
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivitySearchBinding.inflate(layoutInflater)
|
binding = ActivitySearchBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
@ -51,7 +53,7 @@ class SearchActivity : AppCompatActivity() {
|
||||||
bottom = navBarHeight + 80f.px
|
bottom = navBarHeight + 80f.px
|
||||||
)
|
)
|
||||||
|
|
||||||
style = loadData<Int>("searchStyle") ?: 0
|
style = PrefManager.getVal(PrefName.SearchStyle)
|
||||||
var listOnly: Boolean? = intent.getBooleanExtra("listOnly", false)
|
var listOnly: Boolean? = intent.getBooleanExtra("listOnly", false)
|
||||||
if (!listOnly!!) listOnly = null
|
if (!listOnly!!) listOnly = null
|
||||||
|
|
||||||
|
@ -76,7 +78,7 @@ class SearchActivity : AppCompatActivity() {
|
||||||
|
|
||||||
progressAdapter = ProgressAdapter(searched = model.searched)
|
progressAdapter = ProgressAdapter(searched = model.searched)
|
||||||
mediaAdaptor = MediaAdaptor(style, model.searchResults.results, this, matchParent = true)
|
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 gridSize = (screenWidth / 120f).toInt()
|
||||||
val gridLayoutManager = GridLayoutManager(this, gridSize)
|
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 searchTimer = Timer()
|
||||||
private var loading = false
|
private var loading = false
|
||||||
fun search() {
|
fun search() {
|
||||||
|
headerAdaptor.setHistoryVisibility(false)
|
||||||
val size = model.searchResults.results.size
|
val size = model.searchResults.results.size
|
||||||
model.searchResults.results.clear()
|
model.searchResults.results.clear()
|
||||||
binding.searchRecyclerView.post {
|
binding.searchRecyclerView.post {
|
||||||
|
@ -188,6 +199,7 @@ class SearchActivity : AppCompatActivity() {
|
||||||
|
|
||||||
var state: Parcelable? = null
|
var state: Parcelable? = null
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
|
headerAdaptor.addHistory()
|
||||||
super.onPause()
|
super.onPause()
|
||||||
state = binding.searchRecyclerView.layoutManager?.onSaveInstanceState()
|
state = binding.searchRecyclerView.layoutManager?.onSaveInstanceState()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ani.dantotsu.media
|
package ani.dantotsu.media
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
|
@ -9,6 +8,8 @@ import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.animation.AlphaAnimation
|
||||||
|
import android.view.animation.Animation
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -19,19 +20,25 @@ import androidx.recyclerview.widget.RecyclerView.HORIZONTAL
|
||||||
import ani.dantotsu.App.Companion.context
|
import ani.dantotsu.App.Companion.context
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.currContext
|
|
||||||
import ani.dantotsu.databinding.ItemChipBinding
|
import ani.dantotsu.databinding.ItemChipBinding
|
||||||
import ani.dantotsu.databinding.ItemSearchHeaderBinding
|
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 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<SearchAdapter.SearchHeaderViewHolder>() {
|
RecyclerView.Adapter<SearchAdapter.SearchHeaderViewHolder>() {
|
||||||
private val itemViewType = 6969
|
private val itemViewType = 6969
|
||||||
var search: Runnable? = null
|
var search: Runnable? = null
|
||||||
var requestFocus: Runnable? = null
|
var requestFocus: Runnable? = null
|
||||||
private var textWatcher: TextWatcher? = null
|
private var textWatcher: TextWatcher? = null
|
||||||
|
private lateinit var searchHistoryAdapter: SearchHistoryAdapter
|
||||||
|
private lateinit var binding: ItemSearchHeaderBinding
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHeaderViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHeaderViewHolder {
|
||||||
val binding =
|
val binding =
|
||||||
|
@ -41,8 +48,13 @@ class SearchAdapter(private val activity: SearchActivity) :
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
override fun onBindViewHolder(holder: SearchHeaderViewHolder, position: Int) {
|
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 =
|
val imm: InputMethodManager =
|
||||||
activity.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as 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
|
binding.searchBar.hint = activity.result.type
|
||||||
if (currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
if (PrefManager.getVal(PrefName.Incognito)) {
|
||||||
?.getBoolean("incognito", false ) == true){
|
|
||||||
val startIconDrawableRes = R.drawable.ic_incognito_24
|
val startIconDrawableRes = R.drawable.ic_incognito_24
|
||||||
val startIconDrawable: Drawable? =
|
val startIconDrawable: Drawable? =
|
||||||
context?.let { AppCompatResources.getDrawable(it, startIconDrawableRes) }
|
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 beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||||
|
|
||||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: 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)
|
binding.searchBarText.addTextChangedListener(textWatcher)
|
||||||
|
@ -126,14 +148,14 @@ class SearchAdapter(private val activity: SearchActivity) :
|
||||||
it.alpha = 1f
|
it.alpha = 1f
|
||||||
binding.searchResultList.alpha = 0.33f
|
binding.searchResultList.alpha = 0.33f
|
||||||
activity.style = 0
|
activity.style = 0
|
||||||
saveData("searchStyle", 0)
|
PrefManager.setVal(PrefName.SearchStyle, 0)
|
||||||
activity.recycler()
|
activity.recycler()
|
||||||
}
|
}
|
||||||
binding.searchResultList.setOnClickListener {
|
binding.searchResultList.setOnClickListener {
|
||||||
it.alpha = 1f
|
it.alpha = 1f
|
||||||
binding.searchResultGrid.alpha = 0.33f
|
binding.searchResultGrid.alpha = 0.33f
|
||||||
activity.style = 1
|
activity.style = 1
|
||||||
saveData("searchStyle", 1)
|
PrefManager.setVal(PrefName.SearchStyle, 1)
|
||||||
activity.recycler()
|
activity.recycler()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +198,40 @@ class SearchAdapter(private val activity: SearchActivity) :
|
||||||
requestFocus = Runnable { binding.searchBarText.requestFocus() }
|
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
|
override fun getItemCount(): Int = 1
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.databinding.BottomSheetSearchFilterBinding
|
import ani.dantotsu.databinding.BottomSheetSearchFilterBinding
|
||||||
import ani.dantotsu.databinding.ItemChipBinding
|
import ani.dantotsu.databinding.ItemChipBinding
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
private var _binding: BottomSheetSearchFilterBinding? = null
|
private var _binding: BottomSheetSearchFilterBinding? = null
|
||||||
|
@ -103,7 +104,8 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
binding.root.context,
|
binding.root.context,
|
||||||
R.layout.item_dropdown,
|
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()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
100
app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt
Normal file
100
app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt
Normal file
|
@ -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<String, SearchHistoryAdapter.SearchHistoryViewHolder>(
|
||||||
|
DIFF_CALLBACK_INSTALLED
|
||||||
|
) {
|
||||||
|
private var searchHistoryLiveData: SharedPreferenceStringSetLiveData? = null
|
||||||
|
private var searchHistory: MutableSet<String>? = 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<String>()).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<String>() {
|
||||||
|
override fun areItemsTheSame(
|
||||||
|
oldItem: String,
|
||||||
|
newItem: String
|
||||||
|
): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(
|
||||||
|
oldItem: String,
|
||||||
|
newItem: String
|
||||||
|
): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,6 @@ import ani.dantotsu.Refresh
|
||||||
import ani.dantotsu.databinding.ActivityStudioBinding
|
import ani.dantotsu.databinding.ActivityStudioBinding
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.others.LangSet
|
|
||||||
import ani.dantotsu.others.getSerialized
|
import ani.dantotsu.others.getSerialized
|
||||||
import ani.dantotsu.px
|
import ani.dantotsu.px
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
|
@ -36,7 +35,7 @@ class StudioActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityStudioBinding.inflate(layoutInflater)
|
binding = ActivityStudioBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
|
@ -45,9 +45,18 @@ class SubtitleDownloader {
|
||||||
}
|
}
|
||||||
|
|
||||||
//actually downloads lol
|
//actually downloads lol
|
||||||
suspend fun downloadSubtitle(context: Context, url: String, downloadedType: DownloadedType) {
|
suspend fun downloadSubtitle(
|
||||||
|
context: Context,
|
||||||
|
url: String,
|
||||||
|
downloadedType: DownloadedType
|
||||||
|
) {
|
||||||
try {
|
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
|
if (!directory.exists()) { //just in case
|
||||||
directory.mkdirs()
|
directory.mkdirs()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ani.dantotsu.media.anime
|
package ani.dantotsu.media.anime
|
||||||
|
|
||||||
|
import java.util.Locale
|
||||||
import java.util.regex.Matcher
|
import java.util.regex.Matcher
|
||||||
import java.util.regex.Pattern
|
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*"
|
"(episode|ep|e)[\\s:.\\-]*([\\d]+\\.?[\\d]*)[\\s:.\\-]*\\(?\\s*(sub|subbed|dub|dubbed)*\\s*\\)?\\s*"
|
||||||
const val failedEpisodeNumberRegex =
|
const val failedEpisodeNumberRegex =
|
||||||
"(?<!part\\s)\\b(\\d+)\\b"
|
"(?<!part\\s)\\b(\\d+)\\b"
|
||||||
const val seasonRegex = "\\s+(season|s)[\\s:.\\-]*(\\d+)[\\s:.\\-]*"
|
const val seasonRegex = "(season|s)[\\s:.\\-]*(\\d+)[\\s:.\\-]*"
|
||||||
|
const val subdubRegex = "^(soft)?[\\s-]*(sub|dub|mixed)(bed|s)?\\s*$"
|
||||||
|
|
||||||
|
fun setSubDub(text: String, typeToSetTo: SubDubType): String? {
|
||||||
|
val subdubPattern: Pattern = Pattern.compile(subdubRegex, Pattern.CASE_INSENSITIVE)
|
||||||
|
val subdubMatcher: Matcher = subdubPattern.matcher(text)
|
||||||
|
|
||||||
|
return if (subdubMatcher.find()) {
|
||||||
|
val soft = subdubMatcher.group(1)
|
||||||
|
val subdub = subdubMatcher.group(2)
|
||||||
|
val bed = subdubMatcher.group(3) ?: ""
|
||||||
|
|
||||||
|
val toggled = when (typeToSetTo) {
|
||||||
|
SubDubType.SUB -> "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? {
|
fun findSeasonNumber(text: String): Int? {
|
||||||
val seasonPattern: Pattern = Pattern.compile(seasonRegex, Pattern.CASE_INSENSITIVE)
|
val seasonPattern: Pattern = Pattern.compile(seasonRegex, Pattern.CASE_INSENSITIVE)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ani.dantotsu.media.anime
|
package ani.dantotsu.media.anime
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -27,6 +26,8 @@ import ani.dantotsu.others.webview.CookieCatcher
|
||||||
import ani.dantotsu.parsers.AnimeSources
|
import ani.dantotsu.parsers.AnimeSources
|
||||||
import ani.dantotsu.parsers.DynamicAnimeParser
|
import ani.dantotsu.parsers.DynamicAnimeParser
|
||||||
import ani.dantotsu.parsers.WatchSources
|
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.Notifications.Companion.openSettings
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
|
@ -59,7 +60,7 @@ class AnimeWatchAdapter(
|
||||||
_binding = binding
|
_binding = binding
|
||||||
|
|
||||||
//Youtube
|
//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.visibility = View.VISIBLE
|
||||||
binding.animeSourceYT.setOnClickListener {
|
binding.animeSourceYT.setOnClickListener {
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(media.anime.youtube))
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(media.anime.youtube))
|
||||||
|
@ -90,11 +91,9 @@ class AnimeWatchAdapter(
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val offline = if (!isOnline(binding.root.context) || currContext()?.getSharedPreferences(
|
val offline = if (!isOnline(binding.root.context) || PrefManager.getVal(
|
||||||
"Dantotsu",
|
PrefName.OfflineMode
|
||||||
Context.MODE_PRIVATE
|
|
||||||
)
|
)
|
||||||
?.getBoolean("offlineMode", false) == true
|
|
||||||
) View.GONE else View.VISIBLE
|
) View.GONE else View.VISIBLE
|
||||||
|
|
||||||
binding.animeSourceNameContainer.visibility = offline
|
binding.animeSourceNameContainer.visibility = offline
|
||||||
|
@ -113,7 +112,7 @@ class AnimeWatchAdapter(
|
||||||
binding.animeSourceTitle.text = showUserText
|
binding.animeSourceTitle.text = showUserText
|
||||||
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } }
|
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } }
|
||||||
binding.animeSourceDubbedCont.visibility =
|
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
|
binding.animeSourceDubbed.isChecked = selectDub
|
||||||
changing = false
|
changing = false
|
||||||
binding.animeSourceDubbedCont.visibility =
|
binding.animeSourceDubbedCont.visibility =
|
||||||
if (isDubAvailableSeparately) View.VISIBLE else View.GONE
|
if (isDubAvailableSeparately()) View.VISIBLE else View.GONE
|
||||||
source = i
|
source = i
|
||||||
setLanguageList(0, i)
|
setLanguageList(0, i)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +153,7 @@ class AnimeWatchAdapter(
|
||||||
binding.animeSourceDubbed.isChecked = selectDub
|
binding.animeSourceDubbed.isChecked = selectDub
|
||||||
changing = false
|
changing = false
|
||||||
binding.animeSourceDubbedCont.visibility =
|
binding.animeSourceDubbedCont.visibility =
|
||||||
if (isDubAvailableSeparately) View.VISIBLE else View.GONE
|
if (isDubAvailableSeparately()) View.VISIBLE else View.GONE
|
||||||
setLanguageList(i, source)
|
setLanguageList(i, source)
|
||||||
}
|
}
|
||||||
subscribeButton(false)
|
subscribeButton(false)
|
||||||
|
@ -199,7 +198,8 @@ class AnimeWatchAdapter(
|
||||||
var refresh = false
|
var refresh = false
|
||||||
var run = false
|
var run = false
|
||||||
var reversed = media.selected!!.recyclerReversed
|
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.animeSourceTop.rotation = if (reversed) -90f else 90f
|
||||||
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down"
|
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down"
|
||||||
dialogBinding.animeSourceTop.setOnClickListener {
|
dialogBinding.animeSourceTop.setOnClickListener {
|
||||||
|
@ -357,7 +357,9 @@ class AnimeWatchAdapter(
|
||||||
val episodes = media.anime.episodes!!.keys.toTypedArray()
|
val episodes = media.anime.episodes!!.keys.toTypedArray()
|
||||||
|
|
||||||
val anilistEp = (media.userProgress ?: 0).plus(1)
|
val anilistEp = (media.userProgress ?: 0).plus(1)
|
||||||
val appEp = loadData<String>("${media.id}_current_ep")?.toIntOrNull() ?: 1
|
val appEp =
|
||||||
|
PrefManager.getCustomVal<String?>("${media.id}_current_ep", "")?.toIntOrNull()
|
||||||
|
?: 1
|
||||||
|
|
||||||
var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString()
|
var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString()
|
||||||
if (episodes.contains(continueEp)) {
|
if (episodes.contains(continueEp)) {
|
||||||
|
@ -369,7 +371,10 @@ class AnimeWatchAdapter(
|
||||||
media.id,
|
media.id,
|
||||||
continueEp
|
continueEp
|
||||||
)
|
)
|
||||||
if ((binding.itemEpisodeProgress.layoutParams as LinearLayout.LayoutParams).weight > fragment.playerSettings.watchPercentage) {
|
if ((binding.itemEpisodeProgress.layoutParams as LinearLayout.LayoutParams).weight > PrefManager.getVal<Float>(
|
||||||
|
PrefName.WatchPercentage
|
||||||
|
)
|
||||||
|
) {
|
||||||
val e = episodes.indexOf(continueEp)
|
val e = episodes.indexOf(continueEp)
|
||||||
if (e != -1 && e + 1 < episodes.size) {
|
if (e != -1 && e + 1 < episodes.size) {
|
||||||
continueEp = episodes[e + 1]
|
continueEp = episodes[e + 1]
|
||||||
|
@ -396,7 +401,10 @@ class AnimeWatchAdapter(
|
||||||
fragment.onEpisodeClick(continueEp)
|
fragment.onEpisodeClick(continueEp)
|
||||||
}
|
}
|
||||||
if (fragment.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<Float>(
|
||||||
|
PrefName.WatchPercentage
|
||||||
|
)
|
||||||
|
) {
|
||||||
binding.animeSourceContinue.performClick()
|
binding.animeSourceContinue.performClick()
|
||||||
fragment.continueEp = false
|
fragment.continueEp = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.exoplayer.offline.DownloadService
|
import androidx.media3.exoplayer.offline.DownloadService
|
||||||
import androidx.recyclerview.widget.ConcatAdapter
|
import androidx.recyclerview.widget.ConcatAdapter
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import ani.dantotsu.*
|
import ani.dantotsu.*
|
||||||
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
|
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
|
||||||
|
@ -39,9 +40,9 @@ import ani.dantotsu.others.LanguageMapper
|
||||||
import ani.dantotsu.parsers.AnimeParser
|
import ani.dantotsu.parsers.AnimeParser
|
||||||
import ani.dantotsu.parsers.AnimeSources
|
import ani.dantotsu.parsers.AnimeSources
|
||||||
import ani.dantotsu.parsers.HAnimeSources
|
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.extensionprefs.AnimeSourcePreferencesFragment
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.subcriptions.Notifications
|
import ani.dantotsu.subcriptions.Notifications
|
||||||
import ani.dantotsu.subcriptions.Notifications.Group.ANIME_GROUP
|
import ani.dantotsu.subcriptions.Notifications.Group.ANIME_GROUP
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
||||||
|
@ -84,8 +85,6 @@ class AnimeWatchFragment : Fragment() {
|
||||||
var continueEp: Boolean = false
|
var continueEp: Boolean = false
|
||||||
var loaded = false
|
var loaded = false
|
||||||
|
|
||||||
lateinit var playerSettings: PlayerSettings
|
|
||||||
lateinit var uiSettings: UserInterfaceSettings
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
|
@ -118,12 +117,6 @@ class AnimeWatchFragment : Fragment() {
|
||||||
var maxGridSize = (screenWidth / 100f).roundToInt()
|
var maxGridSize = (screenWidth / 100f).roundToInt()
|
||||||
maxGridSize = max(4, maxGridSize - (maxGridSize % 2))
|
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)
|
val gridLayoutManager = GridLayoutManager(requireContext(), maxGridSize)
|
||||||
|
|
||||||
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||||
|
@ -144,6 +137,23 @@ class AnimeWatchFragment : Fragment() {
|
||||||
|
|
||||||
binding.animeSourceRecycler.layoutManager = gridLayoutManager
|
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) {
|
model.scrolledToTop.observe(viewLifecycleOwner) {
|
||||||
if (it) binding.animeSourceRecycler.scrollToPosition(0)
|
if (it) binding.animeSourceRecycler.scrollToPosition(0)
|
||||||
}
|
}
|
||||||
|
@ -155,7 +165,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
media.selected = model.loadSelected(media)
|
media.selected = model.loadSelected(media)
|
||||||
|
|
||||||
subscribed =
|
subscribed =
|
||||||
SubscriptionHelper.getSubscriptions(requireContext()).containsKey(media.id)
|
SubscriptionHelper.getSubscriptions().containsKey(media.id)
|
||||||
|
|
||||||
style = media.selected!!.recyclerStyle
|
style = media.selected!!.recyclerStyle
|
||||||
reverse = media.selected!!.recyclerReversed
|
reverse = media.selected!!.recyclerReversed
|
||||||
|
@ -172,7 +182,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
headerAdapter = AnimeWatchAdapter(it, this, model.watchSources!!)
|
headerAdapter = AnimeWatchAdapter(it, this, model.watchSources!!)
|
||||||
episodeAdapter =
|
episodeAdapter =
|
||||||
EpisodeAdapter(
|
EpisodeAdapter(
|
||||||
style ?: uiSettings.animeDefaultView,
|
style ?: PrefManager.getVal(PrefName.AnimeDefaultView),
|
||||||
media,
|
media,
|
||||||
this,
|
this,
|
||||||
offlineMode = offlineMode
|
offlineMode = offlineMode
|
||||||
|
@ -273,7 +283,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
model.watchSources?.get(selected.sourceIndex)?.showUserTextListener = null
|
model.watchSources?.get(selected.sourceIndex)?.showUserTextListener = null
|
||||||
selected.sourceIndex = i
|
selected.sourceIndex = i
|
||||||
selected.server = null
|
selected.server = null
|
||||||
model.saveSelected(media.id, selected, requireActivity())
|
model.saveSelected(media.id, selected)
|
||||||
media.selected = selected
|
media.selected = selected
|
||||||
return model.watchSources?.get(i)!!
|
return model.watchSources?.get(i)!!
|
||||||
}
|
}
|
||||||
|
@ -281,7 +291,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
fun onLangChange(i: Int) {
|
fun onLangChange(i: Int) {
|
||||||
val selected = model.loadSelected(media)
|
val selected = model.loadSelected(media)
|
||||||
selected.langIndex = i
|
selected.langIndex = i
|
||||||
model.saveSelected(media.id, selected, requireActivity())
|
model.saveSelected(media.id, selected)
|
||||||
media.selected = selected
|
media.selected = selected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +299,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
val selected = model.loadSelected(media)
|
val selected = model.loadSelected(media)
|
||||||
model.watchSources?.get(selected.sourceIndex)?.selectDub = checked
|
model.watchSources?.get(selected.sourceIndex)?.selectDub = checked
|
||||||
selected.preferDub = checked
|
selected.preferDub = checked
|
||||||
model.saveSelected(media.id, selected, requireActivity())
|
model.saveSelected(media.id, selected)
|
||||||
media.selected = selected
|
media.selected = selected
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
model.forceLoadEpisode(
|
model.forceLoadEpisode(
|
||||||
|
@ -308,7 +318,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
reverse = rev
|
reverse = rev
|
||||||
media.selected!!.recyclerStyle = style
|
media.selected!!.recyclerStyle = style
|
||||||
media.selected!!.recyclerReversed = reverse
|
media.selected!!.recyclerReversed = reverse
|
||||||
model.saveSelected(media.id, media.selected!!, requireActivity())
|
model.saveSelected(media.id, media.selected!!)
|
||||||
reload()
|
reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,7 +326,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
media.selected!!.chip = i
|
media.selected!!.chip = i
|
||||||
start = s
|
start = s
|
||||||
end = e
|
end = e
|
||||||
model.saveSelected(media.id, media.selected!!, requireActivity())
|
model.saveSelected(media.id, media.selected!!)
|
||||||
reload()
|
reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,12 +374,10 @@ class AnimeWatchFragment : Fragment() {
|
||||||
if (allSettings.size > 1) {
|
if (allSettings.size > 1) {
|
||||||
val names =
|
val names =
|
||||||
allSettings.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
|
allSettings.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
|
||||||
var selectedIndex = 0
|
|
||||||
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
|
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
|
||||||
.setTitle("Select a Source")
|
.setTitle("Select a Source")
|
||||||
.setSingleChoiceItems(names, selectedIndex) { dialog, which ->
|
.setSingleChoiceItems(names, -1) { dialog, which ->
|
||||||
selectedIndex = which
|
selectedSetting = allSettings[which]
|
||||||
selectedSetting = allSettings[selectedIndex]
|
|
||||||
itemSelected = true
|
itemSelected = true
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
|
||||||
|
@ -419,7 +427,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
|
|
||||||
fun onEpisodeClick(i: String) {
|
fun onEpisodeClick(i: String) {
|
||||||
model.continueMedia = false
|
model.continueMedia = false
|
||||||
model.saveSelected(media.id, media.selected!!, requireActivity())
|
model.saveSelected(media.id, media.selected!!)
|
||||||
model.onEpisodeClick(media, i, requireActivity().supportFragmentManager)
|
model.onEpisodeClick(media, i, requireActivity().supportFragmentManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,17 +466,11 @@ class AnimeWatchFragment : Fragment() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val taskName = AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), i)
|
val taskName = AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), i)
|
||||||
val id = requireContext().getSharedPreferences(
|
val id = PrefManager.getAnimeDownloadPreferences().getString(
|
||||||
ContextCompat.getString(requireContext(), R.string.anime_downloads),
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).getString(
|
|
||||||
taskName,
|
taskName,
|
||||||
""
|
""
|
||||||
) ?: ""
|
) ?: ""
|
||||||
requireContext().getSharedPreferences(
|
PrefManager.getAnimeDownloadPreferences().edit().remove(taskName).apply()
|
||||||
ContextCompat.getString(requireContext(), R.string.anime_downloads),
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).edit().remove(taskName).apply()
|
|
||||||
DownloadService.sendRemoveDownload(
|
DownloadService.sendRemoveDownload(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
ExoplayerDownloadService::class.java,
|
ExoplayerDownloadService::class.java,
|
||||||
|
@ -520,7 +522,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
selected.latest =
|
selected.latest =
|
||||||
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
|
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
|
||||||
|
|
||||||
model.saveSelected(media.id, selected, requireActivity())
|
model.saveSelected(media.id, selected)
|
||||||
headerAdapter.handleEpisodes()
|
headerAdapter.handleEpisodes()
|
||||||
val isDownloaded = model.watchSources!!.isDownloadedSource(media.selected!!.sourceIndex)
|
val isDownloaded = model.watchSources!!.isDownloadedSource(media.selected!!.sourceIndex)
|
||||||
episodeAdapter.offlineMode = isDownloaded
|
episodeAdapter.offlineMode = isDownloaded
|
||||||
|
@ -536,7 +538,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
arr = (arr.reversed() as? ArrayList<Episode>) ?: arr
|
arr = (arr.reversed() as? ArrayList<Episode>) ?: arr
|
||||||
}
|
}
|
||||||
episodeAdapter.arr = arr
|
episodeAdapter.arr = arr
|
||||||
episodeAdapter.updateType(style ?: uiSettings.animeDefaultView)
|
episodeAdapter.updateType(style ?: PrefManager.getVal(PrefName.AnimeDefaultView))
|
||||||
episodeAdapter.notifyItemRangeInserted(0, arr.size)
|
episodeAdapter.notifyItemRangeInserted(0, arr.size)
|
||||||
for (download in downloadManager.animeDownloadedTypes) {
|
for (download in downloadManager.animeDownloadedTypes) {
|
||||||
if (download.title == media.mainName()) {
|
if (download.title == media.mainName()) {
|
||||||
|
|
|
@ -2,14 +2,12 @@ package ani.dantotsu.media.anime
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.animation.LinearInterpolator
|
import android.view.animation.LinearInterpolator
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.lifecycle.coroutineScope
|
import androidx.lifecycle.coroutineScope
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.exoplayer.offline.DownloadIndex
|
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.anime.AnimeDownloaderService
|
||||||
import ani.dantotsu.download.video.Helper
|
import ani.dantotsu.download.video.Helper
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.model.GlideUrl
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
@ -30,8 +29,8 @@ import kotlin.math.ln
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
fun handleProgress(cont: LinearLayout, bar: View, empty: View, mediaId: Int, ep: String) {
|
fun handleProgress(cont: LinearLayout, bar: View, empty: View, mediaId: Int, ep: String) {
|
||||||
val curr = loadData<Long>("${mediaId}_${ep}")
|
val curr = PrefManager.getNullableCustomVal("${mediaId}_${ep}", null, Long::class.java)
|
||||||
val max = loadData<Long>("${mediaId}_${ep}_max")
|
val max = PrefManager.getNullableCustomVal("${mediaId}_${ep}_max", null, Long::class.java)
|
||||||
if (curr != null && max != null) {
|
if (curr != null && max != null) {
|
||||||
cont.visibility = View.VISIBLE
|
cont.visibility = View.VISIBLE
|
||||||
val div = curr.toFloat() / max.toFloat()
|
val div = curr.toFloat() / max.toFloat()
|
||||||
|
@ -110,7 +109,7 @@ class EpisodeAdapter(
|
||||||
when (holder) {
|
when (holder) {
|
||||||
is EpisodeListViewHolder -> {
|
is EpisodeListViewHolder -> {
|
||||||
val binding = holder.binding
|
val binding = holder.binding
|
||||||
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
|
setAnimation(fragment.requireContext(), holder.binding.root)
|
||||||
|
|
||||||
val thumb =
|
val thumb =
|
||||||
ep.thumb?.let { if (it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
|
ep.thumb?.let { if (it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
|
||||||
|
@ -129,7 +128,7 @@ class EpisodeAdapter(
|
||||||
binding.itemEpisodeDesc.visibility =
|
binding.itemEpisodeDesc.visibility =
|
||||||
if (ep.desc != null && ep.desc?.trim(' ') != "") View.VISIBLE else View.GONE
|
if (ep.desc != null && ep.desc?.trim(' ') != "") View.VISIBLE else View.GONE
|
||||||
binding.itemEpisodeDesc.text = ep.desc ?: ""
|
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 (media.userProgress != null) {
|
||||||
if ((ep.number.toFloatOrNull() ?: 9999f) <= media.userProgress!!.toFloat()) {
|
if ((ep.number.toFloatOrNull() ?: 9999f) <= media.userProgress!!.toFloat()) {
|
||||||
|
@ -159,7 +158,7 @@ class EpisodeAdapter(
|
||||||
|
|
||||||
is EpisodeGridViewHolder -> {
|
is EpisodeGridViewHolder -> {
|
||||||
val binding = holder.binding
|
val binding = holder.binding
|
||||||
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
|
setAnimation(fragment.requireContext(), holder.binding.root)
|
||||||
|
|
||||||
val thumb =
|
val thumb =
|
||||||
ep.thumb?.let { if (it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
|
ep.thumb?.let { if (it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
|
||||||
|
@ -202,7 +201,7 @@ class EpisodeAdapter(
|
||||||
|
|
||||||
is EpisodeCompactViewHolder -> {
|
is EpisodeCompactViewHolder -> {
|
||||||
val binding = holder.binding
|
val binding = holder.binding
|
||||||
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
|
setAnimation(fragment.requireContext(), holder.binding.root)
|
||||||
binding.itemEpisodeNumber.text = ep.number
|
binding.itemEpisodeNumber.text = ep.number
|
||||||
binding.itemEpisodeFillerView.visibility =
|
binding.itemEpisodeFillerView.visibility =
|
||||||
if (ep.filler) View.VISIBLE else View.GONE
|
if (ep.filler) View.VISIBLE else View.GONE
|
||||||
|
@ -253,10 +252,7 @@ class EpisodeAdapter(
|
||||||
media.mainName(),
|
media.mainName(),
|
||||||
episodeNumber
|
episodeNumber
|
||||||
)
|
)
|
||||||
val id = fragment.requireContext().getSharedPreferences(
|
val id = PrefManager.getAnimeDownloadPreferences().getString(
|
||||||
ContextCompat.getString(fragment.requireContext(), R.string.anime_downloads),
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
).getString(
|
|
||||||
taskName,
|
taskName,
|
||||||
""
|
""
|
||||||
) ?: ""
|
) ?: ""
|
||||||
|
@ -391,9 +387,10 @@ class EpisodeAdapter(
|
||||||
}, 1000)
|
}, 1000)
|
||||||
} else {
|
} else {
|
||||||
binding.itemDownloadStatus.visibility = View.GONE
|
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
|
// Show download icon
|
||||||
binding.itemDownload.setImageResource(R.drawable.ic_circle_add)
|
binding.itemDownload.setImageResource(R.drawable.ic_download_24)
|
||||||
binding.itemDownload.rotation = 0f
|
binding.itemDownload.rotation = 0f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ import androidx.mediarouter.app.MediaRouteButton
|
||||||
import ani.dantotsu.*
|
import ani.dantotsu.*
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.connections.discord.Discord
|
import ani.dantotsu.connections.discord.Discord
|
||||||
import ani.dantotsu.connections.discord.DiscordService
|
import ani.dantotsu.connections.discord.DiscordService
|
||||||
import ani.dantotsu.connections.discord.DiscordServiceRunningSingleton
|
import ani.dantotsu.connections.discord.DiscordServiceRunningSingleton
|
||||||
|
@ -77,13 +78,12 @@ import ani.dantotsu.media.MediaDetailsViewModel
|
||||||
import ani.dantotsu.media.SubtitleDownloader
|
import ani.dantotsu.media.SubtitleDownloader
|
||||||
import ani.dantotsu.others.AniSkip
|
import ani.dantotsu.others.AniSkip
|
||||||
import ani.dantotsu.others.AniSkip.getType
|
import ani.dantotsu.others.AniSkip.getType
|
||||||
import ani.dantotsu.others.LangSet
|
|
||||||
import ani.dantotsu.others.ResettableTimer
|
import ani.dantotsu.others.ResettableTimer
|
||||||
import ani.dantotsu.others.getSerialized
|
import ani.dantotsu.others.getSerialized
|
||||||
import ani.dantotsu.parsers.*
|
import ani.dantotsu.parsers.*
|
||||||
import ani.dantotsu.settings.PlayerSettings
|
|
||||||
import ani.dantotsu.settings.PlayerSettingsActivity
|
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 ani.dantotsu.themes.ThemeManager
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
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.ConnectionResult
|
||||||
import com.google.android.gms.common.GoogleApiAvailability
|
import com.google.android.gms.common.GoogleApiAvailability
|
||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import com.lagradost.nicehttp.ignoreAllSSLErrors
|
import com.lagradost.nicehttp.ignoreAllSSLErrors
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -99,6 +98,8 @@ import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import okhttp3.internal.immutableListOf
|
import okhttp3.internal.immutableListOf
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.*
|
import java.util.concurrent.*
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
@ -132,7 +133,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
private lateinit var exoSubtitle: ImageButton
|
private lateinit var exoSubtitle: ImageButton
|
||||||
private lateinit var exoSubtitleView: SubtitleView
|
private lateinit var exoSubtitleView: SubtitleView
|
||||||
private lateinit var exoRotate: ImageButton
|
private lateinit var exoRotate: ImageButton
|
||||||
private lateinit var exoQuality: ImageButton
|
|
||||||
private lateinit var exoSpeed: ImageButton
|
private lateinit var exoSpeed: ImageButton
|
||||||
private lateinit var exoScreen: ImageButton
|
private lateinit var exoScreen: ImageButton
|
||||||
private lateinit var exoNext: ImageButton
|
private lateinit var exoNext: ImageButton
|
||||||
|
@ -148,9 +148,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
private lateinit var skipTimeText: TextView
|
private lateinit var skipTimeText: TextView
|
||||||
private lateinit var timeStampText: TextView
|
private lateinit var timeStampText: TextView
|
||||||
private lateinit var animeTitle: TextView
|
private lateinit var animeTitle: TextView
|
||||||
private lateinit var videoName: TextView
|
|
||||||
private lateinit var videoInfo: TextView
|
private lateinit var videoInfo: TextView
|
||||||
private lateinit var serverInfo: TextView
|
|
||||||
private lateinit var episodeTitle: Spinner
|
private lateinit var episodeTitle: Spinner
|
||||||
|
|
||||||
private var orientationListener: OrientationEventListener? = null
|
private var orientationListener: OrientationEventListener? = null
|
||||||
|
@ -187,9 +185,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
private var pipEnabled = false
|
private var pipEnabled = false
|
||||||
private var aspectRatio = Rational(16, 9)
|
private var aspectRatio = Rational(16, 9)
|
||||||
|
|
||||||
var settings = PlayerSettings()
|
|
||||||
private var uiSettings = UserInterfaceSettings()
|
|
||||||
|
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
val model: MediaDetailsViewModel by viewModels()
|
val model: MediaDetailsViewModel by viewModels()
|
||||||
|
|
||||||
|
@ -243,8 +238,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupSubFormatting(playerView: PlayerView, settings: PlayerSettings) {
|
private fun setupSubFormatting(playerView: PlayerView) {
|
||||||
val primaryColor = when (settings.primaryColor) {
|
val primaryColor = when (PrefManager.getVal<Int>(PrefName.PrimaryColor)) {
|
||||||
0 -> Color.BLACK
|
0 -> Color.BLACK
|
||||||
1 -> Color.DKGRAY
|
1 -> Color.DKGRAY
|
||||||
2 -> Color.GRAY
|
2 -> Color.GRAY
|
||||||
|
@ -259,7 +254,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
11 -> Color.TRANSPARENT
|
11 -> Color.TRANSPARENT
|
||||||
else -> Color.WHITE
|
else -> Color.WHITE
|
||||||
}
|
}
|
||||||
val secondaryColor = when (settings.secondaryColor) {
|
val secondaryColor = when (PrefManager.getVal<Int>(PrefName.SecondaryColor)) {
|
||||||
0 -> Color.BLACK
|
0 -> Color.BLACK
|
||||||
1 -> Color.DKGRAY
|
1 -> Color.DKGRAY
|
||||||
2 -> Color.GRAY
|
2 -> Color.GRAY
|
||||||
|
@ -274,14 +269,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
11 -> Color.TRANSPARENT
|
11 -> Color.TRANSPARENT
|
||||||
else -> Color.BLACK
|
else -> Color.BLACK
|
||||||
}
|
}
|
||||||
val outline = when (settings.outline) {
|
val outline = when (PrefManager.getVal<Int>(PrefName.Outline)) {
|
||||||
0 -> EDGE_TYPE_OUTLINE // Normal
|
0 -> EDGE_TYPE_OUTLINE // Normal
|
||||||
1 -> EDGE_TYPE_DEPRESSED // Shine
|
1 -> EDGE_TYPE_DEPRESSED // Shine
|
||||||
2 -> EDGE_TYPE_DROP_SHADOW // Drop shadow
|
2 -> EDGE_TYPE_DROP_SHADOW // Drop shadow
|
||||||
3 -> EDGE_TYPE_NONE // No outline
|
3 -> EDGE_TYPE_NONE // No outline
|
||||||
else -> EDGE_TYPE_OUTLINE // Normal
|
else -> EDGE_TYPE_OUTLINE // Normal
|
||||||
}
|
}
|
||||||
val subBackground = when (settings.subBackground) {
|
val subBackground = when (PrefManager.getVal<Int>(PrefName.SubBackground)) {
|
||||||
0 -> Color.TRANSPARENT
|
0 -> Color.TRANSPARENT
|
||||||
1 -> Color.BLACK
|
1 -> Color.BLACK
|
||||||
2 -> Color.DKGRAY
|
2 -> Color.DKGRAY
|
||||||
|
@ -296,7 +291,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
11 -> Color.MAGENTA
|
11 -> Color.MAGENTA
|
||||||
else -> Color.TRANSPARENT
|
else -> Color.TRANSPARENT
|
||||||
}
|
}
|
||||||
val subWindow = when (settings.subWindow) {
|
val subWindow = when (PrefManager.getVal<Int>(PrefName.SubWindow)) {
|
||||||
0 -> Color.TRANSPARENT
|
0 -> Color.TRANSPARENT
|
||||||
1 -> Color.BLACK
|
1 -> Color.BLACK
|
||||||
2 -> Color.DKGRAY
|
2 -> Color.DKGRAY
|
||||||
|
@ -311,7 +306,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
11 -> Color.MAGENTA
|
11 -> Color.MAGENTA
|
||||||
else -> Color.TRANSPARENT
|
else -> Color.TRANSPARENT
|
||||||
}
|
}
|
||||||
val font = when (settings.font) {
|
val font = when (PrefManager.getVal<Int>(PrefName.Font)) {
|
||||||
0 -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
|
0 -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
|
||||||
1 -> ResourcesCompat.getFont(this, R.font.poppins_bold)
|
1 -> ResourcesCompat.getFont(this, R.font.poppins_bold)
|
||||||
2 -> ResourcesCompat.getFont(this, R.font.poppins)
|
2 -> ResourcesCompat.getFont(this, R.font.poppins)
|
||||||
|
@ -334,7 +329,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityExoplayerBinding.inflate(layoutInflater)
|
binding = ActivityExoplayerBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
@ -357,21 +352,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
finishAndRemoveTask()
|
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)
|
playerView = findViewById(R.id.player_view)
|
||||||
exoQuality = playerView.findViewById(R.id.exo_quality)
|
|
||||||
exoPlay = playerView.findViewById(androidx.media3.ui.R.id.exo_play)
|
exoPlay = playerView.findViewById(androidx.media3.ui.R.id.exo_play)
|
||||||
exoSource = playerView.findViewById(R.id.exo_source)
|
exoSource = playerView.findViewById(R.id.exo_source)
|
||||||
exoSettings = playerView.findViewById(R.id.exo_settings)
|
exoSettings = playerView.findViewById(R.id.exo_settings)
|
||||||
|
@ -406,21 +387,23 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}, AUDIO_CONTENT_TYPE_MOVIE, AUDIOFOCUS_GAIN)
|
}, AUDIO_CONTENT_TYPE_MOVIE, AUDIOFOCUS_GAIN)
|
||||||
|
|
||||||
if (System.getInt(contentResolver, System.ACCELEROMETER_ROTATION, 0) != 1) {
|
if (System.getInt(contentResolver, System.ACCELEROMETER_ROTATION, 0) != 1) {
|
||||||
orientationListener =
|
if (PrefManager.getVal(PrefName.RotationPlayer)) {
|
||||||
object : OrientationEventListener(this, SensorManager.SENSOR_DELAY_UI) {
|
orientationListener =
|
||||||
override fun onOrientationChanged(orientation: Int) {
|
object : OrientationEventListener(this, SensorManager.SENSOR_DELAY_UI) {
|
||||||
if (orientation in 45..135) {
|
override fun onOrientationChanged(orientation: Int) {
|
||||||
if (rotation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) exoRotate.visibility =
|
if (orientation in 45..135) {
|
||||||
View.VISIBLE
|
if (rotation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) exoRotate.visibility =
|
||||||
rotation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
|
View.VISIBLE
|
||||||
} else if (orientation in 225..315) {
|
rotation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
|
||||||
if (rotation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) exoRotate.visibility =
|
} else if (orientation in 225..315) {
|
||||||
View.VISIBLE
|
if (rotation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) exoRotate.visibility =
|
||||||
rotation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
View.VISIBLE
|
||||||
|
rotation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
orientationListener?.enable()
|
||||||
orientationListener?.enable()
|
}
|
||||||
|
|
||||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
||||||
exoRotate.setOnClickListener {
|
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<Boolean>(PrefName.Subtitles)) {
|
||||||
true -> 1f
|
true -> 1f
|
||||||
false -> 0f
|
false -> 0f
|
||||||
}
|
}
|
||||||
val fontSize = settings.fontSize.toFloat()
|
val fontSize = PrefManager.getVal<Int>(PrefName.FontSize).toFloat()
|
||||||
playerView.subtitleView?.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize)
|
playerView.subtitleView?.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize)
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
|
@ -469,17 +452,16 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
} else View.GONE
|
} 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 {
|
exoSkipOpEd.setOnClickListener {
|
||||||
settings.autoSkipOPED = if (settings.autoSkipOPED) {
|
if (PrefManager.getVal(PrefName.AutoSkipOPED)) {
|
||||||
snackString(getString(R.string.disabled_auto_skip))
|
snackString(getString(R.string.disabled_auto_skip))
|
||||||
false
|
PrefManager.setVal(PrefName.AutoSkipOPED, false)
|
||||||
} else {
|
} else {
|
||||||
snackString(getString(R.string.auto_skip))
|
snackString(getString(R.string.auto_skip))
|
||||||
true
|
PrefManager.setVal(PrefName.AutoSkipOPED, true)
|
||||||
}
|
}
|
||||||
saveData("player_settings", settings)
|
exoSkipOpEd.alpha = if (PrefManager.getVal(PrefName.AutoSkipOPED)) 1f else 0.3f
|
||||||
exoSkipOpEd.alpha = if (settings.autoSkipOPED) 1f else 0.3f
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Play Pause
|
//Play Pause
|
||||||
|
@ -506,7 +488,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
// Picture-in-picture
|
// Picture-in-picture
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
pipEnabled =
|
pipEnabled =
|
||||||
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && settings.pip
|
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && PrefManager.getVal(
|
||||||
|
PrefName.Pip
|
||||||
|
)
|
||||||
if (pipEnabled) {
|
if (pipEnabled) {
|
||||||
exoPip.visibility = View.VISIBLE
|
exoPip.visibility = View.VISIBLE
|
||||||
exoPip.setOnClickListener {
|
exoPip.setOnClickListener {
|
||||||
|
@ -539,14 +523,15 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
|
|
||||||
//Skip Time Button
|
//Skip Time Button
|
||||||
if (settings.skipTime > 0) {
|
var skipTime = PrefManager.getVal<Int>(PrefName.SkipTime)
|
||||||
exoSkip.findViewById<TextView>(R.id.exo_skip_time).text = settings.skipTime.toString()
|
if (skipTime > 0) {
|
||||||
|
exoSkip.findViewById<TextView>(R.id.exo_skip_time).text = skipTime.toString()
|
||||||
exoSkip.setOnClickListener {
|
exoSkip.setOnClickListener {
|
||||||
if (isInitialized)
|
if (isInitialized)
|
||||||
exoPlayer.seekTo(exoPlayer.currentPosition + settings.skipTime * 1000)
|
exoPlayer.seekTo(exoPlayer.currentPosition + skipTime * 1000)
|
||||||
}
|
}
|
||||||
exoSkip.setOnLongClickListener {
|
exoSkip.setOnLongClickListener {
|
||||||
val dialog = Dialog(this, R.style.DialogTheme)
|
val dialog = Dialog(this, R.style.MyPopup)
|
||||||
dialog.setContentView(R.layout.item_seekbar_dialog)
|
dialog.setContentView(R.layout.item_seekbar_dialog)
|
||||||
dialog.setCancelable(true)
|
dialog.setCancelable(true)
|
||||||
dialog.setCanceledOnTouchOutside(true)
|
dialog.setCanceledOnTouchOutside(true)
|
||||||
|
@ -554,18 +539,19 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
)
|
)
|
||||||
if (settings.skipTime <= 120) {
|
if (skipTime <= 120) {
|
||||||
dialog.findViewById<Slider>(R.id.seekbar).value = settings.skipTime.toFloat()
|
dialog.findViewById<Slider>(R.id.seekbar).value = skipTime.toFloat()
|
||||||
} else {
|
} else {
|
||||||
dialog.findViewById<Slider>(R.id.seekbar).value = 120f
|
dialog.findViewById<Slider>(R.id.seekbar).value = 120f
|
||||||
}
|
}
|
||||||
dialog.findViewById<Slider>(R.id.seekbar).addOnChangeListener { _, value, _ ->
|
dialog.findViewById<Slider>(R.id.seekbar).addOnChangeListener { _, value, _ ->
|
||||||
settings.skipTime = value.toInt()
|
skipTime = value.toInt()
|
||||||
saveData(player, settings)
|
//saveData(player, settings)
|
||||||
|
PrefManager.setVal(PrefName.SkipTime, skipTime)
|
||||||
playerView.findViewById<TextView>(R.id.exo_skip_time).text =
|
playerView.findViewById<TextView>(R.id.exo_skip_time).text =
|
||||||
settings.skipTime.toString()
|
skipTime.toString()
|
||||||
dialog.findViewById<TextView>(R.id.seekbar_value).text =
|
dialog.findViewById<TextView>(R.id.seekbar_value).text =
|
||||||
settings.skipTime.toString()
|
skipTime.toString()
|
||||||
}
|
}
|
||||||
dialog.findViewById<Slider>(R.id.seekbar)
|
dialog.findViewById<Slider>(R.id.seekbar)
|
||||||
.addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
|
.addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
|
||||||
|
@ -577,7 +563,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
dialog.findViewById<TextView>(R.id.seekbar_title).text =
|
dialog.findViewById<TextView>(R.id.seekbar_title).text =
|
||||||
getString(R.string.skip_time)
|
getString(R.string.skip_time)
|
||||||
dialog.findViewById<TextView>(R.id.seekbar_value).text =
|
dialog.findViewById<TextView>(R.id.seekbar_value).text =
|
||||||
settings.skipTime.toString()
|
skipTime.toString()
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
dialog.window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
dialog.window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
@ -587,7 +573,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
exoSkip.visibility = View.GONE
|
exoSkip.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
val gestureSpeed = (300 * uiSettings.animationSpeed).toLong()
|
val gestureSpeed = (300 * PrefManager.getVal<Float>(PrefName.AnimationSpeed)).toLong()
|
||||||
//Player UI Visibility Handler
|
//Player UI Visibility Handler
|
||||||
val brightnessRunnable = Runnable {
|
val brightnessRunnable = Runnable {
|
||||||
if (exoBrightnessCont.alpha == 1f)
|
if (exoBrightnessCont.alpha == 1f)
|
||||||
|
@ -617,7 +603,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
val overshoot = AnimationUtils.loadInterpolator(this, R.anim.over_shoot)
|
val overshoot = AnimationUtils.loadInterpolator(this, R.anim.over_shoot)
|
||||||
val controllerDuration = (uiSettings.animationSpeed * 200).toLong()
|
val controllerDuration = (300 * PrefManager.getVal<Float>(PrefName.AnimationSpeed)).toLong()
|
||||||
fun handleController() {
|
fun handleController() {
|
||||||
if (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) !isInPictureInPictureMode else true) {
|
if (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) !isInPictureInPictureMode else true) {
|
||||||
if (playerView.isControllerFullyVisible) {
|
if (playerView.isControllerFullyVisible) {
|
||||||
|
@ -702,13 +688,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
var seekTimesR = 0
|
var seekTimesR = 0
|
||||||
|
|
||||||
fun seek(forward: Boolean, event: MotionEvent? = null) {
|
fun seek(forward: Boolean, event: MotionEvent? = null) {
|
||||||
|
val seekTime = PrefManager.getVal<Int>(PrefName.SeekTime)
|
||||||
val (card, text) = if (forward) {
|
val (card, text) = if (forward) {
|
||||||
forwardText.text = "+${settings.seekTime * ++seekTimesF}"
|
forwardText.text = "+${seekTime * ++seekTimesF}"
|
||||||
handler.post { exoPlayer.seekTo(exoPlayer.currentPosition + settings.seekTime * 1000) }
|
handler.post { exoPlayer.seekTo(exoPlayer.currentPosition + seekTime * 1000) }
|
||||||
fastForwardCard to forwardText
|
fastForwardCard to forwardText
|
||||||
} else {
|
} else {
|
||||||
rewindText.text = "-${settings.seekTime * ++seekTimesR}"
|
rewindText.text = "-${seekTime * ++seekTimesR}"
|
||||||
handler.post { exoPlayer.seekTo(exoPlayer.currentPosition - settings.seekTime * 1000) }
|
handler.post { exoPlayer.seekTo(exoPlayer.currentPosition - seekTime * 1000) }
|
||||||
fastRewindCard to rewindText
|
fastRewindCard to rewindText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -763,7 +750,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.doubleTap) {
|
if (!PrefManager.getVal<Boolean>(PrefName.DoubleTap)) {
|
||||||
playerView.findViewById<View>(R.id.exo_fast_forward_button_cont).visibility =
|
playerView.findViewById<View>(R.id.exo_fast_forward_button_cont).visibility =
|
||||||
View.VISIBLE
|
View.VISIBLE
|
||||||
playerView.findViewById<View>(R.id.exo_fast_rewind_button_cont).visibility =
|
playerView.findViewById<View>(R.id.exo_fast_rewind_button_cont).visibility =
|
||||||
|
@ -784,10 +771,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
keyMap[KEYCODE_DPAD_LEFT] = { seek(false) }
|
keyMap[KEYCODE_DPAD_LEFT] = { seek(false) }
|
||||||
|
|
||||||
//Screen Gestures
|
//Screen Gestures
|
||||||
if (settings.gestures || settings.doubleTap) {
|
if (PrefManager.getVal<Boolean>(PrefName.Gestures) || PrefManager.getVal<Boolean>(PrefName.DoubleTap)) {
|
||||||
|
|
||||||
fun doubleTap(forward: Boolean, event: MotionEvent) {
|
fun doubleTap(forward: Boolean, event: MotionEvent) {
|
||||||
if (!locked && isInitialized && settings.doubleTap) {
|
if (!locked && isInitialized && PrefManager.getVal<Boolean>(PrefName.DoubleTap)) {
|
||||||
seek(forward, event)
|
seek(forward, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -844,7 +831,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
isFastForwarding = true
|
isFastForwarding = true
|
||||||
exoPlayer.setPlaybackSpeed(exoPlayer.playbackParameters.speed * 2)
|
exoPlayer.setPlaybackSpeed(exoPlayer.playbackParameters.speed * 2)
|
||||||
fastForward.visibility = View.VISIBLE
|
fastForward.visibility = View.VISIBLE
|
||||||
fastForward.text = ("${exoPlayer.playbackParameters.speed}x")
|
fastForward.text = "${exoPlayer.playbackParameters.speed}x"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopFastForward() {
|
fun stopFastForward() {
|
||||||
|
@ -858,7 +845,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
//FastRewind (Left Panel)
|
//FastRewind (Left Panel)
|
||||||
val fastRewindDetector = GestureDetector(this, object : GesturesListener() {
|
val fastRewindDetector = GestureDetector(this, object : GesturesListener() {
|
||||||
override fun onLongClick(event: MotionEvent) {
|
override fun onLongClick(event: MotionEvent) {
|
||||||
if (settings.fastforward) fastForward()
|
if (PrefManager.getVal(PrefName.FastForward)) fastForward()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDoubleClick(event: MotionEvent) {
|
override fun onDoubleClick(event: MotionEvent) {
|
||||||
|
@ -866,7 +853,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onScrollYClick(y: Float) {
|
override fun onScrollYClick(y: Float) {
|
||||||
if (!locked && settings.gestures) {
|
if (!locked && PrefManager.getVal(PrefName.Gestures)) {
|
||||||
exoBrightness.value = clamp(exoBrightness.value + y / 100, 0f, 10f)
|
exoBrightness.value = clamp(exoBrightness.value + y / 100, 0f, 10f)
|
||||||
if (exoBrightnessCont.visibility != View.VISIBLE) {
|
if (exoBrightnessCont.visibility != View.VISIBLE) {
|
||||||
exoBrightnessCont.visibility = View.VISIBLE
|
exoBrightnessCont.visibility = View.VISIBLE
|
||||||
|
@ -890,7 +877,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
//FastForward (Right Panel)
|
//FastForward (Right Panel)
|
||||||
val fastForwardDetector = GestureDetector(this, object : GesturesListener() {
|
val fastForwardDetector = GestureDetector(this, object : GesturesListener() {
|
||||||
override fun onLongClick(event: MotionEvent) {
|
override fun onLongClick(event: MotionEvent) {
|
||||||
if (settings.fastforward) fastForward()
|
if (PrefManager.getVal(PrefName.FastForward)) fastForward()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDoubleClick(event: MotionEvent) {
|
override fun onDoubleClick(event: MotionEvent) {
|
||||||
|
@ -898,7 +885,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onScrollYClick(y: Float) {
|
override fun onScrollYClick(y: Float) {
|
||||||
if (!locked && settings.gestures) {
|
if (!locked && PrefManager.getVal(PrefName.Gestures)) {
|
||||||
exoVolume.value = clamp(exoVolume.value + y / 100, 0f, 10f)
|
exoVolume.value = clamp(exoVolume.value + y / 100, 0f, 10f)
|
||||||
if (exoVolumeCont.visibility != View.VISIBLE) {
|
if (exoVolumeCont.visibility != View.VISIBLE) {
|
||||||
exoVolumeCont.visibility = View.VISIBLE
|
exoVolumeCont.visibility = View.VISIBLE
|
||||||
|
@ -926,21 +913,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
title = media.userPreferredName
|
title = media.userPreferredName
|
||||||
episodes = media.anime?.episodes ?: return startMainActivity(this)
|
episodes = media.anime?.episodes ?: return startMainActivity(this)
|
||||||
|
|
||||||
videoName = playerView.findViewById(R.id.exo_video_name)
|
|
||||||
videoInfo = playerView.findViewById(R.id.exo_video_info)
|
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
|
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) {
|
model.epChanged.observe(this) {
|
||||||
epChanging = !it
|
epChanging = !it
|
||||||
|
@ -964,10 +939,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
fun change(index: Int) {
|
fun change(index: Int) {
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
changingServer = false
|
changingServer = false
|
||||||
saveData(
|
PrefManager.setCustomVal(
|
||||||
"${media.id}_${episodeArr[currentEpisodeIndex]}",
|
"${media.id}_${episodeArr[currentEpisodeIndex]}",
|
||||||
exoPlayer.currentPosition,
|
exoPlayer.currentPosition
|
||||||
this
|
|
||||||
)
|
)
|
||||||
exoPlayer.seekTo(0)
|
exoPlayer.seekTo(0)
|
||||||
val prev = episodeArr[currentEpisodeIndex]
|
val prev = episodeArr[currentEpisodeIndex]
|
||||||
|
@ -1024,14 +998,16 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
currentEpisodeIndex = episodeArr.indexOf(ep.number)
|
currentEpisodeIndex = episodeArr.indexOf(ep.number)
|
||||||
episodeTitle.setSelection(currentEpisodeIndex)
|
episodeTitle.setSelection(currentEpisodeIndex)
|
||||||
if (isInitialized) releasePlayer()
|
if (isInitialized) releasePlayer()
|
||||||
playbackPosition = loadData("${media.id}_${ep.number}", this) ?: 0
|
playbackPosition = PrefManager.getCustomVal(
|
||||||
|
"${media.id}_${ep.number}",
|
||||||
|
0
|
||||||
|
)
|
||||||
initPlayer()
|
initPlayer()
|
||||||
preloading = false
|
preloading = false
|
||||||
val context = this
|
val context = this
|
||||||
|
val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode)
|
||||||
val incognito = baseContext.getSharedPreferences("Dantotsu", MODE_PRIVATE)
|
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
|
||||||
.getBoolean("incognito", false)
|
if ((isOnline(context) && !offline) && Discord.token != null && !incognito) {
|
||||||
if (isOnline(context) && Discord.token != null && !incognito) {
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val presence = RPC.createPresence(RPC.Companion.RPCData(
|
val presence = RPC.createPresence(RPC.Companion.RPCData(
|
||||||
applicationId = Discord.application_Id,
|
applicationId = Discord.application_Id,
|
||||||
|
@ -1075,7 +1051,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
|
|
||||||
//FullScreen
|
//FullScreen
|
||||||
isFullscreen = loadData("${media.id}_fullscreenInt", this) ?: isFullscreen
|
isFullscreen = PrefManager.getCustomVal("${media.id}_fullscreenInt", isFullscreen)
|
||||||
playerView.resizeMode = when (isFullscreen) {
|
playerView.resizeMode = when (isFullscreen) {
|
||||||
0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||||
1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
||||||
|
@ -1099,11 +1075,11 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
else -> "Original"
|
else -> "Original"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
saveData("${media.id}_fullscreenInt", isFullscreen, this)
|
PrefManager.setCustomVal("${media.id}_fullscreenInt", isFullscreen)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Cast
|
//Cast
|
||||||
if (settings.cast) {
|
if (PrefManager.getVal(PrefName.Cast)) {
|
||||||
playerView.findViewById<MediaRouteButton>(R.id.exo_cast).apply {
|
playerView.findViewById<MediaRouteButton>(R.id.exo_cast).apply {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
try {
|
try {
|
||||||
|
@ -1121,10 +1097,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
|
|
||||||
//Settings
|
//Settings
|
||||||
exoSettings.setOnClickListener {
|
exoSettings.setOnClickListener {
|
||||||
saveData(
|
PrefManager.setCustomVal(
|
||||||
"${media.id}_${media.anime!!.selectedEpisode}",
|
"${media.id}_${media.anime!!.selectedEpisode}",
|
||||||
exoPlayer.currentPosition,
|
exoPlayer.currentPosition
|
||||||
this
|
|
||||||
)
|
)
|
||||||
val intent = Intent(this, PlayerSettingsActivity::class.java).apply {
|
val intent = Intent(this, PlayerSettingsActivity::class.java).apply {
|
||||||
putExtra("subtitle", subtitle)
|
putExtra("subtitle", subtitle)
|
||||||
|
@ -1135,7 +1110,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
|
|
||||||
//Speed
|
//Speed
|
||||||
val speeds =
|
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)
|
arrayOf(1f, 1.25f, 1.5f, 1.75f, 2f, 2.5f, 3f, 4f, 5f, 10f, 25f, 50f)
|
||||||
else
|
else
|
||||||
arrayOf(
|
arrayOf(
|
||||||
|
@ -1155,16 +1130,20 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
)
|
)
|
||||||
|
|
||||||
val speedsName = speeds.map { "${it}x" }.toTypedArray()
|
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<Int>(PrefName.DefaultSpeed)
|
||||||
|
)
|
||||||
|
|
||||||
playbackParameters = PlaybackParameters(speeds[curSpeed])
|
playbackParameters = PlaybackParameters(speeds[curSpeed])
|
||||||
var speed: Float
|
var speed: Float
|
||||||
val speedDialog =
|
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 {
|
exoSpeed.setOnClickListener {
|
||||||
val dialog = speedDialog.setSingleChoiceItems(speedsName, curSpeed) { dialog, i ->
|
val dialog = speedDialog.setSingleChoiceItems(speedsName, curSpeed) { dialog, i ->
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
saveData("${media.id}_speed", i, this)
|
PrefManager.setCustomVal("${media.id}_speed", i)
|
||||||
speed = speeds[i]
|
speed = speeds[i]
|
||||||
curSpeed = i
|
curSpeed = i
|
||||||
playbackParameters = PlaybackParameters(speed)
|
playbackParameters = PlaybackParameters(speed)
|
||||||
|
@ -1177,7 +1156,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
speedDialog.setOnCancelListener { hideSystemBars() }
|
speedDialog.setOnCancelListener { hideSystemBars() }
|
||||||
|
|
||||||
if (settings.autoPlay) {
|
if (PrefManager.getVal(PrefName.AutoPlay)) {
|
||||||
var touchTimer = Timer()
|
var touchTimer = Timer()
|
||||||
fun touched() {
|
fun touched() {
|
||||||
interacted = true
|
interacted = true
|
||||||
|
@ -1198,8 +1177,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isFullscreen = settings.resize
|
isFullscreen = PrefManager.getVal(PrefName.Resize)
|
||||||
playerView.resizeMode = when (settings.resize) {
|
playerView.resizeMode = when (isFullscreen) {
|
||||||
0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||||
1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
||||||
2 -> AspectRatioFrameLayout.RESIZE_MODE_FILL
|
2 -> AspectRatioFrameLayout.RESIZE_MODE_FILL
|
||||||
|
@ -1207,39 +1186,52 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
|
|
||||||
preloading = false
|
preloading = false
|
||||||
val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
|
||||||
?.getBoolean("incognito", false) ?: false
|
|
||||||
val showProgressDialog =
|
val showProgressDialog =
|
||||||
if (settings.askIndividual) loadData<Boolean>("${media.id}_progressDialog")
|
if (PrefManager.getVal(PrefName.AskIndividualPlayer)) PrefManager.getCustomVal(
|
||||||
?: true else false
|
"${media.id}_ProgressDialog",
|
||||||
if (showProgressDialog && Anilist.userid != null && if (media.isAdult) settings.updateForH else true)
|
true
|
||||||
|
) else false
|
||||||
|
if (!incognito && showProgressDialog && Anilist.userid != null && if (media.isAdult) PrefManager.getVal(
|
||||||
|
PrefName.UpdateForHPlayer
|
||||||
|
) else true
|
||||||
|
) {
|
||||||
AlertDialog.Builder(this, R.style.MyPopup)
|
AlertDialog.Builder(this, R.style.MyPopup)
|
||||||
.setTitle(getString(R.string.auto_update, media.userPreferredName))
|
.setTitle(getString(R.string.auto_update, media.userPreferredName))
|
||||||
.apply {
|
.apply {
|
||||||
if (incognito) {
|
|
||||||
setMessage(getString(R.string.incognito_will_not_update))
|
|
||||||
}
|
|
||||||
setOnCancelListener { hideSystemBars() }
|
setOnCancelListener { hideSystemBars() }
|
||||||
setCancelable(false)
|
setCancelable(false)
|
||||||
setPositiveButton(getString(R.string.yes)) { dialog, _ ->
|
setPositiveButton(getString(R.string.yes)) { dialog, _ ->
|
||||||
saveData("${media.id}_progressDialog", false)
|
PrefManager.setCustomVal(
|
||||||
saveData("${media.id}_save_progress", true)
|
"${media.id}_ProgressDialog",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
PrefManager.setCustomVal(
|
||||||
|
"${media.id}_save_progress",
|
||||||
|
true
|
||||||
|
)
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
|
model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
|
||||||
}
|
}
|
||||||
setNegativeButton(getString(R.string.no)) { dialog, _ ->
|
setNegativeButton(getString(R.string.no)) { dialog, _ ->
|
||||||
saveData("${media.id}_progressDialog", false)
|
PrefManager.setCustomVal(
|
||||||
saveData("${media.id}_save_progress", false)
|
"${media.id}_ProgressDialog",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
PrefManager.setCustomVal(
|
||||||
|
"${media.id}_save_progress",
|
||||||
|
false
|
||||||
|
)
|
||||||
toast(getString(R.string.reset_auto_update))
|
toast(getString(R.string.reset_auto_update))
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
|
model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
|
||||||
}
|
}
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
else model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
|
} else model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
|
||||||
|
|
||||||
//Start the recursive Fun
|
//Start the recursive Fun
|
||||||
if (settings.timeStampsEnabled)
|
if (PrefManager.getVal(PrefName.TimeStampsEnabled))
|
||||||
updateTimeStamp()
|
updateTimeStamp()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1247,12 +1239,15 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
private fun initPlayer() {
|
private fun initPlayer() {
|
||||||
checkNotch()
|
checkNotch()
|
||||||
|
|
||||||
saveData("${media.id}_current_ep", media.anime!!.selectedEpisode!!, this)
|
PrefManager.setCustomVal(
|
||||||
|
"${media.id}_current_ep",
|
||||||
|
media.anime!!.selectedEpisode!!
|
||||||
|
)
|
||||||
|
|
||||||
val set = loadData<MutableSet<Int>>("continue_ANIME", this) ?: mutableSetOf()
|
val list = PrefManager.getVal<Set<Int>>(PrefName.ContinuedAnime).toMutableList()
|
||||||
if (set.contains(media.id)) set.remove(media.id)
|
if (list.contains(media.id)) list.remove(media.id)
|
||||||
set.add(media.id)
|
list.add(media.id)
|
||||||
saveData("continue_ANIME", set, this)
|
PrefManager.setVal(PrefName.ContinuedAnime, list.toList())
|
||||||
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
extractor?.onVideoStopped(video)
|
extractor?.onVideoStopped(video)
|
||||||
|
@ -1263,7 +1258,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
video = ext.videos.getOrNull(episode.selectedVideo) ?: return
|
video = ext.videos.getOrNull(episode.selectedVideo) ?: return
|
||||||
|
|
||||||
subtitle = intent.getSerialized("subtitle")
|
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 -> {
|
null -> {
|
||||||
when (episode.selectedSubtitle) {
|
when (episode.selectedSubtitle) {
|
||||||
null -> null
|
null -> null
|
||||||
|
@ -1305,7 +1301,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
println("sub: $sub")
|
println("sub: $sub")
|
||||||
} else {
|
} else {
|
||||||
val subUri = Uri.parse((subtitle!!.file.url))
|
val subUri = Uri.parse(subtitle!!.file.url)
|
||||||
sub = MediaItem.SubtitleConfiguration
|
sub = MediaItem.SubtitleConfiguration
|
||||||
.Builder(subUri)
|
.Builder(subUri)
|
||||||
.setSelectionFlags(C.SELECTION_FLAG_FORCED)
|
.setSelectionFlags(C.SELECTION_FLAG_FORCED)
|
||||||
|
@ -1342,7 +1338,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
dataSource
|
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 {
|
cacheFactory = CacheDataSource.Factory().apply {
|
||||||
setCache(Helper.getSimpleCache(this@ExoplayerView))
|
setCache(Helper.getSimpleCache(this@ExoplayerView))
|
||||||
if (ext.server.offline) {
|
if (ext.server.offline) {
|
||||||
|
@ -1361,7 +1358,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
|
|
||||||
val downloadedMediaItem = if (ext.server.offline) {
|
val downloadedMediaItem = if (ext.server.offline) {
|
||||||
val key = ext.server.name
|
val key = ext.server.name
|
||||||
downloadId = getSharedPreferences(getString(R.string.anime_downloads), MODE_PRIVATE)
|
downloadId = PrefManager.getAnimeDownloadPreferences()
|
||||||
.getString(key, null)
|
.getString(key, null)
|
||||||
if (downloadId != null) {
|
if (downloadId != null) {
|
||||||
Helper.downloadManager(this)
|
Helper.downloadManager(this)
|
||||||
|
@ -1413,16 +1410,12 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
.setRendererDisabled(TRACK_TYPE_VIDEO, false)
|
.setRendererDisabled(TRACK_TYPE_VIDEO, false)
|
||||||
.setRendererDisabled(C.TRACK_TYPE_AUDIO, false)
|
.setRendererDisabled(C.TRACK_TYPE_AUDIO, false)
|
||||||
.setRendererDisabled(C.TRACK_TYPE_TEXT, false)
|
.setRendererDisabled(C.TRACK_TYPE_TEXT, false)
|
||||||
.setMinVideoSize(
|
|
||||||
loadData("maxWidth", this) ?: 720,
|
|
||||||
loadData("maxHeight", this) ?: 480
|
|
||||||
)
|
|
||||||
.setMaxVideoSize(1, 1)
|
.setMaxVideoSize(1, 1)
|
||||||
//.setOverrideForType(
|
//.setOverrideForType(
|
||||||
// TrackSelectionOverride(trackSelector, 2))
|
// TrackSelectionOverride(trackSelector, 2))
|
||||||
)
|
)
|
||||||
|
|
||||||
if (playbackPosition != 0L && !changingServer && !settings.alwaysContinue) {
|
if (playbackPosition != 0L && !changingServer && !PrefManager.getVal<Boolean>(PrefName.AlwaysContinue)) {
|
||||||
val time = String.format(
|
val time = String.format(
|
||||||
"%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(playbackPosition),
|
"%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(playbackPosition),
|
||||||
TimeUnit.MILLISECONDS.toMinutes(playbackPosition) - TimeUnit.HOURS.toMinutes(
|
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 {
|
.setTitle(getString(R.string.continue_from, time)).apply {
|
||||||
setCancelable(false)
|
setCancelable(false)
|
||||||
setPositiveButton(getString(R.string.yes)) { d, _ ->
|
setPositiveButton(getString(R.string.yes)) { d, _ ->
|
||||||
|
@ -1464,9 +1457,12 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
this.playbackParameters = this@ExoplayerView.playbackParameters
|
this.playbackParameters = this@ExoplayerView.playbackParameters
|
||||||
setMediaItem(mediaItem)
|
setMediaItem(mediaItem)
|
||||||
prepare()
|
prepare()
|
||||||
loadData<Long>("${media.id}_${media.anime!!.selectedEpisode}_max")?.apply {
|
PrefManager.getCustomVal(
|
||||||
if (this <= playbackPosition) playbackPosition = max(0, this - 5)
|
"${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)
|
seekTo(playbackPosition)
|
||||||
}
|
}
|
||||||
playerView.player = exoPlayer
|
playerView.player = exoPlayer
|
||||||
|
@ -1539,8 +1535,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
changingServer = true
|
changingServer = true
|
||||||
|
|
||||||
media.selected!!.server = null
|
media.selected!!.server = null
|
||||||
saveData("${media.id}_${media.anime!!.selectedEpisode}", exoPlayer.currentPosition, this)
|
PrefManager.setCustomVal(
|
||||||
model.saveSelected(media.id, media.selected!!, this)
|
"${media.id}_${media.anime!!.selectedEpisode}", exoPlayer.currentPosition
|
||||||
|
)
|
||||||
|
model.saveSelected(media.id, media.selected!!)
|
||||||
model.onEpisodeClick(
|
model.onEpisodeClick(
|
||||||
media, episode.number, this.supportFragmentManager,
|
media, episode.number, this.supportFragmentManager,
|
||||||
launch = false
|
launch = false
|
||||||
|
@ -1548,8 +1546,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun subClick() {
|
private fun subClick() {
|
||||||
saveData("${media.id}_${media.anime!!.selectedEpisode}", exoPlayer.currentPosition, this)
|
PrefManager.setCustomVal(
|
||||||
model.saveSelected(media.id, media.selected!!, this)
|
"${media.id}_${media.anime!!.selectedEpisode}", exoPlayer.currentPosition
|
||||||
|
)
|
||||||
|
model.saveSelected(media.id, media.selected!!)
|
||||||
SubtitleDialogFragment().show(supportFragmentManager, "dialog")
|
SubtitleDialogFragment().show(supportFragmentManager, "dialog")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1561,10 +1561,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
playerView.player?.pause()
|
playerView.player?.pause()
|
||||||
}
|
}
|
||||||
if (exoPlayer.currentPosition > 5000) {
|
if (exoPlayer.currentPosition > 5000) {
|
||||||
saveData(
|
PrefManager.setCustomVal(
|
||||||
"${media.id}_${media.anime!!.selectedEpisode}",
|
"${media.id}_${media.anime!!.selectedEpisode}",
|
||||||
exoPlayer.currentPosition,
|
exoPlayer.currentPosition
|
||||||
this
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1572,7 +1571,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
LangSet.setLocale(this)
|
|
||||||
orientationListener?.enable()
|
orientationListener?.enable()
|
||||||
hideSystemBars()
|
hideSystemBars()
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
|
@ -1590,7 +1588,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
|
|
||||||
private var wasPlaying = false
|
private var wasPlaying = false
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||||
if (settings.focusPause && !epChanging) {
|
if (PrefManager.getVal(PrefName.FocusPause) && !epChanging) {
|
||||||
if (isInitialized && !hasFocus) wasPlaying = exoPlayer.isPlaying
|
if (isInitialized && !hasFocus) wasPlaying = exoPlayer.isPlaying
|
||||||
if (hasFocus) {
|
if (hasFocus) {
|
||||||
if (isInitialized && wasPlaying) exoPlayer.play()
|
if (isInitialized && wasPlaying) exoPlayer.play()
|
||||||
|
@ -1614,19 +1612,16 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
|
|
||||||
override fun onRenderedFirstFrame() {
|
override fun onRenderedFirstFrame() {
|
||||||
super.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 height = (exoPlayer.videoFormat ?: return).height
|
||||||
val width = (exoPlayer.videoFormat ?: return).width
|
val width = (exoPlayer.videoFormat ?: return).width
|
||||||
|
|
||||||
if (video?.format != VideoType.CONTAINER) {
|
|
||||||
saveData("maxHeight", height)
|
|
||||||
saveData("maxWidth", width)
|
|
||||||
}
|
|
||||||
|
|
||||||
aspectRatio = Rational(width, height)
|
aspectRatio = Rational(width, height)
|
||||||
|
|
||||||
videoName.text = episode.selectedExtractor
|
videoInfo.text = "Quality: ${height}p"
|
||||||
videoInfo.text = "$width x $height"
|
|
||||||
|
|
||||||
if (exoPlayer.duration < playbackPosition)
|
if (exoPlayer.duration < playbackPosition)
|
||||||
exoPlayer.seekTo(0)
|
exoPlayer.seekTo(0)
|
||||||
|
@ -1637,14 +1632,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
exoPlayer.seekTo(0)
|
exoPlayer.seekTo(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isTimeStampsLoaded && settings.timeStampsEnabled) {
|
if (!isTimeStampsLoaded && PrefManager.getVal(PrefName.TimeStampsEnabled)) {
|
||||||
val dur = exoPlayer.duration
|
val dur = exoPlayer.duration
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
model.loadTimeStamps(
|
model.loadTimeStamps(
|
||||||
media.idMAL,
|
media.idMAL,
|
||||||
media.anime?.selectedEpisode?.trim()?.toIntOrNull(),
|
media.anime?.selectedEpisode?.trim()?.toIntOrNull(),
|
||||||
dur / 1000,
|
dur / 1000,
|
||||||
settings.useProxyForTimeStamps
|
PrefManager.getVal(PrefName.UseProxyForTimeStamps)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1654,7 +1649,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
private var preloading = false
|
private var preloading = false
|
||||||
private fun updateProgress() {
|
private fun updateProgress() {
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
if (exoPlayer.currentPosition.toFloat() / exoPlayer.duration > settings.watchPercentage) {
|
if (exoPlayer.currentPosition.toFloat() / exoPlayer.duration > PrefManager.getVal<Float>(
|
||||||
|
PrefName.WatchPercentage
|
||||||
|
)
|
||||||
|
) {
|
||||||
preloading = true
|
preloading = true
|
||||||
nextEpisode(false) { i ->
|
nextEpisode(false) { i ->
|
||||||
val ep = episodes[episodeArr[currentEpisodeIndex + i]] ?: return@nextEpisode
|
val ep = episodes[episodeArr[currentEpisodeIndex + i]] ?: return@nextEpisode
|
||||||
|
@ -1685,7 +1683,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
|
|
||||||
val new = currentTimeStamp
|
val new = currentTimeStamp
|
||||||
timeStampText.text = if (new != null) {
|
timeStampText.text = if (new != null) {
|
||||||
if (settings.showTimeStampButton) {
|
if (PrefManager.getVal(PrefName.ShowTimeStampButton)) {
|
||||||
skipTimeButton.visibility = View.VISIBLE
|
skipTimeButton.visibility = View.VISIBLE
|
||||||
exoSkip.visibility = View.GONE
|
exoSkip.visibility = View.GONE
|
||||||
skipTimeText.text = new.skipType.getType()
|
skipTimeText.text = new.skipType.getType()
|
||||||
|
@ -1693,7 +1691,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
|
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
|
new
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
@ -1703,7 +1701,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
new.skipType.getType()
|
new.skipType.getType()
|
||||||
} else {
|
} else {
|
||||||
skipTimeButton.visibility = View.GONE
|
skipTimeButton.visibility = View.GONE
|
||||||
if (settings.skipTime > 0) exoSkip.visibility = View.VISIBLE
|
if (PrefManager.getVal<Int>(PrefName.SkipTime) > 0) exoSkip.visibility =
|
||||||
|
View.VISIBLE
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1736,13 +1735,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println("Track: ${tracks.groups.size}")
|
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) {
|
override fun onPlayerError(error: PlaybackException) {
|
||||||
|
@ -1757,7 +1749,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
else
|
else
|
||||||
-> {
|
-> {
|
||||||
toast("Player Error ${error.errorCode} (${error.errorCodeName}) : ${error.message}")
|
toast("Player Error ${error.errorCode} (${error.errorCodeName}) : ${error.message}")
|
||||||
FirebaseCrashlytics.getInstance().recordException(error)
|
Injekt.get<CrashlyticsInterface>().logException(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1772,7 +1764,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isBuffering = playbackState == Player.STATE_BUFFERING
|
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()
|
if (interacted) exoNext.performClick()
|
||||||
else toast(getString(R.string.autoplay_cancelled))
|
else toast(getString(R.string.autoplay_cancelled))
|
||||||
}
|
}
|
||||||
|
@ -1780,8 +1772,16 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateAniProgress() {
|
private fun updateAniProgress() {
|
||||||
if (exoPlayer.currentPosition / episodeLength > settings.watchPercentage && Anilist.userid != null)
|
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
|
||||||
if (loadData<Boolean>("${media.id}_save_progress") != false && if (media.isAdult) settings.updateForH else true) {
|
if (!incognito && exoPlayer.currentPosition / episodeLength > PrefManager.getVal<Float>(
|
||||||
|
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 {
|
media.anime!!.selectedEpisode?.apply {
|
||||||
updateProgress(media, this)
|
updateProgress(media, this)
|
||||||
}
|
}
|
||||||
|
@ -1794,7 +1794,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
while (isFiller) {
|
while (isFiller) {
|
||||||
if (episodeArr.size > currentEpisodeIndex + i) {
|
if (episodeArr.size > currentEpisodeIndex + i) {
|
||||||
isFiller =
|
isFiller =
|
||||||
if (settings.autoSkipFiller) episodes[episodeArr[currentEpisodeIndex + i]]?.filler
|
if (PrefManager.getVal(PrefName.AutoSkipFiller)) episodes[episodeArr[currentEpisodeIndex + i]]?.filler
|
||||||
?: false else false
|
?: false else false
|
||||||
if (!isFiller) runnable.invoke(i)
|
if (!isFiller) runnable.invoke(i)
|
||||||
i++
|
i++
|
||||||
|
@ -1830,20 +1830,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
finishAndRemoveTask()
|
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
|
// Cast
|
||||||
private fun cast() {
|
private fun cast() {
|
||||||
val videoURL = video?.file?.url ?: return
|
val videoURL = video?.file?.url ?: return
|
||||||
|
@ -1905,7 +1891,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
orientationListener?.enable()
|
orientationListener?.enable()
|
||||||
}
|
}
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
saveData("${media.id}_${episode.number}", exoPlayer.currentPosition, this)
|
PrefManager.setCustomVal(
|
||||||
|
"${media.id}_${episode.number}",
|
||||||
|
exoPlayer.currentPosition
|
||||||
|
)
|
||||||
if (wasPlaying) exoPlayer.play()
|
if (wasPlaying) exoPlayer.play()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1988,7 +1977,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCastSessionAvailable() {
|
override fun onCastSessionAvailable() {
|
||||||
if (isCastApiAvailable) {
|
if (isCastApiAvailable && !this.isDestroyed) {
|
||||||
startCastPlayer()
|
startCastPlayer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.HapticFeedbackConstants
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -18,6 +17,7 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import ani.dantotsu.*
|
import ani.dantotsu.*
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.databinding.BottomSheetSelectorBinding
|
import ani.dantotsu.databinding.BottomSheetSelectorBinding
|
||||||
import ani.dantotsu.databinding.ItemStreamBinding
|
import ani.dantotsu.databinding.ItemStreamBinding
|
||||||
import ani.dantotsu.databinding.ItemUrlBinding
|
import ani.dantotsu.databinding.ItemUrlBinding
|
||||||
|
@ -28,11 +28,14 @@ import ani.dantotsu.others.Download.download
|
||||||
import ani.dantotsu.parsers.Subtitle
|
import ani.dantotsu.parsers.Subtitle
|
||||||
import ani.dantotsu.parsers.VideoExtractor
|
import ani.dantotsu.parsers.VideoExtractor
|
||||||
import ani.dantotsu.parsers.VideoType
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,7 +96,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
||||||
binding.selectorAutoText.text = selected
|
binding.selectorAutoText.text = selected
|
||||||
binding.selectorCancel.setOnClickListener {
|
binding.selectorCancel.setOnClickListener {
|
||||||
media!!.selected!!.server = null
|
media!!.selected!!.server = null
|
||||||
model.saveSelected(media!!.id, media!!.selected!!, requireActivity())
|
model.saveSelected(media!!.id, media!!.selected!!)
|
||||||
tryWith {
|
tryWith {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
@ -142,11 +145,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
binding.selectorRecyclerView.adapter = null
|
binding.selectorRecyclerView.adapter = null
|
||||||
binding.selectorProgressBar.visibility = View.VISIBLE
|
binding.selectorProgressBar.visibility = View.VISIBLE
|
||||||
makeDefault = loadData("make_default") ?: true
|
makeDefault = PrefManager.getVal(PrefName.MakeDefault)
|
||||||
binding.selectorMakeDefault.isChecked = makeDefault
|
binding.selectorMakeDefault.isChecked = makeDefault
|
||||||
binding.selectorMakeDefault.setOnClickListener {
|
binding.selectorMakeDefault.setOnClickListener {
|
||||||
makeDefault = binding.selectorMakeDefault.isChecked
|
makeDefault = binding.selectorMakeDefault.isChecked
|
||||||
saveData("make_default", makeDefault)
|
PrefManager.setVal(PrefName.MakeDefault, makeDefault)
|
||||||
}
|
}
|
||||||
binding.selectorRecyclerView.layoutManager =
|
binding.selectorRecyclerView.layoutManager =
|
||||||
LinearLayoutManager(
|
LinearLayoutManager(
|
||||||
|
@ -265,7 +268,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
||||||
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedVideo = 0
|
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedVideo = 0
|
||||||
startExoplayer(media!!)
|
startExoplayer(media!!)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,96 +303,88 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
||||||
extractor.server.name
|
extractor.server.name
|
||||||
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedVideo =
|
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedVideo =
|
||||||
position
|
position
|
||||||
binding.urlDownload.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
if ((PrefManager.getVal(PrefName.DownloadManager) as Int) != 0) {
|
||||||
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<Int>("settings_download_manager") ?: 0) != 0) {
|
|
||||||
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedExtractor =
|
|
||||||
extractor.server.name
|
|
||||||
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedVideo =
|
|
||||||
position
|
|
||||||
download(
|
download(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!,
|
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!,
|
||||||
media!!.userPreferredName
|
media!!.userPreferredName
|
||||||
)
|
)
|
||||||
} else {
|
} 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) {
|
if (video.format == VideoType.CONTAINER) {
|
||||||
binding.urlSize.visibility = if (video.size != null) View.VISIBLE else View.GONE
|
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
|
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
|
override fun getItemCount(): Int = extractor.videos.size
|
||||||
|
|
||||||
private inner class UrlViewHolder(val binding: ItemUrlBinding) :
|
private inner class UrlViewHolder(val binding: ItemUrlBinding) :
|
||||||
|
@ -422,7 +424,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
||||||
if (makeDefault) {
|
if (makeDefault) {
|
||||||
media!!.selected!!.server = extractor.server.name
|
media!!.selected!!.server = extractor.server.name
|
||||||
media!!.selected!!.video = bindingAdapterPosition
|
media!!.selected!!.video = bindingAdapterPosition
|
||||||
model.saveSelected(media!!.id, media!!.selected!!, requireActivity())
|
model.saveSelected(media!!.id, media!!.selected!!)
|
||||||
}
|
}
|
||||||
startExoplayer(media!!)
|
startExoplayer(media!!)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,9 @@ import ani.dantotsu.BottomSheetDialogFragment
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.databinding.BottomSheetSubtitlesBinding
|
import ani.dantotsu.databinding.BottomSheetSubtitlesBinding
|
||||||
import ani.dantotsu.databinding.ItemSubtitleTextBinding
|
import ani.dantotsu.databinding.ItemSubtitleTextBinding
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.media.MediaDetailsViewModel
|
import ani.dantotsu.media.MediaDetailsViewModel
|
||||||
import ani.dantotsu.parsers.Subtitle
|
import ani.dantotsu.parsers.Subtitle
|
||||||
import ani.dantotsu.saveData
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
|
||||||
class SubtitleDialogFragment : BottomSheetDialogFragment() {
|
class SubtitleDialogFragment : BottomSheetDialogFragment() {
|
||||||
private var _binding: BottomSheetSubtitlesBinding? = null
|
private var _binding: BottomSheetSubtitlesBinding? = null
|
||||||
|
@ -69,7 +68,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
|
||||||
binding.subtitleTitle.setText(R.string.none)
|
binding.subtitleTitle.setText(R.string.none)
|
||||||
model.getMedia().observe(viewLifecycleOwner) { media ->
|
model.getMedia().observe(viewLifecycleOwner) { media ->
|
||||||
val mediaID: Int = media.id
|
val mediaID: Int = media.id
|
||||||
val selSubs: String? = loadData("subLang_${mediaID}", activity)
|
val selSubs = PrefManager.getCustomVal<String?>("subLang_${mediaID}", null)
|
||||||
if (episode.selectedSubtitle != null && selSubs != "None") {
|
if (episode.selectedSubtitle != null && selSubs != "None") {
|
||||||
binding.root.setCardBackgroundColor(TRANSPARENT)
|
binding.root.setCardBackgroundColor(TRANSPARENT)
|
||||||
}
|
}
|
||||||
|
@ -79,7 +78,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
|
||||||
model.setEpisode(episode, "Subtitle")
|
model.setEpisode(episode, "Subtitle")
|
||||||
model.getMedia().observe(viewLifecycleOwner) { media ->
|
model.getMedia().observe(viewLifecycleOwner) { media ->
|
||||||
val mediaID: Int = media.id
|
val mediaID: Int = media.id
|
||||||
saveData("subLang_${mediaID}", "None", activity)
|
PrefManager.setCustomVal("subLang_${mediaID}", "None")
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
@ -108,7 +107,8 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
model.getMedia().observe(viewLifecycleOwner) { media ->
|
model.getMedia().observe(viewLifecycleOwner) { media ->
|
||||||
val mediaID: Int = media.id
|
val mediaID: Int = media.id
|
||||||
val selSubs: String? = loadData("subLang_${mediaID}", activity)
|
val selSubs: String? =
|
||||||
|
PrefManager.getCustomVal<String?>("subLang_${mediaID}", null)
|
||||||
if (episode.selectedSubtitle != position - 1 && selSubs != subtitles[position - 1].language) {
|
if (episode.selectedSubtitle != position - 1 && selSubs != subtitles[position - 1].language) {
|
||||||
binding.root.setCardBackgroundColor(TRANSPARENT)
|
binding.root.setCardBackgroundColor(TRANSPARENT)
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,10 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
|
||||||
model.setEpisode(episode, "Subtitle")
|
model.setEpisode(episode, "Subtitle")
|
||||||
model.getMedia().observe(viewLifecycleOwner) { media ->
|
model.getMedia().observe(viewLifecycleOwner) { media ->
|
||||||
val mediaID: Int = media.id
|
val mediaID: Int = media.id
|
||||||
saveData("subLang_${mediaID}", subtitles[position - 1].language, activity)
|
PrefManager.setCustomVal(
|
||||||
|
"subLang_${mediaID}",
|
||||||
|
subtitles[position - 1].language
|
||||||
|
)
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,7 @@ class MangaChapterAdapter(
|
||||||
}, 1000)
|
}, 1000)
|
||||||
} else {
|
} else {
|
||||||
// Show download icon
|
// Show download icon
|
||||||
binding.itemDownload.setImageResource(R.drawable.ic_circle_add)
|
binding.itemDownload.setImageResource(R.drawable.ic_download_24)
|
||||||
binding.itemDownload.rotation = 0f
|
binding.itemDownload.rotation = 0f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +261,7 @@ class MangaChapterAdapter(
|
||||||
when (holder) {
|
when (holder) {
|
||||||
is ChapterCompactViewHolder -> {
|
is ChapterCompactViewHolder -> {
|
||||||
val binding = holder.binding
|
val binding = holder.binding
|
||||||
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
|
setAnimation(fragment.requireContext(), holder.binding.root)
|
||||||
val ep = arr[position]
|
val ep = arr[position]
|
||||||
val parsedNumber = MangaNameAdapter.findChapterNumber(ep.number)?.toInt()
|
val parsedNumber = MangaNameAdapter.findChapterNumber(ep.number)?.toInt()
|
||||||
binding.itemEpisodeNumber.text = parsedNumber?.toString() ?: ep.number
|
binding.itemEpisodeNumber.text = parsedNumber?.toString() ?: ep.number
|
||||||
|
@ -287,7 +287,7 @@ class MangaChapterAdapter(
|
||||||
val binding = holder.binding
|
val binding = holder.binding
|
||||||
val ep = arr[position]
|
val ep = arr[position]
|
||||||
holder.bind(ep.number, ep.progress)
|
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
|
binding.itemChapterNumber.text = ep.number
|
||||||
if (ep.progress.isNullOrEmpty()) {
|
if (ep.progress.isNullOrEmpty()) {
|
||||||
binding.itemChapterTitle.visibility = View.GONE
|
binding.itemChapterTitle.visibility = View.GONE
|
||||||
|
|
|
@ -2,7 +2,6 @@ package ani.dantotsu.media.manga
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -28,6 +27,8 @@ import ani.dantotsu.others.webview.CookieCatcher
|
||||||
import ani.dantotsu.parsers.DynamicMangaParser
|
import ani.dantotsu.parsers.DynamicMangaParser
|
||||||
import ani.dantotsu.parsers.MangaReadSources
|
import ani.dantotsu.parsers.MangaReadSources
|
||||||
import ani.dantotsu.parsers.MangaSources
|
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.Notifications.Companion.openSettings
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
|
@ -69,12 +70,9 @@ class MangaReadAdapter(
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val offline = if (!isOnline(binding.root.context) || currContext()?.getSharedPreferences(
|
val offline =
|
||||||
"Dantotsu",
|
if (!isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode)
|
||||||
Context.MODE_PRIVATE
|
) View.GONE else View.VISIBLE
|
||||||
)
|
|
||||||
?.getBoolean("offlineMode", false) == true
|
|
||||||
) View.GONE else View.VISIBLE
|
|
||||||
|
|
||||||
binding.animeSourceNameContainer.visibility = offline
|
binding.animeSourceNameContainer.visibility = offline
|
||||||
binding.animeSourceSettings.visibility = offline
|
binding.animeSourceSettings.visibility = offline
|
||||||
|
@ -163,7 +161,8 @@ class MangaReadAdapter(
|
||||||
var refresh = false
|
var refresh = false
|
||||||
var run = false
|
var run = false
|
||||||
var reversed = media.selected!!.recyclerReversed
|
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.animeSourceTop.rotation = if (reversed) -90f else 90f
|
||||||
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down"
|
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down"
|
||||||
dialogBinding.animeSourceTop.setOnClickListener {
|
dialogBinding.animeSourceTop.setOnClickListener {
|
||||||
|
@ -392,7 +391,8 @@ class MangaReadAdapter(
|
||||||
if (media.manga?.chapters != null) {
|
if (media.manga?.chapters != null) {
|
||||||
val chapters = media.manga.chapters!!.keys.toTypedArray()
|
val chapters = media.manga.chapters!!.keys.toTypedArray()
|
||||||
val anilistEp = (media.userProgress ?: 0).plus(1)
|
val anilistEp = (media.userProgress ?: 0).plus(1)
|
||||||
val appEp = loadData<String>("${media.id}_current_chp")?.toIntOrNull() ?: 1
|
val appEp = PrefManager.getCustomVal<String?>("${media.id}_current_chp", null)
|
||||||
|
?.toIntOrNull() ?: 1
|
||||||
var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString()
|
var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString()
|
||||||
val filteredChapters = chapters.filter { chapterKey ->
|
val filteredChapters = chapters.filter { chapterKey ->
|
||||||
val chapter = media.manga.chapters!![chapterKey]!!
|
val chapter = media.manga.chapters!![chapterKey]!!
|
||||||
|
|
|
@ -26,6 +26,7 @@ import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.ConcatAdapter
|
import androidx.recyclerview.widget.ConcatAdapter
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import ani.dantotsu.*
|
import ani.dantotsu.*
|
||||||
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
|
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
|
||||||
|
@ -42,8 +43,9 @@ import ani.dantotsu.parsers.DynamicMangaParser
|
||||||
import ani.dantotsu.parsers.HMangaSources
|
import ani.dantotsu.parsers.HMangaSources
|
||||||
import ani.dantotsu.parsers.MangaParser
|
import ani.dantotsu.parsers.MangaParser
|
||||||
import ani.dantotsu.parsers.MangaSources
|
import ani.dantotsu.parsers.MangaSources
|
||||||
import ani.dantotsu.settings.UserInterfaceSettings
|
|
||||||
import ani.dantotsu.settings.extensionprefs.MangaSourcePreferencesFragment
|
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
|
||||||
import ani.dantotsu.subcriptions.Notifications.Group.MANGA_GROUP
|
import ani.dantotsu.subcriptions.Notifications.Group.MANGA_GROUP
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
||||||
|
@ -86,9 +88,6 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
var continueEp: Boolean = false
|
var continueEp: Boolean = false
|
||||||
var loaded = false
|
var loaded = false
|
||||||
|
|
||||||
val uiSettings = loadData("ui_settings", toast = false)
|
|
||||||
?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
|
@ -139,6 +138,23 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
|
|
||||||
binding.animeSourceRecycler.layoutManager = gridLayoutManager
|
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) {
|
model.scrolledToTop.observe(viewLifecycleOwner) {
|
||||||
if (it) binding.animeSourceRecycler.scrollToPosition(0)
|
if (it) binding.animeSourceRecycler.scrollToPosition(0)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +170,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
media.selected = model.loadSelected(media)
|
media.selected = model.loadSelected(media)
|
||||||
|
|
||||||
subscribed =
|
subscribed =
|
||||||
SubscriptionHelper.getSubscriptions(requireContext()).containsKey(media.id)
|
SubscriptionHelper.getSubscriptions().containsKey(media.id)
|
||||||
|
|
||||||
style = media.selected!!.recyclerStyle
|
style = media.selected!!.recyclerStyle
|
||||||
reverse = media.selected!!.recyclerReversed
|
reverse = media.selected!!.recyclerReversed
|
||||||
|
@ -165,10 +181,14 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
headerAdapter = MangaReadAdapter(it, this, model.mangaReadSources!!)
|
headerAdapter = MangaReadAdapter(it, this, model.mangaReadSources!!)
|
||||||
headerAdapter.scanlatorSelectionListener = this
|
headerAdapter.scanlatorSelectionListener = this
|
||||||
chapterAdapter =
|
chapterAdapter =
|
||||||
MangaChapterAdapter(style ?: uiSettings.mangaDefaultView, media, this)
|
MangaChapterAdapter(
|
||||||
|
style ?: PrefManager.getVal(PrefName.MangaDefaultView), media, this
|
||||||
|
)
|
||||||
|
|
||||||
for (download in downloadManager.mangaDownloadedTypes) {
|
for (download in downloadManager.mangaDownloadedTypes) {
|
||||||
chapterAdapter.stopDownload(download.chapter)
|
if (download.title == media.mainName()) {
|
||||||
|
chapterAdapter.stopDownload(download.chapter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.animeSourceRecycler.adapter =
|
binding.animeSourceRecycler.adapter =
|
||||||
|
@ -284,7 +304,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
model.mangaReadSources?.get(selected.sourceIndex)?.showUserTextListener = null
|
model.mangaReadSources?.get(selected.sourceIndex)?.showUserTextListener = null
|
||||||
selected.sourceIndex = i
|
selected.sourceIndex = i
|
||||||
selected.server = null
|
selected.server = null
|
||||||
model.saveSelected(media.id, selected, requireActivity())
|
model.saveSelected(media.id, selected)
|
||||||
media.selected = selected
|
media.selected = selected
|
||||||
return model.mangaReadSources?.get(i)!!
|
return model.mangaReadSources?.get(i)!!
|
||||||
}
|
}
|
||||||
|
@ -292,14 +312,14 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
fun onLangChange(i: Int) {
|
fun onLangChange(i: Int) {
|
||||||
val selected = model.loadSelected(media)
|
val selected = model.loadSelected(media)
|
||||||
selected.langIndex = i
|
selected.langIndex = i
|
||||||
model.saveSelected(media.id, selected, requireActivity())
|
model.saveSelected(media.id, selected)
|
||||||
media.selected = selected
|
media.selected = selected
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onScanlatorChange(list: List<String>) {
|
fun onScanlatorChange(list: List<String>) {
|
||||||
val selected = model.loadSelected(media)
|
val selected = model.loadSelected(media)
|
||||||
selected.scanlators = list
|
selected.scanlators = list
|
||||||
model.saveSelected(media.id, selected, requireActivity())
|
model.saveSelected(media.id, selected)
|
||||||
media.selected = selected
|
media.selected = selected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +332,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
reverse = rev
|
reverse = rev
|
||||||
media.selected!!.recyclerStyle = style
|
media.selected!!.recyclerStyle = style
|
||||||
media.selected!!.recyclerReversed = reverse
|
media.selected!!.recyclerReversed = reverse
|
||||||
model.saveSelected(media.id, media.selected!!, requireActivity())
|
model.saveSelected(media.id, media.selected!!)
|
||||||
reload()
|
reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +340,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
media.selected!!.chip = i
|
media.selected!!.chip = i
|
||||||
start = s
|
start = s
|
||||||
end = e
|
end = e
|
||||||
model.saveSelected(media.id, media.selected!!, requireActivity())
|
model.saveSelected(media.id, media.selected!!)
|
||||||
reload()
|
reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,12 +388,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
if (allSettings.size > 1) {
|
if (allSettings.size > 1) {
|
||||||
val names =
|
val names =
|
||||||
allSettings.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
|
allSettings.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
|
||||||
var selectedIndex = 0
|
|
||||||
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
|
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
|
||||||
.setTitle("Select a Source")
|
.setTitle("Select a Source")
|
||||||
.setSingleChoiceItems(names, selectedIndex) { dialog, which ->
|
.setSingleChoiceItems(names, -1) { dialog, which ->
|
||||||
selectedIndex = which
|
selectedSetting = allSettings[which]
|
||||||
selectedSetting = allSettings[selectedIndex]
|
|
||||||
itemSelected = true
|
itemSelected = true
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
|
||||||
|
@ -420,7 +438,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
model.continueMedia = false
|
model.continueMedia = false
|
||||||
media.manga?.chapters?.get(i)?.let {
|
media.manga?.chapters?.get(i)?.let {
|
||||||
media.manga?.selectedChapter = i
|
media.manga?.selectedChapter = i
|
||||||
model.saveSelected(media.id, media.selected!!, requireActivity())
|
model.saveSelected(media.id, media.selected!!)
|
||||||
ChapterLoaderDialog.newInstance(it, true)
|
ChapterLoaderDialog.newInstance(it, true)
|
||||||
.show(requireActivity().supportFragmentManager, "dialog")
|
.show(requireActivity().supportFragmentManager, "dialog")
|
||||||
}
|
}
|
||||||
|
@ -558,7 +576,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
selected.latest =
|
selected.latest =
|
||||||
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
|
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
|
||||||
|
|
||||||
model.saveSelected(media.id, selected, requireActivity())
|
model.saveSelected(media.id, selected)
|
||||||
headerAdapter.handleChapters()
|
headerAdapter.handleChapters()
|
||||||
chapterAdapter.notifyItemRangeRemoved(0, chapterAdapter.arr.size)
|
chapterAdapter.notifyItemRangeRemoved(0, chapterAdapter.arr.size)
|
||||||
var arr: ArrayList<MangaChapter> = arrayListOf()
|
var arr: ArrayList<MangaChapter> = arrayListOf()
|
||||||
|
@ -572,7 +590,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
arr = (arr.reversed() as? ArrayList<MangaChapter>) ?: arr
|
arr = (arr.reversed() as? ArrayList<MangaChapter>) ?: arr
|
||||||
}
|
}
|
||||||
chapterAdapter.arr = arr
|
chapterAdapter.arr = arr
|
||||||
chapterAdapter.updateType(style ?: uiSettings.mangaDefaultView)
|
chapterAdapter.updateType(style ?: PrefManager.getVal(PrefName.MangaDefaultView))
|
||||||
chapterAdapter.notifyItemRangeInserted(0, arr.size)
|
chapterAdapter.notifyItemRangeInserted(0, arr.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,7 @@ abstract class BaseImageAdapter(
|
||||||
val activity: MangaReaderActivity,
|
val activity: MangaReaderActivity,
|
||||||
chapter: MangaChapter
|
chapter: MangaChapter
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
val settings = activity.settings.default
|
val settings = activity.defaultSettings
|
||||||
val uiSettings = activity.uiSettings
|
|
||||||
val images = chapter.images()
|
val images = chapter.images()
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
|
|
@ -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.LEFT_TO_RIGHT
|
||||||
import ani.dantotsu.settings.CurrentReaderSettings.Directions.RIGHT_TO_LEFT
|
import ani.dantotsu.settings.CurrentReaderSettings.Directions.RIGHT_TO_LEFT
|
||||||
import ani.dantotsu.settings.CurrentReaderSettings.Layouts.PAGED
|
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.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
||||||
import com.davemorrissey.labs.subscaleview.ImageSource
|
import com.davemorrissey.labs.subscaleview.ImageSource
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||||
|
@ -83,7 +85,7 @@ open class ImageAdapter(
|
||||||
imageView.minScale = scale
|
imageView.minScale = scale
|
||||||
|
|
||||||
ObjectAnimator.ofFloat(parent, "alpha", 0f, 1f)
|
ObjectAnimator.ofFloat(parent, "alpha", 0f, 1f)
|
||||||
.setDuration((400 * uiSettings.animationSpeed).toLong())
|
.setDuration((400 * PrefManager.getVal<Float>(PrefName.AnimationSpeed)).toLong())
|
||||||
.start()
|
.start()
|
||||||
progress.visibility = View.GONE
|
progress.visibility = View.GONE
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import ani.dantotsu.*
|
import ani.dantotsu.*
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.connections.discord.Discord
|
import ani.dantotsu.connections.discord.Discord
|
||||||
import ani.dantotsu.connections.discord.DiscordService
|
import ani.dantotsu.connections.discord.DiscordService
|
||||||
import ani.dantotsu.connections.discord.DiscordServiceRunningSingleton
|
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.MangaChapter
|
||||||
import ani.dantotsu.media.manga.MangaNameAdapter
|
import ani.dantotsu.media.manga.MangaNameAdapter
|
||||||
import ani.dantotsu.others.ImageViewDialog
|
import ani.dantotsu.others.ImageViewDialog
|
||||||
import ani.dantotsu.others.LangSet
|
|
||||||
import ani.dantotsu.parsers.HMangaSources
|
import ani.dantotsu.parsers.HMangaSources
|
||||||
import ani.dantotsu.parsers.MangaImage
|
import ani.dantotsu.parsers.MangaImage
|
||||||
import ani.dantotsu.parsers.MangaSources
|
import ani.dantotsu.parsers.MangaSources
|
||||||
|
import ani.dantotsu.settings.CurrentReaderSettings
|
||||||
import ani.dantotsu.settings.CurrentReaderSettings.Companion.applyWebtoon
|
import ani.dantotsu.settings.CurrentReaderSettings.Companion.applyWebtoon
|
||||||
import ani.dantotsu.settings.CurrentReaderSettings.Directions.*
|
import ani.dantotsu.settings.CurrentReaderSettings.Directions.*
|
||||||
import ani.dantotsu.settings.CurrentReaderSettings.DualPageModes.*
|
import ani.dantotsu.settings.CurrentReaderSettings.DualPageModes.*
|
||||||
import ani.dantotsu.settings.CurrentReaderSettings.Layouts.*
|
import ani.dantotsu.settings.CurrentReaderSettings.Layouts.*
|
||||||
import ani.dantotsu.settings.ReaderSettings
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.UserInterfaceSettings
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import com.alexvasilkov.gestures.views.GestureFrameLayout
|
import com.alexvasilkov.gestures.views.GestureFrameLayout
|
||||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
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 eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
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 java.util.*
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
@ -74,6 +77,8 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
private val model: MediaDetailsViewModel by viewModels()
|
private val model: MediaDetailsViewModel by viewModels()
|
||||||
private val scope = lifecycleScope
|
private val scope = lifecycleScope
|
||||||
|
|
||||||
|
var defaultSettings = CurrentReaderSettings()
|
||||||
|
|
||||||
private lateinit var media: Media
|
private lateinit var media: Media
|
||||||
private lateinit var chapter: MangaChapter
|
private lateinit var chapter: MangaChapter
|
||||||
private lateinit var chapters: MutableMap<String, MangaChapter>
|
private lateinit var chapters: MutableMap<String, MangaChapter>
|
||||||
|
@ -84,13 +89,9 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
private var isContVisible = false
|
private var isContVisible = false
|
||||||
private var showProgressDialog = true
|
private var showProgressDialog = true
|
||||||
|
|
||||||
//private var progressDialog: AlertDialog.Builder? = null
|
|
||||||
private var maxChapterPage = 0L
|
private var maxChapterPage = 0L
|
||||||
private var currentChapterPage = 0L
|
private var currentChapterPage = 0L
|
||||||
|
|
||||||
lateinit var settings: ReaderSettings
|
|
||||||
lateinit var uiSettings: UserInterfaceSettings
|
|
||||||
|
|
||||||
private var notchHeight: Int? = null
|
private var notchHeight: Int? = null
|
||||||
|
|
||||||
private var imageAdapter: BaseImageAdapter? = null
|
private var imageAdapter: BaseImageAdapter? = null
|
||||||
|
@ -98,10 +99,8 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
var sliding = false
|
var sliding = false
|
||||||
var isAnimating = false
|
var isAnimating = false
|
||||||
|
|
||||||
private var rpc: RPC? = null
|
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
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<Boolean>(PrefName.ShowSystemBars)) {
|
||||||
val displayCutout = window.decorView.rootWindowInsets.displayCutout
|
val displayCutout = window.decorView.rootWindowInsets.displayCutout
|
||||||
if (displayCutout != null) {
|
if (displayCutout != null) {
|
||||||
if (displayCutout.boundingRects.size > 0) {
|
if (displayCutout.boundingRects.size > 0) {
|
||||||
|
@ -123,7 +122,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hideBars() {
|
private fun hideBars() {
|
||||||
if (!settings.showSystemBars) hideSystemBars()
|
if (PrefManager.getVal(PrefName.ShowSystemBars)) hideSystemBars()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
@ -138,28 +137,20 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityMangaReaderBinding.inflate(layoutInflater)
|
binding = ActivityMangaReaderBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
binding.mangaReaderBack.setOnClickListener {
|
binding.mangaReaderBack.setOnClickListener {
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultSettings = loadReaderSettings("reader_settings") ?: defaultSettings
|
||||||
|
|
||||||
onBackPressedDispatcher.addCallback(this) {
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
progress { finish() }
|
progress { finish() }
|
||||||
}
|
}
|
||||||
|
|
||||||
settings = loadData("reader_settings", this)
|
controllerDuration = (PrefManager.getVal<Float>(PrefName.AnimationSpeed) * 200).toLong()
|
||||||
?: ReaderSettings().apply { saveData("reader_settings", this) }
|
|
||||||
uiSettings = loadData("ui_settings", this) ?: UserInterfaceSettings().apply {
|
|
||||||
saveData(
|
|
||||||
"ui_settings",
|
|
||||||
this
|
|
||||||
)
|
|
||||||
}
|
|
||||||
controllerDuration = (uiSettings.animationSpeed * 200).toLong()
|
|
||||||
|
|
||||||
hideBars()
|
hideBars()
|
||||||
|
|
||||||
|
@ -182,7 +173,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
binding.mangaReaderSlider.addOnChangeListener { _, value, fromUser ->
|
binding.mangaReaderSlider.addOnChangeListener { _, value, fromUser ->
|
||||||
if (fromUser) {
|
if (fromUser) {
|
||||||
sliding = true
|
sliding = true
|
||||||
if (settings.default.layout != PAGED)
|
if (defaultSettings.layout != PAGED)
|
||||||
binding.mangaReaderRecycler.scrollToPosition((value.toInt() - 1) / (dualPage { 2 }
|
binding.mangaReaderRecycler.scrollToPosition((value.toInt() - 1) / (dualPage { 2 }
|
||||||
?: 1))
|
?: 1))
|
||||||
else
|
else
|
||||||
|
@ -205,34 +196,30 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
else model.getMedia().value ?: return
|
else model.getMedia().value ?: return
|
||||||
model.setMedia(media)
|
model.setMedia(media)
|
||||||
|
|
||||||
if (settings.autoDetectWebtoon && media.countryOfOrigin != "JP") applyWebtoon(settings.default)
|
if (PrefManager.getVal(PrefName.AutoDetectWebtoon) && media.countryOfOrigin != "JP") applyWebtoon(
|
||||||
settings.default = loadData("${media.id}_current_settings") ?: settings.default
|
defaultSettings
|
||||||
|
)
|
||||||
|
defaultSettings = loadReaderSettings("${media.id}_current_settings") ?: defaultSettings
|
||||||
|
|
||||||
chapters = media.manga?.chapters ?: return
|
chapters = media.manga?.chapters ?: return
|
||||||
chapter = chapters[media.manga!!.selectedChapter] ?: return
|
chapter = chapters[media.manga!!.selectedChapter] ?: return
|
||||||
|
|
||||||
model.mangaReadSources = if (media.isAdult) HMangaSources else MangaSources
|
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()) {
|
if (model.mangaReadSources!!.names.isEmpty()) {
|
||||||
//try to reload sources
|
//try to reload sources
|
||||||
try {
|
try {
|
||||||
if (media.isAdult) {
|
val mangaSources = MangaSources
|
||||||
val mangaSources = MangaSources
|
val scope = lifecycleScope
|
||||||
val scope = lifecycleScope
|
scope.launch(Dispatchers.IO) {
|
||||||
scope.launch(Dispatchers.IO) {
|
mangaSources.init(
|
||||||
mangaSources.init(Injekt.get<MangaExtensionManager>().installedExtensionsFlow, this@MangaReaderActivity)
|
Injekt.get<MangaExtensionManager>().installedExtensionsFlow
|
||||||
}
|
)
|
||||||
model.mangaReadSources = mangaSources
|
|
||||||
} else {
|
|
||||||
val mangaSources = HMangaSources
|
|
||||||
val scope = lifecycleScope
|
|
||||||
scope.launch(Dispatchers.IO) {
|
|
||||||
mangaSources.init(Injekt.get<MangaExtensionManager>().installedExtensionsFlow)
|
|
||||||
}
|
|
||||||
model.mangaReadSources = mangaSources
|
|
||||||
}
|
}
|
||||||
|
model.mangaReadSources = mangaSources
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Firebase.crashlytics.recordException(e)
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
logError(e)
|
logError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,13 +242,18 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
showProgressDialog =
|
showProgressDialog =
|
||||||
if (settings.askIndividual) loadData<Boolean>("${media.id}_progressDialog")
|
if (PrefManager.getVal(PrefName.AskIndividualReader)) PrefManager.getCustomVal(
|
||||||
?: true else false
|
"${media.id}_progressDialog",
|
||||||
|
true
|
||||||
|
) else false
|
||||||
|
|
||||||
//Chapter Change
|
//Chapter Change
|
||||||
fun change(index: Int) {
|
fun change(index: Int) {
|
||||||
mangaCache.clear()
|
mangaCache.clear()
|
||||||
saveData("${media.id}_${chaptersArr[currentChapterIndex]}", currentChapterPage, this)
|
PrefManager.setCustomVal(
|
||||||
|
"${media.id}_${chaptersArr[currentChapterIndex]}",
|
||||||
|
currentChapterPage
|
||||||
|
)
|
||||||
ChapterLoaderDialog.newInstance(chapters[chaptersArr[index]]!!)
|
ChapterLoaderDialog.newInstance(chapters[chaptersArr[index]]!!)
|
||||||
.show(supportFragmentManager, "dialog")
|
.show(supportFragmentManager, "dialog")
|
||||||
}
|
}
|
||||||
|
@ -310,7 +302,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
chapter = chap
|
chapter = chap
|
||||||
media.manga!!.selectedChapter = chapter.number
|
media.manga!!.selectedChapter = chapter.number
|
||||||
media.selected = model.loadSelected(media)
|
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)
|
currentChapterIndex = chaptersArr.indexOf(chap.number)
|
||||||
binding.mangaReaderChapterSelect.setSelection(currentChapterIndex)
|
binding.mangaReaderChapterSelect.setSelection(currentChapterIndex)
|
||||||
binding.mangaReaderNextChap.text =
|
binding.mangaReaderNextChap.text =
|
||||||
|
@ -319,9 +311,9 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: ""
|
chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: ""
|
||||||
applySettings()
|
applySettings()
|
||||||
val context = this
|
val context = this
|
||||||
val incognito = context.getSharedPreferences("Dantotsu", 0)
|
val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode)
|
||||||
?.getBoolean("incognito", false) ?: false
|
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
|
||||||
if (isOnline(context) && Discord.token != null && !incognito) {
|
if ((isOnline(context) && !offline) && Discord.token != null && !incognito) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val presence = RPC.createPresence(
|
val presence = RPC.createPresence(
|
||||||
RPC.Companion.RPCData(
|
RPC.Companion.RPCData(
|
||||||
|
@ -369,7 +361,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
private val snapHelper = PagerSnapHelper()
|
private val snapHelper = PagerSnapHelper()
|
||||||
|
|
||||||
fun <T> dualPage(callback: () -> T): T? {
|
fun <T> dualPage(callback: () -> T): T? {
|
||||||
return when (settings.default.dualPageMode) {
|
return when (defaultSettings.dualPageMode) {
|
||||||
No -> null
|
No -> null
|
||||||
Automatic -> {
|
Automatic -> {
|
||||||
val orientation = resources.configuration.orientation
|
val orientation = resources.configuration.orientation
|
||||||
|
@ -384,29 +376,29 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
fun applySettings() {
|
fun applySettings() {
|
||||||
|
|
||||||
saveData("${media.id}_current_settings", settings.default)
|
saveReaderSettings("${media.id}_current_settings", defaultSettings)
|
||||||
hideBars()
|
hideBars()
|
||||||
|
|
||||||
//true colors
|
//true colors
|
||||||
SubsamplingScaleImageView.setPreferredBitmapConfig(
|
SubsamplingScaleImageView.setPreferredBitmapConfig(
|
||||||
if (settings.default.trueColors) Bitmap.Config.ARGB_8888
|
if (defaultSettings.trueColors) Bitmap.Config.ARGB_8888
|
||||||
else Bitmap.Config.RGB_565
|
else Bitmap.Config.RGB_565
|
||||||
)
|
)
|
||||||
|
|
||||||
//keep screen On
|
//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)
|
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
|
||||||
binding.mangaReaderPager.unregisterOnPageChangeCallback(pageChangeCallback)
|
binding.mangaReaderPager.unregisterOnPageChangeCallback(pageChangeCallback)
|
||||||
|
|
||||||
currentChapterPage = loadData("${media.id}_${chapter.number}", this) ?: 1
|
currentChapterPage = PrefManager.getCustomVal("${media.id}_${chapter.number}", 1L)
|
||||||
|
|
||||||
val chapImages = chapter.images()
|
val chapImages = chapter.images()
|
||||||
|
|
||||||
maxChapterPage = 0
|
maxChapterPage = 0
|
||||||
if (chapImages.isNotEmpty()) {
|
if (chapImages.isNotEmpty()) {
|
||||||
maxChapterPage = chapImages.size.toLong()
|
maxChapterPage = chapImages.size.toLong()
|
||||||
saveData("${media.id}_${chapter.number}_max", maxChapterPage)
|
PrefManager.setCustomVal("${media.id}_${chapter.number}_max", maxChapterPage)
|
||||||
|
|
||||||
imageAdapter =
|
imageAdapter =
|
||||||
dualPage { DualPageAdapter(this, chapter) } ?: ImageAdapter(this, chapter)
|
dualPage { DualPageAdapter(this, chapter) } ?: ImageAdapter(this, chapter)
|
||||||
|
@ -421,15 +413,15 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
binding.mangaReaderSlider.visibility = View.GONE
|
binding.mangaReaderSlider.visibility = View.GONE
|
||||||
}
|
}
|
||||||
binding.mangaReaderPageNumber.text =
|
binding.mangaReaderPageNumber.text =
|
||||||
if (settings.default.hidePageNumbers) "" else "${currentChapterPage}/$maxChapterPage"
|
if (defaultSettings.hidePageNumbers) "" else "${currentChapterPage}/$maxChapterPage"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentPage = currentChapterPage.toInt()
|
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
|
binding.mangaReaderSwipy.vertical = true
|
||||||
if (settings.default.direction == TOP_TO_BOTTOM) {
|
if (defaultSettings.direction == TOP_TO_BOTTOM) {
|
||||||
binding.BottomSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1)
|
binding.BottomSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1)
|
||||||
?: getString(R.string.no_chapter)
|
?: getString(R.string.no_chapter)
|
||||||
binding.TopSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1)
|
binding.TopSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1)
|
||||||
|
@ -466,7 +458,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
binding.mangaReaderSwipy.vertical = false
|
binding.mangaReaderSwipy.vertical = false
|
||||||
if (settings.default.direction == RIGHT_TO_LEFT) {
|
if (defaultSettings.direction == RIGHT_TO_LEFT) {
|
||||||
binding.LeftSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1)
|
binding.LeftSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1)
|
||||||
?: getString(R.string.no_chapter)
|
?: getString(R.string.no_chapter)
|
||||||
binding.RightSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1)
|
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.visibility = View.VISIBLE
|
||||||
binding.mangaReaderRecyclerContainer.controller.settings.isRotationEnabled =
|
binding.mangaReaderRecyclerContainer.controller.settings.isRotationEnabled =
|
||||||
settings.default.rotation
|
defaultSettings.rotation
|
||||||
|
|
||||||
val detector = GestureDetectorCompat(this, object : GesturesListener() {
|
val detector = GestureDetectorCompat(this, object : GesturesListener() {
|
||||||
override fun onLongPress(e: MotionEvent) {
|
override fun onLongPress(e: MotionEvent) {
|
||||||
|
@ -530,7 +522,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
val page =
|
val page =
|
||||||
chapter.dualPages().getOrNull(pos) ?: return@dualPage false
|
chapter.dualPages().getOrNull(pos) ?: return@dualPage false
|
||||||
val nextPage = page.second
|
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)
|
onImageLongClicked(pos * 2, nextPage, page.first, callback)
|
||||||
else
|
else
|
||||||
onImageLongClicked(pos * 2, page.first, nextPage, callback)
|
onImageLongClicked(pos * 2, page.first, nextPage, callback)
|
||||||
|
@ -552,11 +544,11 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
|
|
||||||
val manager = PreloadLinearLayoutManager(
|
val manager = PreloadLinearLayoutManager(
|
||||||
this,
|
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
|
RecyclerView.VERTICAL
|
||||||
else
|
else
|
||||||
RecyclerView.HORIZONTAL,
|
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
|
manager.preloadItemCount = 5
|
||||||
|
|
||||||
|
@ -575,7 +567,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
|
|
||||||
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
|
||||||
settings.default.apply {
|
defaultSettings.apply {
|
||||||
if (
|
if (
|
||||||
((direction == TOP_TO_BOTTOM || direction == BOTTOM_TO_TOP)
|
((direction == TOP_TO_BOTTOM || direction == BOTTOM_TO_TOP)
|
||||||
&& (!v.canScrollVertically(-1) || !v.canScrollVertically(1)))
|
&& (!v.canScrollVertically(-1) || !v.canScrollVertically(1)))
|
||||||
|
@ -594,25 +586,25 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
super.onScrolled(v, dx, dy)
|
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)
|
updatePadding(0, 128f.px, 0, 128f.px)
|
||||||
else
|
else
|
||||||
updatePadding(128f.px, 0, 128f.px, 0)
|
updatePadding(128f.px, 0, 128f.px, 0)
|
||||||
|
|
||||||
snapHelper.attachToRecyclerView(
|
snapHelper.attachToRecyclerView(
|
||||||
if (settings.default.layout == CONTINUOUS_PAGED) this
|
if (defaultSettings.layout == CONTINUOUS_PAGED) this
|
||||||
else null
|
else null
|
||||||
)
|
)
|
||||||
|
|
||||||
onVolumeUp = {
|
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)
|
smoothScrollBy(0, -500)
|
||||||
else
|
else
|
||||||
smoothScrollBy(-500, 0)
|
smoothScrollBy(-500, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
onVolumeDown = {
|
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)
|
smoothScrollBy(0, 500)
|
||||||
else
|
else
|
||||||
smoothScrollBy(500, 0)
|
smoothScrollBy(500, 0)
|
||||||
|
@ -627,11 +619,11 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
adapter = imageAdapter
|
adapter = imageAdapter
|
||||||
layoutDirection =
|
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
|
View.LAYOUT_DIRECTION_RTL
|
||||||
else View.LAYOUT_DIRECTION_LTR
|
else View.LAYOUT_DIRECTION_LTR
|
||||||
orientation =
|
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
|
ViewPager2.ORIENTATION_HORIZONTAL
|
||||||
else ViewPager2.ORIENTATION_VERTICAL
|
else ViewPager2.ORIENTATION_VERTICAL
|
||||||
registerOnPageChangeCallback(pageChangeCallback)
|
registerOnPageChangeCallback(pageChangeCallback)
|
||||||
|
@ -654,7 +646,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
return when (event.keyCode) {
|
return when (event.keyCode) {
|
||||||
KEYCODE_VOLUME_UP, KEYCODE_DPAD_UP, KEYCODE_PAGE_UP -> {
|
KEYCODE_VOLUME_UP, KEYCODE_DPAD_UP, KEYCODE_PAGE_UP -> {
|
||||||
if (event.keyCode == KEYCODE_VOLUME_UP)
|
if (event.keyCode == KEYCODE_VOLUME_UP)
|
||||||
if (!settings.default.volumeButtons)
|
if (!defaultSettings.volumeButtons)
|
||||||
return false
|
return false
|
||||||
if (event.action == ACTION_DOWN) {
|
if (event.action == ACTION_DOWN) {
|
||||||
onVolumeUp?.invoke()
|
onVolumeUp?.invoke()
|
||||||
|
@ -664,7 +656,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
|
|
||||||
KEYCODE_VOLUME_DOWN, KEYCODE_DPAD_DOWN, KEYCODE_PAGE_DOWN -> {
|
KEYCODE_VOLUME_DOWN, KEYCODE_DPAD_DOWN, KEYCODE_PAGE_DOWN -> {
|
||||||
if (event.keyCode == KEYCODE_VOLUME_DOWN)
|
if (event.keyCode == KEYCODE_VOLUME_DOWN)
|
||||||
if (!settings.default.volumeButtons)
|
if (!defaultSettings.volumeButtons)
|
||||||
return false
|
return false
|
||||||
if (event.action == ACTION_DOWN) {
|
if (event.action == ACTION_DOWN) {
|
||||||
onVolumeDown?.invoke()
|
onVolumeDown?.invoke()
|
||||||
|
@ -711,14 +703,14 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
fun handleController(shouldShow: Boolean? = null, event: MotionEvent? = null) {
|
fun handleController(shouldShow: Boolean? = null, event: MotionEvent? = null) {
|
||||||
var pressLocation = pressPos.CENTER
|
var pressLocation = pressPos.CENTER
|
||||||
if (!sliding) {
|
if (!sliding) {
|
||||||
if (event != null && settings.default.layout == PAGED) {
|
if (event != null && defaultSettings.layout == PAGED) {
|
||||||
if (event.action != MotionEvent.ACTION_UP) return
|
if (event.action != MotionEvent.ACTION_UP) return
|
||||||
val x = event.rawX.toInt()
|
val x = event.rawX.toInt()
|
||||||
val y = event.rawY.toInt()
|
val y = event.rawY.toInt()
|
||||||
val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
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 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)..<y) {
|
if (screenWidth / 5 in x + 1..<y) {
|
||||||
pressLocation = if (settings.default.direction == RIGHT_TO_LEFT) {
|
pressLocation = if (defaultSettings.direction == RIGHT_TO_LEFT) {
|
||||||
pressPos.RIGHT
|
pressPos.RIGHT
|
||||||
} else {
|
} else {
|
||||||
pressPos.LEFT
|
pressPos.LEFT
|
||||||
|
@ -726,7 +718,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
//if in the last 1/5th of the screen width, right and lower than 1/5th of the screen height, right
|
//if in the last 1/5th of the screen width, right and lower than 1/5th of the screen height, right
|
||||||
else if (x > screenWidth - screenWidth / 5 && y > screenWidth / 5) {
|
else if (x > screenWidth - screenWidth / 5 && y > screenWidth / 5) {
|
||||||
pressLocation = if (settings.default.direction == RIGHT_TO_LEFT) {
|
pressLocation = if (defaultSettings.direction == RIGHT_TO_LEFT) {
|
||||||
pressPos.LEFT
|
pressPos.LEFT
|
||||||
} else {
|
} else {
|
||||||
pressPos.RIGHT
|
pressPos.RIGHT
|
||||||
|
@ -758,12 +750,12 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.showSystemBars) {
|
if (!PrefManager.getVal<Boolean>(PrefName.ShowSystemBars)) {
|
||||||
hideBars()
|
hideBars()
|
||||||
checkNotch()
|
checkNotch()
|
||||||
}
|
}
|
||||||
//horizontal scrollbar
|
//horizontal scrollbar
|
||||||
if (settings.default.horizontalScrollBar) {
|
if (defaultSettings.horizontalScrollBar) {
|
||||||
binding.mangaReaderSliderContainer.updateLayoutParams {
|
binding.mangaReaderSliderContainer.updateLayoutParams {
|
||||||
height = ViewGroup.LayoutParams.WRAP_CONTENT
|
height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
width = ViewGroup.LayoutParams.WRAP_CONTENT
|
width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
@ -790,7 +782,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.mangaReaderSlider.layoutDirection =
|
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
|
View.LAYOUT_DIRECTION_RTL
|
||||||
else View.LAYOUT_DIRECTION_LTR
|
else View.LAYOUT_DIRECTION_LTR
|
||||||
shouldShow?.apply { isContVisible = !this }
|
shouldShow?.apply { isContVisible = !this }
|
||||||
|
@ -828,9 +820,9 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
fun updatePageNumber(page: Long) {
|
fun updatePageNumber(page: Long) {
|
||||||
if (currentChapterPage != page) {
|
if (currentChapterPage != page) {
|
||||||
currentChapterPage = page
|
currentChapterPage = page
|
||||||
saveData("${media.id}_${chapter.number}", page, this)
|
PrefManager.setCustomVal("${media.id}_${chapter.number}", page)
|
||||||
binding.mangaReaderPageNumber.text =
|
binding.mangaReaderPageNumber.text =
|
||||||
if (settings.default.hidePageNumbers) "" else "${currentChapterPage}/$maxChapterPage"
|
if (defaultSettings.hidePageNumbers) "" else "${currentChapterPage}/$maxChapterPage"
|
||||||
if (!sliding) binding.mangaReaderSlider.apply {
|
if (!sliding) binding.mangaReaderSlider.apply {
|
||||||
value = clamp(currentChapterPage.toFloat(), 1f, valueTo)
|
value = clamp(currentChapterPage.toFloat(), 1f, valueTo)
|
||||||
}
|
}
|
||||||
|
@ -851,31 +843,27 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
private fun progress(runnable: Runnable) {
|
private fun progress(runnable: Runnable) {
|
||||||
if (maxChapterPage - currentChapterPage <= 1 && Anilist.userid != null) {
|
if (maxChapterPage - currentChapterPage <= 1 && Anilist.userid != null) {
|
||||||
showProgressDialog =
|
showProgressDialog =
|
||||||
if (settings.askIndividual) loadData<Boolean>("${media.id}_progressDialog")
|
if (PrefManager.getVal(PrefName.AskIndividualReader)) PrefManager.getCustomVal(
|
||||||
?: true else false
|
"${media.id}_progressDialog",
|
||||||
if (showProgressDialog) {
|
true
|
||||||
|
)
|
||||||
|
else false
|
||||||
|
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
|
||||||
|
if (showProgressDialog && !incognito) {
|
||||||
|
|
||||||
val dialogView = layoutInflater.inflate(R.layout.item_custom_dialog, null)
|
val dialogView = layoutInflater.inflate(R.layout.item_custom_dialog, null)
|
||||||
val checkbox = dialogView.findViewById<CheckBox>(R.id.dialog_checkbox)
|
val checkbox = dialogView.findViewById<CheckBox>(R.id.dialog_checkbox)
|
||||||
checkbox.text = getString(R.string.dont_ask_again, media.userPreferredName)
|
checkbox.text = getString(R.string.dont_ask_again, media.userPreferredName)
|
||||||
checkbox.setOnCheckedChangeListener { _, isChecked ->
|
checkbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
saveData("${media.id}_progressDialog", !isChecked)
|
PrefManager.setCustomVal("${media.id}_progressDialog", !isChecked)
|
||||||
showProgressDialog = !isChecked
|
showProgressDialog = !isChecked
|
||||||
}
|
}
|
||||||
val incognito =
|
|
||||||
currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
|
||||||
?.getBoolean("incognito", false) ?: false
|
|
||||||
AlertDialog.Builder(this, R.style.MyPopup)
|
AlertDialog.Builder(this, R.style.MyPopup)
|
||||||
.setTitle(getString(R.string.title_update_progress))
|
.setTitle(getString(R.string.title_update_progress))
|
||||||
.apply {
|
|
||||||
if (incognito) {
|
|
||||||
setMessage(getString(R.string.incognito_will_not_update))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.setView(dialogView)
|
.setView(dialogView)
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
|
.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
|
||||||
saveData("${media.id}_save_progress", true)
|
PrefManager.setCustomVal("${media.id}_save_progress", true)
|
||||||
updateProgress(
|
updateProgress(
|
||||||
media,
|
media,
|
||||||
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
|
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
|
||||||
|
@ -885,7 +873,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
runnable.run()
|
runnable.run()
|
||||||
}
|
}
|
||||||
.setNegativeButton(getString(R.string.no)) { dialog, _ ->
|
.setNegativeButton(getString(R.string.no)) { dialog, _ ->
|
||||||
saveData("${media.id}_save_progress", false)
|
PrefManager.setCustomVal("${media.id}_save_progress", false)
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
runnable.run()
|
runnable.run()
|
||||||
}
|
}
|
||||||
|
@ -893,7 +881,11 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
.create()
|
.create()
|
||||||
.show()
|
.show()
|
||||||
} else {
|
} else {
|
||||||
if (loadData<Boolean>("${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<Boolean>(PrefName.UpdateForHReader) else true
|
||||||
|
)
|
||||||
updateProgress(
|
updateProgress(
|
||||||
media,
|
media,
|
||||||
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
|
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
|
||||||
|
@ -906,6 +898,51 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private fun <T> 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<CrashlyticsInterface>().log("Failed to delete file $fileName")
|
||||||
|
Injekt.get<CrashlyticsInterface>().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? {
|
fun getTransformation(mangaImage: MangaImage): BitmapTransformation? {
|
||||||
return model.loadTransformation(mangaImage, media.selected!!.sourceIndex)
|
return model.loadTransformation(mangaImage, media.selected!!.sourceIndex)
|
||||||
}
|
}
|
||||||
|
@ -916,7 +953,7 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
img2: MangaImage?,
|
img2: MangaImage?,
|
||||||
callback: ((ImageViewDialog) -> Unit)? = null
|
callback: ((ImageViewDialog) -> Unit)? = null
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (!settings.default.longClickImage) return false
|
if (!defaultSettings.longClickImage) return false
|
||||||
val title = "(Page ${pos + 1}${if (img2 != null) "-${pos + 2}" else ""}) ${
|
val title = "(Page ${pos + 1}${if (img2 != null) "-${pos + 2}" else ""}) ${
|
||||||
chaptersTitleArr.getOrNull(currentChapterIndex)?.replace(" : ", " - ") ?: ""
|
chaptersTitleArr.getOrNull(currentChapterIndex)?.replace(" : ", " - ") ?: ""
|
||||||
} [${media.userPreferredName}]"
|
} [${media.userPreferredName}]"
|
||||||
|
@ -930,8 +967,8 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||||
val parserTransformation2 = getTransformation(img2)
|
val parserTransformation2 = getTransformation(img2)
|
||||||
if (parserTransformation2 != null) transforms2.add(parserTransformation2)
|
if (parserTransformation2 != null) transforms2.add(parserTransformation2)
|
||||||
}
|
}
|
||||||
val threshold = settings.default.cropBorderThreshold
|
val threshold = defaultSettings.cropBorderThreshold
|
||||||
if (settings.default.cropBorders) {
|
if (defaultSettings.cropBorders) {
|
||||||
transforms1.add(RemoveBordersTransformation(true, threshold))
|
transforms1.add(RemoveBordersTransformation(true, threshold))
|
||||||
transforms1.add(RemoveBordersTransformation(false, threshold))
|
transforms1.add(RemoveBordersTransformation(false, threshold))
|
||||||
if (img2 != null) {
|
if (img2 != null) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ class ReaderSettingsDialogFragment : BottomSheetDialogFragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val activity = requireActivity() as MangaReaderActivity
|
val activity = requireActivity() as MangaReaderActivity
|
||||||
val settings = activity.settings.default
|
val settings = activity.defaultSettings
|
||||||
|
|
||||||
binding.readerDirectionText.text =
|
binding.readerDirectionText.text =
|
||||||
resources.getStringArray(R.array.manga_directions)[settings.direction.ordinal]
|
resources.getStringArray(R.array.manga_directions)[settings.direction.ordinal]
|
||||||
|
|
|
@ -26,14 +26,11 @@ import ani.dantotsu.download.DownloadedType
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.download.novel.NovelDownloaderService
|
import ani.dantotsu.download.novel.NovelDownloaderService
|
||||||
import ani.dantotsu.download.novel.NovelServiceDataSingleton
|
import ani.dantotsu.download.novel.NovelServiceDataSingleton
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.media.MediaDetailsViewModel
|
import ani.dantotsu.media.MediaDetailsViewModel
|
||||||
import ani.dantotsu.media.novel.novelreader.NovelReaderActivity
|
import ani.dantotsu.media.novel.novelreader.NovelReaderActivity
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.parsers.ShowResponse
|
import ani.dantotsu.parsers.ShowResponse
|
||||||
import ani.dantotsu.saveData
|
|
||||||
import ani.dantotsu.settings.UserInterfaceSettings
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -61,9 +58,6 @@ class NovelReadFragment : Fragment(),
|
||||||
private var continueEp: Boolean = false
|
private var continueEp: Boolean = false
|
||||||
var loaded = false
|
var loaded = false
|
||||||
|
|
||||||
val uiSettings = loadData("ui_settings", toast = false)
|
|
||||||
?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
|
|
||||||
|
|
||||||
override fun downloadTrigger(novelDownloadPackage: NovelDownloadPackage) {
|
override fun downloadTrigger(novelDownloadPackage: NovelDownloadPackage) {
|
||||||
Log.e("downloadTrigger", novelDownloadPackage.link)
|
Log.e("downloadTrigger", novelDownloadPackage.link)
|
||||||
val downloadTask = NovelDownloaderService.DownloadTask(
|
val downloadTask = NovelDownloaderService.DownloadTask(
|
||||||
|
@ -253,7 +247,7 @@ class NovelReadFragment : Fragment(),
|
||||||
if (save) {
|
if (save) {
|
||||||
val selected = model.loadSelected(media)
|
val selected = model.loadSelected(media)
|
||||||
selected.server = query
|
selected.server = query
|
||||||
model.saveSelected(media.id, selected, requireActivity())
|
model.saveSelected(media.id, selected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,7 +257,7 @@ class NovelReadFragment : Fragment(),
|
||||||
selected.sourceIndex = i
|
selected.sourceIndex = i
|
||||||
source = i
|
source = i
|
||||||
selected.server = null
|
selected.server = null
|
||||||
model.saveSelected(media.id, selected, requireActivity())
|
model.saveSelected(media.id, selected)
|
||||||
media.selected = selected
|
media.selected = selected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ class NovelResponseAdapter(
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val binding = holder.binding
|
val binding = holder.binding
|
||||||
val novel = list[position]
|
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 }
|
val cover = GlideUrl(novel.coverUrl.url) { novel.coverUrl.headers }
|
||||||
Glide.with(binding.itemEpisodeImage).load(cover).override(400, 0)
|
Glide.with(binding.itemEpisodeImage).load(cover).override(400, 0)
|
||||||
|
@ -104,7 +104,7 @@ class NovelResponseAdapter(
|
||||||
binding.root.setOnLongClickListener {
|
binding.root.setOnLongClickListener {
|
||||||
val builder = androidx.appcompat.app.AlertDialog.Builder(
|
val builder = androidx.appcompat.app.AlertDialog.Builder(
|
||||||
fragment.requireContext(),
|
fragment.requireContext(),
|
||||||
R.style.DialogTheme
|
R.style.MyPopup
|
||||||
)
|
)
|
||||||
builder.setTitle("Delete ${novel.name}?")
|
builder.setTitle("Delete ${novel.name}?")
|
||||||
builder.setMessage("Are you sure you want to delete ${novel.name}?")
|
builder.setMessage("Are you sure you want to delete ${novel.name}?")
|
||||||
|
|
|
@ -2,6 +2,7 @@ package ani.dantotsu.media.novel.novelreader
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
@ -28,17 +29,16 @@ import androidx.webkit.WebViewCompat
|
||||||
import ani.dantotsu.GesturesListener
|
import ani.dantotsu.GesturesListener
|
||||||
import ani.dantotsu.NoPaddingArrayAdapter
|
import ani.dantotsu.NoPaddingArrayAdapter
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.databinding.ActivityNovelReaderBinding
|
import ani.dantotsu.databinding.ActivityNovelReaderBinding
|
||||||
import ani.dantotsu.hideSystemBars
|
import ani.dantotsu.hideSystemBars
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.others.ImageViewDialog
|
import ani.dantotsu.others.ImageViewDialog
|
||||||
import ani.dantotsu.others.LangSet
|
|
||||||
import ani.dantotsu.saveData
|
|
||||||
import ani.dantotsu.setSafeOnClickListener
|
import ani.dantotsu.setSafeOnClickListener
|
||||||
import ani.dantotsu.settings.CurrentNovelReaderSettings
|
import ani.dantotsu.settings.CurrentNovelReaderSettings
|
||||||
import ani.dantotsu.settings.CurrentReaderSettings
|
import ani.dantotsu.settings.CurrentReaderSettings
|
||||||
import ani.dantotsu.settings.ReaderSettings
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.UserInterfaceSettings
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.snackString
|
import ani.dantotsu.snackString
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import ani.dantotsu.tryWith
|
import ani.dantotsu.tryWith
|
||||||
|
@ -52,8 +52,13 @@ import com.vipulog.ebookreader.RelocationInfo
|
||||||
import com.vipulog.ebookreader.TocItem
|
import com.vipulog.ebookreader.TocItem
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
import java.io.ObjectInputStream
|
||||||
|
import java.io.ObjectOutputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
@ -63,9 +68,6 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
private lateinit var binding: ActivityNovelReaderBinding
|
private lateinit var binding: ActivityNovelReaderBinding
|
||||||
private val scope = lifecycleScope
|
private val scope = lifecycleScope
|
||||||
|
|
||||||
lateinit var settings: ReaderSettings
|
|
||||||
private lateinit var uiSettings: UserInterfaceSettings
|
|
||||||
|
|
||||||
private var notchHeight: Int? = null
|
private var notchHeight: Int? = null
|
||||||
|
|
||||||
var loaded = false
|
var loaded = false
|
||||||
|
@ -78,6 +80,8 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
|
|
||||||
val themes = ArrayList<ReaderTheme>()
|
val themes = ArrayList<ReaderTheme>()
|
||||||
|
|
||||||
|
var defaultSettings = CurrentNovelReaderSettings()
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val forestTheme = ReaderTheme(
|
val forestTheme = ReaderTheme(
|
||||||
|
@ -171,16 +175,12 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityNovelReaderBinding.inflate(layoutInflater)
|
binding = ActivityNovelReaderBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
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<Float>(PrefName.AnimationSpeed) * 200).toLong()
|
||||||
|
|
||||||
setupViews()
|
setupViews()
|
||||||
setupBackPressedHandler()
|
setupBackPressedHandler()
|
||||||
|
@ -286,12 +286,12 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
binding.bookReader.getAppearance {
|
binding.bookReader.getAppearance {
|
||||||
currentTheme = it
|
currentTheme = it
|
||||||
themes.add(0, it)
|
themes.add(0, it)
|
||||||
settings.defaultLN =
|
defaultSettings =
|
||||||
loadData("${sanitizedBookId}_current_settings") ?: settings.defaultLN
|
loadReaderSettings("${sanitizedBookId}_current_settings") ?: defaultSettings
|
||||||
applySettings()
|
applySettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
val cfi = loadData<String>("${sanitizedBookId}_progress")
|
val cfi = PrefManager.getCustomVal("${sanitizedBookId}_progress", null as String?)
|
||||||
|
|
||||||
cfi?.let { binding.bookReader.goto(it) }
|
cfi?.let { binding.bookReader.goto(it) }
|
||||||
binding.progress.visibility = View.GONE
|
binding.progress.visibility = View.GONE
|
||||||
|
@ -304,7 +304,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
binding.novelReaderSlider.value = info.fraction.toFloat()
|
binding.novelReaderSlider.value = info.fraction.toFloat()
|
||||||
val pos = info.tocItem?.let { item -> toc.indexOfFirst { it == item } }
|
val pos = info.tocItem?.let { item -> toc.indexOfFirst { it == item } }
|
||||||
if (pos != null) binding.novelReaderChapterSelect.setSelection(pos)
|
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) {
|
return when (event.keyCode) {
|
||||||
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_PAGE_UP -> {
|
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_PAGE_UP -> {
|
||||||
if (event.keyCode == KeyEvent.KEYCODE_VOLUME_UP)
|
if (event.keyCode == KeyEvent.KEYCODE_VOLUME_UP)
|
||||||
if (!settings.defaultLN.volumeButtons)
|
if (!defaultSettings.volumeButtons)
|
||||||
return false
|
return false
|
||||||
if (event.action == KeyEvent.ACTION_DOWN) {
|
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||||
onVolumeUp?.invoke()
|
onVolumeUp?.invoke()
|
||||||
|
@ -349,7 +349,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
|
|
||||||
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_PAGE_DOWN -> {
|
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_PAGE_DOWN -> {
|
||||||
if (event.keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
|
if (event.keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
|
||||||
if (!settings.defaultLN.volumeButtons)
|
if (!defaultSettings.volumeButtons)
|
||||||
return false
|
return false
|
||||||
if (event.action == KeyEvent.ACTION_DOWN) {
|
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||||
onVolumeDown?.invoke()
|
onVolumeDown?.invoke()
|
||||||
|
@ -365,18 +365,18 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
|
|
||||||
|
|
||||||
fun applySettings() {
|
fun applySettings() {
|
||||||
saveData("${sanitizedBookId}_current_settings", settings.defaultLN)
|
saveReaderSettings("${sanitizedBookId}_current_settings", defaultSettings)
|
||||||
hideBars()
|
hideBars()
|
||||||
|
|
||||||
if (settings.defaultLN.useOledTheme) {
|
if (defaultSettings.useOledTheme) {
|
||||||
themes.forEach { theme ->
|
themes.forEach { theme ->
|
||||||
theme.darkBg = Color.parseColor("#000000")
|
theme.darkBg = Color.parseColor("#000000")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentTheme =
|
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 -> {
|
CurrentNovelReaderSettings.Layouts.PAGED -> {
|
||||||
currentTheme?.flow = ReaderFlow.PAGINATED
|
currentTheme?.flow = ReaderFlow.PAGINATED
|
||||||
}
|
}
|
||||||
|
@ -387,22 +387,22 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
|
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
|
||||||
when (settings.defaultLN.dualPageMode) {
|
when (defaultSettings.dualPageMode) {
|
||||||
CurrentReaderSettings.DualPageModes.No -> currentTheme?.maxColumnCount = 1
|
CurrentReaderSettings.DualPageModes.No -> currentTheme?.maxColumnCount = 1
|
||||||
CurrentReaderSettings.DualPageModes.Automatic -> currentTheme?.maxColumnCount = 2
|
CurrentReaderSettings.DualPageModes.Automatic -> currentTheme?.maxColumnCount = 2
|
||||||
CurrentReaderSettings.DualPageModes.Force -> requestedOrientation =
|
CurrentReaderSettings.DualPageModes.Force -> requestedOrientation =
|
||||||
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTheme?.lineHeight = settings.defaultLN.lineHeight
|
currentTheme?.lineHeight = defaultSettings.lineHeight
|
||||||
currentTheme?.gap = settings.defaultLN.margin
|
currentTheme?.gap = defaultSettings.margin
|
||||||
currentTheme?.maxInlineSize = settings.defaultLN.maxInlineSize
|
currentTheme?.maxInlineSize = defaultSettings.maxInlineSize
|
||||||
currentTheme?.maxBlockSize = settings.defaultLN.maxBlockSize
|
currentTheme?.maxBlockSize = defaultSettings.maxBlockSize
|
||||||
currentTheme?.useDark = settings.defaultLN.useDarkTheme
|
currentTheme?.useDark = defaultSettings.useDarkTheme
|
||||||
|
|
||||||
currentTheme?.let { binding.bookReader.setAppearance(it) }
|
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)
|
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +432,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
fun handleController(shouldShow: Boolean? = null) {
|
fun handleController(shouldShow: Boolean? = null) {
|
||||||
if (!loaded) return
|
if (!loaded) return
|
||||||
|
|
||||||
if (!settings.showSystemBars) {
|
if (!PrefManager.getVal<Boolean>(PrefName.ShowSystemBars)) {
|
||||||
hideBars()
|
hideBars()
|
||||||
applyNotchMargin()
|
applyNotchMargin()
|
||||||
}
|
}
|
||||||
|
@ -465,7 +465,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
|
|
||||||
|
|
||||||
private fun checkNotch() {
|
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<Boolean>(PrefName.ShowSystemBars)) {
|
||||||
val displayCutout = window.decorView.rootWindowInsets.displayCutout
|
val displayCutout = window.decorView.rootWindowInsets.displayCutout
|
||||||
if (displayCutout != null) {
|
if (displayCutout != null) {
|
||||||
if (displayCutout.boundingRects.size > 0) {
|
if (displayCutout.boundingRects.size > 0) {
|
||||||
|
@ -486,8 +486,53 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private fun <T> 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<CrashlyticsInterface>().log("Failed to delete file $fileName")
|
||||||
|
Injekt.get<CrashlyticsInterface>().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() {
|
private fun hideBars() {
|
||||||
if (!settings.showSystemBars) hideSystemBars()
|
if (!PrefManager.getVal<Boolean>(PrefName.ShowSystemBars)) {
|
||||||
|
hideSystemBars()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,7 +30,7 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val activity = requireActivity() as NovelReaderActivity
|
val activity = requireActivity() as NovelReaderActivity
|
||||||
val settings = activity.settings.defaultLN
|
val settings = activity.defaultSettings
|
||||||
val themeLabels = activity.themes.map { it.name }
|
val themeLabels = activity.themes.map { it.name }
|
||||||
binding.themeSelect.adapter =
|
binding.themeSelect.adapter =
|
||||||
NoPaddingArrayAdapter(activity, R.layout.item_dropdown, themeLabels)
|
NoPaddingArrayAdapter(activity, R.layout.item_dropdown, themeLabels)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ani.dantotsu.media.user
|
package ani.dantotsu.media.user
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -17,12 +16,10 @@ import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.Refresh
|
import ani.dantotsu.Refresh
|
||||||
import ani.dantotsu.currContext
|
|
||||||
import ani.dantotsu.databinding.ActivityListBinding
|
import ani.dantotsu.databinding.ActivityListBinding
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.others.LangSet
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.UserInterfaceSettings
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
|
@ -39,7 +36,7 @@ class ListActivity : AppCompatActivity() {
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityListBinding.inflate(layoutInflater)
|
binding = ActivityListBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
@ -67,8 +64,7 @@ class ListActivity : AppCompatActivity() {
|
||||||
binding.listTitle.setTextColor(primaryTextColor)
|
binding.listTitle.setTextColor(primaryTextColor)
|
||||||
binding.listTabLayout.setTabTextColors(secondaryTextColor, primaryTextColor)
|
binding.listTabLayout.setTabTextColors(secondaryTextColor, primaryTextColor)
|
||||||
binding.listTabLayout.setSelectedTabIndicatorColor(primaryTextColor)
|
binding.listTabLayout.setSelectedTabIndicatorColor(primaryTextColor)
|
||||||
val uiSettings = loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
|
if (!PrefManager.getVal<Boolean>(PrefName.ImmersiveMode)) {
|
||||||
if (!uiSettings.immersiveMode) {
|
|
||||||
this.window.statusBarColor =
|
this.window.statusBarColor =
|
||||||
ContextCompat.getColor(this, R.color.nav_bg_inv)
|
ContextCompat.getColor(this, R.color.nav_bg_inv)
|
||||||
binding.root.fitsSystemWindows = true
|
binding.root.fitsSystemWindows = true
|
||||||
|
@ -154,8 +150,10 @@ class ListActivity : AppCompatActivity() {
|
||||||
R.id.release -> "release"
|
R.id.release -> "release"
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
|
PrefManager.setVal(
|
||||||
?.putString("sort_order", sort)?.apply()
|
if (anime) PrefName.AnimeListSortOrder else PrefName.MangaListSortOrder,
|
||||||
|
sort ?: ""
|
||||||
|
)
|
||||||
binding.listProgressBar.visibility = View.VISIBLE
|
binding.listProgressBar.visibility = View.VISIBLE
|
||||||
binding.listViewPager.adapter = null
|
binding.listViewPager.adapter = null
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
|
|
@ -4,12 +4,13 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.tryWithSuspend
|
import ani.dantotsu.tryWithSuspend
|
||||||
|
|
||||||
class ListViewModel : ViewModel() {
|
class ListViewModel : ViewModel() {
|
||||||
var grid = MutableLiveData(loadData<Boolean>("listGrid") ?: true)
|
var grid = MutableLiveData(PrefManager.getVal<Boolean>(PrefName.ListGrid))
|
||||||
|
|
||||||
private val lists = MutableLiveData<MutableMap<String, ArrayList<Media>>>()
|
private val lists = MutableLiveData<MutableMap<String, ArrayList<Media>>>()
|
||||||
fun getLists(): LiveData<MutableMap<String, ArrayList<Media>>> = lists
|
fun getLists(): LiveData<MutableMap<String, ArrayList<Media>>> = lists
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package ani.dantotsu.offline
|
package ani.dantotsu.offline
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -11,6 +10,8 @@ import ani.dantotsu.R
|
||||||
import ani.dantotsu.databinding.FragmentOfflineBinding
|
import ani.dantotsu.databinding.FragmentOfflineBinding
|
||||||
import ani.dantotsu.isOnline
|
import ani.dantotsu.isOnline
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.startMainActivity
|
import ani.dantotsu.startMainActivity
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
|
|
||||||
|
@ -26,8 +27,7 @@ class OfflineFragment : Fragment() {
|
||||||
topMargin = statusBarHeight
|
topMargin = statusBarHeight
|
||||||
bottomMargin = navBarHeight
|
bottomMargin = navBarHeight
|
||||||
}
|
}
|
||||||
offline = requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
offline = PrefManager.getVal(PrefName.OfflineMode)
|
||||||
?.getBoolean("offlineMode", false) ?: false
|
|
||||||
binding.noInternet.text =
|
binding.noInternet.text =
|
||||||
if (offline) "Offline Mode" else getString(R.string.no_internet)
|
if (offline) "Offline Mode" else getString(R.string.no_internet)
|
||||||
binding.refreshButton.visibility = if (offline) View.GONE else View.VISIBLE
|
binding.refreshButton.visibility = if (offline) View.GONE else View.VISIBLE
|
||||||
|
@ -41,7 +41,6 @@ class OfflineFragment : Fragment() {
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
offline = requireContext().getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
offline = PrefManager.getVal(PrefName.OfflineMode)
|
||||||
?.getBoolean("offlineMode", false) ?: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,12 +2,10 @@ package ani.dantotsu.others
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import ani.dantotsu.BottomSheetDialogFragment
|
import ani.dantotsu.BottomSheetDialogFragment
|
||||||
import ani.dantotsu.R
|
|
||||||
import ani.dantotsu.databinding.BottomSheetCustomBinding
|
import ani.dantotsu.databinding.BottomSheetCustomBinding
|
||||||
|
|
||||||
open class CustomBottomDialog : BottomSheetDialogFragment() {
|
open class CustomBottomDialog : BottomSheetDialogFragment() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package ani.dantotsu.others
|
package ani.dantotsu.others
|
||||||
|
|
||||||
const val DisabledReports = false
|
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
|
//if you want a custom build without crash reporting, set this to true
|
|
@ -14,9 +14,10 @@ import ani.dantotsu.FileUrl
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.defaultHeaders
|
import ani.dantotsu.defaultHeaders
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.media.anime.Episode
|
import ani.dantotsu.media.anime.Episode
|
||||||
import ani.dantotsu.parsers.Book
|
import ani.dantotsu.parsers.Book
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.toast
|
import ani.dantotsu.toast
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -36,7 +37,7 @@ object Download {
|
||||||
|
|
||||||
private fun getDownloadDir(context: Context): File {
|
private fun getDownloadDir(context: Context): File {
|
||||||
val direct: File
|
val direct: File
|
||||||
if (loadData<Boolean>("sd_dl") == true) {
|
if (PrefManager.getVal(PrefName.SdDl)) {
|
||||||
val arrayOfFiles = ContextCompat.getExternalFilesDirs(context, null)
|
val arrayOfFiles = ContextCompat.getExternalFilesDirs(context, null)
|
||||||
val parentDirectory = arrayOfFiles[1].toString()
|
val parentDirectory = arrayOfFiles[1].toString()
|
||||||
direct = File(parentDirectory)
|
direct = File(parentDirectory)
|
||||||
|
@ -92,7 +93,7 @@ object Download {
|
||||||
if (!file.url.startsWith("http"))
|
if (!file.url.startsWith("http"))
|
||||||
toast(context.getString(R.string.invalid_url))
|
toast(context.getString(R.string.invalid_url))
|
||||||
else
|
else
|
||||||
when (loadData<Int>("settings_download_manager", context, false) ?: 0) {
|
when (PrefManager.getVal(PrefName.DownloadManager) as Int) {
|
||||||
1 -> oneDM(context, file, notif ?: fileName)
|
1 -> oneDM(context, file, notif ?: fileName)
|
||||||
2 -> adm(context, file, fileName, folder)
|
2 -> adm(context, file, fileName, folder)
|
||||||
else -> defaultDownload(context, file, fileName, folder, notif ?: fileName)
|
else -> defaultDownload(context, file, fileName, folder, notif ?: fileName)
|
||||||
|
@ -117,7 +118,7 @@ object Download {
|
||||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||||
|
|
||||||
val arrayOfFiles = ContextCompat.getExternalFilesDirs(context, null)
|
val arrayOfFiles = ContextCompat.getExternalFilesDirs(context, null)
|
||||||
if (loadData<Boolean>("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 parentDirectory = arrayOfFiles[1].toString() + folder
|
||||||
val direct = File(parentDirectory)
|
val direct = File(parentDirectory)
|
||||||
if (!direct.exists()) direct.mkdirs()
|
if (!direct.exists()) direct.mkdirs()
|
||||||
|
|
|
@ -5,12 +5,14 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import ani.dantotsu.BottomSheetDialogFragment
|
import ani.dantotsu.BottomSheetDialogFragment
|
||||||
import ani.dantotsu.FileUrl
|
import ani.dantotsu.FileUrl
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.databinding.BottomSheetImageBinding
|
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
|
||||||
import ani.dantotsu.media.manga.mangareader.BaseImageAdapter.Companion.loadBitmap_old
|
import ani.dantotsu.media.manga.mangareader.BaseImageAdapter.Companion.loadBitmap_old
|
||||||
import ani.dantotsu.media.manga.mangareader.BaseImageAdapter.Companion.mergeBitmap
|
import ani.dantotsu.media.manga.mangareader.BaseImageAdapter.Companion.mergeBitmap
|
||||||
|
@ -98,7 +100,8 @@ class ImageViewDialog : BottomSheetDialogFragment() {
|
||||||
binding.bottomImageShare.isEnabled = true
|
binding.bottomImageShare.isEnabled = true
|
||||||
binding.bottomImageSave.isEnabled = true
|
binding.bottomImageSave.isEnabled = true
|
||||||
binding.bottomImageSave.setOnClickListener {
|
binding.bottomImageSave.setOnClickListener {
|
||||||
saveImageToDownloads(title, bitmap, requireActivity())
|
if (downloadsPermission(context as AppCompatActivity))
|
||||||
|
saveImageToDownloads(title, bitmap, requireActivity())
|
||||||
}
|
}
|
||||||
binding.bottomImageShare.setOnClickListener {
|
binding.bottomImageShare.setOnClickListener {
|
||||||
shareImage(title, bitmap, requireContext())
|
shareImage(title, bitmap, requireContext())
|
||||||
|
|
|
@ -96,6 +96,7 @@ query {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
res?.body?.string()
|
res?.body?.string()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,7 @@
|
||||||
package ani.dantotsu.others
|
package ani.dantotsu.others
|
||||||
|
|
||||||
import ani.dantotsu.R
|
|
||||||
import ani.dantotsu.client
|
import ani.dantotsu.client
|
||||||
import ani.dantotsu.currContext
|
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.snackString
|
|
||||||
import kotlinx.coroutines.TimeoutCancellationException
|
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
|
|
||||||
object MalScraper {
|
object MalScraper {
|
||||||
|
@ -50,7 +46,7 @@ object MalScraper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import ani.dantotsu.databinding.ActivityImageSearchBinding
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
import ani.dantotsu.media.MediaDetailsActivity
|
import ani.dantotsu.media.MediaDetailsActivity
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.others.LangSet
|
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import ani.dantotsu.toast
|
import ani.dantotsu.toast
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -52,7 +51,7 @@ class ImageSearchActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
|
||||||
initActivity(this)
|
initActivity(this)
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
binding = ActivityImageSearchBinding.inflate(layoutInflater)
|
binding = ActivityImageSearchBinding.inflate(layoutInflater)
|
||||||
|
|
|
@ -5,13 +5,11 @@ import ani.dantotsu.FileUrl
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.asyncMap
|
import ani.dantotsu.asyncMap
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.others.MalSyncBackup
|
import ani.dantotsu.others.MalSyncBackup
|
||||||
import ani.dantotsu.saveData
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.tryWithSuspend
|
import ani.dantotsu.tryWithSuspend
|
||||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||||
import kotlin.properties.Delegates
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class for creating a new Source
|
* 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**
|
* **NOTE : do not forget to override `search` if the site does not support only dub search**
|
||||||
* **/
|
* **/
|
||||||
open val isDubAvailableSeparately by Delegates.notNull<Boolean>()
|
open fun isDubAvailableSeparately(sourceLang: Int? = null): Boolean = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The app changes this, depending on user's choice.
|
* The app changes this, depending on user's choice.
|
||||||
|
@ -182,8 +180,12 @@ abstract class AnimeParser : BaseParser() {
|
||||||
* **/
|
* **/
|
||||||
override suspend fun loadSavedShowResponse(mediaId: Int): ShowResponse? {
|
override suspend fun loadSavedShowResponse(mediaId: Int): ShowResponse? {
|
||||||
checkIfVariablesAreEmpty()
|
checkIfVariablesAreEmpty()
|
||||||
val dub = if (isDubAvailableSeparately) "_${if (selectDub) "dub" else "sub"}" else ""
|
val dub = if (isDubAvailableSeparately()) "_${if (selectDub) "dub" else "sub"}" else ""
|
||||||
var loaded = loadData<ShowResponse>("${saveName}${dub}_$mediaId")
|
var loaded = PrefManager.getNullableCustomVal(
|
||||||
|
"${saveName}${dub}_$mediaId",
|
||||||
|
null,
|
||||||
|
ShowResponse::class.java
|
||||||
|
)
|
||||||
if (loaded == null && malSyncBackupName.isNotEmpty())
|
if (loaded == null && malSyncBackupName.isNotEmpty())
|
||||||
loaded = MalSyncBackup.get(mediaId, malSyncBackupName, selectDub)
|
loaded = MalSyncBackup.get(mediaId, malSyncBackupName, selectDub)
|
||||||
?.also { saveShowResponse(mediaId, it, true) }
|
?.also { saveShowResponse(mediaId, it, true) }
|
||||||
|
@ -200,8 +202,8 @@ abstract class AnimeParser : BaseParser() {
|
||||||
)
|
)
|
||||||
} : ${response.name}"
|
} : ${response.name}"
|
||||||
)
|
)
|
||||||
val dub = if (isDubAvailableSeparately) "_${if (selectDub) "dub" else "sub"}" else ""
|
val dub = if (isDubAvailableSeparately()) "_${if (selectDub) "dub" else "sub"}" else ""
|
||||||
saveData("${saveName}${dub}_$mediaId", response)
|
PrefManager.setCustomVal("${saveName}${dub}_$mediaId", response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,8 +211,6 @@ abstract class AnimeParser : BaseParser() {
|
||||||
class EmptyAnimeParser : AnimeParser() {
|
class EmptyAnimeParser : AnimeParser() {
|
||||||
override val name: String = "None"
|
override val name: String = "None"
|
||||||
override val saveName: String = "None"
|
override val saveName: String = "None"
|
||||||
|
|
||||||
override val isDubAvailableSeparately: Boolean = false
|
|
||||||
override suspend fun loadEpisodes(
|
override suspend fun loadEpisodes(
|
||||||
animeLink: String,
|
animeLink: String,
|
||||||
extra: Map<String, String>?,
|
extra: Map<String, String>?,
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
package ani.dantotsu.parsers
|
package ani.dantotsu.parsers
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import ani.dantotsu.Lazier
|
import ani.dantotsu.Lazier
|
||||||
import ani.dantotsu.lazyList
|
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 eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
object AnimeSources : WatchSources() {
|
object AnimeSources : WatchSources() {
|
||||||
override var list: List<Lazier<BaseParser>> = emptyList()
|
override var list: List<Lazier<BaseParser>> = emptyList()
|
||||||
var pinnedAnimeSources: Set<String> = emptySet()
|
var pinnedAnimeSources: List<String> = emptyList()
|
||||||
|
|
||||||
suspend fun init(fromExtensions: StateFlow<List<AnimeExtension.Installed>>, context: Context) {
|
suspend fun init(fromExtensions: StateFlow<List<AnimeExtension.Installed>>) {
|
||||||
val sharedPrefs = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
pinnedAnimeSources =
|
||||||
pinnedAnimeSources = sharedPrefs.getStringSet("pinned_anime_sources", emptySet()) ?: emptySet()
|
PrefManager.getNullableVal<List<String>>(PrefName.AnimeSourcesOrder, null)
|
||||||
|
?: emptyList()
|
||||||
|
|
||||||
// Initialize with the first value from StateFlow
|
// Initialize with the first value from StateFlow
|
||||||
val initialExtensions = fromExtensions.first()
|
val initialExtensions = fromExtensions.first()
|
||||||
|
@ -24,7 +26,10 @@ object AnimeSources : WatchSources() {
|
||||||
|
|
||||||
// Update as StateFlow emits new values
|
// Update as StateFlow emits new values
|
||||||
fromExtensions.collect { extensions ->
|
fromExtensions.collect { extensions ->
|
||||||
list = sortPinnedAnimeSources(createParsersFromExtensions(extensions), pinnedAnimeSources) + Lazier(
|
list = sortPinnedAnimeSources(
|
||||||
|
createParsersFromExtensions(extensions),
|
||||||
|
pinnedAnimeSources
|
||||||
|
) + Lazier(
|
||||||
{ OfflineAnimeParser() },
|
{ OfflineAnimeParser() },
|
||||||
"Downloaded"
|
"Downloaded"
|
||||||
)
|
)
|
||||||
|
@ -34,7 +39,7 @@ object AnimeSources : WatchSources() {
|
||||||
fun performReorderAnimeSources() {
|
fun performReorderAnimeSources() {
|
||||||
//remove the downloaded source from the list to avoid duplicates
|
//remove the downloaded source from the list to avoid duplicates
|
||||||
list = list.filter { it.name != "Downloaded" }
|
list = list.filter { it.name != "Downloaded" }
|
||||||
list = sortPinnedAnimeSources(list, pinnedAnimeSources) + Lazier(
|
list = sortPinnedAnimeSources(list, pinnedAnimeSources) + Lazier(
|
||||||
{ OfflineAnimeParser() },
|
{ OfflineAnimeParser() },
|
||||||
"Downloaded"
|
"Downloaded"
|
||||||
)
|
)
|
||||||
|
@ -47,13 +52,17 @@ object AnimeSources : WatchSources() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortPinnedAnimeSources(Sources: List<Lazier<BaseParser>>, pinnedAnimeSources: Set<String>): List<Lazier<BaseParser>> {
|
private fun sortPinnedAnimeSources(
|
||||||
//find the pinned sources
|
sources: List<Lazier<BaseParser>>,
|
||||||
val pinnedSources = Sources.filter { pinnedAnimeSources.contains(it.name) }
|
pinnedAnimeSources: List<String>
|
||||||
//find the unpinned sources
|
): List<Lazier<BaseParser>> {
|
||||||
val unpinnedSources = Sources.filter { !pinnedAnimeSources.contains(it.name) }
|
val pinnedSourcesMap = sources.filter { pinnedAnimeSources.contains(it.name) }
|
||||||
//put the pinned sources at the top of the list
|
.associateBy { it.name }
|
||||||
return pinnedSources + unpinnedSources
|
val orderedPinnedSources = pinnedAnimeSources.mapNotNull { name ->
|
||||||
|
pinnedSourcesMap[name]
|
||||||
|
}
|
||||||
|
val unpinnedSources = sources.filterNot { pinnedAnimeSources.contains(it.name) }
|
||||||
|
return orderedPinnedSources + unpinnedSources
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,14 @@ import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import ani.dantotsu.FileUrl
|
import ani.dantotsu.FileUrl
|
||||||
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.logger
|
import ani.dantotsu.logger
|
||||||
import ani.dantotsu.media.anime.AnimeNameAdapter
|
import ani.dantotsu.media.anime.AnimeNameAdapter
|
||||||
import ani.dantotsu.media.manga.ImageData
|
import ani.dantotsu.media.manga.ImageData
|
||||||
import ani.dantotsu.media.manga.MangaCache
|
import ani.dantotsu.media.manga.MangaCache
|
||||||
import ani.dantotsu.snackString
|
import ani.dantotsu.snackString
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
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.AnimesPage
|
||||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
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.extension.manga.model.MangaExtension
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.network.interceptor.CloudflareBypassException
|
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.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
@ -71,8 +74,78 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
|
||||||
override val name = extension.name
|
override val name = extension.name
|
||||||
override val saveName = extension.name
|
override val saveName = extension.name
|
||||||
override val hostUrl = extension.sources.first().name
|
override val hostUrl = extension.sources.first().name
|
||||||
override val isDubAvailableSeparately = false
|
|
||||||
override val isNSFW = extension.isNsfw
|
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(
|
override suspend fun loadEpisodes(
|
||||||
animeLink: String,
|
animeLink: String,
|
||||||
extra: Map<String, String>?,
|
extra: Map<String, String>?,
|
||||||
|
@ -106,6 +179,8 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
|
||||||
}
|
}
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
|
} else if (episodesAreIncrementing(res)) {
|
||||||
|
res.sortedBy { it.episode_number }
|
||||||
} else {
|
} else {
|
||||||
var episodeCounter = 1f
|
var episodeCounter = 1f
|
||||||
// Group by season, sort within each season, and then renumber while keeping episode number 0 as is
|
// 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 }
|
res.groupBy { AnimeNameAdapter.findSeasonNumber(it.name) ?: 0 }
|
||||||
seasonGroups.keys.sortedBy { it.toInt() }
|
seasonGroups.keys.sortedBy { it.toInt() }
|
||||||
.flatMap { season ->
|
.flatMap { season ->
|
||||||
seasonGroups[season]?.sortedBy { it.episode_number }?.map { episode ->
|
seasonGroups[season]?.sortedBy { it.episode_number }?.map { episode ->
|
||||||
if (episode.episode_number != 0f) { // Skip renumbering for episode number 0
|
if (episode.episode_number != 0f) { // Skip renumbering for episode number 0
|
||||||
val potentialNumber =
|
val potentialNumber =
|
||||||
AnimeNameAdapter.findEpisodeNumber(episode.name)
|
AnimeNameAdapter.findEpisodeNumber(episode.name)
|
||||||
if (potentialNumber != null) {
|
if (potentialNumber != null) {
|
||||||
episode.episode_number = potentialNumber
|
episode.episode_number = potentialNumber
|
||||||
} else {
|
} else {
|
||||||
episode.episode_number = episodeCounter
|
episode.episode_number = episodeCounter
|
||||||
|
}
|
||||||
|
episodeCounter++
|
||||||
}
|
}
|
||||||
episodeCounter++
|
episode
|
||||||
}
|
} ?: emptyList()
|
||||||
episode
|
}
|
||||||
} ?: emptyList()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return sortedEpisodes.map { SEpisodeToEpisode(it) }
|
return sortedEpisodes.map { SEpisodeToEpisode(it) }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -135,6 +210,19 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun episodesAreIncrementing(episodes: List<SEpisode>): Boolean {
|
||||||
|
val sortedEpisodes = episodes.sortedBy { it.episode_number }
|
||||||
|
val takenNumbers = mutableListOf<Float>()
|
||||||
|
sortedEpisodes.forEach {
|
||||||
|
if (it.episode_number !in takenNumbers) {
|
||||||
|
takenNumbers.add(it.episode_number)
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun loadVideoServers(
|
override suspend fun loadVideoServers(
|
||||||
episodeLink: String,
|
episodeLink: String,
|
||||||
extra: Map<String, String>?,
|
extra: Map<String, String>?,
|
||||||
|
@ -177,7 +265,7 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
|
||||||
} catch (e: CloudflareBypassException) {
|
} catch (e: CloudflareBypassException) {
|
||||||
logger("Exception in search: $e")
|
logger("Exception in search: $e")
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
snackString( "Failed to bypass Cloudflare")
|
snackString("Failed to bypass Cloudflare")
|
||||||
}
|
}
|
||||||
emptyList()
|
emptyList()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -626,8 +714,6 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
|
||||||
// If the format is still undetermined, log an error
|
// If the format is still undetermined, log an error
|
||||||
if (format == null) {
|
if (format == null) {
|
||||||
logger("Unknown video format: $videoUrl")
|
logger("Unknown video format: $videoUrl")
|
||||||
//FirebaseCrashlytics.getInstance()
|
|
||||||
// .recordException(Exception("Unknown video format: $videoUrl"))
|
|
||||||
format = VideoType.CONTAINER
|
format = VideoType.CONTAINER
|
||||||
}
|
}
|
||||||
val headersMap: Map<String, String> =
|
val headersMap: Map<String, String> =
|
||||||
|
|
|
@ -3,10 +3,9 @@ package ani.dantotsu.parsers
|
||||||
import ani.dantotsu.FileUrl
|
import ani.dantotsu.FileUrl
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.loadData
|
|
||||||
import ani.dantotsu.logger
|
import ani.dantotsu.logger
|
||||||
import ani.dantotsu.media.Media
|
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.animesource.model.SAnime
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import me.xdrop.fuzzywuzzy.FuzzySearch
|
import me.xdrop.fuzzywuzzy.FuzzySearch
|
||||||
|
@ -135,7 +134,11 @@ abstract class BaseParser {
|
||||||
* **/
|
* **/
|
||||||
open suspend fun loadSavedShowResponse(mediaId: Int): ShowResponse? {
|
open suspend fun loadSavedShowResponse(mediaId: Int): ShowResponse? {
|
||||||
checkIfVariablesAreEmpty()
|
checkIfVariablesAreEmpty()
|
||||||
return loadData("${saveName}_$mediaId")
|
return PrefManager.getNullableCustomVal(
|
||||||
|
"${saveName}_$mediaId",
|
||||||
|
null,
|
||||||
|
ShowResponse::class.java
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -151,7 +154,7 @@ abstract class BaseParser {
|
||||||
)
|
)
|
||||||
} : ${response.name}"
|
} : ${response.name}"
|
||||||
)
|
)
|
||||||
saveData("${saveName}_$mediaId", response)
|
PrefManager.setCustomVal("${saveName}_$mediaId", response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
package ani.dantotsu.parsers
|
package ani.dantotsu.parsers
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import ani.dantotsu.Lazier
|
import ani.dantotsu.Lazier
|
||||||
import ani.dantotsu.lazyList
|
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 eu.kanade.tachiyomi.extension.manga.model.MangaExtension
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
object MangaSources : MangaReadSources() {
|
object MangaSources : MangaReadSources() {
|
||||||
override var list: List<Lazier<BaseParser>> = emptyList()
|
override var list: List<Lazier<BaseParser>> = emptyList()
|
||||||
var pinnedMangaSources: Set<String> = emptySet()
|
var pinnedMangaSources: List<String> = emptyList()
|
||||||
|
|
||||||
suspend fun init(fromExtensions: StateFlow<List<MangaExtension.Installed>>, context: Context) {
|
suspend fun init(fromExtensions: StateFlow<List<MangaExtension.Installed>>) {
|
||||||
val sharedPrefs = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
pinnedMangaSources =
|
||||||
pinnedMangaSources = sharedPrefs.getStringSet("pinned_manga_sources", emptySet()) ?: emptySet()
|
PrefManager.getNullableVal<List<String>>(PrefName.MangaSourcesOrder, null)
|
||||||
|
?: emptyList()
|
||||||
|
|
||||||
// Initialize with the first value from StateFlow
|
// Initialize with the first value from StateFlow
|
||||||
val initialExtensions = fromExtensions.first()
|
val initialExtensions = fromExtensions.first()
|
||||||
|
@ -24,7 +26,10 @@ object MangaSources : MangaReadSources() {
|
||||||
|
|
||||||
// Update as StateFlow emits new values
|
// Update as StateFlow emits new values
|
||||||
fromExtensions.collect { extensions ->
|
fromExtensions.collect { extensions ->
|
||||||
list = sortPinnedMangaSources(createParsersFromExtensions(extensions), pinnedMangaSources) + Lazier(
|
list = sortPinnedMangaSources(
|
||||||
|
createParsersFromExtensions(extensions),
|
||||||
|
pinnedMangaSources
|
||||||
|
) + Lazier(
|
||||||
{ OfflineMangaParser() },
|
{ OfflineMangaParser() },
|
||||||
"Downloaded"
|
"Downloaded"
|
||||||
)
|
)
|
||||||
|
@ -47,21 +52,21 @@ object MangaSources : MangaReadSources() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortPinnedMangaSources(Sources: List<Lazier<BaseParser>>, pinnedMangaSources: Set<String>): List<Lazier<BaseParser>> {
|
private fun sortPinnedMangaSources(
|
||||||
//find the pinned sources
|
sources: List<Lazier<BaseParser>>,
|
||||||
val pinnedSources = Sources.filter { pinnedMangaSources.contains(it.name) }
|
pinnedMangaSources: List<String>
|
||||||
//find the unpinned sources
|
): List<Lazier<BaseParser>> {
|
||||||
val unpinnedSources = Sources.filter { !pinnedMangaSources.contains(it.name) }
|
val pinnedSourcesMap = sources.filter { pinnedMangaSources.contains(it.name) }
|
||||||
//put the pinned sources at the top of the list
|
.associateBy { it.name }
|
||||||
return pinnedSources + unpinnedSources
|
val orderedPinnedSources = pinnedMangaSources.mapNotNull { name ->
|
||||||
|
pinnedSourcesMap[name]
|
||||||
|
}
|
||||||
|
val unpinnedSources = sources.filterNot { pinnedMangaSources.contains(it.name) }
|
||||||
|
return orderedPinnedSources + unpinnedSources
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object HMangaSources : MangaReadSources() {
|
object HMangaSources : MangaReadSources() {
|
||||||
val aList: List<Lazier<BaseParser>> = lazyList()
|
private val aList: List<Lazier<BaseParser>> = lazyList()
|
||||||
suspend fun init(fromExtensions: StateFlow<List<MangaExtension.Installed>>) {
|
|
||||||
//todo
|
|
||||||
}
|
|
||||||
|
|
||||||
override val list = listOf(aList, MangaSources.list).flatten()
|
override val list = listOf(aList, MangaSources.list).flatten()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,20 @@ import android.util.Log
|
||||||
import ani.dantotsu.Lazier
|
import ani.dantotsu.Lazier
|
||||||
import ani.dantotsu.parsers.novel.DynamicNovelParser
|
import ani.dantotsu.parsers.novel.DynamicNovelParser
|
||||||
import ani.dantotsu.parsers.novel.NovelExtension
|
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.StateFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
object NovelSources : NovelReadSources() {
|
object NovelSources : NovelReadSources() {
|
||||||
override var list: List<Lazier<BaseParser>> = emptyList()
|
override var list: List<Lazier<BaseParser>> = emptyList()
|
||||||
|
var pinnedNovelSources: List<String> = emptyList()
|
||||||
|
|
||||||
suspend fun init(fromExtensions: StateFlow<List<NovelExtension.Installed>>) {
|
suspend fun init(fromExtensions: StateFlow<List<NovelExtension.Installed>>) {
|
||||||
|
pinnedNovelSources =
|
||||||
|
PrefManager.getNullableVal<List<String>>(PrefName.NovelSourcesOrder, null)
|
||||||
|
?: emptyList()
|
||||||
|
|
||||||
// Initialize with the first value from StateFlow
|
// Initialize with the first value from StateFlow
|
||||||
val initialExtensions = fromExtensions.first()
|
val initialExtensions = fromExtensions.first()
|
||||||
list = createParsersFromExtensions(initialExtensions) + Lazier(
|
list = createParsersFromExtensions(initialExtensions) + Lazier(
|
||||||
|
@ -20,13 +27,25 @@ object NovelSources : NovelReadSources() {
|
||||||
|
|
||||||
// Update as StateFlow emits new values
|
// Update as StateFlow emits new values
|
||||||
fromExtensions.collect { extensions ->
|
fromExtensions.collect { extensions ->
|
||||||
list = createParsersFromExtensions(extensions) + Lazier(
|
list = sortPinnedNovelSources(
|
||||||
|
createParsersFromExtensions(extensions),
|
||||||
|
pinnedNovelSources
|
||||||
|
) + Lazier(
|
||||||
{ OfflineNovelParser() },
|
{ OfflineNovelParser() },
|
||||||
"Downloaded"
|
"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<NovelExtension.Installed>): List<Lazier<BaseParser>> {
|
private fun createParsersFromExtensions(extensions: List<NovelExtension.Installed>): List<Lazier<BaseParser>> {
|
||||||
Log.d("NovelSources", "createParsersFromExtensions")
|
Log.d("NovelSources", "createParsersFromExtensions")
|
||||||
Log.d("NovelSources", extensions.toString())
|
Log.d("NovelSources", extensions.toString())
|
||||||
|
@ -35,4 +54,17 @@ object NovelSources : NovelReadSources() {
|
||||||
Lazier({ DynamicNovelParser(extension) }, name)
|
Lazier({ DynamicNovelParser(extension) }, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sortPinnedNovelSources(
|
||||||
|
parsers: List<Lazier<BaseParser>>,
|
||||||
|
pinnedSources: List<String>
|
||||||
|
): List<Lazier<BaseParser>> {
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -21,7 +21,6 @@ class OfflineAnimeParser : AnimeParser() {
|
||||||
override val name = "Offline"
|
override val name = "Offline"
|
||||||
override val saveName = "Offline"
|
override val saveName = "Offline"
|
||||||
override val hostUrl = "Offline"
|
override val hostUrl = "Offline"
|
||||||
override val isDubAvailableSeparately = false
|
|
||||||
override val isNSFW = false
|
override val isNSFW = false
|
||||||
|
|
||||||
override suspend fun loadEpisodes(
|
override suspend fun loadEpisodes(
|
||||||
|
@ -119,7 +118,7 @@ class OfflineVideoExtractor(val videoServer: VideoServer) : VideoExtractor() {
|
||||||
val sublist = getSubtitle(
|
val sublist = getSubtitle(
|
||||||
videoServer.extraData?.get("title") ?: "",
|
videoServer.extraData?.get("title") ?: "",
|
||||||
videoServer.extraData?.get("episode") ?: ""
|
videoServer.extraData?.get("episode") ?: ""
|
||||||
)?: emptyList()
|
) ?: emptyList()
|
||||||
//we need to return a "fake" video so that the app doesn't crash
|
//we need to return a "fake" video so that the app doesn't crash
|
||||||
val video = Video(
|
val video = Video(
|
||||||
null,
|
null,
|
||||||
|
|
|
@ -62,6 +62,7 @@ data class VideoServer(
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
constructor(name: String, embedUrl: String, extraData: Map<String, String>? = null)
|
constructor(name: String, embedUrl: String, extraData: Map<String, String>? = null)
|
||||||
: this(name, FileUrl(embedUrl), extraData)
|
: this(name, FileUrl(embedUrl), extraData)
|
||||||
|
|
||||||
constructor(name: String, offline: Boolean, extraData: Map<String, String>?)
|
constructor(name: String, offline: Boolean, extraData: Map<String, String>?)
|
||||||
: this(name, FileUrl(""), extraData, null, offline)
|
: this(name, FileUrl(""), extraData, null, offline)
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,9 @@ package ani.dantotsu.parsers.novel
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import ani.dantotsu.currContext
|
|
||||||
import ani.dantotsu.logger
|
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.ExtensionUpdateNotifier
|
||||||
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
|
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
|
||||||
import eu.kanade.tachiyomi.extension.anime.model.AnimeLoadResult
|
import eu.kanade.tachiyomi.extension.anime.model.AnimeLoadResult
|
||||||
|
@ -26,9 +27,7 @@ class NovelExtensionGithubApi {
|
||||||
private val novelExtensionManager: NovelExtensionManager by injectLazy()
|
private val novelExtensionManager: NovelExtensionManager by injectLazy()
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
private val lastExtCheck: Long =
|
private val lastExtCheck: Long = PrefManager.getVal(PrefName.NovelLastExtCheck)
|
||||||
currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
|
|
||||||
?.getLong("last_ext_check", 0) ?: 0
|
|
||||||
|
|
||||||
private var requiresFallbackSource = false
|
private var requiresFallbackSource = false
|
||||||
|
|
||||||
|
@ -86,8 +85,7 @@ class NovelExtensionGithubApi {
|
||||||
novelExtensionManager.availableExtensionsFlow.value
|
novelExtensionManager.availableExtensionsFlow.value
|
||||||
} else {
|
} else {
|
||||||
findExtensions().also {
|
findExtensions().also {
|
||||||
context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
|
PrefManager.setVal(PrefName.NovelLastExtCheck, Date().time)
|
||||||
?.putLong("last_ext_check", Date().time)?.apply()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue