This commit is contained in:
rebelonion 2024-06-26 07:13:50 -05:00
commit c22fd6b66d
78 changed files with 1982 additions and 1674 deletions

View file

@ -12,6 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
CI: true CI: true
SKIP_BUILD: false
steps: steps:
- name: Checkout repo - name: Checkout repo
@ -19,14 +20,12 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Download last SHA artifact - name: Download last SHA artifact
uses: dawidd6/action-download-artifact@v3 uses: dawidd6/action-download-artifact@v3
with: with:
workflow: beta.yml workflow: beta.yml
name: last-sha name: last-sha
path: . path: .
continue-on-error: true continue-on-error: true
- name: Get Commits Since Last Run - name: Get Commits Since Last Run
@ -39,7 +38,9 @@ jobs:
fi fi
echo "Commits since $LAST_SHA:" echo "Commits since $LAST_SHA:"
# Accumulate commit logs in a shell variable # Accumulate commit logs in a shell variable
COMMIT_LOGS=$(git log $LAST_SHA..HEAD --pretty=format:"● %s ~%an") COMMIT_LOGS=$(git log $LAST_SHA..HEAD --pretty=format:"● %s ~%an [֍](https://github.com/${{ github.repository }}/commit/%H)")
# Replace commit messages with pull request links
COMMIT_LOGS=$(echo "$COMMIT_LOGS" | sed -E 's/#([0-9]+)/[#\1](https:\/\/github.com\/rebelonion\/Dantotsu\/pull\/\1)/g')
# URL-encode the newline characters for GitHub Actions # URL-encode the newline characters for GitHub Actions
COMMIT_LOGS="${COMMIT_LOGS//'%'/'%25'}" COMMIT_LOGS="${COMMIT_LOGS//'%'/'%25'}"
COMMIT_LOGS="${COMMIT_LOGS//$'\n'/'%0A'}" COMMIT_LOGS="${COMMIT_LOGS//$'\n'/'%0A'}"
@ -65,7 +66,11 @@ jobs:
echo "Version $VERSION" echo "Version $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: List files in the directory
run: ls -l
- name: Setup JDK 17 - name: Setup JDK 17
if: ${{ env.SKIP_BUILD != 'true' }}
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: 'temurin'
@ -73,18 +78,28 @@ jobs:
cache: gradle cache: gradle
- name: Decode Keystore File - name: Decode Keystore File
if: ${{ github.repository == 'rebelonion/Dantotsu' }}
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
run: ls -l
- name: Make gradlew executable - name: Make gradlew executable
if: ${{ env.SKIP_BUILD != 'true' }}
run: chmod +x ./gradlew run: chmod +x ./gradlew
- name: Build with Gradle - name: Build with Gradle
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 }} if: ${{ env.SKIP_BUILD != 'true' }}
run: |
if [ "${{ github.repository }}" == "rebelonion/Dantotsu" ]; then
./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 }};
else
./gradlew assembleGoogleAlpha;
fi
- name: Upload a Build Artifact - name: Upload a Build Artifact
if: ${{ env.SKIP_BUILD != 'true' }}
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: Dantotsu name: Dantotsu
@ -93,24 +108,202 @@ jobs:
path: "app/build/outputs/apk/google/alpha/app-google-alpha.apk" path: "app/build/outputs/apk/google/alpha/app-google-alpha.apk"
- name: Upload APK to Discord and Telegram - name: Upload APK to Discord and Telegram
if: ${{ github.repository == 'rebelonion/Dantotsu' }}
shell: bash shell: bash
run: | run: |
#Discord # Prepare Discord embed
fetch_user_details() {
local login=$1
user_details=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/users/$login")
name=$(echo "$user_details" | jq -r '.name // .login')
login=$(echo "$user_details" | jq -r '.login')
avatar_url=$(echo "$user_details" | jq -r '.avatar_url')
echo "$name|$login|$avatar_url"
}
# Additional information for the goats
declare -A additional_info
additional_info["ibo"]="\n Discord: <@951737931159187457>\n AniList: [takarealist112](<https://anilist.co/user/takarealist112/>)"
additional_info["aayush262"]="\n Discord: <@918825160654598224>\n AniList: [aayush262](<https://anilist.co/user/aayush262/>)"
additional_info["rebelonion"]="\n Discord: <@714249925248024617>\n AniList: [rebelonion](<https://anilist.co/user/rebelonion/>)\n PornHub: [rebelonion](<https://www.cornhub.com/model/rebelonion>)"
# Decimal color codes for contributors
declare -A contributor_colors
default_color="#ff25f9"
contributor_colors["ibo"]="#ff7500"
contributor_colors["aayush262"]="#5d689d"
contributor_colors["Sadwhy"]="#ff7e95"
contributor_colors["rebelonion"]="#d4e5ed"
hex_to_decimal() { printf '%d' "0x${1#"#"}"; }
# Count recent commits and create an associative array
declare -A recent_commit_counts
while read -r count name; do
recent_commit_counts["$name"]=$count
done < <(echo "$COMMIT_LOG" | sed 's/%0A/\n/g' | grep -oP '(?<=~)[^[]*' | sort | uniq -c | sort -rn)
# Fetch contributors from GitHub
contributors=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/contributors")
# Create a sorted list of contributors based on recent commit counts
sorted_contributors=$(for login in $(echo "$contributors" | jq -r '.[].login'); do
user_info=$(fetch_user_details "$login")
name=$(echo "$user_info" | cut -d'|' -f1)
count=${recent_commit_counts["$name"]:-0}
echo "$count|$login"
done | sort -rn | cut -d'|' -f2)
# Initialize needed variables
developers=""
committers_count=0
max_commits=0
top_contributor=""
top_contributor_count=0
top_contributor_avatar=""
embed_color=$default_color
# Process contributors in the new order
while read -r login; do
user_info=$(fetch_user_details "$login")
name=$(echo "$user_info" | cut -d'|' -f1)
login=$(echo "$user_info" | cut -d'|' -f2)
avatar_url=$(echo "$user_info" | cut -d'|' -f3)
# Only process if they have recent commits
commit_count=${recent_commit_counts["$name"]:-0}
if [ $commit_count -gt 0 ]; then
# Update top contributor information
if [ $commit_count -gt $max_commits ]; then
max_commits=$commit_count
top_contributors=("$login")
top_contributor_count=1
top_contributor_avatar="$avatar_url"
embed_color=$(hex_to_decimal "${contributor_colors[$name]:-$default_color}")
elif [ $commit_count -eq $max_commits ]; then
top_contributors+=("$login")
top_contributor_count=$((top_contributor_count + 1))
embed_color=$default_color
fi
# Get commit count for this contributor on the dev branch
branch_commit_count=$(git rev-list --count dev --author="$login")
extra_info="${additional_info[$name]}"
if [ -n "$extra_info" ]; then
extra_info=$(echo "$extra_info" | sed 's/\\n/\n- /g')
fi
# Construct the developer entry
developer_entry="◗ **${name}** ${extra_info}
- Github: [${login}](https://github.com/${login})
- Commits: ${branch_commit_count}"
# Add the entry to developers, with a newline if it's not the first entry
if [ -n "$developers" ]; then
developers="${developers}
${developer_entry}"
else
developers="${developer_entry}"
fi
committers_count=$((committers_count + 1))
fi
done <<< "$sorted_contributors"
# Set the thumbnail URL and color based on top contributor(s)
if [ $top_contributor_count -eq 1 ]; then
thumbnail_url="$top_contributor_avatar"
else
thumbnail_url="https://i.imgur.com/5o3Y9Jb.gif"
embed_color=$default_color
fi
# Truncate field values
max_length=1000
commit_messages=$(echo "$COMMIT_LOG" | sed 's/%0A/\n/g; s/^/\n/') commit_messages=$(echo "$COMMIT_LOG" | sed 's/%0A/\n/g; s/^/\n/')
# Truncate commit messages if they are too long if [ ${#developers} -gt $max_length ]; then
max_length=1900 # Adjust this value as needed developers="${developers:0:$max_length}... (truncated)"
fi
if [ ${#commit_messages} -gt $max_length ]; then if [ ${#commit_messages} -gt $max_length ]; then
commit_messages="${commit_messages:0:$max_length}... (truncated)" commit_messages="${commit_messages:0:$max_length}... (truncated)"
fi fi
contentbody=$( jq -nc --arg msg "Alpha-Build: <@&1225347048321191996> **$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 # Construct Discord payload
curl -F "chat_id=${{ secrets.TELEGRAM_CHANNEL_ID }}" \ discord_data=$(jq -nc \
-F "document=@app/build/outputs/apk/google/alpha/app-google-alpha.apk" \ --arg field_value "$commit_messages" \
-F "caption=Alpha-Build: ${VERSION}: ${commit_messages}" \ --arg author_value "$developers" \
https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendDocument --arg footer_text "Version $VERSION" \
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \
--arg thumbnail_url "$thumbnail_url" \
--argjson embed_color "$embed_color" \
'{
"content": "<@&1225347048321191996>",
"embeds": [
{
"title": "New Alpha-Build dropped",
"color": $embed_color,
"fields": [
{
"name": "Commits:",
"value": $field_value,
"inline": true
},
{
"name": "Developers:",
"value": $author_value,
"inline": false
}
],
"footer": {
"text": $footer_text
},
"timestamp": $timestamp,
"thumbnail": {
"url": $thumbnail_url
}
}
],
"attachments": []
}')
# Send Discord message
curl -H "Content-Type: application/json" \
-d "$discord_data" \
${{ secrets.DISCORD_WEBHOOK }}
echo "You have only send an embed to discord due to SKIP_BUILD being set to true"
# Upload APK to Discord
if [ "$SKIP_BUILD" != "true" ]; then
curl -F "payload_json=${contentbody}" \
-F "dantotsu_debug=@app/build/outputs/apk/google/alpha/app-google-alpha.apk" \
${{ secrets.DISCORD_WEBHOOK }}
else
echo "Skipping APK upload to Discord due to SKIP_BUILD being set to true"
fi
# Format commit messages for Telegram
telegram_commit_messages=$(echo "$COMMIT_LOG" | sed 's/%0A/\n/g' | while read -r line; do
message=$(echo "$line" | sed -E 's/● (.*) ~(.*) \[֍\]\((.*)\)/● \1 ~\2 <a href="\3">֍<\/a>/')
message=$(echo "$message" | sed -E 's/\[#([0-9]+)\]\((https:\/\/github\.com\/[^)]+)\)/<a href="\2">#\1<\/a>/g')
echo "$message"
done)
telegram_commit_messages="<blockquote>${telegram_commit_messages}</blockquote>"
# Upload APK to Telegram
if [ "$SKIP_BUILD" != "true" ]; then
APK_PATH="app/build/outputs/apk/google/alpha/app-google-alpha.apk"
response=$(curl -sS -f -X POST \
"https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendDocument" \
-F "chat_id=${{ secrets.TELEGRAM_CHANNEL_ID }}" \
-F "document=@$APK_PATH" \
-F "caption=New Alpha-Build dropped 🔥
Commits:
${telegram_commit_messages}
version: ${VERSION}" \
-F "parse_mode=HTML")
else
echo "Skipping Telegram message and APK upload due to SKIP_BUILD being set to true"
fi
env: env:
COMMIT_LOG: ${{ env.COMMIT_LOG }} COMMIT_LOG: ${{ env.COMMIT_LOG }}

View file

@ -201,7 +201,7 @@
android:name=".others.imagesearch.ImageSearchActivity" android:name=".others.imagesearch.ImageSearchActivity"
android:parentActivityName=".MainActivity" /> android:parentActivityName=".MainActivity" />
<activity <activity
android:name=".util.MarkdownCreatorActivity" android:name=".util.ActivityMarkdownCreator"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity android:name=".parsers.ParserTestActivity" /> <activity android:name=".parsers.ParserTestActivity" />
<activity <activity

View file

@ -854,7 +854,7 @@ fun savePrefsToDownloads(
} }
) )
} }
@SuppressLint("StringFormatMatches")
fun savePrefs(serialized: String, path: String, title: String, context: Context): File? { fun savePrefs(serialized: String, path: String, title: String, context: Context): File? {
var file = File(path, "$title.ani") var file = File(path, "$title.ani")
var counter = 1 var counter = 1
@ -874,6 +874,7 @@ fun savePrefs(serialized: String, path: String, title: String, context: Context)
} }
} }
@SuppressLint("StringFormatMatches")
fun savePrefs( fun savePrefs(
serialized: String, serialized: String,
path: String, path: String,
@ -920,7 +921,7 @@ fun shareImage(title: String, bitmap: Bitmap, context: Context) {
intent.putExtra(Intent.EXTRA_STREAM, contentUri) intent.putExtra(Intent.EXTRA_STREAM, contentUri)
context.startActivity(Intent.createChooser(intent, "Share $title")) context.startActivity(Intent.createChooser(intent, "Share $title"))
} }
@SuppressLint("StringFormatMatches")
fun saveImage(image: Bitmap, path: String, imageFileName: String): File? { fun saveImage(image: Bitmap, path: String, imageFileName: String): File? {
val imageFile = File(path, "$imageFileName.png") val imageFile = File(path, "$imageFileName.png")
return try { return try {
@ -1500,7 +1501,6 @@ fun buildMarkwon(
} }
return false return false
} }
override fun onLoadFailed( override fun onLoadFailed(
e: GlideException?, e: GlideException?,
model: Any?, model: Any?,

View file

@ -61,6 +61,7 @@ import ani.dantotsu.settings.saving.internal.PreferenceKeystore
import ani.dantotsu.settings.saving.internal.PreferencePackager import ani.dantotsu.settings.saving.internal.PreferencePackager
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
@ -312,6 +313,7 @@ class MainActivity : AppCompatActivity() {
mainViewPager.adapter = mainViewPager.adapter =
ViewPagerAdapter(supportFragmentManager, lifecycle) ViewPagerAdapter(supportFragmentManager, lifecycle)
mainViewPager.setPageTransformer(ZoomOutPageTransformer()) mainViewPager.setPageTransformer(ZoomOutPageTransformer())
mainViewPager.offscreenPageLimit = 1
navbar.selectTabAt(selectedOption) navbar.selectTabAt(selectedOption)
navbar.setOnTabSelectListener(object : navbar.setOnTabSelectListener(object :
AnimatedBottomBar.OnTabSelectListener { AnimatedBottomBar.OnTabSelectListener {
@ -493,36 +495,29 @@ class MainActivity : AppCompatActivity() {
val password = CharArray(16).apply { fill('0') } val password = CharArray(16).apply { fill('0') }
// Inflate the dialog layout // Inflate the dialog layout
val dialogView = DialogUserAgentBinding.inflate(layoutInflater) val dialogView = DialogUserAgentBinding.inflate(layoutInflater).apply {
dialogView.userAgentTextBox.hint = "Password" userAgentTextBox.hint = "Password"
dialogView.subtitle.visibility = View.VISIBLE subtitle.visibility = View.VISIBLE
dialogView.subtitle.text = getString(R.string.enter_password_to_decrypt_file) subtitle.text = getString(R.string.enter_password_to_decrypt_file)
val dialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle("Enter Password")
.setView(dialogView.root)
.setPositiveButton("OK", null)
.setNegativeButton("Cancel") { dialog, _ ->
password.fill('0')
dialog.dismiss()
callback(null)
} }
.create() customAlertDialog().apply {
setTitle("Enter Password")
dialog.window?.setDimAmount(0.8f) setCustomView(dialogView.root)
dialog.show() setPosButton(R.string.yes) {
val editText = dialogView.userAgentTextBox
// Override the positive button here if (editText.text?.isNotBlank() == true) {
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) editText.text?.toString()?.trim()?.toCharArray(password)
dialog.dismiss()
callback(password) callback(password)
} else { } else {
toast("Password cannot be empty") toast("Password cannot be empty")
} }
} }
setNegButton(R.string.cancel) {
password.fill('0')
callback(null)
}
show()
}
} }
//ViewPager //ViewPager

View file

@ -93,30 +93,41 @@ class AnilistMutations {
) )
} }
suspend fun postActivity(text: String, edit : Int? = null): String { suspend fun postActivity(text: String, edit: Int? = null): String {
val encodedText = text.stringSanitizer() val encodedText = text.stringSanitizer()
val query = "mutation{SaveTextActivity(${if (edit != null) "id:$edit," else ""} text:$encodedText){siteUrl}}" val query =
"mutation{SaveTextActivity(${if (edit != null) "id:$edit," else ""} text:$encodedText){siteUrl}}"
val result = executeQuery<JsonObject>(query) val result = executeQuery<JsonObject>(query)
val errors = result?.get("errors") val errors = result?.get("errors")
return errors?.toString() return errors?.toString()
?: (currContext()?.getString(ani.dantotsu.R.string.success) ?: "Success") ?: (currContext()?.getString(ani.dantotsu.R.string.success) ?: "Success")
} }
suspend fun postMessage(userId: Int, text: String, edit: Int? = null,isPrivate: Boolean = false): String {
val encodedText = text.replace("","").stringSanitizer() suspend fun postMessage(
val query = "mutation{SaveMessageActivity(${if (edit != null) "id:$edit," else ""} recipientId:$userId,message:$encodedText,private:$isPrivate){id}}" userId: Int,
text: String,
edit: Int? = null,
isPrivate: Boolean = false
): String {
val encodedText = text.replace("", "").stringSanitizer()
val query =
"mutation{SaveMessageActivity(${if (edit != null) "id:$edit," else ""} recipientId:$userId,message:$encodedText,private:$isPrivate){id}}"
val result = executeQuery<JsonObject>(query) val result = executeQuery<JsonObject>(query)
val errors = result?.get("errors") val errors = result?.get("errors")
return errors?.toString() return errors?.toString()
?: (currContext()?.getString(ani.dantotsu.R.string.success) ?: "Success") ?: (currContext()?.getString(ani.dantotsu.R.string.success) ?: "Success")
} }
suspend fun postReply(activityId: Int, text: String, edit: Int? = null ): String {
suspend fun postReply(activityId: Int, text: String, edit: Int? = null): String {
val encodedText = text.stringSanitizer() val encodedText = text.stringSanitizer()
val query = "mutation{SaveActivityReply(${if (edit != null) "id:$edit," else ""} activityId:$activityId,text:$encodedText){id}}" val query =
"mutation{SaveActivityReply(${if (edit != null) "id:$edit," else ""} activityId:$activityId,text:$encodedText){id}}"
val result = executeQuery<JsonObject>(query) val result = executeQuery<JsonObject>(query)
val errors = result?.get("errors") val errors = result?.get("errors")
return errors?.toString() return errors?.toString()
?: (currContext()?.getString(ani.dantotsu.R.string.success) ?: "Success") ?: (currContext()?.getString(ani.dantotsu.R.string.success) ?: "Success")
} }
suspend fun postReview(summary: String, body: String, mediaId: Int, score: Int): String { suspend fun postReview(summary: String, body: String, mediaId: Int, score: Int): String {
val encodedSummary = summary.stringSanitizer() val encodedSummary = summary.stringSanitizer()
val encodedBody = body.stringSanitizer() val encodedBody = body.stringSanitizer()
@ -143,7 +154,6 @@ class AnilistMutations {
} }
private fun String.stringSanitizer(): String { private fun String.stringSanitizer(): String {
val sb = StringBuilder() val sb = StringBuilder()
var i = 0 var i = 0

View file

@ -479,6 +479,7 @@ class AnilistQueries {
suspend fun initHomePage(): Map<String, ArrayList<*>> { suspend fun initHomePage(): Map<String, ArrayList<*>> {
val removeList = PrefManager.getCustomVal("removeList", setOf<Int>()) val removeList = PrefManager.getCustomVal("removeList", setOf<Int>())
val hidePrivate = PrefManager.getVal<Boolean>(PrefName.HidePrivate)
val removedMedia = ArrayList<Media>() val removedMedia = ArrayList<Media>()
val toShow: List<Boolean> = val toShow: List<Boolean> =
PrefManager.getVal(PrefName.HomeLayout) // anime continue, anime fav, anime planned, manga continue, manga fav, manga planned, recommendations PrefManager.getVal(PrefName.HomeLayout) // anime continue, anime fav, anime planned, manga continue, manga fav, manga planned, recommendations
@ -528,7 +529,7 @@ class AnilistQueries {
current?.lists?.forEach { li -> current?.lists?.forEach { li ->
li.entries?.reversed()?.forEach { li.entries?.reversed()?.forEach {
val m = Media(it) val m = Media(it)
if (m.id !in removeList) { if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
m.cameFromContinue = true m.cameFromContinue = true
subMap[m.id] = m subMap[m.id] = m
} else { } else {
@ -540,7 +541,7 @@ class AnilistQueries {
repeating?.lists?.forEach { li -> repeating?.lists?.forEach { li ->
li.entries?.reversed()?.forEach { li.entries?.reversed()?.forEach {
val m = Media(it) val m = Media(it)
if (m.id !in removeList) { if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
m.cameFromContinue = true m.cameFromContinue = true
subMap[m.id] = m subMap[m.id] = m
} else { } else {
@ -579,7 +580,7 @@ class AnilistQueries {
current?.lists?.forEach { li -> current?.lists?.forEach { li ->
li.entries?.reversed()?.forEach { li.entries?.reversed()?.forEach {
val m = Media(it) val m = Media(it)
if (m.id !in removeList) { if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
m.cameFromContinue = true m.cameFromContinue = true
subMap[m.id] = m subMap[m.id] = m
} else { } else {
@ -612,7 +613,7 @@ class AnilistQueries {
apiMediaList?.edges?.forEach { apiMediaList?.edges?.forEach {
it.node?.let { i -> it.node?.let { i ->
val m = Media(i).apply { isFav = true } val m = Media(i).apply { isFav = true }
if (m.id !in removeList) { if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
returnArray.add(m) returnArray.add(m)
} else { } else {
removedMedia.add(m) removedMedia.add(m)
@ -1055,172 +1056,110 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
return null return null
} }
private val onListAnime = private fun mediaList(media1: Page?): ArrayList<Media> {
(if (PrefManager.getVal(PrefName.IncludeAnimeList)) "" else "onList:false").replace( val combinedList = arrayListOf<Media>()
"\"", media1?.media?.mapTo(combinedList) { Media(it) }
"" return combinedList
) }
private val isAdult = private fun getPreference(pref: PrefName): Boolean = PrefManager.getVal(pref)
(if (PrefManager.getVal(PrefName.AdultOnly)) "isAdult:true" else "").replace("\"", "") private fun buildQueryString(sort: String, type: String, format: String? = null, country: String? = null): String {
val includeList = if (type == "ANIME" && !getPreference(PrefName.IncludeAnimeList)) "onList:false" else if (type == "MANGA" && !getPreference(PrefName.IncludeMangaList)) "onList:false" else ""
val isAdult = if (getPreference(PrefName.AdultOnly)) "isAdult:true" else ""
val formatFilter = format?.let { "format: $it, " } ?: ""
val countryFilter = country?.let { "countryOfOrigin: $it, " } ?: ""
return """Page(page:1,perPage:50){
pageInfo{hasNextPage total}
media(sort:$sort, type:$type, $formatFilter $countryFilter $includeList $isAdult){
id idMal status chapters episodes nextAiringEpisode{episode}
isAdult type meanScore isFavourite format bannerImage countryOfOrigin
coverImage{large} title{english romaji userPreferred}
mediaListEntry{progress private score(format:POINT_100) status}
}
}"""
}
private fun recentAnimeUpdates(page: Int): String { private fun recentAnimeUpdates(page: Int): String {
return """Page(page:$page,perPage:50){pageInfo{hasNextPage total}airingSchedules(airingAt_greater:0 airingAt_lesser:${System.currentTimeMillis() / 1000 - 10000} sort:TIME_DESC){episode airingAt media{id idMal status chapters episodes nextAiringEpisode{episode} isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large} title{english romaji userPreferred} mediaListEntry{progress private score(format:POINT_100) status}}}}""" val currentTime = System.currentTimeMillis() / 1000
return """Page(page:$page,perPage:50){
pageInfo{hasNextPage total}
airingSchedules(airingAt_greater:0 airingAt_lesser:${currentTime - 10000} sort:TIME_DESC){
episode airingAt media{
id idMal status chapters episodes nextAiringEpisode{episode}
isAdult type meanScore isFavourite format bannerImage countryOfOrigin
coverImage{large} title{english romaji userPreferred}
mediaListEntry{progress private score(format:POINT_100) status}
} }
private fun trendingMovies(page: Int): String {
return """Page(page:$page,perPage:50){pageInfo{hasNextPage total}media(sort:POPULARITY_DESC, type: ANIME, format: MOVIE, $onListAnime, $isAdult){id idMal status chapters episodes nextAiringEpisode{episode}isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large}title{english romaji userPreferred}mediaListEntry{progress private score(format:POINT_100)status}}}"""
} }
}"""
private fun topRatedAnime(page: Int): String {
return """Page(page:$page,perPage:50){pageInfo{hasNextPage total}media(sort: SCORE_DESC, type: ANIME, $onListAnime, $isAdult){id idMal status chapters episodes nextAiringEpisode{episode}isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large}title{english romaji userPreferred}mediaListEntry{progress private score(format:POINT_100)status}}}"""
} }
private fun queryAnimeList(): String {
return """{
recentUpdates:${recentAnimeUpdates(1)}
recentUpdates2:${recentAnimeUpdates(2)}
trendingMovies:${buildQueryString("POPULARITY_DESC", "ANIME", "MOVIE")}
topRated:${buildQueryString("SCORE_DESC", "ANIME")}
mostFav:${buildQueryString("FAVOURITES_DESC", "ANIME")}
}"""
}
private fun queryMangaList(): String {
return """{
trendingManga:${buildQueryString("POPULARITY_DESC", "MANGA", country = "JP")}
trendingManhwa:${buildQueryString("POPULARITY_DESC", "MANGA", country = "KR")}
trendingNovel:${buildQueryString("POPULARITY_DESC", "MANGA", format = "NOVEL", country = "JP")}
topRated:${buildQueryString("SCORE_DESC", "MANGA")}
mostFav:${buildQueryString("FAVOURITES_DESC", "MANGA")}
private fun mostFavAnime(page: Int): String { }"""
return """Page(page:$page,perPage:50){pageInfo{hasNextPage total}media(sort:FAVOURITES_DESC,type: ANIME, $onListAnime, $isAdult){id idMal status chapters episodes nextAiringEpisode{episode}isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large}title{english romaji userPreferred}mediaListEntry{progress private score(format:POINT_100)status}}}"""
} }
suspend fun loadAnimeList(): Map<String, ArrayList<Media>> { suspend fun loadAnimeList(): Map<String, ArrayList<Media>> {
val list = mutableMapOf<String, ArrayList<Media>>() val list = mutableMapOf<String, ArrayList<Media>>()
fun query(): String {
return """{ fun filterRecentUpdates(
recentUpdates:${recentAnimeUpdates(1)} page: Page?,
recentUpdates2:${recentAnimeUpdates(2)} ): ArrayList<Media> {
trendingMovies:${trendingMovies(1)}
trendingMovies2:${trendingMovies(2)}
topRated:${topRatedAnime(1)}
topRated2:${topRatedAnime(2)}
mostFav:${mostFavAnime(1)}
mostFav2:${mostFavAnime(2)}
}""".trimIndent()
}
executeQuery<Query.AnimeList>(query(), force = true)?.data?.apply {
val listOnly: Boolean = PrefManager.getVal(PrefName.RecentlyListOnly) val listOnly: Boolean = PrefManager.getVal(PrefName.RecentlyListOnly)
val adultOnly: Boolean = PrefManager.getVal(PrefName.AdultOnly) val adultOnly: Boolean = PrefManager.getVal(PrefName.AdultOnly)
val idArr = mutableListOf<Int>() val idArr = mutableListOf<Int>()
list["recentUpdates"] = recentUpdates?.airingSchedules?.mapNotNull { i -> return page?.airingSchedules?.mapNotNull { i ->
i.media?.let { i.media?.let {
if (!idArr.contains(it.id)) if (!idArr.contains(it.id)) {
if (!listOnly && it.countryOfOrigin == "JP" && Anilist.adult && adultOnly && it.isAdult == true) { val shouldAdd = when {
idArr.add(it.id) !listOnly && it.countryOfOrigin == "JP" && Anilist.adult && adultOnly && it.isAdult == true -> true
Media(it) !listOnly && !adultOnly && it.countryOfOrigin == "JP" && it.isAdult == false -> true
} else if (!listOnly && !adultOnly && (it.countryOfOrigin == "JP" && it.isAdult == false)) { listOnly && it.mediaListEntry != null -> true
idArr.add(it.id) else -> false
Media(it) }
} else if ((listOnly && it.mediaListEntry != null)) { if (shouldAdd) {
idArr.add(it.id) idArr.add(it.id)
Media(it) Media(it)
} else null } else null
else null } else null
} }
}?.toCollection(ArrayList()) ?: arrayListOf() }?.toCollection(ArrayList()) ?: arrayListOf()
list["trendingMovies"] =
trendingMovies?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
list["topRated"] =
topRated?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
list["mostFav"] =
mostFav?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
list["recentUpdates"]?.addAll(recentUpdates2?.airingSchedules?.mapNotNull { i ->
i.media?.let {
if (!idArr.contains(it.id))
if (!listOnly && it.countryOfOrigin == "JP" && Anilist.adult && adultOnly && it.isAdult == true) {
idArr.add(it.id)
Media(it)
} else if (!listOnly && !adultOnly && (it.countryOfOrigin == "JP" && it.isAdult == false)) {
idArr.add(it.id)
Media(it)
} else if ((listOnly && it.mediaListEntry != null)) {
idArr.add(it.id)
Media(it)
} else null
else null
} }
}?.toCollection(ArrayList()) ?: arrayListOf()) executeQuery<Query.AnimeList>(queryAnimeList(), force = true)?.data?.apply {
list["trendingMovies"]?.addAll(trendingMovies2?.media?.map { Media(it) } list["recentUpdates"] = filterRecentUpdates(recentUpdates)
?.toCollection(ArrayList()) ?: arrayListOf()) list["trendingMovies"] = mediaList(trendingMovies)
list["topRated"]?.addAll( list["topRated"] = mediaList(topRated)
topRated2?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf() list["mostFav"] = mediaList(mostFav)
)
list["mostFav"]?.addAll(
mostFav2?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
)
} }
return list return list
} }
private val onListManga =
(if (PrefManager.getVal(PrefName.IncludeMangaList)) "" else "onList:false").replace(
"\"",
""
)
private fun trendingManga(page: Int): String {
return """Page(page:$page,perPage:50){pageInfo{hasNextPage total}media(sort:POPULARITY_DESC, type: MANGA,countryOfOrigin:JP, $onListManga, $isAdult){id idMal status chapters episodes nextAiringEpisode{episode}isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large}title{english romaji userPreferred}mediaListEntry{progress private score(format:POINT_100)status}}}"""
}
private fun trendingManhwa(page: Int): String {
return """Page(page:$page,perPage:50){pageInfo{hasNextPage total}media(sort:POPULARITY_DESC, type: MANGA, countryOfOrigin:KR, $onListManga, $isAdult){id idMal status chapters episodes nextAiringEpisode{episode}isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large}title{english romaji userPreferred}mediaListEntry{progress private score(format:POINT_100)status}}}"""
}
private fun trendingNovel(page: Int): String {
return """Page(page:$page,perPage:50){pageInfo{hasNextPage total}media(sort:POPULARITY_DESC, type: MANGA, format: NOVEL, countryOfOrigin:JP, $onListManga, $isAdult){id idMal status chapters episodes nextAiringEpisode{episode}isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large}title{english romaji userPreferred}mediaListEntry{progress private score(format:POINT_100)status}}}"""
}
private fun topRatedManga(page: Int): String {
return """Page(page:$page,perPage:50){pageInfo{hasNextPage total}media(sort: SCORE_DESC, type: MANGA, $onListManga, $isAdult){id idMal status chapters episodes nextAiringEpisode{episode}isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large}title{english romaji userPreferred}mediaListEntry{progress private score(format:POINT_100)status}}}"""
}
private fun mostFavManga(page: Int): String {
return """Page(page:$page,perPage:50){pageInfo{hasNextPage total}media(sort:FAVOURITES_DESC,type: MANGA, $onListManga, $isAdult){id idMal status chapters episodes nextAiringEpisode{episode}isAdult type meanScore isFavourite format bannerImage countryOfOrigin coverImage{large}title{english romaji userPreferred}mediaListEntry{progress private score(format:POINT_100)status}}}"""
}
suspend fun loadMangaList(): Map<String, ArrayList<Media>> { suspend fun loadMangaList(): Map<String, ArrayList<Media>> {
val list = mutableMapOf<String, ArrayList<Media>>() val list = mutableMapOf<String, ArrayList<Media>>()
fun query(): String { executeQuery<Query.MangaList>(queryMangaList(), force = true)?.data?.apply {
return """{ list["trendingManga"] = mediaList(trendingManga)
trendingManga:${trendingManga(1)} list["trendingManhwa"] = mediaList(trendingManhwa)
trendingManga2:${trendingManga(2)} list["trendingNovel"] = mediaList(trendingNovel)
trendingManhwa:${trendingManhwa(1)} list["topRated"] = mediaList(topRated)
trendingManhwa2:${trendingManhwa(2)} list["mostFav"] = mediaList(mostFav)
trendingNovel:${trendingNovel(1)}
trendingNovel2:${trendingNovel(2)}
topRated:${topRatedManga(1)}
topRated2:${topRatedManga(2)}
mostFav:${mostFavManga(1)}
mostFav2:${mostFavManga(2)}
}""".trimIndent()
} }
executeQuery<Query.MangaList>(query(), force = true)?.data?.apply {
list["trendingManga"] =
trendingManga?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
list["trendingManhwa"] =
trendingManhwa?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
list["trendingNovel"] =
trendingNovel?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
list["topRated"] =
topRated?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
list["mostFav"] =
mostFav?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
list["trendingManga"]?.addAll(
trendingManga2?.media?.map { Media(it) }?.toList() ?: arrayListOf()
)
list["trendingManhwa"]?.addAll(
trendingManhwa2?.media?.map { Media(it) }?.toList() ?: arrayListOf()
)
list["trendingNovel"]?.addAll(
trendingNovel2?.media?.map { Media(it) }?.toList() ?: arrayListOf()
)
list["topRated"]?.addAll(topRated2?.media?.map { Media(it) }?.toList() ?: arrayListOf())
list["mostFav"]?.addAll(mostFav2?.media?.map { Media(it) }?.toList() ?: arrayListOf())
}
return list return list
} }
suspend fun recentlyUpdated( suspend fun recentlyUpdated(
greater: Long = 0, greater: Long = 0,
lesser: Long = System.currentTimeMillis() / 1000 - 10000 lesser: Long = System.currentTimeMillis() / 1000 - 10000

View file

@ -163,13 +163,9 @@ class Query {
@Serializable @Serializable
data class Data( data class Data(
@SerialName("recentUpdates") val recentUpdates: ani.dantotsu.connections.anilist.api.Page?, @SerialName("recentUpdates") val recentUpdates: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("recentUpdates2") val recentUpdates2: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("trendingMovies") val trendingMovies: ani.dantotsu.connections.anilist.api.Page?, @SerialName("trendingMovies") val trendingMovies: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("trendingMovies2") val trendingMovies2: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("topRated") val topRated: ani.dantotsu.connections.anilist.api.Page?, @SerialName("topRated") val topRated: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("topRated2") val topRated2: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("mostFav") val mostFav: ani.dantotsu.connections.anilist.api.Page?, @SerialName("mostFav") val mostFav: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("mostFav2") val mostFav2: ani.dantotsu.connections.anilist.api.Page?,
) )
} }
@ -181,15 +177,10 @@ class Query {
@Serializable @Serializable
data class Data( data class Data(
@SerialName("trendingManga") val trendingManga: ani.dantotsu.connections.anilist.api.Page?, @SerialName("trendingManga") val trendingManga: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("trendingManga2") val trendingManga2: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("trendingManhwa") val trendingManhwa: ani.dantotsu.connections.anilist.api.Page?, @SerialName("trendingManhwa") val trendingManhwa: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("trendingManhwa2") val trendingManhwa2: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("trendingNovel") val trendingNovel: ani.dantotsu.connections.anilist.api.Page?, @SerialName("trendingNovel") val trendingNovel: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("trendingNovel2") val trendingNovel2: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("topRated") val topRated: ani.dantotsu.connections.anilist.api.Page?, @SerialName("topRated") val topRated: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("topRated2") val topRated2: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("mostFav") val mostFav: ani.dantotsu.connections.anilist.api.Page?, @SerialName("mostFav") val mostFav: ani.dantotsu.connections.anilist.api.Page?,
@SerialName("mostFav2") val mostFav2: ani.dantotsu.connections.anilist.api.Page?,
) )
} }

View file

@ -49,6 +49,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.anggrayudi.storage.file.openInputStream import com.anggrayudi.storage.file.openInputStream
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
@ -203,25 +204,22 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
val type: MediaType = MediaType.ANIME val type: MediaType = MediaType.ANIME
// Alert dialog to confirm deletion // Alert dialog to confirm deletion
val builder = requireContext().customAlertDialog().apply {
androidx.appcompat.app.AlertDialog.Builder(requireContext(), R.style.MyPopup) setTitle("Delete ${item.title}?")
builder.setTitle("Delete ${item.title}?") setMessage("Are you sure you want to delete ${item.title}?")
builder.setMessage("Are you sure you want to delete ${item.title}?") setPosButton(R.string.yes) {
builder.setPositiveButton("Yes") { _, _ ->
downloadManager.removeMedia(item.title, type) downloadManager.removeMedia(item.title, type)
val mediaIds = val mediaIds = PrefManager.getAnimeDownloadPreferences().all?.filter { it.key.contains(item.title) }?.values ?: emptySet()
PrefManager.getAnimeDownloadPreferences().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
} }
getDownloads() getDownloads()
} }
builder.setNegativeButton("No") { _, _ -> setNegButton(R.string.no) {
// Do nothing // Do nothing
} }
val dialog = builder.show() show()
dialog.window?.setDimAmount(0.8f) }
true true
} }
} }

View file

@ -46,6 +46,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.anggrayudi.storage.file.openInputStream import com.anggrayudi.storage.file.openInputStream
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
@ -201,19 +202,15 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
MediaType.NOVEL MediaType.NOVEL
} }
// Alert dialog to confirm deletion // Alert dialog to confirm deletion
val builder = requireContext().customAlertDialog().apply {
androidx.appcompat.app.AlertDialog.Builder(requireContext(), R.style.MyPopup) setTitle("Delete ${item.title}?")
builder.setTitle("Delete ${item.title}?") setMessage("Are you sure you want to delete ${item.title}?")
builder.setMessage("Are you sure you want to delete ${item.title}?") setPosButton(R.string.yes) {
builder.setPositiveButton("Yes") { _, _ ->
downloadManager.removeMedia(item.title, type) downloadManager.removeMedia(item.title, type)
getDownloads() getDownloads()
} }
builder.setNegativeButton("No") { _, _ -> setNegButton(R.string.no)
// Do nothing }.show()
}
val dialog = builder.show()
dialog.window?.setDimAmount(0.8f)
true true
} }
} }

View file

@ -3,7 +3,6 @@ package ani.dantotsu.download.video
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -29,10 +28,10 @@ import ani.dantotsu.download.anime.AnimeDownloaderService
import ani.dantotsu.download.anime.AnimeServiceDataSingleton import ani.dantotsu.download.anime.AnimeServiceDataSingleton
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaType import ani.dantotsu.media.MediaType
import ani.dantotsu.parsers.Subtitle
import ani.dantotsu.parsers.Video import ani.dantotsu.parsers.Video
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
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
@ -72,19 +71,19 @@ object Helper {
episodeImage episodeImage
) )
val downloadsManger = Injekt.get<DownloadsManager>() val downloadsManager = Injekt.get<DownloadsManager>()
val downloadCheck = downloadsManger val downloadCheck = downloadsManager
.queryDownload(title, episode, MediaType.ANIME) .queryDownload(title, episode, MediaType.ANIME)
if (downloadCheck) { if (downloadCheck) {
AlertDialog.Builder(context, R.style.MyPopup) context.customAlertDialog().apply {
.setTitle("Download Exists") setTitle("Download Exists")
.setMessage("A download for this episode already exists. Do you want to overwrite it?") setMessage("A download for this episode already exists. Do you want to overwrite it?")
.setPositiveButton("Yes") { _, _ -> setPosButton(R.string.yes) {
PrefManager.getAnimeDownloadPreferences().edit() PrefManager.getAnimeDownloadPreferences().edit()
.remove(animeDownloadTask.getTaskName()) .remove(animeDownloadTask.getTaskName())
.apply() .apply()
downloadsManger.removeDownload( downloadsManager.removeDownload(
DownloadedType( DownloadedType(
title, title,
episode, episode,
@ -99,8 +98,9 @@ object Helper {
} }
} }
} }
.setNegativeButton("No") { _, _ -> } setNegButton(R.string.no)
.show() show()
}
} else { } else {
AnimeServiceDataSingleton.downloadQueue.offer(animeDownloadTask) AnimeServiceDataSingleton.downloadQueue.offer(animeDownloadTask)
if (!AnimeServiceDataSingleton.isServiceRunning) { if (!AnimeServiceDataSingleton.isServiceRunning) {

View file

@ -482,13 +482,12 @@ class HomeFragment : Fragment() {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
model.setListImages() model.setListImages()
} }
CoroutineScope(Dispatchers.IO).launch {
model.initUserStatus()
}
var empty = true var empty = true
val homeLayoutShow: List<Boolean> = val homeLayoutShow: List<Boolean> =
PrefManager.getVal(PrefName.HomeLayout) PrefManager.getVal(PrefName.HomeLayout)
model.initHomePage() model.initHomePage()
model.initUserStatus()
(array.indices).forEach { i -> (array.indices).forEach { i ->
if (homeLayoutShow.elementAt(i)) { if (homeLayoutShow.elementAt(i)) {
empty = false empty = false

View file

@ -12,12 +12,14 @@ 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.DialogUserAgentBinding
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.PreferenceKeystore
import ani.dantotsu.settings.saving.internal.PreferencePackager import ani.dantotsu.settings.saving.internal.PreferencePackager
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
class LoginFragment : Fragment() { class LoginFragment : Fragment() {
@ -94,38 +96,31 @@ class LoginFragment : Fragment() {
val password = CharArray(16).apply { fill('0') } val password = CharArray(16).apply { fill('0') }
// Inflate the dialog layout // Inflate the dialog layout
val dialogView = val dialogView = DialogUserAgentBinding.inflate(layoutInflater).apply {
LayoutInflater.from(requireActivity()).inflate(R.layout.dialog_user_agent, null) userAgentTextBox.hint = "Password"
dialogView.findViewById<TextInputEditText>(R.id.userAgentTextBox)?.hint = "Password" subtitle.visibility = View.VISIBLE
val subtitleTextView = dialogView.findViewById<TextView>(R.id.subtitle) subtitle.text = getString(R.string.enter_password_to_decrypt_file)
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) requireActivity().customAlertDialog().apply {
dialog.show() setTitle("Enter Password")
setCustomView(dialogView.root)
// Override the positive button here setPosButton(R.string.ok){
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { val editText = dialogView.userAgentTextBox
val editText = dialog.findViewById<TextInputEditText>(R.id.userAgentTextBox) if (editText.text?.isNotBlank() == true) {
if (editText?.text?.isNotBlank() == true) {
editText.text?.toString()?.trim()?.toCharArray(password) editText.text?.toString()?.trim()?.toCharArray(password)
dialog.dismiss()
callback(password) callback(password)
} else { } else {
toast("Password cannot be empty") toast("Password cannot be empty")
} }
} }
setNegButton(R.string.cancel) {
password.fill('0')
callback(null)
}
}.show()
} }
private fun restartApp() { private fun restartApp() {

View file

@ -5,7 +5,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.util.AttributeSet import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
@ -16,7 +15,6 @@ import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.blurImage import ani.dantotsu.blurImage
@ -32,6 +30,7 @@ import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.User import ani.dantotsu.profile.User
import ani.dantotsu.profile.UsersDialogFragment import ani.dantotsu.profile.UsersDialogFragment
import ani.dantotsu.profile.activity.ActivityItemBuilder import ani.dantotsu.profile.activity.ActivityItemBuilder
import ani.dantotsu.profile.activity.RepliesBottomDialog
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString

View file

@ -1,6 +1,5 @@
package ani.dantotsu.home.status package ani.dantotsu.home.status
import android.content.Context
import android.content.Intent import android.content.Intent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
@ -16,7 +15,7 @@ import ani.dantotsu.profile.User
import ani.dantotsu.setAnimation import ani.dantotsu.setAnimation
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.MarkdownCreatorActivity import ani.dantotsu.util.ActivityMarkdownCreator
class UserStatusAdapter(private val user: ArrayList<User>) : class UserStatusAdapter(private val user: ArrayList<User>) :
RecyclerView.Adapter<UserStatusAdapter.UsersViewHolder>() { RecyclerView.Adapter<UserStatusAdapter.UsersViewHolder>() {
@ -43,7 +42,7 @@ class UserStatusAdapter(private val user: ArrayList<User>) :
if (user[bindingAdapterPosition].id == Anilist.userid) { if (user[bindingAdapterPosition].id == Anilist.userid) {
ContextCompat.startActivity( ContextCompat.startActivity(
itemView.context, itemView.context,
Intent(itemView.context, MarkdownCreatorActivity::class.java) Intent(itemView.context, ActivityMarkdownCreator::class.java)
.putExtra("type", "activity"), .putExtra("type", "activity"),
null null
) )

View file

@ -30,6 +30,7 @@ class CalendarActivity : AppCompatActivity() {
private lateinit var binding: ActivityListBinding private lateinit var binding: ActivityListBinding
private val scope = lifecycleScope private val scope = lifecycleScope
private var selectedTabIdx = 1 private var selectedTabIdx = 1
private var showOnlyLibrary = false
private val model: OtherDetailsViewModel by viewModels() private val model: OtherDetailsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -38,8 +39,6 @@ class CalendarActivity : AppCompatActivity() {
ThemeManager(this).applyTheme() ThemeManager(this).applyTheme()
binding = ActivityListBinding.inflate(layoutInflater) binding = ActivityListBinding.inflate(layoutInflater)
val primaryColor = getThemeColor(com.google.android.material.R.attr.colorSurface) val primaryColor = getThemeColor(com.google.android.material.R.attr.colorSurface)
val primaryTextColor = getThemeColor(com.google.android.material.R.attr.colorPrimary) val primaryTextColor = getThemeColor(com.google.android.material.R.attr.colorPrimary)
val secondaryTextColor = getThemeColor(com.google.android.material.R.attr.colorOutline) val secondaryTextColor = getThemeColor(com.google.android.material.R.attr.colorOutline)
@ -79,6 +78,17 @@ class CalendarActivity : AppCompatActivity() {
override fun onTabReselected(tab: TabLayout.Tab?) {} override fun onTabReselected(tab: TabLayout.Tab?) {}
}) })
binding.listed.setOnClickListener {
showOnlyLibrary = !showOnlyLibrary
binding.listed.setImageResource(
if (showOnlyLibrary) R.drawable.ic_round_collections_bookmark_24
else R.drawable.ic_round_library_books_24
)
scope.launch {
model.loadCalendar(showOnlyLibrary)
}
}
model.getCalendar().observe(this) { model.getCalendar().observe(this) {
if (it != null) { if (it != null) {
binding.listProgressBar.visibility = View.GONE binding.listProgressBar.visibility = View.GONE
@ -97,11 +107,10 @@ class CalendarActivity : AppCompatActivity() {
live.observe(this) { live.observe(this) {
if (it) { if (it) {
scope.launch { scope.launch {
withContext(Dispatchers.IO) { model.loadCalendar() } withContext(Dispatchers.IO) { model.loadCalendar(showOnlyLibrary) }
live.postValue(false) live.postValue(false)
} }
} }
} }
} }
} }

View file

@ -26,25 +26,50 @@ class OtherDetailsViewModel : ViewModel() {
if (author.value == null) author.postValue(Anilist.query.getAuthorDetails(m)) if (author.value == null) author.postValue(Anilist.query.getAuthorDetails(m))
} }
private var cachedAllCalendarData: Map<String, MutableList<Media>>? = null
private var cachedLibraryCalendarData: Map<String, MutableList<Media>>? = null
private val calendar: MutableLiveData<Map<String, MutableList<Media>>> = MutableLiveData(null) private val calendar: MutableLiveData<Map<String, MutableList<Media>>> = MutableLiveData(null)
fun getCalendar(): LiveData<Map<String, MutableList<Media>>> = calendar fun getCalendar(): LiveData<Map<String, MutableList<Media>>> = calendar
suspend fun loadCalendar() { suspend fun loadCalendar(showOnlyLibrary: Boolean = false) {
if (cachedAllCalendarData == null || cachedLibraryCalendarData == null) {
val curr = System.currentTimeMillis() / 1000 val curr = System.currentTimeMillis() / 1000
val res = Anilist.query.recentlyUpdated(curr - 86400, curr + (86400 * 6)) val res = Anilist.query.recentlyUpdated(curr - 86400, curr + (86400 * 6))
val df = DateFormat.getDateInstance(DateFormat.FULL) val df = DateFormat.getDateInstance(DateFormat.FULL)
val map = mutableMapOf<String, MutableList<Media>>() val allMap = mutableMapOf<String, MutableList<Media>>()
val libraryMap = mutableMapOf<String, MutableList<Media>>()
val idMap = mutableMapOf<String, MutableList<Int>>() val idMap = mutableMapOf<String, MutableList<Int>>()
res?.forEach {
val userId = Anilist.userid ?: 0
val userLibrary = Anilist.query.getMediaLists(true, userId)
val libraryMediaIds = userLibrary.flatMap { it.value }.map { it.id }
res.forEach {
val v = it.relation?.split(",")?.map { i -> i.toLong() }!! val v = it.relation?.split(",")?.map { i -> i.toLong() }!!
val dateInfo = df.format(Date(v[1] * 1000)) val dateInfo = df.format(Date(v[1] * 1000))
val list = map.getOrPut(dateInfo) { mutableListOf() } val list = allMap.getOrPut(dateInfo) { mutableListOf() }
val libraryList = if (libraryMediaIds.contains(it.id)) {
libraryMap.getOrPut(dateInfo) { mutableListOf() }
} else {
null
}
val idList = idMap.getOrPut(dateInfo) { mutableListOf() } val idList = idMap.getOrPut(dateInfo) { mutableListOf() }
it.relation = "Episode ${v[0]}" it.relation = "Episode ${v[0]}"
if (!idList.contains(it.id)) { if (!idList.contains(it.id)) {
idList.add(it.id) idList.add(it.id)
list.add(it) list.add(it)
libraryList?.add(it)
} }
} }
calendar.postValue(map)
cachedAllCalendarData = allMap
cachedLibraryCalendarData = libraryMap
}
val cacheToUse: Map<String, MutableList<Media>> = if (showOnlyLibrary) {
cachedLibraryCalendarData ?: emptyMap()
} else {
cachedAllCalendarData ?: emptyMap()
}
calendar.postValue(cacheToUse)
} }
} }

View file

@ -3,7 +3,6 @@ package ani.dantotsu.media
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.SpannableString
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -21,7 +20,7 @@ import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.MarkdownCreatorActivity import ani.dantotsu.util.ActivityMarkdownCreator
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -59,7 +58,7 @@ class ReviewActivity : AppCompatActivity() {
binding.followFilterButton.setOnClickListener { binding.followFilterButton.setOnClickListener {
ContextCompat.startActivity( ContextCompat.startActivity(
this, this,
Intent(this, MarkdownCreatorActivity::class.java) Intent(this, ActivityMarkdownCreator::class.java)
.putExtra("type", "review"), .putExtra("type", "review"),
null null
) )

View file

@ -183,6 +183,12 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
binding.searchByImage.setOnClickListener { binding.searchByImage.setOnClickListener {
activity.startActivity(Intent(activity, ImageSearchActivity::class.java)) activity.startActivity(Intent(activity, ImageSearchActivity::class.java))
} }
binding.clearHistory.setOnClickListener {
it.startAnimation(fadeOutAnimation())
it.visibility = View.GONE
searchHistoryAdapter.clearHistory()
}
updateClearHistoryVisibility()
fun searchTitle() { fun searchTitle() {
activity.result.apply { activity.result.apply {
search = search =
@ -300,11 +306,17 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
} }
binding.searchResultLayout.visibility = View.VISIBLE binding.searchResultLayout.visibility = View.VISIBLE
binding.clearHistory.visibility = View.GONE
binding.searchHistoryList.visibility = View.GONE binding.searchHistoryList.visibility = View.GONE
binding.searchByImage.visibility = View.GONE binding.searchByImage.visibility = View.GONE
} }
} }
private fun updateClearHistoryVisibility() {
binding.clearHistory.visibility =
if (searchHistoryAdapter.itemCount > 0) View.VISIBLE else View.GONE
}
private fun fadeInAnimation(): Animation { private fun fadeInAnimation(): Animation {
return AlphaAnimation(0f, 1f).apply { return AlphaAnimation(0f, 1f).apply {
duration = 150 duration = 150
@ -375,4 +387,3 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
override fun getItemCount(): Int = chips.size override fun getItemCount(): Int = chips.size
} }
} }

View file

@ -49,6 +49,12 @@ class SearchHistoryAdapter(private val type: String, private val searchClicked:
PrefManager.setVal(historyType, searchHistory) PrefManager.setVal(historyType, searchHistory)
} }
fun clearHistory() {
searchHistory?.clear()
PrefManager.setVal(historyType, searchHistory)
submitList(searchHistory?.toList())
}
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int

View file

@ -8,7 +8,6 @@ import android.view.ViewGroup
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import androidx.core.view.isGone import androidx.core.view.isGone
@ -19,7 +18,7 @@ import ani.dantotsu.FileUrl
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.currActivity import ani.dantotsu.currActivity
import ani.dantotsu.databinding.DialogLayoutBinding import ani.dantotsu.databinding.DialogLayoutBinding
import ani.dantotsu.databinding.ItemAnimeWatchBinding import ani.dantotsu.databinding.ItemMediaSourceBinding
import ani.dantotsu.databinding.ItemChipBinding import ani.dantotsu.databinding.ItemChipBinding
import ani.dantotsu.displayTimer import ani.dantotsu.displayTimer
import ani.dantotsu.isOnline import ani.dantotsu.isOnline
@ -33,12 +32,14 @@ import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.others.webview.CookieCatcher 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.OfflineAnimeParser
import ani.dantotsu.parsers.WatchSources import ani.dantotsu.parsers.WatchSources
import ani.dantotsu.px import ani.dantotsu.px
import ani.dantotsu.settings.FAQActivity import ani.dantotsu.settings.FAQActivity
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_SUBSCRIPTION_CHECK import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_SUBSCRIPTION_CHECK
@ -54,16 +55,13 @@ class AnimeWatchAdapter(
) : RecyclerView.Adapter<AnimeWatchAdapter.ViewHolder>() { ) : RecyclerView.Adapter<AnimeWatchAdapter.ViewHolder>() {
private var autoSelect = true private var autoSelect = true
var subscribe: MediaDetailsActivity.PopImageButton? = null var subscribe: MediaDetailsActivity.PopImageButton? = null
private var _binding: ItemAnimeWatchBinding? = null private var _binding: ItemMediaSourceBinding? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val bind = ItemAnimeWatchBinding.inflate(LayoutInflater.from(parent.context), parent, false) val bind = ItemMediaSourceBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(bind) return ViewHolder(bind)
} }
private var nestedDialog: AlertDialog? = null
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val binding = holder.binding val binding = holder.binding
_binding = binding _binding = binding
@ -75,7 +73,7 @@ class AnimeWatchAdapter(
null null
) )
} }
//Youtube // Youtube
if (media.anime?.youtube != null && PrefManager.getVal(PrefName.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 {
@ -89,7 +87,7 @@ class AnimeWatchAdapter(
R.string.subbed R.string.subbed
) )
//PreferDub // PreferDub
var changing = false var changing = false
binding.animeSourceDubbed.setOnCheckedChangeListener { _, isChecked -> binding.animeSourceDubbed.setOnCheckedChangeListener { _, isChecked ->
binding.animeSourceDubbedText.text = binding.animeSourceDubbedText.text =
@ -99,8 +97,8 @@ class AnimeWatchAdapter(
if (!changing) fragment.onDubClicked(isChecked) if (!changing) fragment.onDubClicked(isChecked)
} }
//Wrong Title // Wrong Title
binding.animeSourceSearch.setOnClickListener { binding.mediaSourceSearch.setOnClickListener {
SourceSearchDialogFragment().show( SourceSearchDialogFragment().show(
fragment.requireActivity().supportFragmentManager, fragment.requireActivity().supportFragmentManager,
null null
@ -108,37 +106,37 @@ class AnimeWatchAdapter(
} }
val offline = !isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode) val offline = !isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode)
binding.animeSourceNameContainer.isGone = offline binding.mediaSourceNameContainer.isGone = offline
binding.animeSourceSettings.isGone = offline binding.mediaSourceSettings.isGone = offline
binding.animeSourceSearch.isGone = offline binding.mediaSourceSearch.isGone = offline
binding.animeSourceTitle.isGone = offline binding.mediaSourceTitle.isGone = offline
//Source Selection // Source Selection
var source = var source =
media.selected!!.sourceIndex.let { if (it >= watchSources.names.size) 0 else it } media.selected!!.sourceIndex.let { if (it >= watchSources.names.size) 0 else it }
setLanguageList(media.selected!!.langIndex, source) setLanguageList(media.selected!!.langIndex, source)
if (watchSources.names.isNotEmpty() && source in 0 until watchSources.names.size) { if (watchSources.names.isNotEmpty() && source in 0 until watchSources.names.size) {
binding.animeSource.setText(watchSources.names[source]) binding.mediaSource.setText(watchSources.names[source])
watchSources[source].apply { watchSources[source].apply {
this.selectDub = media.selected!!.preferDub this.selectDub = media.selected!!.preferDub
binding.animeSourceTitle.text = showUserText binding.mediaSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } } showUserTextListener = { MainScope().launch { binding.mediaSourceTitle.text = it } }
binding.animeSourceDubbedCont.isVisible = isDubAvailableSeparately() binding.animeSourceDubbedCont.isVisible = isDubAvailableSeparately()
} }
} }
binding.animeSource.setAdapter( binding.mediaSource.setAdapter(
ArrayAdapter( ArrayAdapter(
fragment.requireContext(), fragment.requireContext(),
R.layout.item_dropdown, R.layout.item_dropdown,
watchSources.names watchSources.names
) )
) )
binding.animeSourceTitle.isSelected = true binding.mediaSourceTitle.isSelected = true
binding.animeSource.setOnItemClickListener { _, _, i, _ -> binding.mediaSource.setOnItemClickListener { _, _, i, _ ->
fragment.onSourceChange(i).apply { fragment.onSourceChange(i).apply {
binding.animeSourceTitle.text = showUserText binding.mediaSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } } showUserTextListener = { MainScope().launch { binding.mediaSourceTitle.text = it } }
changing = true changing = true
binding.animeSourceDubbed.isChecked = selectDub binding.animeSourceDubbed.isChecked = selectDub
changing = false changing = false
@ -150,15 +148,15 @@ class AnimeWatchAdapter(
fragment.loadEpisodes(i, false) fragment.loadEpisodes(i, false)
} }
binding.animeSourceLanguage.setOnItemClickListener { _, _, i, _ -> binding.mediaSourceLanguage.setOnItemClickListener { _, _, i, _ ->
// Check if 'extension' and 'selected' properties exist and are accessible // Check if 'extension' and 'selected' properties exist and are accessible
(watchSources[source] as? DynamicAnimeParser)?.let { ext -> (watchSources[source] as? DynamicAnimeParser)?.let { ext ->
ext.sourceLanguage = i ext.sourceLanguage = i
fragment.onLangChange(i) fragment.onLangChange(i)
fragment.onSourceChange(media.selected!!.sourceIndex).apply { fragment.onSourceChange(media.selected!!.sourceIndex).apply {
binding.animeSourceTitle.text = showUserText binding.mediaSourceTitle.text = showUserText
showUserTextListener = showUserTextListener =
{ MainScope().launch { binding.animeSourceTitle.text = it } } { MainScope().launch { binding.mediaSourceTitle.text = it } }
changing = true changing = true
binding.animeSourceDubbed.isChecked = selectDub binding.animeSourceDubbed.isChecked = selectDub
changing = false changing = false
@ -170,19 +168,19 @@ class AnimeWatchAdapter(
} ?: run { } } ?: run { }
} }
//settings // Settings
binding.animeSourceSettings.setOnClickListener { binding.mediaSourceSettings.setOnClickListener {
(watchSources[source] as? DynamicAnimeParser)?.let { ext -> (watchSources[source] as? DynamicAnimeParser)?.let { ext ->
fragment.openSettings(ext.extension) fragment.openSettings(ext.extension)
} }
} }
//Icons // Icons
//subscribe // Subscribe
subscribe = MediaDetailsActivity.PopImageButton( subscribe = MediaDetailsActivity.PopImageButton(
fragment.lifecycleScope, fragment.lifecycleScope,
binding.animeSourceSubscribe, binding.mediaSourceSubscribe,
R.drawable.ic_round_notifications_active_24, R.drawable.ic_round_notifications_active_24,
R.drawable.ic_round_notifications_none_24, R.drawable.ic_round_notifications_none_24,
R.color.bg_opp, R.color.bg_opp,
@ -190,45 +188,45 @@ class AnimeWatchAdapter(
fragment.subscribed, fragment.subscribed,
true true
) { ) {
fragment.onNotificationPressed(it, binding.animeSource.text.toString()) fragment.onNotificationPressed(it, binding.mediaSource.text.toString())
} }
subscribeButton(false) subscribeButton(false)
binding.animeSourceSubscribe.setOnLongClickListener { binding.mediaSourceSubscribe.setOnLongClickListener {
openSettings(fragment.requireContext(), CHANNEL_SUBSCRIPTION_CHECK) openSettings(fragment.requireContext(), CHANNEL_SUBSCRIPTION_CHECK)
} }
//Nested Button // Nested Button
binding.animeNestedButton.setOnClickListener { binding.mediaNestedButton.setOnClickListener {
val dialogView = val dialogBinding = DialogLayoutBinding.inflate(fragment.layoutInflater)
LayoutInflater.from(fragment.requireContext()).inflate(R.layout.dialog_layout, null) dialogBinding.apply {
val dialogBinding = DialogLayoutBinding.bind(dialogView)
var refresh = false var refresh = false
var run = false var run = false
var reversed = media.selected!!.recyclerReversed var reversed = media.selected!!.recyclerReversed
var style = var style =
media.selected!!.recyclerStyle ?: PrefManager.getVal(PrefName.AnimeDefaultView) media.selected!!.recyclerStyle ?: PrefManager.getVal(PrefName.AnimeDefaultView)
dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down" mediaSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.animeSourceTop.setOnClickListener { sortText.text = if (reversed) "Down to Up" else "Up to Down"
mediaSourceTop.setOnClickListener {
reversed = !reversed reversed = !reversed
dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f mediaSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down" sortText.text = if (reversed) "Down to Up" else "Up to Down"
run = true run = true
} }
//Grids // Grids
var selected = when (style) { var selected = when (style) {
0 -> dialogBinding.animeSourceList 0 -> mediaSourceList
1 -> dialogBinding.animeSourceGrid 1 -> mediaSourceGrid
2 -> dialogBinding.animeSourceCompact 2 -> mediaSourceCompact
else -> dialogBinding.animeSourceList else -> mediaSourceList
} }
when (style) { when (style) {
0 -> dialogBinding.layoutText.setText(R.string.list) 0 -> layoutText.setText(R.string.list)
1 -> dialogBinding.layoutText.setText(R.string.grid) 1 -> layoutText.setText(R.string.grid)
2 -> dialogBinding.layoutText.setText(R.string.compact) 2 -> layoutText.setText(R.string.compact)
else -> dialogBinding.animeSourceList else -> mediaSourceList
} }
selected.alpha = 1f selected.alpha = 1f
fun selected(it: ImageButton) { fun selected(it: ImageButton) {
@ -236,29 +234,29 @@ class AnimeWatchAdapter(
selected = it selected = it
selected.alpha = 1f selected.alpha = 1f
} }
dialogBinding.animeSourceList.setOnClickListener { mediaSourceList.setOnClickListener {
selected(it as ImageButton) selected(it as ImageButton)
style = 0 style = 0
dialogBinding.layoutText.setText(R.string.list) layoutText.setText(R.string.list)
run = true run = true
} }
dialogBinding.animeSourceGrid.setOnClickListener { mediaSourceGrid.setOnClickListener {
selected(it as ImageButton) selected(it as ImageButton)
style = 1 style = 1
dialogBinding.layoutText.setText(R.string.grid) layoutText.setText(R.string.grid)
run = true run = true
} }
dialogBinding.animeSourceCompact.setOnClickListener { mediaSourceCompact.setOnClickListener {
selected(it as ImageButton) selected(it as ImageButton)
style = 2 style = 2
dialogBinding.layoutText.setText(R.string.compact) layoutText.setText(R.string.compact)
run = true run = true
} }
dialogBinding.animeWebviewContainer.setOnClickListener { mediaWebviewContainer.setOnClickListener {
if (!WebViewUtil.supportsWebView(fragment.requireContext())) { if (!WebViewUtil.supportsWebView(fragment.requireContext())) {
toast(R.string.webview_not_installed) toast(R.string.webview_not_installed)
} }
//start CookieCatcher activity // Start CookieCatcher activity
if (watchSources.names.isNotEmpty() && source in 0 until watchSources.names.size) { if (watchSources.names.isNotEmpty() && source in 0 until watchSources.names.size) {
val sourceAHH = watchSources[source] as? DynamicAnimeParser val sourceAHH = watchSources[source] as? DynamicAnimeParser
val sourceHttp = val sourceHttp =
@ -272,7 +270,8 @@ class AnimeWatchAdapter(
} catch (e: Exception) { } catch (e: Exception) {
emptyMap() emptyMap()
} }
val intent = Intent(fragment.requireContext(), CookieCatcher::class.java) val intent =
Intent(fragment.requireContext(), CookieCatcher::class.java)
.putExtra("url", url) .putExtra("url", url)
.putExtra("headers", headersMap as HashMap<String, String>) .putExtra("headers", headersMap as HashMap<String, String>)
startActivity(fragment.requireContext(), intent, null) startActivity(fragment.requireContext(), intent, null)
@ -280,27 +279,24 @@ class AnimeWatchAdapter(
} }
} }
//hidden // Hidden
dialogBinding.animeScanlatorContainer.visibility = View.GONE mangaScanlatorContainer.visibility = View.GONE
dialogBinding.animeDownloadContainer.visibility = View.GONE animeDownloadContainer.visibility = View.GONE
fragment.requireContext().customAlertDialog().apply {
nestedDialog = AlertDialog.Builder(fragment.requireContext(), R.style.MyPopup) setTitle("Options")
.setTitle("Options") setCustomView(dialogBinding.root)
.setView(dialogView) setPosButton("OK") {
.setPositiveButton("OK") { _, _ ->
if (run) fragment.onIconPressed(style, reversed) if (run) fragment.onIconPressed(style, reversed)
if (refresh) fragment.loadEpisodes(source, true) if (refresh) fragment.loadEpisodes(source, true)
} }
.setNegativeButton("Cancel") { _, _ -> setNegButton("Cancel") {
if (refresh) fragment.loadEpisodes(source, true) if (refresh) fragment.loadEpisodes(source, true)
} }
.setOnCancelListener { show()
if (refresh) fragment.loadEpisodes(source, true)
} }
.create()
nestedDialog?.show()
} }
//Episode Handling }
// Episode Handling
handleEpisodes() handleEpisodes()
} }
@ -308,7 +304,7 @@ class AnimeWatchAdapter(
subscribe?.enabled(enabled) subscribe?.enabled(enabled)
} }
//Chips // Chips
fun updateChips(limit: Int, names: Array<String>, arr: Array<Int>, selected: Int = 0) { fun updateChips(limit: Int, names: Array<String>, arr: Array<Int>, selected: Int = 0) {
val binding = _binding val binding = _binding
if (binding != null) { if (binding != null) {
@ -319,13 +315,13 @@ class AnimeWatchAdapter(
val chip = val chip =
ItemChipBinding.inflate( ItemChipBinding.inflate(
LayoutInflater.from(fragment.context), LayoutInflater.from(fragment.context),
binding.animeSourceChipGroup, binding.mediaSourceChipGroup,
false false
).root ).root
chip.isCheckable = true chip.isCheckable = true
fun selected() { fun selected() {
chip.isChecked = true chip.isChecked = true
binding.animeWatchChipScroll.smoothScrollTo( binding.mediaWatchChipScroll.smoothScrollTo(
(chip.left - screenWidth / 2) + (chip.width / 2), (chip.left - screenWidth / 2) + (chip.width / 2),
0 0
) )
@ -344,14 +340,14 @@ class AnimeWatchAdapter(
selected() selected()
fragment.onChipClicked(position, limit * (position), last - 1) fragment.onChipClicked(position, limit * (position), last - 1)
} }
binding.animeSourceChipGroup.addView(chip) binding.mediaSourceChipGroup.addView(chip)
if (selected == position) { if (selected == position) {
selected() selected()
select = chip select = chip
} }
} }
if (select != null) if (select != null)
binding.animeWatchChipScroll.apply { binding.mediaWatchChipScroll.apply {
post { post {
scrollTo( scrollTo(
(select.left - screenWidth / 2) + (select.width / 2), (select.left - screenWidth / 2) + (select.width / 2),
@ -363,7 +359,7 @@ class AnimeWatchAdapter(
} }
fun clearChips() { fun clearChips() {
_binding?.animeSourceChipGroup?.removeAllViews() _binding?.mediaSourceChipGroup?.removeAllViews()
} }
fun handleEpisodes() { fun handleEpisodes() {
@ -379,15 +375,15 @@ class AnimeWatchAdapter(
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)) {
binding.animeSourceContinue.visibility = View.VISIBLE binding.sourceContinue.visibility = View.VISIBLE
handleProgress( handleProgress(
binding.itemEpisodeProgressCont, binding.itemMediaProgressCont,
binding.itemEpisodeProgress, binding.itemMediaProgress,
binding.itemEpisodeProgressEmpty, binding.itemMediaProgressEmpty,
media.id, media.id,
continueEp continueEp
) )
if ((binding.itemEpisodeProgress.layoutParams as LinearLayout.LayoutParams).weight > PrefManager.getVal<Float>( if ((binding.itemMediaProgress.layoutParams as LinearLayout.LayoutParams).weight > PrefManager.getVal<Float>(
PrefName.WatchPercentage PrefName.WatchPercentage
) )
) { ) {
@ -395,9 +391,9 @@ class AnimeWatchAdapter(
if (e != -1 && e + 1 < episodes.size) { if (e != -1 && e + 1 < episodes.size) {
continueEp = episodes[e + 1] continueEp = episodes[e + 1]
handleProgress( handleProgress(
binding.itemEpisodeProgressCont, binding.itemMediaProgressCont,
binding.itemEpisodeProgress, binding.itemMediaProgress,
binding.itemEpisodeProgressEmpty, binding.itemMediaProgressEmpty,
media.id, media.id,
continueEp continueEp
) )
@ -407,51 +403,63 @@ class AnimeWatchAdapter(
val cleanedTitle = ep.title?.let { MediaNameAdapter.removeEpisodeNumber(it) } val cleanedTitle = ep.title?.let { MediaNameAdapter.removeEpisodeNumber(it) }
binding.itemEpisodeImage.loadImage( binding.itemMediaImage.loadImage(
ep.thumb ?: FileUrl[media.banner ?: media.cover], 0 ep.thumb ?: FileUrl[media.banner ?: media.cover], 0
) )
if (ep.filler) binding.itemEpisodeFillerView.visibility = View.VISIBLE if (ep.filler) binding.itemEpisodeFillerView.visibility = View.VISIBLE
binding.animeSourceContinueText.text = binding.mediaSourceContinueText.text =
currActivity()!!.getString( currActivity()!!.getString(
R.string.continue_episode, ep.number, if (ep.filler) R.string.continue_episode, ep.number, if (ep.filler)
currActivity()!!.getString(R.string.filler_tag) currActivity()!!.getString(R.string.filler_tag)
else else
"", cleanedTitle "", cleanedTitle
) )
binding.animeSourceContinue.setOnClickListener { binding.sourceContinue.setOnClickListener {
fragment.onEpisodeClick(continueEp) fragment.onEpisodeClick(continueEp)
} }
if (fragment.continueEp) { if (fragment.continueEp) {
if ( if (
(binding.itemEpisodeProgress.layoutParams as LinearLayout.LayoutParams) (binding.itemMediaProgress.layoutParams as LinearLayout.LayoutParams)
.weight < PrefManager.getVal<Float>(PrefName.WatchPercentage) .weight < PrefManager.getVal<Float>(PrefName.WatchPercentage)
) { ) {
binding.animeSourceContinue.performClick() binding.sourceContinue.performClick()
fragment.continueEp = false fragment.continueEp = false
} }
} }
} else { } else {
binding.animeSourceContinue.visibility = View.GONE binding.sourceContinue.visibility = View.GONE
} }
binding.animeSourceProgressBar.visibility = View.GONE binding.sourceProgressBar.visibility = View.GONE
val sourceFound = media.anime.episodes!!.isNotEmpty() val sourceFound = media.anime.episodes!!.isNotEmpty()
binding.animeSourceNotFound.isGone = sourceFound val isDownloadedSource = watchSources[media.selected!!.sourceIndex] is OfflineAnimeParser
if (isDownloadedSource) {
binding.sourceNotFound.text = if (sourceFound) {
currActivity()!!.getString(R.string.source_not_found)
} else {
currActivity()!!.getString(R.string.download_not_found)
}
} else {
binding.sourceNotFound.text = currActivity()!!.getString(R.string.source_not_found)
}
binding.sourceNotFound.isGone = sourceFound
binding.faqbutton.isGone = sourceFound binding.faqbutton.isGone = sourceFound
if (!sourceFound && PrefManager.getVal(PrefName.SearchSources) && autoSelect) { if (!sourceFound && PrefManager.getVal(PrefName.SearchSources) && autoSelect) {
if (binding.animeSource.adapter.count > media.selected!!.sourceIndex + 1) { if (binding.mediaSource.adapter.count > media.selected!!.sourceIndex + 1) {
val nextIndex = media.selected!!.sourceIndex + 1 val nextIndex = media.selected!!.sourceIndex + 1
binding.animeSource.setText( binding.mediaSource.setText(
binding.animeSource.adapter binding.mediaSource.adapter
.getItem(nextIndex).toString(), false .getItem(nextIndex).toString(), false
) )
fragment.onSourceChange(nextIndex).apply { fragment.onSourceChange(nextIndex).apply {
binding.animeSourceTitle.text = showUserText binding.mediaSourceTitle.text = showUserText
showUserTextListener = showUserTextListener =
{ MainScope().launch { binding.animeSourceTitle.text = it } } { MainScope().launch { binding.mediaSourceTitle.text = it } }
binding.animeSourceDubbed.isChecked = selectDub binding.animeSourceDubbed.isChecked = selectDub
binding.animeSourceDubbedCont.isVisible = isDubAvailableSeparately() binding.animeSourceDubbedCont.isVisible = isDubAvailableSeparately()
setLanguageList(0, nextIndex) setLanguageList(0, nextIndex)
@ -460,13 +468,13 @@ class AnimeWatchAdapter(
fragment.loadEpisodes(nextIndex, false) fragment.loadEpisodes(nextIndex, false)
} }
} }
binding.animeSource.setOnClickListener { autoSelect = false } binding.mediaSource.setOnClickListener { autoSelect = false }
} else { } else {
binding.animeSourceContinue.visibility = View.GONE binding.sourceContinue.visibility = View.GONE
binding.animeSourceNotFound.visibility = View.GONE binding.sourceNotFound.visibility = View.GONE
binding.faqbutton.visibility = View.GONE binding.faqbutton.visibility = View.GONE
clearChips() clearChips()
binding.animeSourceProgressBar.visibility = View.VISIBLE binding.sourceProgressBar.visibility = View.VISIBLE
} }
} }
} }
@ -480,9 +488,9 @@ class AnimeWatchAdapter(
ext.sourceLanguage = lang ext.sourceLanguage = lang
} }
try { try {
binding?.animeSourceLanguage?.setText(parser.extension.sources[lang].lang) binding?.mediaSourceLanguage?.setText(parser.extension.sources[lang].lang)
} catch (e: IndexOutOfBoundsException) { } catch (e: IndexOutOfBoundsException) {
binding?.animeSourceLanguage?.setText( binding?.mediaSourceLanguage?.setText(
parser.extension.sources.firstOrNull()?.lang ?: "Unknown" parser.extension.sources.firstOrNull()?.lang ?: "Unknown"
) )
} }
@ -493,9 +501,9 @@ class AnimeWatchAdapter(
) )
val items = adapter.count val items = adapter.count
binding?.animeSourceLanguageContainer?.visibility = binding?.mediaSourceLanguageContainer?.visibility =
if (items > 1) View.VISIBLE else View.GONE if (items > 1) View.VISIBLE else View.GONE
binding?.animeSourceLanguage?.setAdapter(adapter) binding?.mediaSourceLanguage?.setAdapter(adapter)
} }
} }
@ -503,7 +511,7 @@ class AnimeWatchAdapter(
override fun getItemCount(): Int = 1 override fun getItemCount(): Int = 1
inner class ViewHolder(val binding: ItemAnimeWatchBinding) : inner class ViewHolder(val binding: ItemMediaSourceBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
init { init {
displayTimer(media, binding.animeSourceContainer) displayTimer(media, binding.animeSourceContainer)

View file

@ -32,7 +32,7 @@ import ani.dantotsu.FileUrl
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.addons.download.DownloadAddonManager import ani.dantotsu.addons.download.DownloadAddonManager
import ani.dantotsu.connections.anilist.api.MediaStreamingEpisode import ani.dantotsu.connections.anilist.api.MediaStreamingEpisode
import ani.dantotsu.databinding.FragmentAnimeWatchBinding import ani.dantotsu.databinding.FragmentMediaSourceBinding
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.DownloadsManager.Companion.compareName import ani.dantotsu.download.DownloadsManager.Companion.compareName
@ -63,6 +63,7 @@ import ani.dantotsu.toast
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.StoragePermissions.Companion.accessAlertDialog import ani.dantotsu.util.StoragePermissions.Companion.accessAlertDialog
import ani.dantotsu.util.StoragePermissions.Companion.hasDirAccess import ani.dantotsu.util.StoragePermissions.Companion.hasDirAccess
import ani.dantotsu.util.customAlertDialog
import com.anggrayudi.storage.file.extension import com.anggrayudi.storage.file.extension
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
@ -80,7 +81,7 @@ import kotlin.math.max
import kotlin.math.roundToInt import kotlin.math.roundToInt
class AnimeWatchFragment : Fragment() { class AnimeWatchFragment : Fragment() {
private var _binding: FragmentAnimeWatchBinding? = null private var _binding: FragmentMediaSourceBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private val model: MediaDetailsViewModel by activityViewModels() private val model: MediaDetailsViewModel by activityViewModels()
@ -107,7 +108,7 @@ class AnimeWatchFragment : Fragment() {
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
_binding = FragmentAnimeWatchBinding.inflate(inflater, container, false) _binding = FragmentMediaSourceBinding.inflate(inflater, container, false)
return _binding?.root return _binding?.root
} }
@ -128,7 +129,7 @@ class AnimeWatchFragment : Fragment() {
) )
binding.animeSourceRecycler.updatePadding(bottom = binding.animeSourceRecycler.paddingBottom + navBarHeight) binding.mediaSourceRecycler.updatePadding(bottom = binding.mediaSourceRecycler.paddingBottom + navBarHeight)
screenWidth = resources.displayMetrics.widthPixels.dp screenWidth = resources.displayMetrics.widthPixels.dp
var maxGridSize = (screenWidth / 100f).roundToInt() var maxGridSize = (screenWidth / 100f).roundToInt()
@ -152,13 +153,13 @@ class AnimeWatchFragment : Fragment() {
} }
} }
binding.animeSourceRecycler.layoutManager = gridLayoutManager binding.mediaSourceRecycler.layoutManager = gridLayoutManager
binding.ScrollTop.setOnClickListener { binding.ScrollTop.setOnClickListener {
binding.animeSourceRecycler.scrollToPosition(10) binding.mediaSourceRecycler.scrollToPosition(10)
binding.animeSourceRecycler.smoothScrollToPosition(0) binding.mediaSourceRecycler.smoothScrollToPosition(0)
} }
binding.animeSourceRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.mediaSourceRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
@ -172,7 +173,7 @@ class AnimeWatchFragment : Fragment() {
} }
}) })
model.scrolledToTop.observe(viewLifecycleOwner) { model.scrolledToTop.observe(viewLifecycleOwner) {
if (it) binding.animeSourceRecycler.scrollToPosition(0) if (it) binding.mediaSourceRecycler.scrollToPosition(0)
} }
continueEp = model.continueMedia ?: false continueEp = model.continueMedia ?: false
@ -205,7 +206,7 @@ class AnimeWatchFragment : Fragment() {
offlineMode = offlineMode offlineMode = offlineMode
) )
binding.animeSourceRecycler.adapter = binding.mediaSourceRecycler.adapter =
ConcatAdapter(headerAdapter, episodeAdapter) ConcatAdapter(headerAdapter, episodeAdapter)
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
@ -266,7 +267,7 @@ class AnimeWatchFragment : Fragment() {
} }
media.anime?.episodes = episodes media.anime?.episodes = episodes
//CHIP GROUP // CHIP GROUP
val total = episodes.size val total = episodes.size
val divisions = total.toDouble() / 10 val divisions = total.toDouble() / 10
start = 0 start = 0
@ -396,17 +397,15 @@ class AnimeWatchFragment : Fragment() {
if (allSettings.size > 1) { if (allSettings.size > 1) {
val names = val names =
allSettings.map { LanguageMapper.getLanguageName(it.lang) }.toTypedArray() allSettings.map { LanguageMapper.getLanguageName(it.lang) }.toTypedArray()
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup) requireContext()
.setTitle("Select a Source") .customAlertDialog()
.setSingleChoiceItems(names, -1) { dialog, which -> .apply {
setTitle("Select a Source")
singleChoiceItems(names) { which ->
selectedSetting = allSettings[which] selectedSetting = allSettings[which]
itemSelected = true itemSelected = true
dialog.dismiss()
// Move the fragment transaction here
requireActivity().runOnUiThread { requireActivity().runOnUiThread {
val fragment = val fragment = AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) {
AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) {
changeUIVisibility(true) changeUIVisibility(true)
loadEpisodes(media.selected!!.sourceIndex, true) loadEpisodes(media.selected!!.sourceIndex, true)
} }
@ -417,13 +416,13 @@ class AnimeWatchFragment : Fragment() {
.commit() .commit()
} }
} }
.setOnDismissListener { onDismiss {
if (!itemSelected) { if (!itemSelected) {
changeUIVisibility(true) changeUIVisibility(true)
} }
} }
.show() show()
dialog.window?.setDimAmount(0.8f) }
} else { } else {
// If there's only one setting, proceed with the fragment transaction // If there's only one setting, proceed with the fragment transaction
requireActivity().runOnUiThread { requireActivity().runOnUiThread {
@ -432,11 +431,12 @@ class AnimeWatchFragment : Fragment() {
changeUIVisibility(true) changeUIVisibility(true)
loadEpisodes(media.selected!!.sourceIndex, true) loadEpisodes(media.selected!!.sourceIndex, true)
} }
parentFragmentManager.beginTransaction() parentFragmentManager.beginTransaction().apply {
.setCustomAnimations(R.anim.slide_up, R.anim.slide_down) setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
.replace(R.id.fragmentExtensionsContainer, fragment) replace(R.id.fragmentExtensionsContainer, fragment)
.addToBackStack(null) addToBackStack(null)
.commit() commit()
}
} }
} }
@ -635,7 +635,7 @@ class AnimeWatchFragment : Fragment() {
private fun reload() { private fun reload() {
val selected = model.loadSelected(media) val selected = model.loadSelected(media)
//Find latest episode for subscription // Find latest episode for subscription
selected.latest = selected.latest =
media.anime?.episodes?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f media.anime?.episodes?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f
selected.latest = selected.latest =
@ -679,14 +679,14 @@ class AnimeWatchFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
binding.mediaInfoProgressBar.visibility = progress binding.mediaInfoProgressBar.visibility = progress
binding.animeSourceRecycler.layoutManager?.onRestoreInstanceState(state) binding.mediaSourceRecycler.layoutManager?.onRestoreInstanceState(state)
requireActivity().setNavigationTheme() requireActivity().setNavigationTheme()
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
state = binding.animeSourceRecycler.layoutManager?.onSaveInstanceState() state = binding.mediaSourceRecycler.layoutManager?.onSaveInstanceState()
} }
companion object { companion object {

View file

@ -23,6 +23,7 @@ import ani.dantotsu.media.MediaNameAdapter
import ani.dantotsu.media.MediaType import ani.dantotsu.media.MediaType
import ani.dantotsu.setAnimation import ani.dantotsu.setAnimation
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.util.customAlertDialog
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
@ -106,8 +107,8 @@ class EpisodeAdapter(
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 }
Glide.with(binding.itemEpisodeImage).load(thumb ?: media.cover).override(400, 0) Glide.with(binding.itemMediaImage).load(thumb ?: media.cover).override(400, 0)
.into(binding.itemEpisodeImage) .into(binding.itemMediaImage)
binding.itemEpisodeNumber.text = ep.number binding.itemEpisodeNumber.text = ep.number
binding.itemEpisodeTitle.text = if (ep.number == title) "Episode $title" else title binding.itemEpisodeTitle.text = if (ep.number == title) "Episode $title" else title
@ -140,9 +141,9 @@ class EpisodeAdapter(
} }
handleProgress( handleProgress(
binding.itemEpisodeProgressCont, binding.itemMediaProgressCont,
binding.itemEpisodeProgress, binding.itemMediaProgress,
binding.itemEpisodeProgressEmpty, binding.itemMediaProgressEmpty,
media.id, media.id,
ep.number ep.number
) )
@ -154,8 +155,8 @@ class EpisodeAdapter(
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 }
Glide.with(binding.itemEpisodeImage).load(thumb ?: media.cover).override(400, 0) Glide.with(binding.itemMediaImage).load(thumb ?: media.cover).override(400, 0)
.into(binding.itemEpisodeImage) .into(binding.itemMediaImage)
binding.itemEpisodeNumber.text = ep.number binding.itemEpisodeNumber.text = ep.number
binding.itemEpisodeTitle.text = title binding.itemEpisodeTitle.text = title
@ -183,9 +184,9 @@ class EpisodeAdapter(
binding.itemEpisodeViewed.visibility = View.GONE binding.itemEpisodeViewed.visibility = View.GONE
} }
handleProgress( handleProgress(
binding.itemEpisodeProgressCont, binding.itemMediaProgressCont,
binding.itemEpisodeProgress, binding.itemMediaProgress,
binding.itemEpisodeProgressEmpty, binding.itemMediaProgressEmpty,
media.id, media.id,
ep.number ep.number
) )
@ -208,9 +209,9 @@ class EpisodeAdapter(
} }
} }
handleProgress( handleProgress(
binding.itemEpisodeProgressCont, binding.itemMediaProgressCont,
binding.itemEpisodeProgress, binding.itemMediaProgress,
binding.itemEpisodeProgressEmpty, binding.itemMediaProgressEmpty,
media.id, media.id,
ep.number ep.number
) )
@ -318,16 +319,14 @@ class EpisodeAdapter(
fragment.onAnimeEpisodeStopDownloadClick(episodeNumber) fragment.onAnimeEpisodeStopDownloadClick(episodeNumber)
return@setOnClickListener return@setOnClickListener
} else if (downloadedEpisodes.contains(episodeNumber)) { } else if (downloadedEpisodes.contains(episodeNumber)) {
val builder = AlertDialog.Builder(currContext(), R.style.MyPopup) binding.root.context.customAlertDialog().apply {
builder.setTitle("Delete Episode") setTitle("Delete Episode")
builder.setMessage("Are you sure you want to delete Episode ${episodeNumber}?") setMessage("Are you sure you want to delete Episode $episodeNumber?")
builder.setPositiveButton("Yes") { _, _ -> setPosButton(R.string.yes) {
fragment.onAnimeEpisodeRemoveDownloadClick(episodeNumber) fragment.onAnimeEpisodeRemoveDownloadClick(episodeNumber)
} }
builder.setNegativeButton("No") { _, _ -> setNegButton(R.string.no)
} }.show()
val dialog = builder.show()
dialog.window?.setDimAmount(0.8f)
return@setOnClickListener return@setOnClickListener
} else { } else {
fragment.onAnimeEpisodeDownloadClick(episodeNumber) fragment.onAnimeEpisodeDownloadClick(episodeNumber)

View file

@ -444,15 +444,12 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
if (subtitles.isNotEmpty()) { if (subtitles.isNotEmpty()) {
val subtitleNames = subtitles.map { it.language } val subtitleNames = subtitles.map { it.language }
var subtitleToDownload: Subtitle? = null var subtitleToDownload: Subtitle? = null
val alertDialog = AlertDialog.Builder(context, R.style.MyPopup) requireActivity().customAlertDialog().apply {
.setTitle(R.string.download_subtitle) setTitle(R.string.download_subtitle)
.setSingleChoiceItems( singleChoiceItems(subtitleNames.toTypedArray()) {which ->
subtitleNames.toTypedArray(),
-1
) { _, which ->
subtitleToDownload = subtitles[which] subtitleToDownload = subtitles[which]
} }
.setPositiveButton(R.string.download) { dialog, _ -> setPosButton(R.string.download) {
scope.launch { scope.launch {
if (subtitleToDownload != null) { if (subtitleToDownload != null) {
SubtitleDownloader.downloadSubtitle( SubtitleDownloader.downloadSubtitle(
@ -466,13 +463,9 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
) )
} }
} }
dialog.dismiss()
} }
.setNegativeButton(R.string.cancel) { dialog, _ -> setNegButton(R.string.cancel) {}
dialog.dismiss() }.show()
}
.show()
alertDialog.window?.setDimAmount(0.8f)
} else { } else {
snackString(R.string.no_subtitles_available) snackString(R.string.no_subtitles_available)
} }
@ -576,65 +569,63 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
if (audioTracks.isNotEmpty()) { if (audioTracks.isNotEmpty()) {
val audioNamesArray = audioTracks.toTypedArray() val audioNamesArray = audioTracks.toTypedArray()
val checkedItems = BooleanArray(audioNamesArray.size) { false } val checkedItems = BooleanArray(audioNamesArray.size) { false }
val alertDialog = AlertDialog.Builder(currContext, R.style.MyPopup)
.setTitle(R.string.download_audio_tracks) currContext.customAlertDialog().apply{ // ToTest
.setMultiChoiceItems(audioNamesArray, checkedItems) { _, which, isChecked -> setTitle(R.string.download_audio_tracks)
val audioPair = Pair(extractor.audioTracks[which].url, extractor.audioTracks[which].lang) multiChoiceItems(audioNamesArray, checkedItems) {
it.forEachIndexed { index, isChecked ->
val audioPair = Pair(extractor.audioTracks[index].url, extractor.audioTracks[index].lang)
if (isChecked) { if (isChecked) {
selectedAudioTracks.add(audioPair) selectedAudioTracks.add(audioPair)
} else { } else {
selectedAudioTracks.remove(audioPair) selectedAudioTracks.remove(audioPair)
} }
} }
.setPositiveButton(R.string.download) { _, _ -> }
dialog?.dismiss() setPosButton(R.string.download) {
go() go()
} }
.setNegativeButton(R.string.skip) { dialog, _ -> setNegButton(R.string.skip) {
selectedAudioTracks = mutableListOf() selectedAudioTracks = mutableListOf()
go() go()
dialog.dismiss()
} }
.setNeutralButton(R.string.cancel) { dialog, _ -> setNeutralButton(R.string.cancel) {
selectedAudioTracks = mutableListOf() selectedAudioTracks = mutableListOf()
dialog.dismiss()
} }
.show() show()
alertDialog.window?.setDimAmount(0.8f) }
} else { } else {
go() go()
} }
} }
if (subtitles.isNotEmpty()) { if (subtitles.isNotEmpty()) { // ToTest
val subtitleNamesArray = subtitleNames.toTypedArray() val subtitleNamesArray = subtitleNames.toTypedArray()
val checkedItems = BooleanArray(subtitleNamesArray.size) { false } val checkedItems = BooleanArray(subtitleNamesArray.size) { false }
val alertDialog = AlertDialog.Builder(currContext, R.style.MyPopup) currContext.customAlertDialog().apply {
.setTitle(R.string.download_subtitle) setTitle(R.string.download_subtitle)
.setMultiChoiceItems(subtitleNamesArray, checkedItems) { _, which, isChecked -> multiChoiceItems(subtitleNamesArray, checkedItems) {
val subtitlePair = Pair(subtitles[which].file.url, subtitles[which].language) it.forEachIndexed { index, isChecked ->
val subtitlePair = Pair(subtitles[index].file.url, subtitles[index].language)
if (isChecked) { if (isChecked) {
selectedSubtitles.add(subtitlePair) selectedSubtitles.add(subtitlePair)
} else { } else {
selectedSubtitles.remove(subtitlePair) selectedSubtitles.remove(subtitlePair)
} }
} }
.setPositiveButton(R.string.download) { _, _ -> }
dialog?.dismiss() setPosButton(R.string.download) {
checkAudioTracks() checkAudioTracks()
} }
.setNegativeButton(R.string.skip) { dialog, _ -> setNegButton(R.string.skip) {
selectedSubtitles = mutableListOf() selectedSubtitles = mutableListOf()
checkAudioTracks() checkAudioTracks()
dialog.dismiss()
} }
.setNeutralButton(R.string.cancel) { dialog, _ -> setNeutralButton(R.string.cancel) {
selectedSubtitles = mutableListOf() selectedSubtitles = mutableListOf()
dialog.dismiss()
} }
.show() show()
alertDialog.window?.setDimAmount(0.8f) }
} else { } else {
checkAudioTracks() checkAudioTracks()
} }

View file

@ -21,6 +21,7 @@ import ani.dantotsu.setAnimation
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.ColorEditor.Companion.adjustColorForContrast import ani.dantotsu.util.ColorEditor.Companion.adjustColorForContrast
import ani.dantotsu.util.ColorEditor.Companion.getContrastRatio import ani.dantotsu.util.ColorEditor.Companion.getContrastRatio
import ani.dantotsu.util.customAlertDialog
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.Section import com.xwray.groupie.Section
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
@ -385,19 +386,14 @@ class CommentItem(
* @param callback the callback to call when the user clicks yes * @param callback the callback to call when the user clicks yes
*/ */
private fun dialogBuilder(title: String, message: String, callback: () -> Unit) { private fun dialogBuilder(title: String, message: String, callback: () -> Unit) {
val alertDialog = commentsFragment.activity.customAlertDialog().apply {
android.app.AlertDialog.Builder(commentsFragment.activity, R.style.MyPopup) setTitle(title)
.setTitle(title) setMessage(message)
.setMessage(message) setPosButton("Yes") {
.setPositiveButton("Yes") { dialog, _ ->
callback() callback()
dialog.dismiss()
} }
.setNegativeButton("No") { dialog, _ -> setNegButton("No") {}
dialog.dismiss() }.show()
}
val dialog = alertDialog.show()
dialog?.window?.setDimAmount(0.8f)
} }
private val usernameColors: Array<String> = arrayOf( private val usernameColors: Array<String> = arrayOf(

View file

@ -25,6 +25,7 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.comments.Comment import ani.dantotsu.connections.comments.Comment
import ani.dantotsu.connections.comments.CommentResponse import ani.dantotsu.connections.comments.CommentResponse
import ani.dantotsu.connections.comments.CommentsAPI import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.databinding.DialogEdittextBinding
import ani.dantotsu.databinding.FragmentCommentsBinding import ani.dantotsu.databinding.FragmentCommentsBinding
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
@ -34,6 +35,7 @@ import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.Section import com.xwray.groupie.Section
import io.noties.markwon.editor.MarkwonEditor import io.noties.markwon.editor.MarkwonEditor
@ -162,33 +164,26 @@ class CommentsFragment : Fragment() {
} }
binding.commentFilter.setOnClickListener { binding.commentFilter.setOnClickListener {
val alertDialog = AlertDialog.Builder(activity, R.style.MyPopup) activity.customAlertDialog().apply {
.setTitle("Enter a chapter/episode number tag") val customView = DialogEdittextBinding.inflate(layoutInflater)
.setView(R.layout.dialog_edittext) setTitle("Enter a chapter/episode number tag")
.setPositiveButton("OK") { dialog, _ -> setCustomView(customView.root)
val editText = setPosButton("OK") {
(dialog as AlertDialog).findViewById<EditText>(R.id.dialogEditText) val text = customView.dialogEditText.text.toString()
val text = editText?.text.toString()
filterTag = text.toIntOrNull() filterTag = text.toIntOrNull()
lifecycleScope.launch { lifecycleScope.launch {
loadAndDisplayComments() loadAndDisplayComments()
} }
dialog.dismiss()
} }
.setNeutralButton("Clear") { dialog, _ -> setNeutralButton("Clear") {
filterTag = null filterTag = null
lifecycleScope.launch { lifecycleScope.launch {
loadAndDisplayComments() loadAndDisplayComments()
} }
dialog.dismiss()
} }
.setNegativeButton("Cancel") { dialog, _ -> setNegButton("Cancel") { filterTag = null }
filterTag = null show()
dialog.dismiss()
} }
val dialog = alertDialog.show()
dialog?.window?.setDimAmount(0.8f)
} }
var isFetching = false var isFetching = false
@ -303,13 +298,12 @@ class CommentsFragment : Fragment() {
activity.binding.commentLabel.setOnClickListener { activity.binding.commentLabel.setOnClickListener {
//alert dialog to enter a number, with a cancel and ok button //alert dialog to enter a number, with a cancel and ok button
val alertDialog = AlertDialog.Builder(activity, R.style.MyPopup) activity.customAlertDialog().apply {
.setTitle("Enter a chapter/episode number tag") val customView = DialogEdittextBinding.inflate(layoutInflater)
.setView(R.layout.dialog_edittext) setTitle("Enter a chapter/episode number tag")
.setPositiveButton("OK") { dialog, _ -> setCustomView(customView.root)
val editText = setPosButton("OK") {
(dialog as AlertDialog).findViewById<EditText>(R.id.dialogEditText) val text = customView.dialogEditText.text.toString()
val text = editText?.text.toString()
tag = text.toIntOrNull() tag = text.toIntOrNull()
if (tag == null) { if (tag == null) {
activity.binding.commentLabel.background = ResourcesCompat.getDrawable( activity.binding.commentLabel.background = ResourcesCompat.getDrawable(
@ -324,28 +318,25 @@ class CommentsFragment : Fragment() {
null null
) )
} }
dialog.dismiss()
} }
.setNeutralButton("Clear") { dialog, _ -> setNeutralButton("Clear") {
tag = null tag = null
activity.binding.commentLabel.background = ResourcesCompat.getDrawable( activity.binding.commentLabel.background = ResourcesCompat.getDrawable(
resources, resources,
R.drawable.ic_label_off_24, R.drawable.ic_label_off_24,
null null
) )
dialog.dismiss()
} }
.setNegativeButton("Cancel") { dialog, _ -> setNegButton("Cancel") {
tag = null tag = null
activity.binding.commentLabel.background = ResourcesCompat.getDrawable( activity.binding.commentLabel.background = ResourcesCompat.getDrawable(
resources, resources,
R.drawable.ic_label_off_24, R.drawable.ic_label_off_24,
null null
) )
dialog.dismiss()
} }
val dialog = alertDialog.show() show()
dialog?.window?.setDimAmount(0.8f) }
} }
} }
@ -362,12 +353,6 @@ class CommentsFragment : Fragment() {
} }
} }
} }
@SuppressLint("NotifyDataSetChanged")
override fun onStart() {
super.onStart()
}
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
@ -579,9 +564,9 @@ class CommentsFragment : Fragment() {
* Called when the user tries to comment for the first time * Called when the user tries to comment for the first time
*/ */
private fun showCommentRulesDialog() { private fun showCommentRulesDialog() {
val alertDialog = AlertDialog.Builder(activity, R.style.MyPopup) activity.customAlertDialog().apply{
.setTitle("Commenting Rules") setTitle("Commenting Rules")
.setMessage( setMessage(
"I WILL BAN YOU WITHOUT HESITATION\n" + "I WILL BAN YOU WITHOUT HESITATION\n" +
"1. No racism\n" + "1. No racism\n" +
"2. No hate speech\n" + "2. No hate speech\n" +
@ -594,16 +579,13 @@ class CommentsFragment : Fragment() {
"10. No illegal content\n" + "10. No illegal content\n" +
"11. Anything you know you shouldn't comment\n" "11. Anything you know you shouldn't comment\n"
) )
.setPositiveButton("I Understand") { dialog, _ -> setPosButton("I Understand") {
dialog.dismiss()
PrefManager.setVal(PrefName.FirstComment, false) PrefManager.setVal(PrefName.FirstComment, false)
processComment() processComment()
} }
.setNegativeButton("Cancel") { dialog, _ -> setNegButton(R.string.cancel)
dialog.dismiss() show()
} }
val dialog = alertDialog.show()
dialog?.window?.setDimAmount(0.8f)
} }
private fun processComment() { private fun processComment() {

View file

@ -1,6 +1,5 @@
package ani.dantotsu.media.manga package ani.dantotsu.media.manga
import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -19,8 +18,9 @@ import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.currActivity import ani.dantotsu.currActivity
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.databinding.CustomDialogLayoutBinding
import ani.dantotsu.databinding.DialogLayoutBinding import ani.dantotsu.databinding.DialogLayoutBinding
import ani.dantotsu.databinding.ItemAnimeWatchBinding import ani.dantotsu.databinding.ItemMediaSourceBinding
import ani.dantotsu.databinding.ItemChipBinding import ani.dantotsu.databinding.ItemChipBinding
import ani.dantotsu.isOnline import ani.dantotsu.isOnline
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
@ -35,11 +35,13 @@ 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.parsers.OfflineMangaParser
import ani.dantotsu.px import ani.dantotsu.px
import ani.dantotsu.settings.FAQActivity import ani.dantotsu.settings.FAQActivity
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_SUBSCRIPTION_CHECK import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_SUBSCRIPTION_CHECK
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
@ -55,31 +57,29 @@ class MangaReadAdapter(
) : RecyclerView.Adapter<MangaReadAdapter.ViewHolder>() { ) : RecyclerView.Adapter<MangaReadAdapter.ViewHolder>() {
var subscribe: MediaDetailsActivity.PopImageButton? = null var subscribe: MediaDetailsActivity.PopImageButton? = null
private var _binding: ItemAnimeWatchBinding? = null private var _binding: ItemMediaSourceBinding? = null
val hiddenScanlators = mutableListOf<String>() val hiddenScanlators = mutableListOf<String>()
var scanlatorSelectionListener: ScanlatorSelectionListener? = null var scanlatorSelectionListener: ScanlatorSelectionListener? = null
var options = listOf<String>() var options = listOf<String>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val bind = ItemAnimeWatchBinding.inflate(LayoutInflater.from(parent.context), parent, false) val bind = ItemMediaSourceBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(bind) return ViewHolder(bind)
} }
private var nestedDialog: AlertDialog? = null
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val binding = holder.binding val binding = holder.binding
_binding = binding _binding = binding
binding.sourceTitle.setText(R.string.chaps) binding.sourceTitle.setText(R.string.chaps)
//Fuck u launch // Fuck u launch
binding.faqbutton.setOnClickListener { binding.faqbutton.setOnClickListener {
val intent = Intent(fragment.requireContext(), FAQActivity::class.java) val intent = Intent(fragment.requireContext(), FAQActivity::class.java)
startActivity(fragment.requireContext(), intent, null) startActivity(fragment.requireContext(), intent, null)
} }
//Wrong Title // Wrong Title
binding.animeSourceSearch.setOnClickListener { binding.mediaSourceSearch.setOnClickListener {
SourceSearchDialogFragment().show( SourceSearchDialogFragment().show(
fragment.requireActivity().supportFragmentManager, fragment.requireActivity().supportFragmentManager,
null null
@ -87,54 +87,54 @@ class MangaReadAdapter(
} }
val offline = !isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode) val offline = !isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode)
binding.animeSourceNameContainer.isGone = offline binding.mediaSourceNameContainer.isGone = offline
binding.animeSourceSettings.isGone = offline binding.mediaSourceSettings.isGone = offline
binding.animeSourceSearch.isGone = offline binding.mediaSourceSearch.isGone = offline
binding.animeSourceTitle.isGone = offline binding.mediaSourceTitle.isGone = offline
//Source Selection // Source Selection
var source = var source =
media.selected!!.sourceIndex.let { if (it >= mangaReadSources.names.size) 0 else it } media.selected!!.sourceIndex.let { if (it >= mangaReadSources.names.size) 0 else it }
setLanguageList(media.selected!!.langIndex, source) setLanguageList(media.selected!!.langIndex, source)
if (mangaReadSources.names.isNotEmpty() && source in 0 until mangaReadSources.names.size) { if (mangaReadSources.names.isNotEmpty() && source in 0 until mangaReadSources.names.size) {
binding.animeSource.setText(mangaReadSources.names[source]) binding.mediaSource.setText(mangaReadSources.names[source])
mangaReadSources[source].apply { mangaReadSources[source].apply {
binding.animeSourceTitle.text = showUserText binding.mediaSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } } showUserTextListener = { MainScope().launch { binding.mediaSourceTitle.text = it } }
} }
} }
media.selected?.scanlators?.let { media.selected?.scanlators?.let {
hiddenScanlators.addAll(it) hiddenScanlators.addAll(it)
} }
binding.animeSource.setAdapter( binding.mediaSource.setAdapter(
ArrayAdapter( ArrayAdapter(
fragment.requireContext(), fragment.requireContext(),
R.layout.item_dropdown, R.layout.item_dropdown,
mangaReadSources.names mangaReadSources.names
) )
) )
binding.animeSourceTitle.isSelected = true binding.mediaSourceTitle.isSelected = true
binding.animeSource.setOnItemClickListener { _, _, i, _ -> binding.mediaSource.setOnItemClickListener { _, _, i, _ ->
fragment.onSourceChange(i).apply { fragment.onSourceChange(i).apply {
binding.animeSourceTitle.text = showUserText binding.mediaSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } } showUserTextListener = { MainScope().launch { binding.mediaSourceTitle.text = it } }
source = i source = i
setLanguageList(0, i) setLanguageList(0, i)
} }
subscribeButton(false) subscribeButton(false)
//invalidate if it's the last source // Invalidate if it's the last source
val invalidate = i == mangaReadSources.names.size - 1 val invalidate = i == mangaReadSources.names.size - 1
fragment.loadChapters(i, invalidate) fragment.loadChapters(i, invalidate)
} }
binding.animeSourceLanguage.setOnItemClickListener { _, _, i, _ -> binding.mediaSourceLanguage.setOnItemClickListener { _, _, i, _ ->
// Check if 'extension' and 'selected' properties exist and are accessible // Check if 'extension' and 'selected' properties exist and are accessible
(mangaReadSources[source] as? DynamicMangaParser)?.let { ext -> (mangaReadSources[source] as? DynamicMangaParser)?.let { ext ->
ext.sourceLanguage = i ext.sourceLanguage = i
fragment.onLangChange(i, ext.saveName) fragment.onLangChange(i, ext.saveName)
fragment.onSourceChange(media.selected!!.sourceIndex).apply { fragment.onSourceChange(media.selected!!.sourceIndex).apply {
binding.animeSourceTitle.text = showUserText binding.mediaSourceTitle.text = showUserText
showUserTextListener = showUserTextListener =
{ MainScope().launch { binding.animeSourceTitle.text = it } } { MainScope().launch { binding.mediaSourceTitle.text = it } }
setLanguageList(i, source) setLanguageList(i, source)
} }
subscribeButton(false) subscribeButton(false)
@ -143,17 +143,17 @@ class MangaReadAdapter(
} }
} }
//settings // Settings
binding.animeSourceSettings.setOnClickListener { binding.mediaSourceSettings.setOnClickListener {
(mangaReadSources[source] as? DynamicMangaParser)?.let { ext -> (mangaReadSources[source] as? DynamicMangaParser)?.let { ext ->
fragment.openSettings(ext.extension) fragment.openSettings(ext.extension)
} }
} }
//Grids // Grids
subscribe = MediaDetailsActivity.PopImageButton( subscribe = MediaDetailsActivity.PopImageButton(
fragment.lifecycleScope, fragment.lifecycleScope,
binding.animeSourceSubscribe, binding.mediaSourceSubscribe,
R.drawable.ic_round_notifications_active_24, R.drawable.ic_round_notifications_active_24,
R.drawable.ic_round_notifications_none_24, R.drawable.ic_round_notifications_none_24,
R.color.bg_opp, R.color.bg_opp,
@ -161,45 +161,43 @@ class MangaReadAdapter(
fragment.subscribed, fragment.subscribed,
true true
) { ) {
fragment.onNotificationPressed(it, binding.animeSource.text.toString()) fragment.onNotificationPressed(it, binding.mediaSource.text.toString())
} }
subscribeButton(false) subscribeButton(false)
binding.animeSourceSubscribe.setOnLongClickListener { binding.mediaSourceSubscribe.setOnLongClickListener {
openSettings(fragment.requireContext(), CHANNEL_SUBSCRIPTION_CHECK) openSettings(fragment.requireContext(), CHANNEL_SUBSCRIPTION_CHECK)
} }
binding.animeNestedButton.setOnClickListener { binding.mediaNestedButton.setOnClickListener {
val dialogBinding = DialogLayoutBinding.inflate(fragment.layoutInflater)
val dialogView =
LayoutInflater.from(fragment.requireContext()).inflate(R.layout.dialog_layout, null)
val dialogBinding = DialogLayoutBinding.bind(dialogView)
var refresh = false var refresh = false
var run = false var run = false
var reversed = media.selected!!.recyclerReversed var reversed = media.selected!!.recyclerReversed
var style = var style =
media.selected!!.recyclerStyle ?: PrefManager.getVal(PrefName.MangaDefaultView) media.selected!!.recyclerStyle ?: PrefManager.getVal(PrefName.MangaDefaultView)
dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f dialogBinding.apply {
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down" mediaSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.animeSourceTop.setOnClickListener { sortText.text = if (reversed) "Down to Up" else "Up to Down"
mediaSourceTop.setOnClickListener {
reversed = !reversed reversed = !reversed
dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f mediaSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down" sortText.text = if (reversed) "Down to Up" else "Up to Down"
run = true run = true
} }
//Grids // Grids
dialogBinding.animeSourceGrid.visibility = View.GONE mediaSourceGrid.visibility = View.GONE
var selected = when (style) { var selected = when (style) {
0 -> dialogBinding.animeSourceList 0 -> mediaSourceList
1 -> dialogBinding.animeSourceCompact 1 -> mediaSourceCompact
else -> dialogBinding.animeSourceList else -> mediaSourceList
} }
when (style) { when (style) {
0 -> dialogBinding.layoutText.setText(R.string.list) 0 -> layoutText.setText(R.string.list)
1 -> dialogBinding.layoutText.setText(R.string.compact) 1 -> layoutText.setText(R.string.compact)
else -> dialogBinding.animeSourceList else -> mediaSourceList
} }
selected.alpha = 1f selected.alpha = 1f
fun selected(it: ImageButton) { fun selected(it: ImageButton) {
@ -207,67 +205,66 @@ class MangaReadAdapter(
selected = it selected = it
selected.alpha = 1f selected.alpha = 1f
} }
dialogBinding.animeSourceList.setOnClickListener { mediaSourceList.setOnClickListener {
selected(it as ImageButton) selected(it as ImageButton)
style = 0 style = 0
dialogBinding.layoutText.setText(R.string.list) layoutText.setText(R.string.list)
run = true run = true
} }
dialogBinding.animeSourceCompact.setOnClickListener { mediaSourceCompact.setOnClickListener {
selected(it as ImageButton) selected(it as ImageButton)
style = 1 style = 1
dialogBinding.layoutText.setText(R.string.compact) layoutText.setText(R.string.compact)
run = true run = true
} }
dialogBinding.animeWebviewContainer.setOnClickListener { mediaWebviewContainer.setOnClickListener {
if (!WebViewUtil.supportsWebView(fragment.requireContext())) { if (!WebViewUtil.supportsWebView(fragment.requireContext())) {
toast(R.string.webview_not_installed) toast(R.string.webview_not_installed)
} }
//start CookieCatcher activity // Start CookieCatcher activity
if (mangaReadSources.names.isNotEmpty() && source in 0 until mangaReadSources.names.size) { if (mangaReadSources.names.isNotEmpty() && source in 0 until mangaReadSources.names.size) {
val sourceAHH = mangaReadSources[source] as? DynamicMangaParser val sourceAHH = mangaReadSources[source] as? DynamicMangaParser
val sourceHttp = sourceAHH?.extension?.sources?.firstOrNull() as? HttpSource val sourceHttp = sourceAHH?.extension?.sources?.firstOrNull() as? HttpSource
val url = sourceHttp?.baseUrl val url = sourceHttp?.baseUrl
url?.let { url?.let {
refresh = true refresh = true
val intent = Intent(fragment.requireContext(), CookieCatcher::class.java) val intent =
Intent(fragment.requireContext(), CookieCatcher::class.java)
.putExtra("url", url) .putExtra("url", url)
startActivity(fragment.requireContext(), intent, null) startActivity(fragment.requireContext(), intent, null)
} }
} }
} }
//Multi download // Multi download
dialogBinding.downloadNo.text = "0" downloadNo.text = "0"
dialogBinding.animeDownloadTop.setOnClickListener { mediaDownloadTop.setOnClickListener {
//Alert dialog asking for the number of chapters to download // Alert dialog asking for the number of chapters to download
val alertDialog = AlertDialog.Builder(currContext(), R.style.MyPopup) fragment.requireContext().customAlertDialog().apply {
alertDialog.setTitle("Multi Chapter Downloader") setTitle("Multi Chapter Downloader")
alertDialog.setMessage("Enter the number of chapters to download") setMessage("Enter the number of chapters to download")
val input = NumberPicker(currContext()) val input = NumberPicker(currContext())
input.minValue = 1 input.minValue = 1
input.maxValue = 20 input.maxValue = 20
input.value = 1 input.value = 1
alertDialog.setView(input) setCustomView(input)
alertDialog.setPositiveButton("OK") { _, _ -> setPosButton(R.string.ok) {
dialogBinding.downloadNo.text = "${input.value}" downloadNo.text = "${input.value}"
}
setNegButton(R.string.cancel)
show()
} }
alertDialog.setNegativeButton("Cancel") { dialog, _ -> dialog.cancel() }
val dialog = alertDialog.show()
dialog.window?.setDimAmount(0.8f)
} }
//Scanlator // Scanlator
dialogBinding.animeScanlatorContainer.isVisible = options.count() > 1 mangaScanlatorContainer.isVisible = options.count() > 1
dialogBinding.scanlatorNo.text = "${options.count()}" scanlatorNo.text = "${options.count()}"
dialogBinding.animeScanlatorTop.setOnClickListener { mangaScanlatorTop.setOnClickListener {
val dialogView2 = CustomDialogLayoutBinding.inflate(fragment.layoutInflater)
LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null) val dialogView = CustomDialogLayoutBinding.inflate(fragment.layoutInflater)
val checkboxContainer = val checkboxContainer = dialogView.checkboxContainer
dialogView2.findViewById<LinearLayout>(R.id.checkboxContainer) val tickAllButton = dialogView.toggleButton
val tickAllButton = dialogView2.findViewById<ImageButton>(R.id.toggleButton)
// Function to get the right image resource for the toggle button
fun getToggleImageResource(container: ViewGroup): Int { fun getToggleImageResource(container: ViewGroup): Int {
var allChecked = true var allChecked = true
var allUnchecked = true var allUnchecked = true
@ -287,17 +284,14 @@ class MangaReadAdapter(
} }
} }
// Dynamically add checkboxes
options.forEach { option -> options.forEach { option ->
val checkBox = CheckBox(currContext()).apply { val checkBox = CheckBox(currContext()).apply {
text = option text = option
setOnCheckedChangeListener { _, _ -> setOnCheckedChangeListener { _, _ ->
// Update image resource when you change a checkbox
tickAllButton.setImageResource(getToggleImageResource(checkboxContainer)) tickAllButton.setImageResource(getToggleImageResource(checkboxContainer))
} }
} }
// Set checked if its already selected
if (media.selected!!.scanlators != null) { if (media.selected!!.scanlators != null) {
checkBox.isChecked = media.selected!!.scanlators?.contains(option) != true checkBox.isChecked = media.selected!!.scanlators?.contains(option) != true
scanlatorSelectionListener?.onScanlatorsSelected() scanlatorSelectionListener?.onScanlatorsSelected()
@ -307,10 +301,9 @@ class MangaReadAdapter(
checkboxContainer.addView(checkBox) checkboxContainer.addView(checkBox)
} }
// Create AlertDialog fragment.requireContext().customAlertDialog().apply {
val dialog = AlertDialog.Builder(currContext(), R.style.MyPopup) setCustomView(dialogView.root)
.setView(dialogView2) setPosButton("OK") {
.setPositiveButton("OK") { _, _ ->
hiddenScanlators.clear() hiddenScanlators.clear()
for (i in 0 until checkboxContainer.childCount) { for (i in 0 until checkboxContainer.childCount) {
val checkBox = checkboxContainer.getChildAt(i) as CheckBox val checkBox = checkboxContainer.getChildAt(i) as CheckBox
@ -321,46 +314,38 @@ class MangaReadAdapter(
fragment.onScanlatorChange(hiddenScanlators) fragment.onScanlatorChange(hiddenScanlators)
scanlatorSelectionListener?.onScanlatorsSelected() scanlatorSelectionListener?.onScanlatorsSelected()
} }
.setNegativeButton("Cancel", null) setNegButton("Cancel")
.show() }.show()
dialog.window?.setDimAmount(0.8f)
// Standard image resource
tickAllButton.setImageResource(getToggleImageResource(checkboxContainer)) tickAllButton.setImageResource(getToggleImageResource(checkboxContainer))
// Listens to ticked checkboxes and changes image resource accordingly
tickAllButton.setOnClickListener { tickAllButton.setOnClickListener {
// Toggle checkboxes
for (i in 0 until checkboxContainer.childCount) { for (i in 0 until checkboxContainer.childCount) {
val checkBox = checkboxContainer.getChildAt(i) as CheckBox val checkBox = checkboxContainer.getChildAt(i) as CheckBox
checkBox.isChecked = !checkBox.isChecked checkBox.isChecked = !checkBox.isChecked
} }
// Update image resource
tickAllButton.setImageResource(getToggleImageResource(checkboxContainer)) tickAllButton.setImageResource(getToggleImageResource(checkboxContainer))
} }
} }
nestedDialog = AlertDialog.Builder(fragment.requireContext(), R.style.MyPopup) fragment.requireContext().customAlertDialog().apply {
.setTitle("Options") setTitle("Options")
.setView(dialogView) setCustomView(root)
.setPositiveButton("OK") { _, _ -> setPosButton("OK") {
if (run) fragment.onIconPressed(style, reversed) if (run) fragment.onIconPressed(style, reversed)
if (dialogBinding.downloadNo.text != "0") { if (downloadNo.text != "0") {
fragment.multiDownload(dialogBinding.downloadNo.text.toString().toInt()) fragment.multiDownload(downloadNo.text.toString().toInt())
} }
if (refresh) fragment.loadChapters(source, true) if (refresh) fragment.loadChapters(source, true)
} }
.setNegativeButton("Cancel") { _, _ -> setNegButton("Cancel") {
if (refresh) fragment.loadChapters(source, true) if (refresh) fragment.loadChapters(source, true)
} }
.setOnCancelListener { show()
if (refresh) fragment.loadChapters(source, true)
} }
.create()
nestedDialog?.show()
} }
//Chapter Handling }
// Chapter Handling
handleChapters() handleChapters()
} }
@ -368,7 +353,7 @@ class MangaReadAdapter(
subscribe?.enabled(enabled) subscribe?.enabled(enabled)
} }
//Chips // Chips
fun updateChips(limit: Int, names: Array<String>, arr: Array<Int>, selected: Int = 0) { fun updateChips(limit: Int, names: Array<String>, arr: Array<Int>, selected: Int = 0) {
val binding = _binding val binding = _binding
if (binding != null) { if (binding != null) {
@ -379,13 +364,13 @@ class MangaReadAdapter(
val chip = val chip =
ItemChipBinding.inflate( ItemChipBinding.inflate(
LayoutInflater.from(fragment.context), LayoutInflater.from(fragment.context),
binding.animeSourceChipGroup, binding.mediaSourceChipGroup,
false false
).root ).root
chip.isCheckable = true chip.isCheckable = true
fun selected() { fun selected() {
chip.isChecked = true chip.isChecked = true
binding.animeWatchChipScroll.smoothScrollTo( binding.mediaWatchChipScroll.smoothScrollTo(
(chip.left - screenWidth / 2) + (chip.width / 2), (chip.left - screenWidth / 2) + (chip.width / 2),
0 0
) )
@ -403,7 +388,7 @@ class MangaReadAdapter(
} else { } else {
names[last - 1] names[last - 1]
} }
//chip.text = "${names[limit * (position)]} - ${names[last - 1]}" // chip.text = "${names[limit * (position)]} - ${names[last - 1]}"
val chipText = "$startChapterString - $endChapterString" val chipText = "$startChapterString - $endChapterString"
chip.text = chipText chip.text = chipText
chip.setTextColor( chip.setTextColor(
@ -417,14 +402,14 @@ class MangaReadAdapter(
selected() selected()
fragment.onChipClicked(position, limit * (position), last - 1) fragment.onChipClicked(position, limit * (position), last - 1)
} }
binding.animeSourceChipGroup.addView(chip) binding.mediaSourceChipGroup.addView(chip)
if (selected == position) { if (selected == position) {
selected() selected()
select = chip select = chip
} }
} }
if (select != null) if (select != null)
binding.animeWatchChipScroll.apply { binding.mediaWatchChipScroll.apply {
post { post {
scrollTo( scrollTo(
(select.left - screenWidth / 2) + (select.width / 2), (select.left - screenWidth / 2) + (select.width / 2),
@ -436,7 +421,7 @@ class MangaReadAdapter(
} }
fun clearChips() { fun clearChips() {
_binding?.animeSourceChipGroup?.removeAllViews() _binding?.mediaSourceChipGroup?.removeAllViews()
} }
fun handleChapters() { fun handleChapters() {
@ -462,70 +447,86 @@ class MangaReadAdapter(
} }
if (formattedChapters.contains(continueEp)) { if (formattedChapters.contains(continueEp)) {
continueEp = chapters[formattedChapters.indexOf(continueEp)] continueEp = chapters[formattedChapters.indexOf(continueEp)]
binding.animeSourceContinue.visibility = View.VISIBLE binding.sourceContinue.visibility = View.VISIBLE
handleProgress( handleProgress(
binding.itemEpisodeProgressCont, binding.itemMediaProgressCont,
binding.itemEpisodeProgress, binding.itemMediaProgress,
binding.itemEpisodeProgressEmpty, binding.itemMediaProgressEmpty,
media.id, media.id,
continueEp continueEp
) )
if ((binding.itemEpisodeProgress.layoutParams as LinearLayout.LayoutParams).weight > 0.8f) { if ((binding.itemMediaProgress.layoutParams as LinearLayout.LayoutParams).weight > 0.8f) {
val e = chapters.indexOf(continueEp) val e = chapters.indexOf(continueEp)
if (e != -1 && e + 1 < chapters.size) { if (e != -1 && e + 1 < chapters.size) {
continueEp = chapters[e + 1] continueEp = chapters[e + 1]
} }
} }
val ep = media.manga.chapters!![continueEp]!! val ep = media.manga.chapters!![continueEp]!!
binding.itemEpisodeImage.loadImage(media.banner ?: media.cover) binding.itemMediaImage.loadImage(media.banner ?: media.cover)
binding.animeSourceContinueText.text = binding.mediaSourceContinueText.text =
currActivity()!!.getString( currActivity()!!.getString(
R.string.continue_chapter, R.string.continue_chapter,
ep.number, ep.number,
if (!ep.title.isNullOrEmpty()) ep.title else "" if (!ep.title.isNullOrEmpty()) ep.title else ""
) )
binding.animeSourceContinue.setOnClickListener { binding.sourceContinue.setOnClickListener {
fragment.onMangaChapterClick(continueEp) fragment.onMangaChapterClick(continueEp)
} }
if (fragment.continueEp) { if (fragment.continueEp) {
if ((binding.itemEpisodeProgress.layoutParams as LinearLayout.LayoutParams).weight < 0.8f) { if ((binding.itemMediaProgress.layoutParams as LinearLayout.LayoutParams).weight < 0.8f) {
binding.animeSourceContinue.performClick() binding.sourceContinue.performClick()
fragment.continueEp = false fragment.continueEp = false
} }
} }
} else { } else {
binding.animeSourceContinue.visibility = View.GONE binding.sourceContinue.visibility = View.GONE
} }
binding.animeSourceProgressBar.visibility = View.GONE
val sourceFound = media.manga.chapters!!.isNotEmpty() binding.sourceProgressBar.visibility = View.GONE
binding.animeSourceNotFound.isGone = sourceFound
val sourceFound = filteredChapters.isNotEmpty()
val isDownloadedSource = mangaReadSources[media.selected!!.sourceIndex] is OfflineMangaParser
if (isDownloadedSource) {
binding.sourceNotFound.text = if (sourceFound) {
currActivity()!!.getString(R.string.source_not_found)
} else {
currActivity()!!.getString(R.string.download_not_found)
}
} else {
binding.sourceNotFound.text = currActivity()!!.getString(R.string.source_not_found)
}
binding.sourceNotFound.isGone = sourceFound
binding.faqbutton.isGone = sourceFound binding.faqbutton.isGone = sourceFound
if (!sourceFound && PrefManager.getVal(PrefName.SearchSources)) { if (!sourceFound && PrefManager.getVal(PrefName.SearchSources)) {
if (binding.animeSource.adapter.count > media.selected!!.sourceIndex + 1) { if (binding.mediaSource.adapter.count > media.selected!!.sourceIndex + 1) {
val nextIndex = media.selected!!.sourceIndex + 1 val nextIndex = media.selected!!.sourceIndex + 1
binding.animeSource.setText( binding.mediaSource.setText(
binding.animeSource.adapter binding.mediaSource.adapter
.getItem(nextIndex).toString(), false .getItem(nextIndex).toString(), false
) )
fragment.onSourceChange(nextIndex).apply { fragment.onSourceChange(nextIndex).apply {
binding.animeSourceTitle.text = showUserText binding.mediaSourceTitle.text = showUserText
showUserTextListener = showUserTextListener =
{ MainScope().launch { binding.animeSourceTitle.text = it } } { MainScope().launch { binding.mediaSourceTitle.text = it } }
setLanguageList(0, nextIndex) setLanguageList(0, nextIndex)
} }
subscribeButton(false) subscribeButton(false)
// invalidate if it's the last source // Invalidate if it's the last source
val invalidate = nextIndex == mangaReadSources.names.size - 1 val invalidate = nextIndex == mangaReadSources.names.size - 1
fragment.loadChapters(nextIndex, invalidate) fragment.loadChapters(nextIndex, invalidate)
} }
} }
} else { } else {
binding.animeSourceContinue.visibility = View.GONE binding.sourceContinue.visibility = View.GONE
binding.animeSourceNotFound.visibility = View.GONE binding.sourceNotFound.visibility = View.GONE
binding.faqbutton.visibility = View.GONE binding.faqbutton.visibility = View.GONE
clearChips() clearChips()
binding.animeSourceProgressBar.visibility = View.VISIBLE binding.sourceProgressBar.visibility = View.VISIBLE
} }
} }
} }
@ -539,9 +540,9 @@ class MangaReadAdapter(
ext.sourceLanguage = lang ext.sourceLanguage = lang
} }
try { try {
binding?.animeSourceLanguage?.setText(parser.extension.sources[lang].lang) binding?.mediaSourceLanguage?.setText(parser.extension.sources[lang].lang)
} catch (e: IndexOutOfBoundsException) { } catch (e: IndexOutOfBoundsException) {
binding?.animeSourceLanguage?.setText( binding?.mediaSourceLanguage?.setText(
parser.extension.sources.firstOrNull()?.lang ?: "Unknown" parser.extension.sources.firstOrNull()?.lang ?: "Unknown"
) )
} }
@ -551,9 +552,9 @@ class MangaReadAdapter(
parser.extension.sources.map { LanguageMapper.getLanguageName(it.lang) } parser.extension.sources.map { LanguageMapper.getLanguageName(it.lang) }
) )
val items = adapter.count val items = adapter.count
binding?.animeSourceLanguageContainer?.isVisible = items > 1 binding?.mediaSourceLanguageContainer?.isVisible = items > 1
binding?.animeSourceLanguage?.setAdapter(adapter) binding?.mediaSourceLanguage?.setAdapter(adapter)
} }
} }
@ -561,7 +562,7 @@ class MangaReadAdapter(
override fun getItemCount(): Int = 1 override fun getItemCount(): Int = 1
inner class ViewHolder(val binding: ItemAnimeWatchBinding) : inner class ViewHolder(val binding: ItemMediaSourceBinding) :
RecyclerView.ViewHolder(binding.root) RecyclerView.ViewHolder(binding.root)
} }

View file

@ -2,7 +2,6 @@ package ani.dantotsu.media.manga
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -31,7 +30,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.databinding.FragmentAnimeWatchBinding import ani.dantotsu.databinding.FragmentMediaSourceBinding
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.DownloadsManager.Companion.compareName import ani.dantotsu.download.DownloadsManager.Companion.compareName
@ -60,6 +59,7 @@ import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.StoragePermissions.Companion.accessAlertDialog import ani.dantotsu.util.StoragePermissions.Companion.accessAlertDialog
import ani.dantotsu.util.StoragePermissions.Companion.hasDirAccess import ani.dantotsu.util.StoragePermissions.Companion.hasDirAccess
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
@ -74,7 +74,7 @@ import kotlin.math.max
import kotlin.math.roundToInt import kotlin.math.roundToInt
open class MangaReadFragment : Fragment(), ScanlatorSelectionListener { open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
private var _binding: FragmentAnimeWatchBinding? = null private var _binding: FragmentMediaSourceBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private val model: MediaDetailsViewModel by activityViewModels() private val model: MediaDetailsViewModel by activityViewModels()
@ -101,7 +101,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
_binding = FragmentAnimeWatchBinding.inflate(inflater, container, false) _binding = FragmentMediaSourceBinding.inflate(inflater, container, false)
return _binding?.root return _binding?.root
} }
@ -121,7 +121,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
ContextCompat.RECEIVER_EXPORTED ContextCompat.RECEIVER_EXPORTED
) )
binding.animeSourceRecycler.updatePadding(bottom = binding.animeSourceRecycler.paddingBottom + navBarHeight) binding.mediaSourceRecycler.updatePadding(bottom = binding.mediaSourceRecycler.paddingBottom + navBarHeight)
screenWidth = resources.displayMetrics.widthPixels.dp screenWidth = resources.displayMetrics.widthPixels.dp
var maxGridSize = (screenWidth / 100f).roundToInt() var maxGridSize = (screenWidth / 100f).roundToInt()
@ -144,13 +144,13 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
} }
} }
binding.animeSourceRecycler.layoutManager = gridLayoutManager binding.mediaSourceRecycler.layoutManager = gridLayoutManager
binding.ScrollTop.setOnClickListener { binding.ScrollTop.setOnClickListener {
binding.animeSourceRecycler.scrollToPosition(10) binding.mediaSourceRecycler.scrollToPosition(10)
binding.animeSourceRecycler.smoothScrollToPosition(0) binding.mediaSourceRecycler.smoothScrollToPosition(0)
} }
binding.animeSourceRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.mediaSourceRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
@ -164,7 +164,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
} }
}) })
model.scrolledToTop.observe(viewLifecycleOwner) { model.scrolledToTop.observe(viewLifecycleOwner) {
if (it) binding.animeSourceRecycler.scrollToPosition(0) if (it) binding.mediaSourceRecycler.scrollToPosition(0)
} }
continueEp = model.continueMedia ?: false continueEp = model.continueMedia ?: false
@ -199,7 +199,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
} }
} }
binding.animeSourceRecycler.adapter = binding.mediaSourceRecycler.adapter =
ConcatAdapter(headerAdapter, chapterAdapter) ConcatAdapter(headerAdapter, chapterAdapter)
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
@ -214,8 +214,8 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
reload() reload()
} }
} else { } else {
binding.animeNotSupported.visibility = View.VISIBLE binding.mediaNotSupported.visibility = View.VISIBLE
binding.animeNotSupported.text = binding.mediaNotSupported.text =
getString(R.string.not_supported, media.format ?: "") getString(R.string.not_supported, media.format ?: "")
} }
} }
@ -231,10 +231,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
} }
fun multiDownload(n: Int) { fun multiDownload(n: Int) {
//get last viewed chapter // Get last viewed chapter
val selected = media.userProgress val selected = media.userProgress
val chapters = media.manga?.chapters?.values?.toList() val chapters = media.manga?.chapters?.values?.toList()
//filter by selected language // Filter by selected language
val progressChapterIndex = (chapters?.indexOfFirst { val progressChapterIndex = (chapters?.indexOfFirst {
MediaNameAdapter.findChapterNumber(it.number)?.toInt() == selected MediaNameAdapter.findChapterNumber(it.number)?.toInt() == selected
} ?: 0) + 1 } ?: 0) + 1
@ -244,7 +244,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
// Calculate the end index // Calculate the end index
val endIndex = minOf(progressChapterIndex + n, chapters.size) val endIndex = minOf(progressChapterIndex + n, chapters.size)
//make sure there are enough chapters // Make sure there are enough chapters
val chaptersToDownload = chapters.subList(progressChapterIndex, endIndex) val chaptersToDownload = chapters.subList(progressChapterIndex, endIndex)
@ -386,16 +386,13 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
if (allSettings.size > 1) { if (allSettings.size > 1) {
val names = val names =
allSettings.map { LanguageMapper.getLanguageName(it.lang) }.toTypedArray() allSettings.map { LanguageMapper.getLanguageName(it.lang) }.toTypedArray()
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup) requireContext().customAlertDialog().apply {
.setTitle("Select a Source") setTitle("Select a Source")
.setSingleChoiceItems(names, -1) { dialog, which -> singleChoiceItems(names) { which ->
selectedSetting = allSettings[which] selectedSetting = allSettings[which]
itemSelected = true itemSelected = true
dialog.dismiss()
// Move the fragment transaction here val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id) {
val fragment =
MangaSourcePreferencesFragment().getInstance(selectedSetting.id) {
changeUIVisibility(true) changeUIVisibility(true)
loadChapters(media.selected!!.sourceIndex, true) loadChapters(media.selected!!.sourceIndex, true)
} }
@ -405,13 +402,14 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
.addToBackStack(null) .addToBackStack(null)
.commit() .commit()
} }
.setOnDismissListener { onDismiss{
if (!itemSelected) { if (!itemSelected) {
changeUIVisibility(true) changeUIVisibility(true)
} }
} }
.show() show()
dialog.window?.setDimAmount(0.8f)
}
} else { } else {
// If there's only one setting, proceed with the fragment transaction // If there's only one setting, proceed with the fragment transaction
val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id) { val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id) {
@ -584,7 +582,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
private fun reload() { private fun reload() {
val selected = model.loadSelected(media) val selected = model.loadSelected(media)
//Find latest chapter for subscription // Find latest chapter for subscription
selected.latest = selected.latest =
media.manga?.chapters?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f media.manga?.chapters?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f
selected.latest = selected.latest =
@ -618,14 +616,14 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
binding.mediaInfoProgressBar.visibility = progress binding.mediaInfoProgressBar.visibility = progress
binding.animeSourceRecycler.layoutManager?.onRestoreInstanceState(state) binding.mediaSourceRecycler.layoutManager?.onRestoreInstanceState(state)
requireActivity().setNavigationTheme() requireActivity().setNavigationTheme()
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
state = binding.animeSourceRecycler.layoutManager?.onSaveInstanceState() state = binding.mediaSourceRecycler.layoutManager?.onSaveInstanceState()
} }
companion object { companion object {

View file

@ -83,6 +83,7 @@ import ani.dantotsu.showSystemBarsRetractView
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
import ani.dantotsu.util.customAlertDialog
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
@ -1013,28 +1014,27 @@ class MangaReaderActivity : AppCompatActivity() {
PrefManager.setCustomVal("${media.id}_progressDialog", !isChecked) PrefManager.setCustomVal("${media.id}_progressDialog", !isChecked)
showProgressDialog = !isChecked showProgressDialog = !isChecked
} }
AlertDialog.Builder(this, R.style.MyPopup) customAlertDialog().apply {
.setTitle(getString(R.string.title_update_progress)) setTitle(R.string.title_update_progress)
.setView(dialogView) setCustomView(dialogView)
.setCancelable(false) setCancelable(false)
.setPositiveButton(getString(R.string.yes)) { dialog, _ -> setPosButton(R.string.yes) {
PrefManager.setCustomVal("${media.id}_save_progress", true) PrefManager.setCustomVal("${media.id}_save_progress", true)
updateProgress( updateProgress(
media, media,
MediaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!) MediaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
.toString() .toString()
) )
dialog.dismiss()
runnable.run() runnable.run()
} }
.setNegativeButton(getString(R.string.no)) { dialog, _ -> setNegButton(R.string.no) {
PrefManager.setCustomVal("${media.id}_save_progress", false) PrefManager.setCustomVal("${media.id}_save_progress", false)
dialog.dismiss()
runnable.run() runnable.run()
} }
.setOnCancelListener { hideSystemBars() } setOnCancelListener { hideSystemBars() }
.create() show()
.show()
}
} else { } else {
if (!incognito && PrefManager.getCustomVal( if (!incognito && PrefManager.getCustomVal(
"${media.id}_save_progress", "${media.id}_save_progress",

View file

@ -50,16 +50,16 @@ class NovelReadAdapter(
val source = val source =
media.selected!!.sourceIndex.let { if (it >= novelReadSources.names.size) 0 else it } media.selected!!.sourceIndex.let { if (it >= novelReadSources.names.size) 0 else it }
if (novelReadSources.names.isNotEmpty() && source in 0 until novelReadSources.names.size) { if (novelReadSources.names.isNotEmpty() && source in 0 until novelReadSources.names.size) {
binding.animeSource.setText(novelReadSources.names[source], false) binding.mediaSource.setText(novelReadSources.names[source], false)
} }
binding.animeSource.setAdapter( binding.mediaSource.setAdapter(
ArrayAdapter( ArrayAdapter(
fragment.requireContext(), fragment.requireContext(),
R.layout.item_dropdown, R.layout.item_dropdown,
novelReadSources.names novelReadSources.names
) )
) )
binding.animeSource.setOnItemClickListener { _, _, i, _ -> binding.mediaSource.setOnItemClickListener { _, _, i, _ ->
fragment.onSourceChange(i) fragment.onSourceChange(i)
search() search()
} }

View file

@ -20,7 +20,7 @@ import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.databinding.FragmentAnimeWatchBinding import ani.dantotsu.databinding.FragmentMediaSourceBinding
import ani.dantotsu.download.DownloadedType 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
@ -47,7 +47,7 @@ class NovelReadFragment : Fragment(),
DownloadTriggerCallback, DownloadTriggerCallback,
DownloadedCheckCallback { DownloadedCheckCallback {
private var _binding: FragmentAnimeWatchBinding? = null private var _binding: FragmentMediaSourceBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private val model: MediaDetailsViewModel by activityViewModels() private val model: MediaDetailsViewModel by activityViewModels()
@ -214,11 +214,11 @@ class NovelReadFragment : Fragment(),
ContextCompat.RECEIVER_EXPORTED ContextCompat.RECEIVER_EXPORTED
) )
binding.animeSourceRecycler.updatePadding(bottom = binding.animeSourceRecycler.paddingBottom + navBarHeight) binding.mediaSourceRecycler.updatePadding(bottom = binding.mediaSourceRecycler.paddingBottom + navBarHeight)
binding.animeSourceRecycler.layoutManager = LinearLayoutManager(requireContext()) binding.mediaSourceRecycler.layoutManager = LinearLayoutManager(requireContext())
model.scrolledToTop.observe(viewLifecycleOwner) { model.scrolledToTop.observe(viewLifecycleOwner) {
if (it) binding.animeSourceRecycler.scrollToPosition(0) if (it) binding.mediaSourceRecycler.scrollToPosition(0)
} }
continueEp = model.continueMedia ?: false continueEp = model.continueMedia ?: false
@ -237,7 +237,7 @@ class NovelReadFragment : Fragment(),
this, this,
this this
) // probably a better way to do this but it works ) // probably a better way to do this but it works
binding.animeSourceRecycler.adapter = binding.mediaSourceRecycler.adapter =
ConcatAdapter(headerAdapter, novelResponseAdapter) ConcatAdapter(headerAdapter, novelResponseAdapter)
loaded = true loaded = true
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
@ -290,7 +290,7 @@ class NovelReadFragment : Fragment(),
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
_binding = FragmentAnimeWatchBinding.inflate(inflater, container, false) _binding = FragmentMediaSourceBinding.inflate(inflater, container, false)
return _binding?.root return _binding?.root
} }
@ -304,12 +304,12 @@ class NovelReadFragment : Fragment(),
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
binding.mediaInfoProgressBar.visibility = progress binding.mediaInfoProgressBar.visibility = progress
binding.animeSourceRecycler.layoutManager?.onRestoreInstanceState(state) binding.mediaSourceRecycler.layoutManager?.onRestoreInstanceState(state)
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
state = binding.animeSourceRecycler.layoutManager?.onSaveInstanceState() state = binding.mediaSourceRecycler.layoutManager?.onSaveInstanceState()
} }
companion object { companion object {

View file

@ -13,6 +13,7 @@ import ani.dantotsu.parsers.ShowResponse
import ani.dantotsu.setAnimation import ani.dantotsu.setAnimation
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
@ -38,7 +39,7 @@ class NovelResponseAdapter(
val binding = holder.binding val binding = holder.binding
val novel = list[position] val novel = list[position]
setAnimation(fragment.requireContext(), holder.binding.root) setAnimation(fragment.requireContext(), holder.binding.root)
binding.itemEpisodeImage.loadImage(novel.coverUrl, 400, 0) binding.itemMediaImage.loadImage(novel.coverUrl, 400, 0)
val color =fragment.requireContext().getThemeColor(com.google.android.material.R.attr.colorOnBackground) val color =fragment.requireContext().getThemeColor(com.google.android.material.R.attr.colorOnBackground)
binding.itemEpisodeTitle.text = novel.name binding.itemEpisodeTitle.text = novel.name
@ -93,13 +94,10 @@ class NovelResponseAdapter(
} }
binding.root.setOnLongClickListener { binding.root.setOnLongClickListener {
val builder = androidx.appcompat.app.AlertDialog.Builder( it.context.customAlertDialog().apply {
fragment.requireContext(), setTitle("Delete ${novel.name}?")
R.style.MyPopup setMessage("Are you sure you want to delete ${novel.name}?")
) setPosButton(R.string.yes) {
builder.setTitle("Delete ${novel.name}?")
builder.setMessage("Are you sure you want to delete ${novel.name}?")
builder.setPositiveButton("Yes") { _, _ ->
downloadedCheckCallback.deleteDownload(novel) downloadedCheckCallback.deleteDownload(novel)
deleteDownload(novel.link) deleteDownload(novel.link)
snackString("Deleted ${novel.name}") snackString("Deleted ${novel.name}")
@ -109,11 +107,9 @@ class NovelResponseAdapter(
binding.itemEpisodeFiller.text = "" binding.itemEpisodeFiller.text = ""
} }
} }
builder.setNegativeButton("No") { _, _ -> setNegButton(R.string.no)
// Do nothing show()
} }
val dialog = builder.show()
dialog.window?.setDimAmount(0.8f)
true true
} }
} }

View file

@ -47,6 +47,7 @@ class ListActivity : AppCompatActivity() {
window.statusBarColor = primaryColor window.statusBarColor = primaryColor
window.navigationBarColor = primaryColor window.navigationBarColor = primaryColor
binding.listed.visibility = View.GONE
binding.listTabLayout.setBackgroundColor(primaryColor) binding.listTabLayout.setBackgroundColor(primaryColor)
binding.listAppBar.setBackgroundColor(primaryColor) binding.listAppBar.setBackgroundColor(primaryColor)
binding.listTitle.setTextColor(primaryTextColor) binding.listTitle.setTextColor(primaryTextColor)

View file

@ -39,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() },

View file

@ -38,7 +38,6 @@ import ani.dantotsu.snackString
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.util.MarkdownCreatorActivity
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View file

@ -19,8 +19,7 @@ import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.setBaseline import ani.dantotsu.util.ActivityMarkdownCreator
import ani.dantotsu.util.MarkdownCreatorActivity
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import eu.kanade.tachiyomi.util.system.getSerializableCompat import eu.kanade.tachiyomi.util.system.getSerializableCompat
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -44,30 +43,16 @@ class ActivityFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
type = arguments?.getSerializableCompat<ActivityType>("type") as ActivityType arguments?.let {
userId = arguments?.getInt("userId") type = it.getSerializableCompat<ActivityType>("type") as ActivityType
activityId = arguments?.getInt("activityId") userId = it.getInt("userId")
binding.titleBar.visibility = if (type == ActivityType.OTHER_USER) View.VISIBLE else View.GONE activityId = it.getInt("activityId")
binding.titleText.text = if (userId == Anilist.userid) getString(R.string.create_new_activity) else getString(R.string.write_a_message)
binding.titleImage.setOnClickListener {
if(userId == Anilist.userid) {
ContextCompat.startActivity(
requireContext(),
Intent(context, MarkdownCreatorActivity::class.java)
.putExtra("type", "activity"),
null
)
} else{
ContextCompat.startActivity(
requireContext(),
Intent(context, MarkdownCreatorActivity::class.java)
.putExtra("type", "message")
.putExtra("userId", userId),
null
)
}
} }
binding.titleBar.visibility =
if (type == ActivityType.OTHER_USER) View.VISIBLE else View.GONE
binding.titleText.text =
if (userId == Anilist.userid) getString(R.string.create_new_activity) else getString(R.string.write_a_message)
binding.titleImage.setOnClickListener { handleTitleImageClick() }
binding.listRecyclerView.adapter = adapter binding.listRecyclerView.adapter = adapter
binding.listRecyclerView.layoutManager = LinearLayoutManager(context) binding.listRecyclerView.layoutManager = LinearLayoutManager(context)
binding.listProgressBar.isVisible = true binding.listProgressBar.isVisible = true
@ -106,6 +91,13 @@ class ActivityFragment : Fragment() {
}) })
} }
private fun handleTitleImageClick() {
val intent = Intent(context, ActivityMarkdownCreator::class.java).apply {
putExtra("type", if (userId == Anilist.userid) "activity" else "message")
putExtra("userId", userId)
}
ContextCompat.startActivity(requireContext(), intent, null)
}
private suspend fun getList() { private suspend fun getList() {
val list = when (type) { val list = when (type) {
@ -114,14 +106,14 @@ class ActivityFragment : Fragment() {
ActivityType.OTHER_USER -> getActivities(userId = userId) ActivityType.OTHER_USER -> getActivities(userId = userId)
ActivityType.ONE -> getActivities(activityId = activityId) ActivityType.ONE -> getActivities(activityId = activityId)
} }
adapter.addAll(list.map { ActivityItem(it, ::onActivityClick, adapter ,requireActivity()) }) adapter.addAll(list.map { ActivityItem(it, adapter, ::onActivityClick) })
} }
private suspend fun getActivities( private suspend fun getActivities(
global: Boolean = false, global: Boolean = false,
userId: Int? = null, userId: Int? = null,
activityId: Int? = null, activityId: Int? = null,
filter:Boolean = false filter: Boolean = false
): List<Activity> { ): List<Activity> {
val res = Anilist.query.getFeed(userId, global, page, activityId)?.data?.page?.activities val res = Anilist.query.getFeed(userId, global, page, activityId)?.data?.page?.activities
page += 1 page += 1
@ -142,37 +134,33 @@ class ActivityFragment : Fragment() {
} }
private fun onActivityClick(id: Int, type: String) { private fun onActivityClick(id: Int, type: String) {
when (type) { val intent = when (type) {
"USER" -> { "USER" -> Intent(requireContext(), ProfileActivity::class.java).putExtra("userId", id)
ContextCompat.startActivity( "MEDIA" -> Intent(
requireContext(), Intent(requireContext(), ProfileActivity::class.java) requireContext(),
.putExtra("userId", id), null MediaDetailsActivity::class.java
) ).putExtra("mediaId", id)
}
"MEDIA" -> { else -> return
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java)
.putExtra("mediaId", id), null
)
}
} }
ContextCompat.startActivity(requireContext(), intent, null)
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (this::binding.isInitialized) { if (this::binding.isInitialized) {
binding.root.requestLayout() binding.root.requestLayout()
} }
} }
companion object { companion object {
enum class ActivityType { enum class ActivityType { GLOBAL, USER, OTHER_USER, ONE }
GLOBAL, USER, OTHER_USER, ONE
}
fun newInstance(type: ActivityType, userId: Int? = null, activityId: Int? = null): ActivityFragment { fun newInstance(
type: ActivityType,
userId: Int? = null,
activityId: Int? = null
): ActivityFragment {
return ActivityFragment().apply { return ActivityFragment().apply {
arguments = Bundle().apply { arguments = Bundle().apply {
putSerializable("type", type) putSerializable("type", type)

View file

@ -5,21 +5,19 @@ import android.view.View
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.blurImage import ani.dantotsu.blurImage
import ani.dantotsu.buildMarkwon import ani.dantotsu.buildMarkwon
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Activity import ani.dantotsu.connections.anilist.api.Activity
import ani.dantotsu.databinding.ItemActivityBinding import ani.dantotsu.databinding.ItemActivityBinding
import ani.dantotsu.home.status.RepliesBottomDialog
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.profile.User import ani.dantotsu.profile.User
import ani.dantotsu.profile.UsersDialogFragment import ani.dantotsu.profile.UsersDialogFragment
import ani.dantotsu.setAnimation import ani.dantotsu.setAnimation
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
import ani.dantotsu.util.MarkdownCreatorActivity import ani.dantotsu.util.ActivityMarkdownCreator
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -30,9 +28,8 @@ import kotlinx.coroutines.withContext
class ActivityItem( class ActivityItem(
private val activity: Activity, private val activity: Activity,
val clickCallback: (Int, type: String) -> Unit,
private val parentAdapter: GroupieAdapter, private val parentAdapter: GroupieAdapter,
private val fragActivity: FragmentActivity val clickCallback: (Int, type: String) -> Unit,
) : BindableItem<ItemActivityBinding>() { ) : BindableItem<ItemActivityBinding>() {
private lateinit var binding: ItemActivityBinding private lateinit var binding: ItemActivityBinding
@ -55,14 +52,14 @@ class ActivityItem(
} }
binding.activityRepliesContainer.setOnClickListener { binding.activityRepliesContainer.setOnClickListener {
RepliesBottomDialog.newInstance(activity.id) RepliesBottomDialog.newInstance(activity.id)
.show(fragActivity.supportFragmentManager, "replies") .show((context as FragmentActivity).supportFragmentManager, "replies")
} }
binding.replyCount.text = activity.replyCount.toString() binding.replyCount.text = activity.replyCount.toString()
binding.activityReplies.setColorFilter(ContextCompat.getColor(binding.root.context, R.color.bg_opp)) binding.activityReplies.setColorFilter(ContextCompat.getColor(binding.root.context, R.color.bg_opp))
binding.activityLikeContainer.setOnLongClickListener { binding.activityLikeContainer.setOnLongClickListener {
UsersDialogFragment().apply { UsersDialogFragment().apply {
userList(userList) userList(userList)
show(fragActivity.supportFragmentManager, "dialog") show((context as FragmentActivity).supportFragmentManager, "dialog")
} }
true true
} }
@ -152,7 +149,7 @@ class ActivityItem(
binding.activityEdit.setOnClickListener { binding.activityEdit.setOnClickListener {
ContextCompat.startActivity( ContextCompat.startActivity(
context, context,
Intent(context, MarkdownCreatorActivity::class.java) Intent(context, ActivityMarkdownCreator::class.java)
.putExtra("type", "activity") .putExtra("type", "activity")
.putExtra("other", activity.text) .putExtra("other", activity.text)
.putExtra("edit", activity.id), .putExtra("edit", activity.id),
@ -183,7 +180,7 @@ class ActivityItem(
binding.activityEdit.setOnClickListener { binding.activityEdit.setOnClickListener {
ContextCompat.startActivity( ContextCompat.startActivity(
context, context,
Intent(context, MarkdownCreatorActivity::class.java) Intent(context, ActivityMarkdownCreator::class.java)
.putExtra("type", "message") .putExtra("type", "message")
.putExtra("other", activity.message) .putExtra("other", activity.message)
.putExtra("edit", activity.id) .putExtra("edit", activity.id)

View file

@ -15,7 +15,7 @@ import ani.dantotsu.profile.User
import ani.dantotsu.profile.UsersDialogFragment import ani.dantotsu.profile.UsersDialogFragment
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
import ani.dantotsu.util.MarkdownCreatorActivity import ani.dantotsu.util.ActivityMarkdownCreator
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -81,7 +81,7 @@ class ActivityReplyItem(
binding.activityReply.setOnClickListener { binding.activityReply.setOnClickListener {
ContextCompat.startActivity( ContextCompat.startActivity(
context, context,
Intent(context, MarkdownCreatorActivity::class.java) Intent(context, ActivityMarkdownCreator::class.java)
.putExtra("type", "replyActivity") .putExtra("type", "replyActivity")
.putExtra("parentId", parentId) .putExtra("parentId", parentId)
.putExtra("other", "@${reply.user.name} "), .putExtra("other", "@${reply.user.name} "),
@ -92,7 +92,7 @@ class ActivityReplyItem(
binding.activityEdit.setOnClickListener { binding.activityEdit.setOnClickListener {
ContextCompat.startActivity( ContextCompat.startActivity(
context, context,
Intent(context, MarkdownCreatorActivity::class.java) Intent(context, ActivityMarkdownCreator::class.java)
.putExtra("type", "replyActivity") .putExtra("type", "replyActivity")
.putExtra("parentId", parentId) .putExtra("parentId", parentId)
.putExtra("other", reply.text) .putExtra("other", reply.text)

View file

@ -1,4 +1,4 @@
package ani.dantotsu.home.status package ani.dantotsu.profile.activity
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
@ -14,12 +14,10 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.ActivityReply import ani.dantotsu.connections.anilist.api.ActivityReply
import ani.dantotsu.databinding.BottomSheetRecyclerBinding import ani.dantotsu.databinding.BottomSheetRecyclerBinding
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.activity.ActivityReplyItem
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.MarkdownCreatorActivity import ani.dantotsu.util.ActivityMarkdownCreator
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -50,7 +48,7 @@ class RepliesBottomDialog : BottomSheetDialogFragment() {
binding.replyButton.setOnClickListener { binding.replyButton.setOnClickListener {
ContextCompat.startActivity( ContextCompat.startActivity(
context, context,
Intent(context, MarkdownCreatorActivity::class.java) Intent(context, ActivityMarkdownCreator::class.java)
.putExtra("type", "replyActivity") .putExtra("type", "replyActivity")
.putExtra("parentId", activityId), .putExtra("parentId", activityId),
null null
@ -73,14 +71,10 @@ class RepliesBottomDialog : BottomSheetDialogFragment() {
adapter.update( adapter.update(
replies.map { replies.map {
ActivityReplyItem( ActivityReplyItem(
it, it, activityId, requireActivity(), adapter,
activityId, ) { i, _ ->
requireActivity(), onClick(i)
adapter,
clickCallback = { int, _ ->
onClick(int)
} }
)
} }
) )
} else { } else {

View file

@ -20,7 +20,7 @@ import ani.dantotsu.profile.notification.NotificationFragment.Companion.Notifica
import nl.joery.animatedbottombar.AnimatedBottomBar import nl.joery.animatedbottombar.AnimatedBottomBar
class NotificationActivity : AppCompatActivity() { class NotificationActivity : AppCompatActivity() {
private lateinit var binding: ActivityNotificationBinding lateinit var binding: ActivityNotificationBinding
private var selected: Int = 0 private var selected: Int = 0
lateinit var navBar: AnimatedBottomBar lateinit var navBar: AnimatedBottomBar
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -38,8 +38,8 @@ class NotificationActivity : AppCompatActivity() {
bottomMargin = navBarHeight bottomMargin = navBarHeight
} }
val tabs = listOf( val tabs = listOf(
Pair(R.drawable.ic_round_movie_filter_24, "Media"),
Pair(R.drawable.ic_round_person_24, "User"), Pair(R.drawable.ic_round_person_24, "User"),
Pair(R.drawable.ic_round_movie_filter_24, "Media"),
Pair(R.drawable.ic_round_notifications_active_24, "Subs"), Pair(R.drawable.ic_round_notifications_active_24, "Subs"),
Pair(R.drawable.ic_round_comment_24, "Comments") Pair(R.drawable.ic_round_comment_24, "Comments")
) )
@ -49,7 +49,6 @@ class NotificationActivity : AppCompatActivity() {
val getOne = intent.getIntExtra("activityId", -1) val getOne = intent.getIntExtra("activityId", -1)
if (getOne != -1) navBar.isVisible = false if (getOne != -1) navBar.isVisible = false
binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne) binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne)
binding.notificationViewPager.setOffscreenPageLimit(4)
binding.notificationViewPager.setCurrentItem(selected, false) binding.notificationViewPager.setCurrentItem(selected, false)
navBar.selectTabAt(selected) navBar.selectTabAt(selected)
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener { navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {
@ -84,8 +83,8 @@ class NotificationActivity : AppCompatActivity() {
override fun getItemCount(): Int = if (id != -1) 1 else 4 override fun getItemCount(): Int = if (id != -1) 1 else 4
override fun createFragment(position: Int): Fragment = when (position) { override fun createFragment(position: Int): Fragment = when (position) {
0 -> NotificationFragment.newInstance(if (id != -1) NotificationType.ONE else NotificationType.MEDIA, id) 0 -> NotificationFragment.newInstance(NotificationType.USER)
1 -> NotificationFragment.newInstance(NotificationType.USER) 1 -> NotificationFragment.newInstance(if (id != -1) NotificationType.ONE else NotificationType.MEDIA, id)
2 -> NotificationFragment.newInstance(NotificationType.SUBSCRIPTION) 2 -> NotificationFragment.newInstance(NotificationType.SUBSCRIPTION)
3 -> NotificationFragment.newInstance(NotificationType.COMMENT) 3 -> NotificationFragment.newInstance(NotificationType.COMMENT)
else -> NotificationFragment.newInstance(NotificationType.MEDIA) else -> NotificationFragment.newInstance(NotificationType.MEDIA)

View file

@ -46,8 +46,10 @@ class NotificationFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
type = arguments?.getSerializableCompat<NotificationType>("type") as NotificationType arguments?.let {
getID = arguments?.getInt("id") ?: -1 getID = it.getInt("id")
type = it.getSerializableCompat<NotificationType>("type") as NotificationType
}
binding.notificationRecyclerView.adapter = adapter binding.notificationRecyclerView.adapter = adapter
binding.notificationRecyclerView.layoutManager = LinearLayoutManager(context) binding.notificationRecyclerView.layoutManager = LinearLayoutManager(context)
binding.notificationProgressBar.isVisible = true binding.notificationProgressBar.isVisible = true
@ -158,47 +160,31 @@ class NotificationFragment : Fragment() {
!binding.notificationRecyclerView.canScrollVertically(1) !binding.notificationRecyclerView.canScrollVertically(1)
} }
fun onClick( fun onClick(id: Int, optional: Int?, type: NotificationClickType) {
id: Int, val intent = when (type) {
optional: Int?, NotificationClickType.USER -> Intent(requireContext(), ProfileActivity::class.java).apply {
type: NotificationClickType putExtra("userId", id)
) {
when (type) {
NotificationClickType.USER -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), ProfileActivity::class.java)
.putExtra("userId", id), null
)
} }
NotificationClickType.MEDIA -> { NotificationClickType.MEDIA -> Intent(requireContext(), MediaDetailsActivity::class.java).apply {
ContextCompat.startActivity( putExtra("mediaId", id)
requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java)
.putExtra("mediaId", id), null
)
} }
NotificationClickType.ACTIVITY -> { NotificationClickType.ACTIVITY -> Intent(requireContext(), FeedActivity::class.java).apply {
ContextCompat.startActivity( putExtra("activityId", id)
requireContext(), Intent(requireContext(), FeedActivity::class.java)
.putExtra("activityId", id), null
)
} }
NotificationClickType.COMMENT -> { NotificationClickType.COMMENT -> Intent(requireContext(), MediaDetailsActivity::class.java).apply {
ContextCompat.startActivity( putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java) putExtra("mediaId", id)
.putExtra("FRAGMENT_TO_LOAD", "COMMENTS") putExtra("commentId", optional ?: -1)
.putExtra("mediaId", id)
.putExtra("commentId", optional ?: -1),
null
)
} }
NotificationClickType.UNDEFINED -> { NotificationClickType.UNDEFINED -> null
// Do nothing
} }
intent?.let {
ContextCompat.startActivity(requireContext(), it, null)
} }
} }
@ -206,18 +192,12 @@ class NotificationFragment : Fragment() {
super.onResume() super.onResume()
if (this::binding.isInitialized) { if (this::binding.isInitialized) {
binding.root.requestLayout() binding.root.requestLayout()
binding.root.setBaseline((activity as NotificationActivity).navBar)
} }
} }
companion object { companion object {
enum class NotificationClickType { enum class NotificationClickType { USER, MEDIA, ACTIVITY, COMMENT, UNDEFINED }
USER, MEDIA, ACTIVITY, COMMENT, UNDEFINED enum class NotificationType { MEDIA, USER, SUBSCRIPTION, COMMENT, ONE }
}
enum class NotificationType {
MEDIA, USER, SUBSCRIPTION, COMMENT, ONE
}
fun newInstance(type: NotificationType, id: Int = -1): NotificationFragment { fun newInstance(type: NotificationType, id: Int = -1): NotificationFragment {
return NotificationFragment().apply { return NotificationFragment().apply {

View file

@ -35,6 +35,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName 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 ani.dantotsu.util.customAlertDialog
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
@ -178,25 +179,20 @@ class ExtensionsActivity : AppCompatActivity() {
entry.name.lowercase().replace("_", " ") entry.name.lowercase().replace("_", " ")
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString() } .replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString() }
}.toTypedArray() }.toTypedArray()
val builder = AlertDialog.Builder(this, R.style.MyPopup)
val listOrder: String = PrefManager.getVal(PrefName.LangSort) val listOrder: String = PrefManager.getVal(PrefName.LangSort)
val index = LanguageMapper.Companion.Language.entries.toTypedArray() val index = LanguageMapper.Companion.Language.entries.toTypedArray()
.indexOfFirst { it.code == listOrder } .indexOfFirst { it.code == listOrder }
builder.setTitle("Language") customAlertDialog().apply {
builder.setSingleChoiceItems(languageOptions, index) { dialog, i -> setTitle("Language")
PrefManager.setVal( singleChoiceItems(languageOptions, index) { selected ->
PrefName.LangSort, PrefManager.setVal(PrefName.LangSort, LanguageMapper.Companion.Language.entries[selected].code)
LanguageMapper.Companion.Language.entries[i].code val currentFragment = supportFragmentManager.findFragmentByTag("f${viewPager.currentItem}")
)
val currentFragment =
supportFragmentManager.findFragmentByTag("f${viewPager.currentItem}")
if (currentFragment is SearchQueryHandler) { if (currentFragment is SearchQueryHandler) {
currentFragment.notifyDataChanged() currentFragment.notifyDataChanged()
} }
dialog.dismiss()
} }
val dialog = builder.show() show()
dialog.window?.setDimAmount(0.8f) }
} }
binding.settingsContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { binding.settingsContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = statusBarHeight topMargin = statusBarHeight
@ -247,10 +243,10 @@ class ExtensionsActivity : AppCompatActivity() {
) )
view.repositoryItem.text = item.removePrefix("https://raw.githubusercontent.com") view.repositoryItem.text = item.removePrefix("https://raw.githubusercontent.com")
view.repositoryItem.setOnClickListener { view.repositoryItem.setOnClickListener {
AlertDialog.Builder(this@ExtensionsActivity, R.style.MyPopup) customAlertDialog().apply {
.setTitle(R.string.rem_repository) setTitle(R.string.rem_repository)
.setMessage(item) setMessage(item)
.setPositiveButton(getString(R.string.ok)) { dialog, _ -> setPosButton(R.string.ok) {
val repos = PrefManager.getVal<Set<String>>(prefName).minus(item) val repos = PrefManager.getVal<Set<String>>(prefName).minus(item)
PrefManager.setVal(prefName, repos) PrefManager.setVal(prefName, repos)
repoInventory.removeView(view.root) repoInventory.removeView(view.root)
@ -267,13 +263,10 @@ class ExtensionsActivity : AppCompatActivity() {
else -> {} else -> {}
} }
} }
dialog.dismiss()
} }
.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> setNegButton(R.string.cancel)
dialog.dismiss() show()
} }
.create()
.show()
} }
view.repositoryItem.setOnLongClickListener { view.repositoryItem.setOnLongClickListener {
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
@ -324,20 +317,18 @@ class ExtensionsActivity : AppCompatActivity() {
dialogView.repoInventory.apply { dialogView.repoInventory.apply {
getSavedRepositories(this, type) getSavedRepositories(this, type)
} }
val alertDialog = AlertDialog.Builder(this@ExtensionsActivity, R.style.MyPopup) processEditorAction(dialogView.repositoryTextBox, type)
.setTitle(R.string.edit_repositories) customAlertDialog().apply {
.setView(dialogView.root) setTitle(R.string.edit_repositories)
.setPositiveButton(getString(R.string.add)) { _, _ -> setCustomView(dialogView.root)
if (!dialogView.repositoryTextBox.text.isNullOrBlank()) setPosButton(R.string.add) {
if (!dialogView.repositoryTextBox.text.isNullOrBlank()) {
processUserInput(dialogView.repositoryTextBox.text.toString(), type) processUserInput(dialogView.repositoryTextBox.text.toString(), type)
} }
.setNegativeButton(getString(R.string.close)) { dialog, _ ->
dialog.dismiss()
} }
.create() setNegButton(R.string.close)
processEditorAction(dialogView.repositoryTextBox, type) show()
alertDialog.show() }
alertDialog.window?.setDimAmount(0.8f)
} }
} }
} }

View file

@ -50,6 +50,11 @@ class FAQActivity : AppCompatActivity() {
currContext()?.getString(R.string.question_5) ?: "", currContext()?.getString(R.string.question_5) ?: "",
currContext()?.getString(R.string.answer_5) ?: "" currContext()?.getString(R.string.answer_5) ?: ""
), ),
Triple(
R.drawable.ic_anilist,
currContext()?.getString(R.string.question_18) ?: "",
currContext()?.getString(R.string.answer_18) ?: ""
),
Triple( Triple(
R.drawable.ic_anilist, R.drawable.ic_anilist,
currContext()?.getString(R.string.question_6) ?: "", currContext()?.getString(R.string.question_6) ?: "",
@ -60,6 +65,11 @@ class FAQActivity : AppCompatActivity() {
currContext()?.getString(R.string.question_7) ?: "", currContext()?.getString(R.string.question_7) ?: "",
currContext()?.getString(R.string.answer_7) ?: "" currContext()?.getString(R.string.answer_7) ?: ""
), ),
Triple(
R.drawable.ic_round_magnet_24,
currContext()?.getString(R.string.question_19) ?: "",
currContext()?.getString(R.string.answer_19) ?: ""
),
Triple( Triple(
R.drawable.ic_round_lock_open_24, R.drawable.ic_round_lock_open_24,
currContext()?.getString(R.string.question_9) ?: "", currContext()?.getString(R.string.question_9) ?: "",

View file

@ -25,13 +25,15 @@ import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.databinding.FragmentExtensionsBinding import ani.dantotsu.databinding.FragmentExtensionsBinding
import ani.dantotsu.others.LanguageMapper import ani.dantotsu.others.LanguageMapper.Companion.getLanguageName
import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
@ -72,16 +74,15 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
if (allSettings.isNotEmpty()) { if (allSettings.isNotEmpty()) {
var selectedSetting = allSettings[0] var selectedSetting = allSettings[0]
if (allSettings.size > 1) { if (allSettings.size > 1) {
val names = allSettings.map { LanguageMapper.getLanguageName(it.lang) } val names = allSettings.map { getLanguageName(it.lang) }
.toTypedArray() .toTypedArray()
var selectedIndex = 0 var selectedIndex = 0
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup) requireContext().customAlertDialog().apply {
.setTitle("Select a Source") setTitle("Select a Source")
.setSingleChoiceItems(names, selectedIndex) { dialog, which -> singleChoiceItems(names, selectedIndex) { which ->
itemSelected = true itemSelected = true
selectedIndex = which selectedIndex = which
selectedSetting = allSettings[selectedIndex] selectedSetting = allSettings[selectedIndex]
dialog.dismiss()
val fragment = val fragment =
AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) { AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) {
@ -93,13 +94,13 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
.addToBackStack(null) .addToBackStack(null)
.commit() .commit()
} }
.setOnDismissListener { onDismiss {
if (!itemSelected) { if (!itemSelected) {
changeUIVisibility(true) changeUIVisibility(true)
} }
} }
.show() show()
dialog.window?.setDimAmount(0.8f) }
} else { } else {
// If there's only one setting, proceed with the fragment transaction // If there's only one setting, proceed with the fragment transaction
val fragment = val fragment =
@ -295,7 +296,7 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = getItem(position) val extension = getItem(position)
val nsfw = if (extension.isNsfw) "(18+)" else "" val nsfw = if (extension.isNsfw) "(18+)" else ""
val lang = LanguageMapper.getLanguageName(extension.lang) val lang = getLanguageName(extension.lang)
holder.extensionNameTextView.text = extension.name holder.extensionNameTextView.text = extension.name
val versionText = "$lang ${extension.versionName} $nsfw" val versionText = "$lang ${extension.versionName} $nsfw"
holder.extensionVersionTextView.text = versionText holder.extensionVersionTextView.text = versionText

View file

@ -34,6 +34,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
@ -74,13 +75,12 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
val names = allSettings.map { LanguageMapper.getLanguageName(it.lang) } val names = allSettings.map { LanguageMapper.getLanguageName(it.lang) }
.toTypedArray() .toTypedArray()
var selectedIndex = 0 var selectedIndex = 0
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup) requireContext().customAlertDialog().apply {
.setTitle("Select a Source") setTitle("Select a Source")
.setSingleChoiceItems(names, selectedIndex) { dialog, which -> singleChoiceItems(names, selectedIndex) { which ->
itemSelected = true itemSelected = true
selectedIndex = which selectedIndex = which
selectedSetting = allSettings[selectedIndex] selectedSetting = allSettings[selectedIndex]
dialog.dismiss()
// Move the fragment transaction here // Move the fragment transaction here
val fragment = val fragment =
@ -93,13 +93,13 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
.addToBackStack(null) .addToBackStack(null)
.commit() .commit()
} }
.setOnDismissListener { onDismiss {
if (!itemSelected) { if (!itemSelected) {
changeUIVisibility(true) changeUIVisibility(true)
} }
} }
.show() show()
dialog.window?.setDimAmount(0.8f) }
} else { } else {
// If there's only one setting, proceed with the fragment transaction // If there's only one setting, proceed with the fragment transaction
val fragment = val fragment =

View file

@ -28,6 +28,7 @@ import ani.dantotsu.snackString
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.slider.Slider.OnChangeListener import com.google.android.material.slider.Slider.OnChangeListener
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -100,20 +101,19 @@ class PlayerSettingsActivity : AppCompatActivity() {
R.string.default_playback_speed, R.string.default_playback_speed,
speedsName[PrefManager.getVal(PrefName.DefaultSpeed)] speedsName[PrefManager.getVal(PrefName.DefaultSpeed)]
) )
val speedDialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.default_speed))
binding.playerSettingsSpeed.setOnClickListener { binding.playerSettingsSpeed.setOnClickListener {
val dialog = customAlertDialog().apply {
speedDialog.setSingleChoiceItems( setTitle(getString(R.string.default_speed))
singleChoiceItems(
speedsName, speedsName,
PrefManager.getVal(PrefName.DefaultSpeed) PrefManager.getVal(PrefName.DefaultSpeed)
) { dialog, i -> ) { i ->
PrefManager.setVal(PrefName.DefaultSpeed, i) PrefManager.setVal(PrefName.DefaultSpeed, i)
binding.playerSettingsSpeed.text = binding.playerSettingsSpeed.text =
getString(R.string.default_playback_speed, speedsName[i]) getString(R.string.default_playback_speed, speedsName[i])
dialog.dismiss() }
}.show() show()
dialog.window?.setDimAmount(0.8f) }
} }
binding.playerSettingsCursedSpeeds.isChecked = PrefManager.getVal(PrefName.CursedSpeeds) binding.playerSettingsCursedSpeeds.isChecked = PrefManager.getVal(PrefName.CursedSpeeds)
@ -278,17 +278,17 @@ class PlayerSettingsActivity : AppCompatActivity() {
} }
val resizeModes = arrayOf("Original", "Zoom", "Stretch") val resizeModes = arrayOf("Original", "Zoom", "Stretch")
val resizeDialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.default_resize_mode))
binding.playerResizeMode.setOnClickListener { binding.playerResizeMode.setOnClickListener {
val dialog = resizeDialog.setSingleChoiceItems( customAlertDialog().apply {
setTitle(getString(R.string.default_resize_mode))
singleChoiceItems(
resizeModes, resizeModes,
PrefManager.getVal<Int>(PrefName.Resize) PrefManager.getVal<Int>(PrefName.Resize)
) { dialog, count -> ) { count ->
PrefManager.setVal(PrefName.Resize, count) PrefManager.setVal(PrefName.Resize, count)
dialog.dismiss() }
}.show() show()
dialog.window?.setDimAmount(0.8f) }
} }
fun toggleSubOptions(isChecked: Boolean) { fun toggleSubOptions(isChecked: Boolean) {
@ -332,18 +332,18 @@ class PlayerSettingsActivity : AppCompatActivity() {
"Blue", "Blue",
"Magenta" "Magenta"
) )
val primaryColorDialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.primary_sub_color))
binding.videoSubColorPrimary.setOnClickListener { binding.videoSubColorPrimary.setOnClickListener {
val dialog = primaryColorDialog.setSingleChoiceItems( customAlertDialog().apply {
setTitle(getString(R.string.primary_sub_color))
singleChoiceItems(
colorsPrimary, colorsPrimary,
PrefManager.getVal(PrefName.PrimaryColor) PrefManager.getVal(PrefName.PrimaryColor)
) { dialog, count -> ) { count ->
PrefManager.setVal(PrefName.PrimaryColor, count) PrefManager.setVal(PrefName.PrimaryColor, count)
dialog.dismiss()
updateSubPreview() updateSubPreview()
}.show() }
dialog.window?.setDimAmount(0.8f) show()
}
} }
val colorsSecondary = arrayOf( val colorsSecondary = arrayOf(
"Black", "Black",
@ -359,32 +359,32 @@ class PlayerSettingsActivity : AppCompatActivity() {
"Magenta", "Magenta",
"Transparent" "Transparent"
) )
val secondaryColorDialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.outline_sub_color))
binding.videoSubColorSecondary.setOnClickListener { binding.videoSubColorSecondary.setOnClickListener {
val dialog = secondaryColorDialog.setSingleChoiceItems( customAlertDialog().apply {
setTitle(getString(R.string.outline_sub_color))
singleChoiceItems(
colorsSecondary, colorsSecondary,
PrefManager.getVal(PrefName.SecondaryColor) PrefManager.getVal(PrefName.SecondaryColor)
) { dialog, count -> ) { count ->
PrefManager.setVal(PrefName.SecondaryColor, count) PrefManager.setVal(PrefName.SecondaryColor, count)
dialog.dismiss()
updateSubPreview() updateSubPreview()
}.show() }
dialog.window?.setDimAmount(0.8f) show()
}
} }
val typesOutline = arrayOf("Outline", "Shine", "Drop Shadow", "None") val typesOutline = arrayOf("Outline", "Shine", "Drop Shadow", "None")
val outlineDialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.outline_type))
binding.videoSubOutline.setOnClickListener { binding.videoSubOutline.setOnClickListener {
val dialog = outlineDialog.setSingleChoiceItems( customAlertDialog().apply {
setTitle(getString(R.string.outline_type))
singleChoiceItems(
typesOutline, typesOutline,
PrefManager.getVal(PrefName.Outline) PrefManager.getVal(PrefName.Outline)
) { dialog, count -> ) { count ->
PrefManager.setVal(PrefName.Outline, count) PrefManager.setVal(PrefName.Outline, count)
dialog.dismiss()
updateSubPreview() updateSubPreview()
}.show() }
dialog.window?.setDimAmount(0.8f) show()
}
} }
val colorsSubBackground = arrayOf( val colorsSubBackground = arrayOf(
"Transparent", "Transparent",
@ -400,18 +400,18 @@ class PlayerSettingsActivity : AppCompatActivity() {
"Blue", "Blue",
"Magenta" "Magenta"
) )
val subBackgroundDialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.sub_background_color_select))
binding.videoSubColorBackground.setOnClickListener { binding.videoSubColorBackground.setOnClickListener {
val dialog = subBackgroundDialog.setSingleChoiceItems( customAlertDialog().apply {
setTitle(getString(R.string.sub_background_color_select))
singleChoiceItems(
colorsSubBackground, colorsSubBackground,
PrefManager.getVal(PrefName.SubBackground) PrefManager.getVal(PrefName.SubBackground)
) { dialog, count -> ) { count ->
PrefManager.setVal(PrefName.SubBackground, count) PrefManager.setVal(PrefName.SubBackground, count)
dialog.dismiss()
updateSubPreview() updateSubPreview()
}.show() }
dialog.window?.setDimAmount(0.8f) show()
}
} }
val colorsSubWindow = arrayOf( val colorsSubWindow = arrayOf(
@ -428,18 +428,18 @@ class PlayerSettingsActivity : AppCompatActivity() {
"Blue", "Blue",
"Magenta" "Magenta"
) )
val subWindowDialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.sub_window_color_select))
binding.videoSubColorWindow.setOnClickListener { binding.videoSubColorWindow.setOnClickListener {
val dialog = subWindowDialog.setSingleChoiceItems( customAlertDialog().apply {
setTitle(getString(R.string.sub_window_color_select))
singleChoiceItems(
colorsSubWindow, colorsSubWindow,
PrefManager.getVal(PrefName.SubWindow) PrefManager.getVal(PrefName.SubWindow)
) { dialog, count -> ) { count ->
PrefManager.setVal(PrefName.SubWindow, count) PrefManager.setVal(PrefName.SubWindow, count)
dialog.dismiss()
updateSubPreview() updateSubPreview()
}.show() }
dialog.window?.setDimAmount(0.8f) show()
}
} }
binding.videoSubAlpha.value = PrefManager.getVal(PrefName.SubAlpha) binding.videoSubAlpha.value = PrefManager.getVal(PrefName.SubAlpha)
@ -459,18 +459,18 @@ class PlayerSettingsActivity : AppCompatActivity() {
"Levenim MT Bold", "Levenim MT Bold",
"Blocky" "Blocky"
) )
val fontDialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.subtitle_font))
binding.videoSubFont.setOnClickListener { binding.videoSubFont.setOnClickListener {
val dialog = fontDialog.setSingleChoiceItems( customAlertDialog().apply {
setTitle(getString(R.string.subtitle_font))
singleChoiceItems(
fonts, fonts,
PrefManager.getVal(PrefName.Font) PrefManager.getVal(PrefName.Font)
) { dialog, count -> ) { count ->
PrefManager.setVal(PrefName.Font, count) PrefManager.setVal(PrefName.Font, count)
dialog.dismiss()
updateSubPreview() updateSubPreview()
}.show() }
dialog.window?.setDimAmount(0.8f) show()
}
} }
binding.subtitleFontSize.setText(PrefManager.getVal<Int>(PrefName.FontSize).toString()) binding.subtitleFontSize.setText(PrefManager.getVal<Int>(PrefName.FontSize).toString())
binding.subtitleFontSize.setOnEditorActionListener { _, actionId, _ -> binding.subtitleFontSize.setOnEditorActionListener { _, actionId, _ ->

View file

@ -20,6 +20,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.databinding.ActivitySettingsCommonBinding import ani.dantotsu.databinding.ActivitySettingsCommonBinding
import ani.dantotsu.databinding.DialogSetPasswordBinding
import ani.dantotsu.databinding.DialogUserAgentBinding import ani.dantotsu.databinding.DialogUserAgentBinding
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
@ -37,6 +38,7 @@ import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.util.LauncherWrapper import ani.dantotsu.util.LauncherWrapper
import ani.dantotsu.util.StoragePermissions import ani.dantotsu.util.StoragePermissions
import ani.dantotsu.util.customAlertDialog
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -160,18 +162,16 @@ class SettingsCommonActivity : AppCompatActivity() {
icon = R.drawable.ic_download_24, icon = R.drawable.ic_download_24,
onClick = { onClick = {
val managers = arrayOf("Default", "1DM", "ADM") val managers = arrayOf("Default", "1DM", "ADM")
val downloadManagerDialog = customAlertDialog().apply {
AlertDialog.Builder(context, R.style.MyPopup) setTitle(getString(R.string.download_manager))
.setTitle(R.string.download_manager) singleChoiceItems(
var downloadManager: Int = PrefManager.getVal(PrefName.DownloadManager) managers,
val dialog = downloadManagerDialog.setSingleChoiceItems( PrefManager.getVal(PrefName.DownloadManager)
managers, downloadManager ) { count ->
) { dialog, count -> PrefManager.setVal(PrefName.DownloadManager, count)
downloadManager = count }
PrefManager.setVal(PrefName.DownloadManager, downloadManager) show()
dialog.dismiss() }
}.show()
dialog.window?.setDimAmount(0.8f)
} }
), ),
Settings( Settings(
@ -180,90 +180,67 @@ class SettingsCommonActivity : AppCompatActivity() {
desc = getString(R.string.app_lock_desc), desc = getString(R.string.app_lock_desc),
icon = R.drawable.ic_round_lock_open_24, icon = R.drawable.ic_round_lock_open_24,
onClick = { onClick = {
val passwordDialog = AlertDialog.Builder(context, R.style.MyPopup) customAlertDialog().apply {
.setTitle(R.string.app_lock) val view = DialogSetPasswordBinding.inflate(layoutInflater)
.setView(R.layout.dialog_set_password) setTitle(R.string.app_lock)
.setPositiveButton(R.string.ok) { dialog, _ -> setCustomView(view.root)
val passwordInput = setPosButton(R.string.ok) {
(dialog as AlertDialog).findViewById<EditText>(R.id.passwordInput) if (view.forgotPasswordCheckbox.isChecked) {
val confirmPasswordInput =
dialog.findViewById<EditText>(R.id.confirmPasswordInput)
val forgotPasswordCheckbox =
dialog.findViewById<CheckBox>(R.id.forgotPasswordCheckbox)
if (forgotPasswordCheckbox?.isChecked == true) {
PrefManager.setVal(PrefName.OverridePassword, true) PrefManager.setVal(PrefName.OverridePassword, true)
} }
val password = view.passwordInput.text.toString()
val password = passwordInput?.text.toString() val confirmPassword = view.confirmPasswordInput.text.toString()
val confirmPassword = confirmPasswordInput?.text.toString()
if (password == confirmPassword && password.isNotEmpty()) { if (password == confirmPassword && password.isNotEmpty()) {
PrefManager.setVal(PrefName.AppPassword, password) PrefManager.setVal(PrefName.AppPassword, password)
if (dialog.findViewById<CheckBox>(R.id.biometricCheckbox)?.isChecked == true) { if (view.biometricCheckbox.isChecked) {
val canBiometricPrompt = val canBiometricPrompt =
BiometricManager.from(applicationContext) BiometricManager.from(applicationContext)
.canAuthenticate( .canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
BiometricManager.Authenticators.BIOMETRIC_WEAK
) == BiometricManager.BIOMETRIC_SUCCESS
if (canBiometricPrompt) { if (canBiometricPrompt) {
val biometricPrompt = val biometricPrompt =
BiometricPromptUtils.createBiometricPrompt( BiometricPromptUtils.createBiometricPrompt(this@SettingsCommonActivity) { _ ->
this@SettingsCommonActivity
) { _ ->
val token = UUID.randomUUID().toString() val token = UUID.randomUUID().toString()
PrefManager.setVal( PrefManager.setVal(
PrefName.BiometricToken, PrefName.BiometricToken,
token token
) )
toast(R.string.success) toast(R.string.success)
dialog.dismiss()
} }
val promptInfo = val promptInfo =
BiometricPromptUtils.createPromptInfo( BiometricPromptUtils.createPromptInfo(this@SettingsCommonActivity)
this@SettingsCommonActivity
)
biometricPrompt.authenticate(promptInfo) biometricPrompt.authenticate(promptInfo)
} }
} else { } else {
PrefManager.setVal(PrefName.BiometricToken, "") PrefManager.setVal(PrefName.BiometricToken, "")
toast(R.string.success) toast(R.string.success)
dialog.dismiss()
} }
} else { } else {
toast(R.string.password_mismatch) toast(R.string.password_mismatch)
} }
} }
.setNegativeButton(R.string.cancel) { dialog, _ -> setNegButton(R.string.cancel)
dialog.dismiss() setNeutralButton(R.string.remove){
}
.setNeutralButton(R.string.remove) { dialog, _ ->
PrefManager.setVal(PrefName.AppPassword, "") PrefManager.setVal(PrefName.AppPassword, "")
PrefManager.setVal(PrefName.BiometricToken, "") PrefManager.setVal(PrefName.BiometricToken, "")
PrefManager.setVal(PrefName.OverridePassword, false) PrefManager.setVal(PrefName.OverridePassword, false)
toast(R.string.success) toast(R.string.success)
dialog.dismiss()
} }
.create() setOnShowListener {
view.passwordInput.requestFocus()
passwordDialog.window?.setDimAmount(0.8f)
passwordDialog.setOnShowListener {
passwordDialog.findViewById<EditText>(R.id.passwordInput)
?.requestFocus()
val biometricCheckbox =
passwordDialog.findViewById<CheckBox>(R.id.biometricCheckbox)
val forgotPasswordCheckbox =
passwordDialog.findViewById<CheckBox>(R.id.forgotPasswordCheckbox)
val canAuthenticate = val canAuthenticate =
BiometricManager.from(applicationContext).canAuthenticate( BiometricManager.from(applicationContext).canAuthenticate(
BiometricManager.Authenticators.BIOMETRIC_WEAK BiometricManager.Authenticators.BIOMETRIC_WEAK
) == BiometricManager.BIOMETRIC_SUCCESS ) == BiometricManager.BIOMETRIC_SUCCESS
biometricCheckbox?.isVisible = canAuthenticate view.biometricCheckbox.isVisible = canAuthenticate
biometricCheckbox?.isChecked = view.biometricCheckbox.isChecked =
PrefManager.getVal(PrefName.BiometricToken, "").isNotEmpty() PrefManager.getVal(PrefName.BiometricToken, "").isNotEmpty()
forgotPasswordCheckbox?.isChecked = view.forgotPasswordCheckbox.isChecked =
PrefManager.getVal(PrefName.OverridePassword) PrefManager.getVal(PrefName.OverridePassword)
} }
passwordDialog.show() show()
}
} }
), ),
@ -328,10 +305,10 @@ class SettingsCommonActivity : AppCompatActivity() {
desc = getString(R.string.change_download_location_desc), desc = getString(R.string.change_download_location_desc),
icon = R.drawable.ic_round_source_24, icon = R.drawable.ic_round_source_24,
onClick = { onClick = {
val dialog = AlertDialog.Builder(context, R.style.MyPopup) context.customAlertDialog().apply{
.setTitle(R.string.change_download_location) setTitle(R.string.change_download_location)
.setMessage(R.string.download_location_msg) setMessage(R.string.download_location_msg)
.setPositiveButton(R.string.ok) { dialog, _ -> setPosButton(R.string.ok){
val oldUri = PrefManager.getVal<String>(PrefName.DownloadsDir) val oldUri = PrefManager.getVal<String>(PrefName.DownloadsDir)
launcher.registerForCallback { success -> launcher.registerForCallback { success ->
if (success) { if (success) {
@ -354,12 +331,10 @@ class SettingsCommonActivity : AppCompatActivity() {
} }
} }
launcher.launch() launcher.launch()
dialog.dismiss() }
}.setNeutralButton(R.string.cancel) { dialog, _ -> setNegButton(R.string.cancel)
dialog.dismiss() show()
}.create() }
dialog.window?.setDimAmount(0.8f)
dialog.show()
} }
), ),
Settings( Settings(
@ -372,6 +347,17 @@ class SettingsCommonActivity : AppCompatActivity() {
PrefManager.setVal(PrefName.ContinueMedia, isChecked) PrefManager.setVal(PrefName.ContinueMedia, isChecked)
} }
), ),
Settings(
type = 2,
name = getString(R.string.hide_private),
desc = getString(R.string.hide_private_desc),
icon = R.drawable.ic_round_remove_red_eye_24,
isChecked = PrefManager.getVal(PrefName.HidePrivate),
switch = { isChecked, _ ->
PrefManager.setVal(PrefName.HidePrivate, isChecked)
restartApp()
}
),
Settings( Settings(
type = 2, type = 2,
name = getString(R.string.search_source_list), name = getString(R.string.search_source_list),

View file

@ -81,11 +81,11 @@ class SettingsExtensionsActivity : AppCompatActivity() {
view.repositoryItem.text = view.repositoryItem.text =
item.removePrefix("https://raw.githubusercontent.com/") item.removePrefix("https://raw.githubusercontent.com/")
view.repositoryItem.setOnClickListener { view.repositoryItem.setOnClickListener {
AlertDialog.Builder(context, R.style.MyPopup) context.customAlertDialog().apply {
.setTitle(R.string.rem_repository).setMessage(item) setTitle(R.string.rem_repository)
.setPositiveButton(getString(R.string.ok)) { dialog, _ -> setMessage(item)
val repos = setPosButton(R.string.ok) {
PrefManager.getVal<Set<String>>(repoList).minus(item) val repos = PrefManager.getVal<Set<String>>(repoList).minus(item)
PrefManager.setVal(repoList, repos) PrefManager.setVal(repoList, repos)
setExtensionOutput(repoInventory, type) setExtensionOutput(repoInventory, type)
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
@ -93,18 +93,16 @@ class SettingsExtensionsActivity : AppCompatActivity() {
MediaType.ANIME -> { MediaType.ANIME -> {
animeExtensionManager.findAvailableExtensions() animeExtensionManager.findAvailableExtensions()
} }
MediaType.MANGA -> { MediaType.MANGA -> {
mangaExtensionManager.findAvailableExtensions() mangaExtensionManager.findAvailableExtensions()
} }
else -> {} else -> {}
} }
} }
dialog.dismiss() }
}.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> setNegButton(R.string.cancel)
dialog.dismiss() show()
}.create().show() }
} }
view.repositoryItem.setOnLongClickListener { view.repositoryItem.setOnLongClickListener {
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
@ -209,27 +207,27 @@ class SettingsExtensionsActivity : AppCompatActivity() {
val editText = dialogView.userAgentTextBox.apply { val editText = dialogView.userAgentTextBox.apply {
hint = getString(R.string.manga_add_repository) hint = getString(R.string.manga_add_repository)
} }
val alertDialog = AlertDialog.Builder(context, R.style.MyPopup) context.customAlertDialog().apply {
.setTitle(R.string.manga_add_repository).setView(dialogView.root) setTitle(R.string.manga_add_repository)
.setPositiveButton(getString(R.string.ok)) { dialog, _ -> setCustomView(dialogView.root)
setPosButton(R.string.ok) {
if (!editText.text.isNullOrBlank()) processUserInput( if (!editText.text.isNullOrBlank()) processUserInput(
editText.text.toString(), editText.text.toString(),
MediaType.MANGA, MediaType.MANGA,
it.attachView it.attachView
) )
dialog.dismiss() }
}.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> setNegButton(R.string.cancel)
dialog.dismiss() attach { dialog ->
}.create()
processEditorAction( processEditorAction(
alertDialog, dialog,
editText, editText,
MediaType.MANGA, MediaType.MANGA,
it.attachView it.attachView
) )
alertDialog.show() }
alertDialog.window?.setDimAmount(0.8f) }.show()
}, },
attach = { attach = {
setExtensionOutput(it.attachView, MediaType.MANGA) setExtensionOutput(it.attachView, MediaType.MANGA)
@ -258,24 +256,18 @@ class SettingsExtensionsActivity : AppCompatActivity() {
val dialogView = DialogUserAgentBinding.inflate(layoutInflater) val dialogView = DialogUserAgentBinding.inflate(layoutInflater)
val editText = dialogView.userAgentTextBox val editText = dialogView.userAgentTextBox
editText.setText(PrefManager.getVal<String>(PrefName.DefaultUserAgent)) editText.setText(PrefManager.getVal<String>(PrefName.DefaultUserAgent))
val alertDialog = AlertDialog.Builder(context, R.style.MyPopup) context.customAlertDialog().apply {
.setTitle(R.string.user_agent).setView(dialogView.root) setTitle(R.string.user_agent)
.setPositiveButton(getString(R.string.ok)) { dialog, _ -> setCustomView(dialogView.root)
PrefManager.setVal( setPosButton(R.string.ok) {
PrefName.DefaultUserAgent, PrefManager.setVal(PrefName.DefaultUserAgent, editText.text.toString())
editText.text.toString() }
) setNeutralButton(R.string.reset) {
dialog.dismiss()
}.setNeutralButton(getString(R.string.reset)) { dialog, _ ->
PrefManager.removeVal(PrefName.DefaultUserAgent) PrefManager.removeVal(PrefName.DefaultUserAgent)
editText.setText("") editText.setText("")
dialog.dismiss() }
}.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> setNegButton(R.string.cancel)
dialog.dismiss() }.show()
}.create()
alertDialog.show()
alertDialog.window?.setDimAmount(0.8f)
} }
), ),
Settings( Settings(

View file

@ -25,6 +25,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName 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 ani.dantotsu.util.customAlertDialog
import java.util.Locale import java.util.Locale
class SettingsNotificationActivity : AppCompatActivity() { class SettingsNotificationActivity : AppCompatActivity() {
@ -77,26 +78,16 @@ class SettingsNotificationActivity : AppCompatActivity() {
desc = getString(R.string.subscriptions_info), desc = getString(R.string.subscriptions_info),
icon = R.drawable.ic_round_notifications_none_24, icon = R.drawable.ic_round_notifications_none_24,
onClick = { onClick = {
val speedDialog = AlertDialog.Builder(context, R.style.MyPopup) context.customAlertDialog().apply {
.setTitle(R.string.subscriptions_checking_time) setTitle(R.string.subscriptions_checking_time)
val dialog = singleChoiceItems(timeNames, curTime) { i ->
speedDialog.setSingleChoiceItems(timeNames, curTime) { dialog, i ->
curTime = i curTime = i
it.settingsTitle.text = it.settingsTitle.text = getString(R.string.subscriptions_checking_time_s, timeNames[i])
getString( PrefManager.setVal(PrefName.SubscriptionNotificationInterval, curTime)
R.string.subscriptions_checking_time_s, TaskScheduler.create(context, PrefManager.getVal(PrefName.UseAlarmManager)).scheduleAllTasks(context)
timeNames[i] }
) show()
PrefManager.setVal( }
PrefName.SubscriptionNotificationInterval,
curTime
)
dialog.dismiss()
TaskScheduler.create(
context, PrefManager.getVal(PrefName.UseAlarmManager)
).scheduleAllTasks(context)
}.show()
dialog.window?.setDimAmount(0.8f)
}, },
onLongClick = { onLongClick = {
TaskScheduler.create( TaskScheduler.create(

View file

@ -9,31 +9,25 @@ import ani.dantotsu.loadImage
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.notifications.subscription.SubscriptionHelper import ani.dantotsu.notifications.subscription.SubscriptionHelper
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.Item
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
class SubscriptionItem( class SubscriptionItem(
val id: Int, val id: Int,
private val media: SubscriptionHelper.Companion.SubscribeMedia, private val media: SubscriptionHelper.Companion.SubscribeMedia,
private val adapter: GroupieAdapter private val adapter: GroupieAdapter,
private val onItemRemoved: (Int) -> Unit
) : BindableItem<ItemSubscriptionBinding>() { ) : BindableItem<ItemSubscriptionBinding>() {
private lateinit var binding: ItemSubscriptionBinding private lateinit var binding: ItemSubscriptionBinding
override fun bind(p0: ItemSubscriptionBinding, p1: Int) {
val context = p0.root.context override fun bind(viewBinding: ItemSubscriptionBinding, position: Int) {
binding = p0 binding = viewBinding
val parserName = if (media.isAnime) val context = binding.root.context
SubscriptionHelper.getAnimeParser(media.id).name
else binding.subscriptionName.text = media.name
SubscriptionHelper.getMangaParser(media.id).name
val mediaName = media.name
val showName = "$mediaName ($parserName)"
binding.subscriptionName.text = showName
binding.root.setOnClickListener { binding.root.setOnClickListener {
ContextCompat.startActivity( ContextCompat.startActivity(
context, context,
Intent(context, MediaDetailsActivity::class.java).putExtra( Intent(context, MediaDetailsActivity::class.java).putExtra("mediaId", media.id),
"mediaId", media.id
),
null null
) )
} }
@ -41,14 +35,11 @@ class SubscriptionItem(
binding.deleteSubscription.setOnClickListener { binding.deleteSubscription.setOnClickListener {
SubscriptionHelper.deleteSubscription(id, true) SubscriptionHelper.deleteSubscription(id, true)
adapter.remove(this) adapter.remove(this)
onItemRemoved(id)
} }
} }
override fun getLayout(): Int { override fun getLayout(): Int = R.layout.item_subscription
return R.layout.item_subscription
}
override fun initializeViewBinding(p0: View): ItemSubscriptionBinding { override fun initializeViewBinding(view: View): ItemSubscriptionBinding = ItemSubscriptionBinding.bind(view)
return ItemSubscriptionBinding.bind(p0)
}
} }

View file

@ -0,0 +1,113 @@
package ani.dantotsu.settings
import android.app.AlertDialog
import android.content.Context
import android.graphics.drawable.Drawable
import android.view.View
import android.view.ViewGroup
import ani.dantotsu.R
import ani.dantotsu.databinding.ItemExtensionBinding
import ani.dantotsu.notifications.subscription.SubscriptionHelper
import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.viewbinding.BindableItem
class SubscriptionSource(
private val parserName: String,
private val subscriptions: MutableList<SubscriptionHelper.Companion.SubscribeMedia>,
private val adapter: GroupieAdapter,
private var parserIcon: Drawable? = null,
private val onGroupRemoved: (SubscriptionSource) -> Unit
) : BindableItem<ItemExtensionBinding>() {
private lateinit var binding: ItemExtensionBinding
private var isExpanded = false
override fun bind(viewBinding: ItemExtensionBinding, position: Int) {
binding = viewBinding
binding.extensionNameTextView.text = parserName
updateSubscriptionCount()
binding.extensionSubscriptions.visibility = View.VISIBLE
binding.extensionSubscriptions.setOnClickListener(null)
binding.root.setOnClickListener {
isExpanded = !isExpanded
toggleSubscriptions()
}
binding.subscriptionCount.setOnClickListener {
showRemoveAllSubscriptionsDialog(it.context)
}
binding.extensionIconImageView.visibility = View.VISIBLE
val layoutParams = binding.extensionIconImageView.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.leftMargin = 28
binding.extensionIconImageView.layoutParams = layoutParams
parserIcon?.let {
binding.extensionIconImageView.setImageDrawable(it)
} ?: run {
binding.extensionIconImageView.setImageResource(R.drawable.control_background_40dp)
}
binding.extensionPinImageView.visibility = View.GONE
binding.extensionVersionTextView.visibility = View.GONE
binding.closeTextView.visibility = View.GONE
binding.settingsImageView.visibility = View.GONE
}
private fun updateSubscriptionCount() {
binding.subscriptionCount.text = subscriptions.size.toString()
binding.subscriptionCount.visibility = if (subscriptions.isEmpty()) View.GONE else View.VISIBLE
}
private fun showRemoveAllSubscriptionsDialog(context: Context) {
AlertDialog.Builder(context, R.style.MyPopup)
.setTitle(R.string.remove_all_subscriptions)
.setMessage(context.getString(R.string.remove_all_subscriptions_desc, parserName))
.setPositiveButton(R.string.apply) { _, _ ->
removeAllSubscriptions()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
private fun removeAllSubscriptions() {
subscriptions.forEach { subscription ->
SubscriptionHelper.deleteSubscription(subscription.id, false)
}
if (isExpanded) {
val startPosition = adapter.getAdapterPosition(this) + 1
repeat(subscriptions.size) {
adapter.removeGroupAtAdapterPosition(startPosition)
}
}
subscriptions.clear()
onGroupRemoved(this)
}
private fun removeSubscription(id: Any?) {
subscriptions.removeAll { it.id == id }
updateSubscriptionCount()
if (subscriptions.isEmpty()) {
onGroupRemoved(this)
} else {
adapter.notifyItemChanged(adapter.getAdapterPosition(this))
}
}
private fun toggleSubscriptions() {
val startPosition = adapter.getAdapterPosition(this) + 1
if (isExpanded) {
subscriptions.forEachIndexed { index, subscribeMedia ->
adapter.add(startPosition + index, SubscriptionItem(subscribeMedia.id, subscribeMedia, adapter) { removedId ->
removeSubscription(removedId)
})
}
} else {
repeat(subscriptions.size) {
adapter.removeGroupAtAdapterPosition(startPosition)
}
}
}
override fun getLayout(): Int = R.layout.item_extension
override fun initializeViewBinding(view: View): ItemExtensionBinding = ItemExtensionBinding.bind(view)
}

View file

@ -1,5 +1,6 @@
package ani.dantotsu.settings package ani.dantotsu.settings
import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -9,13 +10,21 @@ import ani.dantotsu.BottomSheetDialogFragment
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.databinding.BottomSheetRecyclerBinding import ani.dantotsu.databinding.BottomSheetRecyclerBinding
import ani.dantotsu.notifications.subscription.SubscriptionHelper import ani.dantotsu.notifications.subscription.SubscriptionHelper
import ani.dantotsu.parsers.novel.NovelExtensionManager
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class SubscriptionsBottomDialog : BottomSheetDialogFragment() { class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
private var _binding: BottomSheetRecyclerBinding? = null private var _binding: BottomSheetRecyclerBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private val adapter: GroupieAdapter = GroupieAdapter() private val adapter: GroupieAdapter = GroupieAdapter()
private var subscriptions: Map<Int, SubscriptionHelper.Companion.SubscribeMedia> = mapOf() private var subscriptions: Map<Int, SubscriptionHelper.Companion.SubscribeMedia> = mapOf()
private val animeExtension: AnimeExtensionManager = Injekt.get()
private val mangaExtensions: MangaExtensionManager = Injekt.get()
private val novelExtensions: NovelExtensionManager = Injekt.get()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -36,8 +45,33 @@ class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
val context = requireContext() val context = requireContext()
binding.title.text = context.getString(R.string.subscriptions) binding.title.text = context.getString(R.string.subscriptions)
binding.replyButton.visibility = View.GONE binding.replyButton.visibility = View.GONE
subscriptions.forEach { (id, media) ->
adapter.add(SubscriptionItem(id, media, adapter)) val groupedSubscriptions = subscriptions.values.groupBy {
if (it.isAnime) SubscriptionHelper.getAnimeParser(it.id).name
else SubscriptionHelper.getMangaParser(it.id).name
}
groupedSubscriptions.forEach { (parserName, mediaList) ->
adapter.add(SubscriptionSource(
parserName,
mediaList.toMutableList(),
adapter,
getParserIcon(parserName)
) { group ->
adapter.remove(group)
})
}
}
private fun getParserIcon(parserName: String): Drawable? {
return when {
animeExtension.installedExtensionsFlow.value.any { it.name == parserName } ->
animeExtension.installedExtensionsFlow.value.find { it.name == parserName }?.icon
mangaExtensions.installedExtensionsFlow.value.any { it.name == parserName } ->
mangaExtensions.installedExtensionsFlow.value.find { it.name == parserName }?.icon
novelExtensions.installedExtensionsFlow.value.any { it.name == parserName } ->
novelExtensions.installedExtensionsFlow.value.find { it.name == parserName }?.icon
else -> null
} }
} }

View file

@ -14,6 +14,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName 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 ani.dantotsu.util.customAlertDialog
class UserInterfaceSettingsActivity : AppCompatActivity() { class UserInterfaceSettingsActivity : AppCompatActivity() {
lateinit var binding: ActivityUserInterfaceSettingsBinding lateinit var binding: ActivityUserInterfaceSettingsBinding
@ -38,20 +39,23 @@ class UserInterfaceSettingsActivity : AppCompatActivity() {
binding.uiSettingsHomeLayout.setOnClickListener { binding.uiSettingsHomeLayout.setOnClickListener {
val set = PrefManager.getVal<List<Boolean>>(PrefName.HomeLayout).toMutableList() val set = PrefManager.getVal<List<Boolean>>(PrefName.HomeLayout).toMutableList()
val views = resources.getStringArray(R.array.home_layouts) val views = resources.getStringArray(R.array.home_layouts)
val dialog = AlertDialog.Builder(this, R.style.MyPopup) customAlertDialog().apply {
.setTitle(getString(R.string.home_layout_show)).apply { setTitle(getString(R.string.home_layout_show))
setMultiChoiceItems( multiChoiceItems(
views, items = views,
PrefManager.getVal<List<Boolean>>(PrefName.HomeLayout).toBooleanArray() checkedItems = PrefManager.getVal<List<Boolean>>(PrefName.HomeLayout).toBooleanArray()
) { _, i, value -> ) { selectedItems ->
set[i] = value for (i in selectedItems.indices) {
set[i] = selectedItems[i]
} }
setPositiveButton("Done") { _, _ -> }
setPosButton(R.string.ok) {
PrefManager.setVal(PrefName.HomeLayout, set) PrefManager.setVal(PrefName.HomeLayout, set)
restartApp() restartApp()
} }
}.show() show()
dialog.window?.setDimAmount(0.8f) }
} }
binding.uiSettingsSmallView.isChecked = PrefManager.getVal(PrefName.SmallView) binding.uiSettingsSmallView.isChecked = PrefManager.getVal(PrefName.SmallView)

View file

@ -22,6 +22,7 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
CheckUpdate(Pref(Location.General, Boolean::class, true)), CheckUpdate(Pref(Location.General, Boolean::class, true)),
VerboseLogging(Pref(Location.General, Boolean::class, false)), VerboseLogging(Pref(Location.General, Boolean::class, false)),
DohProvider(Pref(Location.General, Int::class, 0)), DohProvider(Pref(Location.General, Int::class, 0)),
HidePrivate(Pref(Location.General, Boolean::class, false)),
DefaultUserAgent( DefaultUserAgent(
Pref( Pref(
Location.General, Location.General,

View file

@ -26,7 +26,7 @@ import io.noties.markwon.editor.MarkwonEditorTextWatcher
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
class MarkdownCreatorActivity : AppCompatActivity() { class ActivityMarkdownCreator : AppCompatActivity() {
private lateinit var binding: ActivityMarkdownCreatorBinding private lateinit var binding: ActivityMarkdownCreatorBinding
private lateinit var type: String private lateinit var type: String
private var text: String = "" private var text: String = ""
@ -80,24 +80,28 @@ class MarkdownCreatorActivity : AppCompatActivity() {
finish() finish()
return return
} }
binding.markdownCreatorTitle.text = when (type) { val editId = intent.getIntExtra("edit", -1)
"activity" -> getString(R.string.create_new_activity) val userId = intent.getIntExtra("userId", -1)
"review" -> getString(R.string.create_new_review)
"message" -> getString(R.string.create_new_message)
"replyActivity" -> {
parentId = intent.getIntExtra("parentId", -1) parentId = intent.getIntExtra("parentId", -1)
if (parentId == -1) { when (type) {
"replyActivity" -> if (parentId == -1) {
toast("Error: No parent ID") toast("Error: No parent ID")
finish() finish()
return return
} }
getString(R.string.create_new_reply)
"message" -> {
if (editId == -1) {
binding.privateCheckbox.visibility = ViewGroup.VISIBLE
}
}
}
var private = false
binding.privateCheckbox.setOnCheckedChangeListener { _, isChecked ->
private = isChecked
} }
else -> ""
}
ping = intent.getStringExtra("other") ping = intent.getStringExtra("other")
val userId = intent.getIntExtra("userId", -1)
text = ping ?: "" text = ping ?: ""
binding.editText.setText(text) binding.editText.setText(text)
binding.editText.addTextChangedListener { binding.editText.addTextChangedListener {
@ -116,12 +120,11 @@ class MarkdownCreatorActivity : AppCompatActivity() {
toast(getString(R.string.cannot_be_empty)) toast(getString(R.string.cannot_be_empty))
return@setOnClickListener return@setOnClickListener
} }
AlertDialog.Builder(this, R.style.MyPopup).apply { customAlertDialog().apply {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.post_to_anilist_warning) setMessage(R.string.post_to_anilist_warning)
setPositiveButton(R.string.ok) { _, _ -> setPosButton(R.string.ok) {
launchIO { launchIO {
val editId = intent.getIntExtra("edit", -1)
val isEdit = editId != -1 val isEdit = editId != -1
val success = when (type) { val success = when (type) {
"activity" -> if (isEdit) { "activity" -> if (isEdit) {
@ -135,25 +138,27 @@ class MarkdownCreatorActivity : AppCompatActivity() {
} else { } else {
Anilist.mutation.postReply(parentId, text) Anilist.mutation.postReply(parentId, text)
} }
"message" -> if (isEdit) { //TODO private "message" -> if (isEdit) {
Anilist.mutation.postMessage(userId , text, editId) Anilist.mutation.postMessage(userId, text, editId)
} else { } else {
Anilist.mutation.postMessage(userId , text) Anilist.mutation.postMessage(userId, text, isPrivate = private)
} }
else -> "Error: Unknown type" else -> "Error: Unknown type"
} }
toast(success) toast(success)
finish() finish()
} }
} }
setNeutralButton(R.string.open_rules) { _, _ -> setNeutralButton(R.string.open_rules) {
openLinkInBrowser("https://anilist.co/forum/thread/14") openLinkInBrowser("https://anilist.co/forum/thread/14")
} }
setNegativeButton(R.string.cancel, null) setNegButton(R.string.cancel)
}.show() show()
}
} }
binding.createButton.setOnLongClickListener { binding.previewCheckbox.setOnClickListener {
isPreviewMode = !isPreviewMode isPreviewMode = !isPreviewMode
previewMarkdown(isPreviewMode) previewMarkdown(isPreviewMode)
if (isPreviewMode) { if (isPreviewMode) {
@ -161,7 +166,6 @@ class MarkdownCreatorActivity : AppCompatActivity() {
} else { } else {
toast("Preview disabled") toast("Preview disabled")
} }
true
} }
binding.editText.requestFocus() binding.editText.requestFocus()
setupMarkdownButtons() setupMarkdownButtons()
@ -187,9 +191,11 @@ class MarkdownCreatorActivity : AppCompatActivity() {
MarkdownFormat.UNORDERED_LIST -> { MarkdownFormat.UNORDERED_LIST -> {
lines.joinToString("\n") { "- $it" } lines.joinToString("\n") { "- $it" }
} }
MarkdownFormat.ORDERED_LIST -> { MarkdownFormat.ORDERED_LIST -> {
lines.mapIndexed { index, line -> "${index + 1}. $line" }.joinToString("\n") lines.mapIndexed { index, line -> "${index + 1}. $line" }.joinToString("\n")
} }
else -> { else -> {
if (format.syntax.contains("%s")) { if (format.syntax.contains("%s")) {
String.format(format.syntax, selectedText) String.format(format.syntax, selectedText)
@ -222,7 +228,6 @@ class MarkdownCreatorActivity : AppCompatActivity() {
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
) )
boxBackgroundMode = TextInputLayout.BOX_BACKGROUND_OUTLINE boxBackgroundMode = TextInputLayout.BOX_BACKGROUND_OUTLINE
hint = "Paste your link here"
isHintEnabled = true isHintEnabled = true
} }
@ -241,30 +246,20 @@ class MarkdownCreatorActivity : AppCompatActivity() {
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT ViewGroup.LayoutParams.MATCH_PARENT
) )
setPadding(64, 64, 64, 0) setPadding(0, 0, 0, 0)
} }
customAlertDialog().apply {
val dialog = AlertDialog.Builder(this, R.style.MyPopup).apply { setTitle("Paste your link here")
setView(container) setCustomView(container)
setPositiveButton(getString(R.string.ok)) { dialog, _ -> setPosButton(getString(R.string.ok)) {
val input = inputEditText.text.toString() val input = inputEditText.text.toString()
val formattedText = String.format(format.syntax, input) val formattedText = String.format(format.syntax, input)
binding.editText.text?.insert(position, formattedText) binding.editText.text?.insert(position, formattedText)
binding.editText.setSelection(position + formattedText.length) binding.editText.setSelection(position + formattedText.length)
dialog.dismiss()
} }
setNegativeButton(getString(R.string.cancel)) { dialog, _ -> setNegButton(getString(R.string.cancel))
dialog.dismiss() }.show()
}
}.create()
val widthInDp = 245
val layoutParams = ViewGroup.LayoutParams(
(widthInDp * resources.displayMetrics.density).toInt(),
ViewGroup.LayoutParams.WRAP_CONTENT
)
dialog.window?.setLayout(layoutParams.width, layoutParams.height)
dialog.show()
inputEditText.requestFocus() inputEditText.requestFocus()
} }

View file

@ -1,5 +1,6 @@
package ani.dantotsu.util package ani.dantotsu.util
import android.app.Activity
import android.app.AlertDialog import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.view.View import android.view.View
@ -20,7 +21,24 @@ class AlertDialogBuilder(private val context: Context) {
private var selectedItemIndex: Int = -1 private var selectedItemIndex: Int = -1
private var onItemSelected: ((Int) -> Unit)? = null private var onItemSelected: ((Int) -> Unit)? = null
private var customView: View? = null private var customView: View? = null
private var onShow: (() -> Unit)? = null
private var attach: ((dialog: AlertDialog) -> Unit)? = null private var attach: ((dialog: AlertDialog) -> Unit)? = null
private var onDismiss: (() -> Unit)? = null
private var onCancel: (() -> Unit)? = null
private var cancelable: Boolean = true
fun setCancelable(cancelable: Boolean): AlertDialogBuilder {
this.cancelable = cancelable
return this
}
fun setOnShowListener(onShow: () -> Unit): AlertDialogBuilder {
this.onShow = onShow
return this
}
fun setOnCancelListener(onCancel: () -> Unit): AlertDialogBuilder {
this.onCancel = onCancel
return this
}
fun setTitle(title: String?): AlertDialogBuilder { fun setTitle(title: String?): AlertDialogBuilder {
this.title = title this.title = title
return this return this
@ -45,6 +63,10 @@ class AlertDialogBuilder(private val context: Context) {
this.customView = view this.customView = view
return this return this
} }
fun setCustomView(layoutResId: Int): AlertDialogBuilder {
this.customView = View.inflate(context, layoutResId, null)
return this
}
fun setPosButton(title: String?, onClick: (() -> Unit)? = null): AlertDialogBuilder { fun setPosButton(title: String?, onClick: (() -> Unit)? = null): AlertDialogBuilder {
this.posButtonTitle = title this.posButtonTitle = title
@ -52,11 +74,7 @@ class AlertDialogBuilder(private val context: Context) {
return this return this
} }
fun setPosButton( fun setPosButton(int: Int, formatArgs: Int? = null, onClick: (() -> Unit)? = null): AlertDialogBuilder {
int: Int,
formatArgs: Int? = null,
onClick: (() -> Unit)? = null
): AlertDialogBuilder {
this.posButtonTitle = context.getString(int, formatArgs) this.posButtonTitle = context.getString(int, formatArgs)
this.onPositiveButtonClick = onClick this.onPositiveButtonClick = onClick
return this return this
@ -68,11 +86,7 @@ class AlertDialogBuilder(private val context: Context) {
return this return this
} }
fun setNegButton( fun setNegButton(int: Int, formatArgs: Int? = null, onClick: (() -> Unit)? = null): AlertDialogBuilder {
int: Int,
formatArgs: Int? = null,
onClick: (() -> Unit)? = null
): AlertDialogBuilder {
this.negButtonTitle = context.getString(int, formatArgs) this.negButtonTitle = context.getString(int, formatArgs)
this.onNegativeButtonClick = onClick this.onNegativeButtonClick = onClick
return this return this
@ -84,11 +98,7 @@ class AlertDialogBuilder(private val context: Context) {
return this return this
} }
fun setNeutralButton( fun setNeutralButton(int: Int, formatArgs: Int? = null, onClick: (() -> Unit)? = null): AlertDialogBuilder {
int: Int,
formatArgs: Int? = null,
onClick: (() -> Unit)? = null
): AlertDialogBuilder {
this.neutralButtonTitle = context.getString(int, formatArgs) this.neutralButtonTitle = context.getString(int, formatArgs)
this.onNeutralButtonClick = onClick this.onNeutralButtonClick = onClick
return this return this
@ -99,22 +109,19 @@ class AlertDialogBuilder(private val context: Context) {
return this return this
} }
fun singleChoiceItems( fun onDismiss(onDismiss: (() -> Unit)? = null): AlertDialogBuilder {
items: Array<String>, this.onDismiss = onDismiss
selectedItemIndex: Int = -1, return this
onItemSelected: (Int) -> Unit }
): AlertDialogBuilder {
fun singleChoiceItems(items: Array<String>, selectedItemIndex: Int = -1, onItemSelected: (Int) -> Unit): AlertDialogBuilder {
this.items = items this.items = items
this.selectedItemIndex = selectedItemIndex this.selectedItemIndex = selectedItemIndex
this.onItemSelected = onItemSelected this.onItemSelected = onItemSelected
return this return this
} }
fun multiChoiceItems( fun multiChoiceItems(items: Array<String>, checkedItems: BooleanArray? = null, onItemsSelected: (BooleanArray) -> Unit): AlertDialogBuilder {
items: Array<String>,
checkedItems: BooleanArray? = null,
onItemsSelected: (BooleanArray) -> Unit
): AlertDialogBuilder {
this.items = items this.items = items
this.checkedItems = checkedItems ?: BooleanArray(items.size) { false } this.checkedItems = checkedItems ?: BooleanArray(items.size) { false }
this.onItemsSelected = onItemsSelected this.onItemsSelected = onItemsSelected
@ -122,15 +129,18 @@ class AlertDialogBuilder(private val context: Context) {
} }
fun show() { fun show() {
if (context is Activity && context.isFinishing) return // Ensure context is valid
val builder = AlertDialog.Builder(context, R.style.MyPopup) val builder = AlertDialog.Builder(context, R.style.MyPopup)
if (title != null) builder.setTitle(title) if (title != null) builder.setTitle(title)
if (message != null) builder.setMessage(message) if (message != null) builder.setMessage(message)
if (customView != null) builder.setView(customView) if (customView != null) builder.setView(customView)
if (items != null) { if (items != null) {
if (onItemSelected != null) { if (onItemSelected != null) {
builder.setSingleChoiceItems(items, selectedItemIndex) { _, which -> builder.setSingleChoiceItems(items, selectedItemIndex) { dialog, which ->
selectedItemIndex = which selectedItemIndex = which
onItemSelected?.invoke(which) onItemSelected?.invoke(which)
dialog.dismiss()
} }
} else if (checkedItems != null && onItemsSelected != null) { } else if (checkedItems != null && onItemsSelected != null) {
builder.setMultiChoiceItems(items, checkedItems) { _, which, isChecked -> builder.setMultiChoiceItems(items, checkedItems) { _, which, isChecked ->
@ -157,13 +167,23 @@ class AlertDialogBuilder(private val context: Context) {
dialog.dismiss() dialog.dismiss()
} }
} }
builder.setCancelable(false) if (onCancel != null) {
builder.setOnCancelListener {
onCancel?.invoke()
}
}
builder.setCancelable(cancelable)
val dialog = builder.create() val dialog = builder.create()
attach?.invoke(dialog) attach?.invoke(dialog)
dialog.setOnDismissListener {
onDismiss?.invoke()
}
dialog.setOnShowListener {
onShow?.invoke()
}
dialog.window?.setDimAmount(0.8f) dialog.window?.setDimAmount(0.8f)
dialog.show() dialog.show()
} }
} }
fun Context.customAlertDialog(): AlertDialogBuilder { fun Context.customAlertDialog(): AlertDialogBuilder {

View file

@ -71,20 +71,17 @@ class StoragePermissions {
complete(true) complete(true)
return return
} }
val builder = AlertDialog.Builder(this, R.style.MyPopup) customAlertDialog().apply {
builder.setTitle(getString(R.string.dir_access)) setTitle(getString(R.string.dir_access))
builder.setMessage(getString(R.string.dir_access_msg)) setMessage(getString(R.string.dir_access_msg))
builder.setPositiveButton(getString(R.string.ok)) { dialog, _ -> setPosButton(getString(R.string.ok)) {
launcher.registerForCallback(complete) launcher.registerForCallback(complete)
launcher.launch() launcher.launch()
dialog.dismiss()
} }
builder.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> setNegButton(getString(R.string.cancel)) {
dialog.dismiss()
complete(false) complete(false)
} }
val dialog = builder.show() }.show()
dialog.window?.setDimAmount(0.8f)
} }
private fun pathToUri(path: String): Uri { private fun pathToUri(path: String): Uri {

View file

@ -64,10 +64,6 @@ internal class ExtensionGithubApi {
val repos = val repos =
PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos).toMutableList() PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos).toMutableList()
if (repos.isEmpty()) {
repos.add("https://raw.githubusercontent.com/aniyomiorg/aniyomi-extensions/repo")
PrefManager.setVal(PrefName.AnimeExtensionRepos, repos.toSet())
}
repos.forEach { repos.forEach {
try { try {
@ -95,9 +91,10 @@ internal class ExtensionGithubApi {
// Sanity check - a small number of extensions probably means something broke // Sanity check - a small number of extensions probably means something broke
// with the repo generator // with the repo generator
if (repoExtensions.size < 10) { //if (repoExtensions.size < 10) {
throw Exception() // throw Exception()
} //}
// No official repo now so this won't be needed anymore. User-made repo can have less than 10 extensions
extensions.addAll(repoExtensions) extensions.addAll(repoExtensions)
} catch (e: Throwable) { } catch (e: Throwable) {
@ -157,10 +154,6 @@ internal class ExtensionGithubApi {
val repos = val repos =
PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos).toMutableList() PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos).toMutableList()
if (repos.isEmpty()) {
repos.add("https://raw.githubusercontent.com/keiyoushi/extensions/main")
PrefManager.setVal(PrefName.MangaExtensionRepos, repos.toSet())
}
repos.forEach { repos.forEach {
try { try {
@ -188,9 +181,10 @@ internal class ExtensionGithubApi {
// Sanity check - a small number of extensions probably means something broke // Sanity check - a small number of extensions probably means something broke
// with the repo generator // with the repo generator
if (repoExtensions.size < 10) { //if (repoExtensions.size < 10) {
throw Exception() // throw Exception()
} //}
// No official repo now so this won't be needed anymore. User made repo can have less than 10 extensions.
extensions.addAll(repoExtensions) extensions.addAll(repoExtensions)
} catch (e: Throwable) { } catch (e: Throwable) {

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="@android:color/white"
android:pathData="M13.26,3C8.17,2.86 4,6.95 4,12L2.21,12c-0.45,0 -0.67,0.54 -0.35,0.85l2.79,2.8c0.2,0.2 0.51,0.2 0.71,0l2.79,-2.8c0.31,-0.31 0.09,-0.85 -0.36,-0.85L6,12c0,-3.9 3.18,-7.05 7.1,-7 3.72,0.05 6.85,3.18 6.9,6.9 0.05,3.91 -3.1,7.1 -7,7.1 -1.61,0 -3.1,-0.55 -4.28,-1.48 -0.4,-0.31 -0.96,-0.28 -1.32,0.08 -0.42,0.42 -0.39,1.13 0.08,1.49C9,20.29 10.91,21 13,21c5.05,0 9.14,-4.17 9,-9.26 -0.13,-4.69 -4.05,-8.61 -8.74,-8.74zM12.75,8c-0.41,0 -0.75,0.34 -0.75,0.75v3.68c0,0.35 0.19,0.68 0.49,0.86l3.12,1.85c0.36,0.21 0.82,0.09 1.03,-0.26 0.21,-0.36 0.09,-0.82 -0.26,-1.03l-2.88,-1.71v-3.4c0,-0.4 -0.34,-0.74 -0.75,-0.74z"/>
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M3,6c-0.55,0 -1,0.45 -1,1v13c0,1.1 0.9,2 2,2h13c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L5,20c-0.55,0 -1,-0.45 -1,-1L4,7c0,-0.55 -0.45,-1 -1,-1zM20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,11h-8c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h8c0.55,0 1,0.45 1,1s-0.45,1 -1,1zM14,15h-4c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h4c0.55,0 1,0.45 1,1s-0.45,1 -1,1zM18,7h-8c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h8c0.55,0 1,0.45 1,1s-0.45,1 -1,1z"/>
</vector>

View file

@ -55,6 +55,15 @@
app:srcCompat="@drawable/ic_round_search_24" app:srcCompat="@drawable/ic_round_search_24"
app:tint="?attr/colorOnBackground" /> app:tint="?attr/colorOnBackground" />
<ImageButton
android:id="@+id/listed"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/listed_in_library"
app:srcCompat="@drawable/ic_round_library_books_24"
app:tint="?attr/colorOnBackground" />
<ImageButton <ImageButton
android:id="@+id/random" android:id="@+id/random"
android:layout_width="40dp" android:layout_width="40dp"

View file

@ -1,18 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="false"> android:fitsSystemWindows="false"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<FrameLayout <LinearLayout
android:id="@+id/markdownCreatorToolbar" android:id="@+id/markdownCreatorToolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="48dp"
@ -31,32 +27,44 @@
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<TextView <Space
android:id="@+id/markdownCreatorTitle" android:layout_width="0dp"
android:layout_width="wrap_content" android:layout_height="match_parent"
android:layout_height="48dp" android:layout_weight="1" />
android:layout_gravity="center_vertical"
android:layout_marginStart="44dp"
android:ellipsize="end"
android:fontFamily="@font/poppins_bold"
android:gravity="center|start"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
android:textColor="?attr/colorOnBackground"
android:textSize="18sp"
tools:text="@string/placeholder" />
<Button <CheckBox
android:id="@+id/createButton" android:id="@+id/privateCheckbox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical" android:layout_gravity="end|center_vertical"
android:fontFamily="@font/poppins_bold" android:layout_marginEnd="8dp"
android:maxLines="1" android:visibility="gone"
android:text="@string/publish" android:contentDescription="@string/preview"
app:cornerRadius="16dp" android:fontFamily="@font/poppins_semi_bold"
tools:ignore="ButtonStyle" /> android:text="@string/private_mode"
</FrameLayout> tools:ignore="ContentDescription" />
<CheckBox
android:id="@+id/previewCheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="8dp"
android:contentDescription="@string/preview"
android:fontFamily="@font/poppins_semi_bold"
android:text="@string/preview"
tools:ignore="ContentDescription" />
<ImageButton
android:id="@+id/createButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center"
android:layout_marginEnd="12dp"
android:background="@drawable/ic_round_send_24"
tools:ignore="ContentDescription"
tools:visibility="visible" />
</LinearLayout>
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -73,20 +81,19 @@
<EditText <EditText
android:id="@+id/editText" android:id="@+id/editText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="590dp" android:layout_height="match_parent"
android:fontFamily="@font/poppins" android:fontFamily="@font/poppins_semi_bold"
android:inputType="textMultiLine"
android:padding="16dp"
android:nestedScrollingEnabled="true"
android:gravity="top|start" android:gravity="top|start"
android:hint="@string/reply_hint"
android:inputType="textMultiLine"
android:nestedScrollingEnabled="true"
android:padding="16dp"
android:textColor="?attr/colorOnBackground" android:textColor="?attr/colorOnBackground"
android:textIsSelectable="true" android:textIsSelectable="true"
android:textSize="12sp" android:textSize="18sp"
android:hint="@string/reply_hint"
tools:ignore="LabelFor" /> tools:ignore="LabelFor" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
</LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/markdownOptionsContainer" android:id="@+id/markdownOptionsContainer"
@ -99,155 +106,170 @@
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:id="@+id/formatBold" android:id="@+id/formatBold"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_bold_24" android:src="@drawable/format_bold_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatItalic" android:id="@+id/formatItalic"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_italic_24" android:src="@drawable/format_italic_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatStrikethrough" android:id="@+id/formatStrikethrough"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_strikethrough_24" android:src="@drawable/format_strikethrough_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatSpoiler" android:id="@+id/formatSpoiler"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_spoiler_24" android:src="@drawable/format_spoiler_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatLink" android:id="@+id/formatLink"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_link_24" android:src="@drawable/format_link_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatImage" android:id="@+id/formatImage"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_image_24" android:src="@drawable/format_image_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatYoutube" android:id="@+id/formatYoutube"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_youtube_24" android:src="@drawable/format_youtube_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/formatVideo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_video_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:id="@+id/formatListOrdered" android:id="@+id/formatListOrdered"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_list_numbered_24" android:src="@drawable/format_list_numbered_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatListUnordered" android:id="@+id/formatListUnordered"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_list_bulleted_24" android:src="@drawable/format_list_bulleted_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatTitle" android:id="@+id/formatTitle"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_title_24" android:src="@drawable/format_title_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatCenter" android:id="@+id/formatCenter"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_align_center_24" android:src="@drawable/format_align_center_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatQuote" android:id="@+id/formatQuote"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_quote_24" android:src="@drawable/format_quote_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/formatCode" android:id="@+id/formatCode"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:padding="6dp" android:padding="6dp"
android:layout_marginEnd="10dp"
android:src="@drawable/format_code_24" android:src="@drawable/format_code_24"
app:tint="?attr/colorOnBackground" app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/formatVideo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="6dp"
android:src="@drawable/format_video_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </LinearLayout>

View file

@ -42,6 +42,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:layout_marginBottom="67dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
@ -50,15 +51,15 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:nestedScrollingEnabled="true" android:nestedScrollingEnabled="true"
tools:ignore="SpeakableTextPresentCheck" /> tools:ignore="SpeakableTextPresentCheck" />
</LinearLayout>
</LinearLayout>
<nl.joery.animatedbottombar.AnimatedBottomBar <nl.joery.animatedbottombar.AnimatedBottomBar
android:id="@+id/notificationNavBar" android:id="@+id/notificationNavBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom" android:layout_gravity="center_horizontal|bottom"
android:layout_marginTop="-64dp" android:layout_marginTop="-67dp"
android:background="?attr/colorSurface" android:background="?attr/colorSurface"
android:padding="0dp" android:padding="0dp"
app:abb_animationInterpolator="@anim/over_shoot" app:abb_animationInterpolator="@anim/over_shoot"
@ -73,4 +74,5 @@
app:itemTextAppearanceActive="@style/NavBarText" app:itemTextAppearanceActive="@style/NavBarText"
app:itemTextAppearanceInactive="@style/NavBarText" app:itemTextAppearanceInactive="@style/NavBarText"
app:itemTextColor="@color/tab_layout_icon" /> app:itemTextColor="@color/tab_layout_icon" />
</LinearLayout> </LinearLayout>

View file

@ -54,7 +54,7 @@
app:cardElevation="0dp"> app:cardElevation="0dp">
<ImageButton <ImageButton
android:id="@+id/animeSourceList" android:id="@+id/mediaSourceList"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:alpha="0.33" android:alpha="0.33"
@ -73,7 +73,7 @@
app:cardElevation="0dp"> app:cardElevation="0dp">
<ImageButton <ImageButton
android:id="@+id/animeSourceGrid" android:id="@+id/mediaSourceGrid"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:alpha="0.33" android:alpha="0.33"
@ -91,7 +91,7 @@
app:cardElevation="0dp"> app:cardElevation="0dp">
<ImageButton <ImageButton
android:id="@+id/animeSourceCompact" android:id="@+id/mediaSourceCompact"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:alpha="0.33" android:alpha="0.33"
@ -140,7 +140,7 @@
app:cardElevation="0dp"> app:cardElevation="0dp">
<ImageButton <ImageButton
android:id="@+id/animeSourceTop" android:id="@+id/mediaSourceTop"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
@ -189,7 +189,7 @@
app:cardElevation="0dp"> app:cardElevation="0dp">
<ImageButton <ImageButton
android:id="@+id/animeDownloadTop" android:id="@+id/mediaDownloadTop"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
@ -200,7 +200,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/animeScanlatorContainer" android:id="@+id/mangaScanlatorContainer"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical"> android:layout_gravity="center_vertical">
@ -235,7 +235,7 @@
app:cardElevation="0dp"> app:cardElevation="0dp">
<ImageButton <ImageButton
android:id="@+id/animeScanlatorTop" android:id="@+id/mangaScanlatorTop"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
@ -246,7 +246,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/animeWebviewContainer" android:id="@+id/mediaWebviewContainer"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
@ -280,7 +280,7 @@
android:layout_height="48dp"> android:layout_height="48dp">
<ImageView <ImageView
android:id="@+id/animeWebViewTop" android:id="@+id/mediaWebViewTop"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_gravity="center|center_horizontal" android:layout_gravity="center|center_horizontal"

View file

@ -7,7 +7,7 @@
tools:context=".media.anime.AnimeWatchFragment"> tools:context=".media.anime.AnimeWatchFragment">
<TextView <TextView
android:id="@+id/animeNotSupported" android:id="@+id/mediaNotSupported"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
@ -31,7 +31,7 @@
tools:visibility="gone" /> tools:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/animeSourceRecycler" android:id="@+id/mediaSourceRecycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
@ -39,7 +39,7 @@
android:paddingEnd="24dp" android:paddingEnd="24dp"
android:paddingBottom="128dp" android:paddingBottom="128dp"
tools:itemCount="1" tools:itemCount="1"
tools:listitem="@layout/item_anime_watch" /> tools:listitem="@layout/item_media_source" />
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/ScrollTop" android:id="@+id/ScrollTop"

View file

@ -65,19 +65,6 @@
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
<ImageButton
android:id="@+id/addStory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleX="2.2"
android:scaleY="2.2"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_circle_add"
app:layout_goneMarginBottom="40dp"
app:layout_goneMarginRight="8dp"
app:layout_constraintBottom_toBottomOf="@id/textActivityContainer"
app:layout_constraintEnd_toEndOf="@id/textActivityContainer" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -215,7 +202,7 @@
android:id="@+id/activityRepliesContainer" android:id="@+id/activityRepliesContainer"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="12dp"
android:orientation="vertical" android:orientation="vertical"
tools:ignore="UseCompoundDrawables"> tools:ignore="UseCompoundDrawables">
@ -243,7 +230,7 @@
android:id="@+id/activityLikeContainer" android:id="@+id/activityLikeContainer"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:padding="12dp"
android:orientation="vertical" android:orientation="vertical"
tools:ignore="UseCompoundDrawables"> tools:ignore="UseCompoundDrawables">

View file

@ -129,7 +129,6 @@
android:textSize="12sp" android:textSize="12sp"
tools:visibility="visible" tools:visibility="visible"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/activityDelete" android:id="@+id/activityDelete"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -14,21 +14,21 @@
android:background="?attr/colorSurfaceVariant" /> android:background="?attr/colorSurfaceVariant" />
<LinearLayout <LinearLayout
android:id="@+id/itemEpisodeProgressCont" android:id="@+id/itemMediaProgressCont"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:orientation="horizontal"> android:orientation="horizontal">
<View <View
android:id="@+id/itemEpisodeProgress" android:id="@+id/itemMediaProgress"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="2dp" android:layout_height="2dp"
android:layout_weight="0" android:layout_weight="0"
android:background="?attr/colorPrimary" /> android:background="?attr/colorPrimary" />
<View <View
android:id="@+id/itemEpisodeProgressEmpty" android:id="@+id/itemMediaProgressEmpty"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="2dp" android:layout_height="2dp"
android:layout_weight="1" /> android:layout_weight="1" />

View file

@ -13,7 +13,7 @@
app:cardElevation="4dp"> app:cardElevation="4dp">
<ImageView <ImageView
android:id="@+id/itemEpisodeImage" android:id="@+id/itemMediaImage"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="96dp" android:layout_height="96dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
@ -85,21 +85,21 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/itemEpisodeProgressCont" android:id="@+id/itemMediaProgressCont"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:orientation="horizontal"> android:orientation="horizontal">
<View <View
android:id="@+id/itemEpisodeProgress" android:id="@+id/itemMediaProgress"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="2dp" android:layout_height="2dp"
android:layout_weight="0" android:layout_weight="0"
android:background="?attr/colorPrimary" /> android:background="?attr/colorPrimary" />
<View <View
android:id="@+id/itemEpisodeProgressEmpty" android:id="@+id/itemMediaProgressEmpty"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="2dp" android:layout_height="2dp"
android:layout_weight="1" /> android:layout_weight="1" />

View file

@ -47,7 +47,7 @@
android:indeterminate="true" /> android:indeterminate="true" />
<ImageView <ImageView
android:id="@+id/itemEpisodeImage" android:id="@+id/itemMediaImage"
android:layout_width="164dp" android:layout_width="164dp"
android:layout_height="109dp" android:layout_height="109dp"
android:layout_gravity="center" android:layout_gravity="center"
@ -77,7 +77,7 @@
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<LinearLayout <LinearLayout
android:id="@+id/itemEpisodeProgressCont" android:id="@+id/itemMediaProgress_cont"
android:layout_width="164dp" android:layout_width="164dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
@ -85,14 +85,14 @@
android:visibility="gone"> android:visibility="gone">
<View <View
android:id="@+id/itemEpisodeProgress" android:id="@+id/itemMediaProgress"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="2dp" android:layout_height="2dp"
android:layout_weight="0" android:layout_weight="0"
android:background="?attr/colorPrimary" /> android:background="?attr/colorPrimary" />
<View <View
android:id="@+id/itemEpisodeProgressEmpty" android:id="@+id/itemMediaProgressEmpty"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="2dp" android:layout_height="2dp"
android:layout_weight="1" android:layout_weight="1"

View file

@ -25,7 +25,7 @@
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginEnd="3dp" android:layout_marginEnd="9dp"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<LinearLayout <LinearLayout
@ -53,6 +53,26 @@
tools:ignore="SmallSp" /> tools:ignore="SmallSp" />
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/subscriptionCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="12dp"
android:fontFamily="@font/poppins_bold"
android:visibility="gone"/>
<ImageView
android:id="@+id/extensionSubscriptions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="0"
android:src="@drawable/ui_bg"
android:visibility="gone"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription"/>
<ImageView <ImageView
android:id="@+id/closeTextView" android:id="@+id/closeTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -38,7 +38,7 @@
tools:visibility="visible" /> tools:visibility="visible" />
<TextView <TextView
android:id="@+id/animeSourceTitle" android:id="@+id/mediaSourceTitle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
@ -57,7 +57,7 @@
android:orientation="horizontal"> android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/animeSourceNameContainer" android:id="@+id/mediaSourceNameContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu" style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="56dp" android:layout_height="56dp"
@ -72,7 +72,7 @@
app:startIconDrawable="@drawable/ic_round_source_24"> app:startIconDrawable="@drawable/ic_round_source_24">
<AutoCompleteTextView <AutoCompleteTextView
android:id="@+id/animeSource" android:id="@+id/mediaSource"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
@ -91,7 +91,7 @@
<ImageView <ImageView
android:id="@+id/animeSourceSettings" android:id="@+id/mediaSourceSettings"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
@ -107,7 +107,7 @@
android:baselineAligned="false"> android:baselineAligned="false">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/animeSourceLanguageContainer" android:id="@+id/mediaSourceLanguageContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu" style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="56dp" android:layout_height="56dp"
@ -123,7 +123,7 @@
app:startIconDrawable="@drawable/ic_round_source_24"> app:startIconDrawable="@drawable/ic_round_source_24">
<AutoCompleteTextView <AutoCompleteTextView
android:id="@+id/animeSourceLanguage" android:id="@+id/mediaSourceLanguage"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
@ -193,7 +193,7 @@
<TextView <TextView
android:id="@+id/animeSourceSearch" android:id="@+id/mediaSourceSearch"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
@ -224,7 +224,7 @@
android:textSize="16sp" /> android:textSize="16sp" />
<ImageView <ImageView
android:id="@+id/animeSourceSubscribe" android:id="@+id/mediaSourceSubscribe"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
@ -235,7 +235,7 @@
tools:ignore="ContentDescription,ImageContrastCheck" /> tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView <ImageView
android:id="@+id/animeNestedButton" android:id="@+id/mediaNestedButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="end" android:layout_gravity="end"
@ -247,7 +247,7 @@
</LinearLayout> </LinearLayout>
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/animeSourceContinue" android:id="@+id/sourceContinue"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="80dp" android:layout_height="80dp"
android:layout_gravity="center" android:layout_gravity="center"
@ -259,7 +259,7 @@
tools:visibility="visible"> tools:visibility="visible">
<ImageView <ImageView
android:id="@+id/itemEpisodeImage" android:id="@+id/itemMediaImage"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scaleType="centerCrop" android:scaleType="centerCrop"
@ -280,7 +280,7 @@
android:visibility="gone" /> android:visibility="gone" />
<TextView <TextView
android:id="@+id/animeSourceContinueText" android:id="@+id/mediaSourceContinueText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_margin="16dp"
@ -293,21 +293,21 @@
app:drawableEndCompat="@drawable/ic_round_play_arrow_24" /> app:drawableEndCompat="@drawable/ic_round_play_arrow_24" />
<LinearLayout <LinearLayout
android:id="@+id/itemEpisodeProgressCont" android:id="@+id/itemMediaProgressCont"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:orientation="horizontal"> android:orientation="horizontal">
<View <View
android:id="@+id/itemEpisodeProgress" android:id="@+id/itemMediaProgress"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="2dp" android:layout_height="2dp"
android:layout_weight="0" android:layout_weight="0"
android:background="?attr/colorPrimary" /> android:background="?attr/colorPrimary" />
<View <View
android:id="@+id/itemEpisodeProgressEmpty" android:id="@+id/itemMediaProgressEmpty"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="2dp" android:layout_height="2dp"
android:layout_weight="1" /> android:layout_weight="1" />
@ -318,7 +318,7 @@
</LinearLayout> </LinearLayout>
<HorizontalScrollView <HorizontalScrollView
android:id="@+id/animeWatchChipScroll" android:id="@+id/mediaWatchChipScroll"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
@ -328,7 +328,7 @@
android:scrollbars="none"> android:scrollbars="none">
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
android:id="@+id/animeSourceChipGroup" android:id="@+id/mediaSourceChipGroup"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:singleLine="true" app:singleLine="true"
@ -336,7 +336,7 @@
</HorizontalScrollView> </HorizontalScrollView>
<ProgressBar <ProgressBar
android:id="@+id/animeSourceProgressBar" android:id="@+id/sourceProgressBar"
style="?android:attr/progressBarStyle" style="?android:attr/progressBarStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -350,7 +350,7 @@
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/animeSourceNotFound" android:id="@+id/sourceNotFound"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"
@ -364,6 +364,7 @@
android:id="@+id/faqbutton" android:id="@+id/faqbutton"
android:layout_width="120dp" android:layout_width="120dp"
android:layout_height="50dp" android:layout_height="50dp"
android:layout_marginTop="-16dp"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:backgroundTint="?attr/colorPrimaryContainer" android:backgroundTint="?attr/colorPrimaryContainer"
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"

View file

@ -25,7 +25,7 @@
app:startIconDrawable="@drawable/ic_round_source_24"> app:startIconDrawable="@drawable/ic_round_source_24">
<AutoCompleteTextView <AutoCompleteTextView
android:id="@+id/animeSource" android:id="@+id/mediaSource"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"

View file

@ -38,7 +38,7 @@
android:indeterminate="true" /> android:indeterminate="true" />
<ImageView <ImageView
android:id="@+id/itemEpisodeImage" android:id="@+id/itemMediaImage"
android:layout_width="108dp" android:layout_width="108dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center" android:layout_gravity="center"

View file

@ -117,6 +117,32 @@
app:drawableTint="?attr/colorPrimary" /> app:drawableTint="?attr/colorPrimary" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/clearHistory"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_gravity="center"
android:padding="8dp"
app:boxStrokeColor="@color/text_input_layout_stroke_color"
app:cardBackgroundColor="@color/nav_bg"
app:cardCornerRadius="16dp"
app:contentPadding="4dp"
app:contentPaddingLeft="8dp"
app:contentPaddingRight="8dp"
tools:ignore="ContentDescription,TextContrastCheck">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="4dp"
android:fontFamily="@font/poppins_bold"
android:text="@string/clear_history"
android:textColor="?attr/colorPrimary"
app:drawableStartCompat="@drawable/ic_round_history_24"
app:drawableTint="?attr/colorPrimary" />
</androidx.cardview.widget.CardView>
<ani.dantotsu.FadingEdgeRecyclerView <ani.dantotsu.FadingEdgeRecyclerView
android:id="@+id/searchChipRecycler" android:id="@+id/searchChipRecycler"
android:layout_width="0dp" android:layout_width="0dp"

View file

@ -4,7 +4,6 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginVertical="16dp"
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="8dp"
android:orientation="horizontal" android:orientation="horizontal"
tools:ignore="UseCompoundDrawables"> tools:ignore="UseCompoundDrawables">

View file

@ -156,8 +156,12 @@
<string name="chap">Chapter</string> <string name="chap">Chapter</string>
<string name="wrong"><u>Wrong Title?</u></string> <string name="wrong"><u>Wrong Title?</u></string>
<string name="source_not_found"> <string name="source_not_found">
Couldn\'t find anything X( \n Nothing came up from this source.\n
Try another source. Let\'s look elsewhere.
</string>
<string name="download_not_found">
Your downloads are feeling a bit lonely…\n
Try downloading something.
</string> </string>
<string name="not_supported">%1$s is not supported!</string> <string name="not_supported">%1$s is not supported!</string>
<string name="server_selector">Select Server</string> <string name="server_selector">Select Server</string>
@ -413,6 +417,8 @@
<string name="use_alarm_manager_reliable">Use Alarm Manager for reliable Notifications</string> <string name="use_alarm_manager_reliable">Use Alarm Manager for reliable Notifications</string>
<string name="use_alarm_manager_confirm">Using Alarm Manger can help fight against battery optimization, but may consume more battery. It also requires the Alarm Manager permission.</string> <string name="use_alarm_manager_confirm">Using Alarm Manger can help fight against battery optimization, but may consume more battery. It also requires the Alarm Manager permission.</string>
<string name="use">Use</string> <string name="use">Use</string>
<string name="remove_all_subscriptions">Remove All Subscriptions</string>
<string name="remove_all_subscriptions_desc">Are you sure you want to remove all subscriptions for %1$s?</string>
<string name="checking_subscriptions">Notification for Checking Subscriptions</string> <string name="checking_subscriptions">Notification for Checking Subscriptions</string>
<string name="subscriptions_checking_time_s">Subscriptions Update Frequency : %1$s</string> <string name="subscriptions_checking_time_s">Subscriptions Update Frequency : %1$s</string>
<string name="subscriptions_checking_time">Subscriptions Update Frequency</string> <string name="subscriptions_checking_time">Subscriptions Update Frequency</string>
@ -629,13 +635,7 @@
<string name="answer_6">This is because it updates every 48 hours automatically (by Anilist). If you really need to update your stats, you can force update your stats after going to this [link](https://anilist.co/settings/lists).</string> <string name="answer_6">This is because it updates every 48 hours automatically (by Anilist). If you really need to update your stats, you can force update your stats after going to this [link](https://anilist.co/settings/lists).</string>
<string name="question_7">How to download Anime?</string> <string name="question_7">How to download Anime?</string>
<string name="answer_7">There are two methods of downloading currently. One is internal and the other is external. If you download internally, then it can be viewed within the app but only the app can open that episode, you cannot move it or share it. The other option is to use external downloading. It requires a download manager to download and a separate video player to watch. External downloads can be shared but you cannot view it within the Dantotsu app.\n\n•To download internally:\n\n1. Tap the download button.\n2. Pick the server and the video quality.\n3. Profit.\n\n•To download externally:\n\n 1. Download 1DM or ADM from Google Play Store. <string name="answer_7">There are two methods of downloading. Internal and external. If you download internally, then it can be viewed within the app and tracking will work normally for it. The other option is to use external downloader. It requires a download manager to download and a separate video player to watch. External downloads cannot be viewed within the Dantotsu app.\n\n•To download internally:\n\n1. Tap the download button.\n2. For the first time, it will ask you to set a download location. All your downloads will be stored there.\n3. Pick the server and the video quality.\n4. Profit.\n\n•To download externally:\n\n 1. Download 1DM or ADM from Google Play Store.\n2. Enter the app, give storage access and set your preferences (downloading speed, downloading path etc(optional))\n3. Now go to \`Dantotsu > Settings > Common > Download Managers\` and choose the download manager you just set up.\n4. Go to your desired episode and press on the download icon of any server. There may be a popup to set your preferences again, just press on "Download" and it will be saved in the directed path.\n\nNote: External downloads are also possible without a manager but it\'s not recommended.\n\nNerd Note: Internally downloaded episodes are stored in \`{your set location}/Dantotsu/Anime/*`\nYou can change your download location in settings but your previous downloaded episodes will not show up in the app anymore.</string>
\n2. Enter the app, give storage access and set your preferences (downloading speed, downloading path etc(optional))
\n3. Now go to \`Dantotsu > Settings > Common > Download Managers\` and choose the download manager you just set up.
\n4. Go to your desired episode and press on the download icon of any server. There may be a popup to set your preferences again, just press on "Download" and it will be saved in the directed path.
\n\nNote: Direct downloads are also possible without a manager but it\'s not recommended.
\n\nNerd Note: Internally downloaded episodes are stored in \`Android/data/ani.dantotsu.*/files/Anime_Downloads\`\nYou cannot play those files as they are in \`.exo\` format, split into hundreds of pieces and are encrypted.</string>
<string name="question_9">How to enable NSFW content?</string> <string name="question_9">How to enable NSFW content?</string>
<string name="answer_9">You can enable NSFW content by enabling 18+ contents from this [link](https://anilist.co/settings/media). You also have to enable NSFW extensions in \`Settings > Extensions > NSFW extensions\` </string> <string name="answer_9">You can enable NSFW content by enabling 18+ contents from this [link](https://anilist.co/settings/media). You also have to enable NSFW extensions in \`Settings > Extensions > NSFW extensions\` </string>
@ -664,6 +664,11 @@
<string name="question_17">Some useful tips and tricks</string> <string name="question_17">Some useful tips and tricks</string>
<string name="answer_17">The following presents some tips and tricks you may or may not know about - \n \n \n - By hold pressing the Dantotsu logo in settings, you can check if there are any new updates manually. \n \n - Hold pressing an error message/tag/synonym or title will copy it. \n \n - You can open an episode with other apps by hold pressing any server for that episode. This helps in streaming the episode using other video players or download the episode using download managers. \n \n - You can set up custom lists using this [link](https://anilist.co/settings/lists). (you need to be signed in) \n \n - If your episode/chapter is not being progressed automatically after you finish watching/reading it, then hold press the status bar(planning/repeating/watching button) of that anime/manga. The next time you start a chapter/finish an episode, you will stumble upon a popup. Press yes there. </string> <string name="answer_17">The following presents some tips and tricks you may or may not know about - \n \n \n - By hold pressing the Dantotsu logo in settings, you can check if there are any new updates manually. \n \n - Hold pressing an error message/tag/synonym or title will copy it. \n \n - You can open an episode with other apps by hold pressing any server for that episode. This helps in streaming the episode using other video players or download the episode using download managers. \n \n - You can set up custom lists using this [link](https://anilist.co/settings/lists). (you need to be signed in) \n \n - If your episode/chapter is not being progressed automatically after you finish watching/reading it, then hold press the status bar(planning/repeating/watching button) of that anime/manga. The next time you start a chapter/finish an episode, you will stumble upon a popup. Press yes there. </string>
<string name="question_18">I can\'t login to Anilist.</string>
<string name="answer_18">The reason this happens is probably because you\'re not using the default browser.\n\n>You have to set Chrome as your default browser to login to Anilist.\n\n>It takes a few seconds for the login button to display changes.\n\nIf it doesn\'t work then you could possibly be IP banned from Anilist or your ISP banned Anilist themselves. We believe that this is highly unlikely so open [Anilist](https://anilist.co) in your browser to see if that\`s the case.</string>
<string name="question_19">What is torrent? How do I use it?</string>
<string name="answer_19">Torrent or formally known as BitTorrent is an internet communication protocol for peer\-to\-peer file sharing. A torrent network doesn\'t use a centralised server to host files. It shares files in a decentralised manner. A torrent network has two types of peers. Seeders &amp; Leachers.\n\n• Seeders : These are peers who completed downloading and has the full file. And now they are sharing this file to other peers which is called seeding. A torrent cannot work without at least one seeder. The more seeder a torrent has, the better.\n\n• Leachers : These are peers who are currently downloading the file. If they currently have 40&#37; downloaded then they will share the 40&#37; to any other peers who requests it.\n\nUnlike a centralised server, torrents have no bandwidth limit. You can get your files from hundreds of seeders at the highest possible speed your internet can offer. But many ISP throttle torrent to slow them down because it\'s demanding on their infrastructure. Use a VPN to circumvent this.\n\n• How to use torrents :\n\n1. Install the Torrent Add-on from \`Dantotsu &gt; Settings &gt; Add-ons &gt; Torrent Add-on.\` \n2. Get a source that provides torrents.\n3. USE A VPN. Using VPN while torrenting only has upsides. Without a VPN your IP address will be exposed, your ISP will throttle your network and you could possibly be fined if you live in a country first world country. DO NOT USE IT WITHOUT A VPN IF YOU DON\'T KNOW WHAT YOU\'RE DOING.\n4. Now use that source to start torrenting.</string>
<string name="subscribed_notification">Subscribed! Receiving notifications, when new episodes are released on %1$s.</string> <string name="subscribed_notification">Subscribed! Receiving notifications, when new episodes are released on %1$s.</string>
@ -711,6 +716,7 @@
<string name="installed_manga">Installed Manga</string> <string name="installed_manga">Installed Manga</string>
<string name="color_picker">Color Picker</string> <string name="color_picker">Color Picker</string>
<string name="random_selection">Random Selection</string> <string name="random_selection">Random Selection</string>
<string name="listed_in_library">Listed in Library</string>
<string name="incognito_mode">Incognito Mode</string> <string name="incognito_mode">Incognito Mode</string>
<string name="appwidget_text">EXAMPLE</string> <string name="appwidget_text">EXAMPLE</string>
<string name="configure">Configure</string> <string name="configure">Configure</string>
@ -948,6 +954,8 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
<string name="use_unique_theme_for_each_item_desc">Use color from media\'s banner</string> <string name="use_unique_theme_for_each_item_desc">Use color from media\'s banner</string>
<string name="use_custom_theme_desc">Use your own color for the theme</string> <string name="use_custom_theme_desc">Use your own color for the theme</string>
<string name="color_picker_desc">Choose a color</string> <string name="color_picker_desc">Choose a color</string>
<string name="hide_private">Hide private Media from home screen</string>
<string name="hide_private_desc">Long click "Continue Watching" to access</string>
<string name="torrent_addon">Torrent Add-on</string> <string name="torrent_addon">Torrent Add-on</string>
<string name="enable_torrent">Enable Torrent</string> <string name="enable_torrent">Enable Torrent</string>
<string name="anime_downloader_addon">Anime Downloader Add-on</string> <string name="anime_downloader_addon">Anime Downloader Add-on</string>
@ -959,6 +967,7 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
<string name="install_addon">Install Add-on</string> <string name="install_addon">Install Add-on</string>
<string name="download_addon_not_found">Add-on not found</string> <string name="download_addon_not_found">Add-on not found</string>
<string name="image">Image</string> <string name="image">Image</string>
<string name="clear_history">Clear History</string>
<string name="failed_ext_install_conflict">Failed to install extension due to conflict</string> <string name="failed_ext_install_conflict">Failed to install extension due to conflict</string>
<string name="reading">READING</string> <string name="reading">READING</string>
<string name="watching">WATCHING</string> <string name="watching">WATCHING</string>
@ -1033,5 +1042,6 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
<string name="bio_prompt_info_desc">Use your fingerprint or face to unlock the app</string> <string name="bio_prompt_info_desc">Use your fingerprint or face to unlock the app</string>
<string name="enable_forgot_password">Enable Forgot Password (hold clear button for 10 seconds)</string> <string name="enable_forgot_password">Enable Forgot Password (hold clear button for 10 seconds)</string>
<string name="hide_notification_dot">Hide Notification Dot</string> <string name="hide_notification_dot">Hide Notification Dot</string>
<string name="private_mode">Private</string>
</resources> </resources>

View file

@ -9,6 +9,7 @@
<item name="elevationOverlayEnabled">false</item> <item name="elevationOverlayEnabled">false</item>
<item name="windowActionBar">false</item> <item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
<item name="android:layoutDirection">ltr</item>
<item name="android:windowContentTransitions">true</item> <item name="android:windowContentTransitions">true</item>
<item name="android:fontFamily">@font/poppins</item> <item name="android:fontFamily">@font/poppins</item>
<item name="shapeAppearanceLargeComponent">@style/ShapeAppearanceOverlay.Demo</item> <item name="shapeAppearanceLargeComponent">@style/ShapeAppearanceOverlay.Demo</item>

View file

@ -1,8 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Define your preferences here. For example: --> <!-- Define your preferences here. For example: -->
<CheckBoxPreference z
android:defaultValue="true"
android:key="some_key"
android:title="Some Title" />
</PreferenceScreen> </PreferenceScreen>