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
env:
CI: true
SKIP_BUILD: false
steps:
- name: Checkout repo
@ -19,14 +20,12 @@ jobs:
with:
fetch-depth: 0
- name: Download last SHA artifact
uses: dawidd6/action-download-artifact@v3
with:
workflow: beta.yml
name: last-sha
path: .
continue-on-error: true
- name: Get Commits Since Last Run
@ -39,7 +38,9 @@ jobs:
fi
echo "Commits since $LAST_SHA:"
# 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
COMMIT_LOGS="${COMMIT_LOGS//'%'/'%25'}"
COMMIT_LOGS="${COMMIT_LOGS//$'\n'/'%0A'}"
@ -65,7 +66,11 @@ jobs:
echo "Version $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: List files in the directory
run: ls -l
- name: Setup JDK 17
if: ${{ env.SKIP_BUILD != 'true' }}
uses: actions/setup-java@v4
with:
distribution: 'temurin'
@ -73,18 +78,28 @@ jobs:
cache: gradle
- name: Decode Keystore File
if: ${{ github.repository == 'rebelonion/Dantotsu' }}
run: echo "${{ secrets.KEYSTORE_FILE }}" | base64 -d > $GITHUB_WORKSPACE/key.keystore
- name: List files in the directory
run: ls -l
- name: Make gradlew executable
if: ${{ env.SKIP_BUILD != 'true' }}
run: chmod +x ./gradlew
- 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
if: ${{ env.SKIP_BUILD != 'true' }}
uses: actions/upload-artifact@v4
with:
name: Dantotsu
@ -93,24 +108,202 @@ jobs:
path: "app/build/outputs/apk/google/alpha/app-google-alpha.apk"
- name: Upload APK to Discord and Telegram
if: ${{ github.repository == 'rebelonion/Dantotsu' }}
shell: bash
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/')
# Truncate commit messages if they are too long
max_length=1900 # Adjust this value as needed
if [ ${#developers} -gt $max_length ]; then
developers="${developers:0:$max_length}... (truncated)"
fi
if [ ${#commit_messages} -gt $max_length ]; then
commit_messages="${commit_messages:0:$max_length}... (truncated)"
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
curl -F "chat_id=${{ secrets.TELEGRAM_CHANNEL_ID }}" \
-F "document=@app/build/outputs/apk/google/alpha/app-google-alpha.apk" \
-F "caption=Alpha-Build: ${VERSION}: ${commit_messages}" \
https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendDocument
# Construct Discord payload
discord_data=$(jq -nc \
--arg field_value "$commit_messages" \
--arg author_value "$developers" \
--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:
COMMIT_LOG: ${{ env.COMMIT_LOG }}

View file

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

View file

@ -854,7 +854,7 @@ fun savePrefsToDownloads(
}
)
}
@SuppressLint("StringFormatMatches")
fun savePrefs(serialized: String, path: String, title: String, context: Context): File? {
var file = File(path, "$title.ani")
var counter = 1
@ -874,6 +874,7 @@ fun savePrefs(serialized: String, path: String, title: String, context: Context)
}
}
@SuppressLint("StringFormatMatches")
fun savePrefs(
serialized: String,
path: String,
@ -920,7 +921,7 @@ fun shareImage(title: String, bitmap: Bitmap, context: Context) {
intent.putExtra(Intent.EXTRA_STREAM, contentUri)
context.startActivity(Intent.createChooser(intent, "Share $title"))
}
@SuppressLint("StringFormatMatches")
fun saveImage(image: Bitmap, path: String, imageFileName: String): File? {
val imageFile = File(path, "$imageFileName.png")
return try {
@ -1500,7 +1501,6 @@ fun buildMarkwon(
}
return false
}
override fun onLoadFailed(
e: GlideException?,
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.themes.ThemeManager
import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputEditText
@ -312,6 +313,7 @@ class MainActivity : AppCompatActivity() {
mainViewPager.adapter =
ViewPagerAdapter(supportFragmentManager, lifecycle)
mainViewPager.setPageTransformer(ZoomOutPageTransformer())
mainViewPager.offscreenPageLimit = 1
navbar.selectTabAt(selectedOption)
navbar.setOnTabSelectListener(object :
AnimatedBottomBar.OnTabSelectListener {
@ -493,36 +495,29 @@ class MainActivity : AppCompatActivity() {
val password = CharArray(16).apply { fill('0') }
// Inflate the dialog layout
val dialogView = DialogUserAgentBinding.inflate(layoutInflater)
dialogView.userAgentTextBox.hint = "Password"
dialogView.subtitle.visibility = View.VISIBLE
dialogView.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)
val dialogView = DialogUserAgentBinding.inflate(layoutInflater).apply {
userAgentTextBox.hint = "Password"
subtitle.visibility = View.VISIBLE
subtitle.text = getString(R.string.enter_password_to_decrypt_file)
}
.create()
dialog.window?.setDimAmount(0.8f)
dialog.show()
// Override the positive button here
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
val editText = dialog.findViewById<TextInputEditText>(R.id.userAgentTextBox)
if (editText?.text?.isNotBlank() == true) {
customAlertDialog().apply {
setTitle("Enter Password")
setCustomView(dialogView.root)
setPosButton(R.string.yes) {
val editText = dialogView.userAgentTextBox
if (editText.text?.isNotBlank() == true) {
editText.text?.toString()?.trim()?.toCharArray(password)
dialog.dismiss()
callback(password)
} else {
toast("Password cannot be empty")
}
}
setNegButton(R.string.cancel) {
password.fill('0')
callback(null)
}
show()
}
}
//ViewPager

View file

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

View file

@ -479,6 +479,7 @@ class AnilistQueries {
suspend fun initHomePage(): Map<String, ArrayList<*>> {
val removeList = PrefManager.getCustomVal("removeList", setOf<Int>())
val hidePrivate = PrefManager.getVal<Boolean>(PrefName.HidePrivate)
val removedMedia = ArrayList<Media>()
val toShow: List<Boolean> =
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 ->
li.entries?.reversed()?.forEach {
val m = Media(it)
if (m.id !in removeList) {
if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
m.cameFromContinue = true
subMap[m.id] = m
} else {
@ -540,7 +541,7 @@ class AnilistQueries {
repeating?.lists?.forEach { li ->
li.entries?.reversed()?.forEach {
val m = Media(it)
if (m.id !in removeList) {
if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
m.cameFromContinue = true
subMap[m.id] = m
} else {
@ -579,7 +580,7 @@ class AnilistQueries {
current?.lists?.forEach { li ->
li.entries?.reversed()?.forEach {
val m = Media(it)
if (m.id !in removeList) {
if (m.id !in removeList && if (hidePrivate) !m.isListPrivate else true) {
m.cameFromContinue = true
subMap[m.id] = m
} else {
@ -612,7 +613,7 @@ class AnilistQueries {
apiMediaList?.edges?.forEach {
it.node?.let { i ->
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)
} else {
removedMedia.add(m)
@ -1055,172 +1056,110 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
return null
}
private val onListAnime =
(if (PrefManager.getVal(PrefName.IncludeAnimeList)) "" else "onList:false").replace(
"\"",
""
)
private val isAdult =
(if (PrefManager.getVal(PrefName.AdultOnly)) "isAdult:true" else "").replace("\"", "")
private fun mediaList(media1: Page?): ArrayList<Media> {
val combinedList = arrayListOf<Media>()
media1?.media?.mapTo(combinedList) { Media(it) }
return combinedList
}
private fun getPreference(pref: PrefName): Boolean = PrefManager.getVal(pref)
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 {
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>> {
val list = mutableMapOf<String, ArrayList<Media>>()
fun query(): String {
return """{
recentUpdates:${recentAnimeUpdates(1)}
recentUpdates2:${recentAnimeUpdates(2)}
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 {
fun filterRecentUpdates(
page: Page?,
): ArrayList<Media> {
val listOnly: Boolean = PrefManager.getVal(PrefName.RecentlyListOnly)
val adultOnly: Boolean = PrefManager.getVal(PrefName.AdultOnly)
val idArr = mutableListOf<Int>()
list["recentUpdates"] = recentUpdates?.airingSchedules?.mapNotNull { i ->
return page?.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)) {
if (!idArr.contains(it.id)) {
val shouldAdd = when {
!listOnly && it.countryOfOrigin == "JP" && Anilist.adult && adultOnly && it.isAdult == true -> true
!listOnly && !adultOnly && it.countryOfOrigin == "JP" && it.isAdult == false -> true
listOnly && it.mediaListEntry != null -> true
else -> false
}
if (shouldAdd) {
idArr.add(it.id)
Media(it)
} else null
else null
} else null
}
}?.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())
list["trendingMovies"]?.addAll(trendingMovies2?.media?.map { Media(it) }
?.toCollection(ArrayList()) ?: arrayListOf())
list["topRated"]?.addAll(
topRated2?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
)
list["mostFav"]?.addAll(
mostFav2?.media?.map { Media(it) }?.toCollection(ArrayList()) ?: arrayListOf()
)
executeQuery<Query.AnimeList>(queryAnimeList(), force = true)?.data?.apply {
list["recentUpdates"] = filterRecentUpdates(recentUpdates)
list["trendingMovies"] = mediaList(trendingMovies)
list["topRated"] = mediaList(topRated)
list["mostFav"] = mediaList(mostFav)
}
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>> {
val list = mutableMapOf<String, ArrayList<Media>>()
fun query(): String {
return """{
trendingManga:${trendingManga(1)}
trendingManga2:${trendingManga(2)}
trendingManhwa:${trendingManhwa(1)}
trendingManhwa2:${trendingManhwa(2)}
trendingNovel:${trendingNovel(1)}
trendingNovel2:${trendingNovel(2)}
topRated:${topRatedManga(1)}
topRated2:${topRatedManga(2)}
mostFav:${mostFavManga(1)}
mostFav2:${mostFavManga(2)}
}""".trimIndent()
executeQuery<Query.MangaList>(queryMangaList(), force = true)?.data?.apply {
list["trendingManga"] = mediaList(trendingManga)
list["trendingManhwa"] = mediaList(trendingManhwa)
list["trendingNovel"] = mediaList(trendingNovel)
list["topRated"] = mediaList(topRated)
list["mostFav"] = mediaList(mostFav)
}
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
}
suspend fun recentlyUpdated(
greater: Long = 0,
lesser: Long = System.currentTimeMillis() / 1000 - 10000

View file

@ -163,13 +163,9 @@ class Query {
@Serializable
data class Data(
@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("trendingMovies2") val trendingMovies2: 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("mostFav2") val mostFav2: ani.dantotsu.connections.anilist.api.Page?,
)
}
@ -181,15 +177,10 @@ class Query {
@Serializable
data class Data(
@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("trendingManhwa2") val trendingManhwa2: 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("topRated2") val topRated2: 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.snackString
import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.anggrayudi.storage.file.openInputStream
import com.google.android.material.card.MaterialCardView
import com.google.android.material.imageview.ShapeableImageView
@ -203,25 +204,22 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
val type: MediaType = MediaType.ANIME
// Alert dialog to confirm deletion
val builder =
androidx.appcompat.app.AlertDialog.Builder(requireContext(), R.style.MyPopup)
builder.setTitle("Delete ${item.title}?")
builder.setMessage("Are you sure you want to delete ${item.title}?")
builder.setPositiveButton("Yes") { _, _ ->
requireContext().customAlertDialog().apply {
setTitle("Delete ${item.title}?")
setMessage("Are you sure you want to delete ${item.title}?")
setPosButton(R.string.yes) {
downloadManager.removeMedia(item.title, type)
val mediaIds =
PrefManager.getAnimeDownloadPreferences().all?.filter { it.key.contains(item.title) }?.values
?: emptySet()
val mediaIds = PrefManager.getAnimeDownloadPreferences().all?.filter { it.key.contains(item.title) }?.values ?: emptySet()
if (mediaIds.isEmpty()) {
snackString("No media found") // if this happens, terrible things have happened
}
getDownloads()
}
builder.setNegativeButton("No") { _, _ ->
setNegButton(R.string.no) {
// Do nothing
}
val dialog = builder.show()
dialog.window?.setDimAmount(0.8f)
show()
}
true
}
}

View file

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

View file

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

View file

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

View file

@ -12,12 +12,14 @@ import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.databinding.DialogUserAgentBinding
import ani.dantotsu.databinding.FragmentLoginBinding
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.settings.saving.internal.PreferenceKeystore
import ani.dantotsu.settings.saving.internal.PreferencePackager
import ani.dantotsu.toast
import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.textfield.TextInputEditText
class LoginFragment : Fragment() {
@ -94,38 +96,31 @@ class LoginFragment : Fragment() {
val password = CharArray(16).apply { fill('0') }
// Inflate the dialog layout
val dialogView =
LayoutInflater.from(requireActivity()).inflate(R.layout.dialog_user_agent, null)
dialogView.findViewById<TextInputEditText>(R.id.userAgentTextBox)?.hint = "Password"
val subtitleTextView = dialogView.findViewById<TextView>(R.id.subtitle)
subtitleTextView?.visibility = View.VISIBLE
subtitleTextView?.text = "Enter your password to decrypt the file"
val dialog = AlertDialog.Builder(requireActivity(), R.style.MyPopup)
.setTitle("Enter Password")
.setView(dialogView)
.setPositiveButton("OK", null)
.setNegativeButton("Cancel") { dialog, _ ->
password.fill('0')
dialog.dismiss()
callback(null)
val dialogView = DialogUserAgentBinding.inflate(layoutInflater).apply {
userAgentTextBox.hint = "Password"
subtitle.visibility = View.VISIBLE
subtitle.text = getString(R.string.enter_password_to_decrypt_file)
}
.create()
dialog.window?.setDimAmount(0.8f)
dialog.show()
// Override the positive button here
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
val editText = dialog.findViewById<TextInputEditText>(R.id.userAgentTextBox)
if (editText?.text?.isNotBlank() == true) {
requireActivity().customAlertDialog().apply {
setTitle("Enter Password")
setCustomView(dialogView.root)
setPosButton(R.string.ok){
val editText = dialogView.userAgentTextBox
if (editText.text?.isNotBlank() == true) {
editText.text?.toString()?.trim()?.toCharArray(password)
dialog.dismiss()
callback(password)
} else {
toast("Password cannot be empty")
}
}
setNegButton(R.string.cancel) {
password.fill('0')
callback(null)
}
}.show()
}
private fun restartApp() {

View file

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

View file

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

View file

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

View file

@ -26,25 +26,50 @@ class OtherDetailsViewModel : ViewModel() {
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)
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 res = Anilist.query.recentlyUpdated(curr - 86400, curr + (86400 * 6))
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>>()
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 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() }
it.relation = "Episode ${v[0]}"
if (!idList.contains(it.id)) {
idList.add(it.id)
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.content.Intent
import android.os.Bundle
import android.text.SpannableString
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@ -21,7 +20,7 @@ import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.MarkdownCreatorActivity
import ani.dantotsu.util.ActivityMarkdownCreator
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -59,7 +58,7 @@ class ReviewActivity : AppCompatActivity() {
binding.followFilterButton.setOnClickListener {
ContextCompat.startActivity(
this,
Intent(this, MarkdownCreatorActivity::class.java)
Intent(this, ActivityMarkdownCreator::class.java)
.putExtra("type", "review"),
null
)

View file

@ -183,6 +183,12 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
binding.searchByImage.setOnClickListener {
activity.startActivity(Intent(activity, ImageSearchActivity::class.java))
}
binding.clearHistory.setOnClickListener {
it.startAnimation(fadeOutAnimation())
it.visibility = View.GONE
searchHistoryAdapter.clearHistory()
}
updateClearHistoryVisibility()
fun searchTitle() {
activity.result.apply {
search =
@ -300,11 +306,17 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
}
binding.searchResultLayout.visibility = View.VISIBLE
binding.clearHistory.visibility = View.GONE
binding.searchHistoryList.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 {
return AlphaAnimation(0f, 1f).apply {
duration = 150
@ -375,4 +387,3 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
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)
}
fun clearHistory() {
searchHistory?.clear()
PrefManager.setVal(historyType, searchHistory)
submitList(searchHistory?.toList())
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -83,6 +83,7 @@ import ani.dantotsu.showSystemBarsRetractView
import ani.dantotsu.snackString
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.tryWith
import ani.dantotsu.util.customAlertDialog
import com.alexvasilkov.gestures.views.GestureFrameLayout
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
@ -1013,28 +1014,27 @@ class MangaReaderActivity : AppCompatActivity() {
PrefManager.setCustomVal("${media.id}_progressDialog", !isChecked)
showProgressDialog = !isChecked
}
AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.title_update_progress))
.setView(dialogView)
.setCancelable(false)
.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
customAlertDialog().apply {
setTitle(R.string.title_update_progress)
setCustomView(dialogView)
setCancelable(false)
setPosButton(R.string.yes) {
PrefManager.setCustomVal("${media.id}_save_progress", true)
updateProgress(
media,
MediaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
.toString()
)
dialog.dismiss()
runnable.run()
}
.setNegativeButton(getString(R.string.no)) { dialog, _ ->
setNegButton(R.string.no) {
PrefManager.setCustomVal("${media.id}_save_progress", false)
dialog.dismiss()
runnable.run()
}
.setOnCancelListener { hideSystemBars() }
.create()
.show()
setOnCancelListener { hideSystemBars() }
show()
}
} else {
if (!incognito && PrefManager.getCustomVal(
"${media.id}_save_progress",

View file

@ -50,16 +50,16 @@ class NovelReadAdapter(
val source =
media.selected!!.sourceIndex.let { if (it >= novelReadSources.names.size) 0 else it }
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(
fragment.requireContext(),
R.layout.item_dropdown,
novelReadSources.names
)
)
binding.animeSource.setOnItemClickListener { _, _, i, _ ->
binding.mediaSource.setOnItemClickListener { _, _, i, _ ->
fragment.onSourceChange(i)
search()
}

View file

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

View file

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

View file

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

View file

@ -39,7 +39,7 @@ object AnimeSources : WatchSources() {
}
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 = sortPinnedAnimeSources(list, pinnedAnimeSources) + Lazier(
{ OfflineAnimeParser() },

View file

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

View file

@ -19,8 +19,7 @@ import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.setBaseline
import ani.dantotsu.util.MarkdownCreatorActivity
import ani.dantotsu.util.ActivityMarkdownCreator
import com.xwray.groupie.GroupieAdapter
import eu.kanade.tachiyomi.util.system.getSerializableCompat
import kotlinx.coroutines.launch
@ -44,30 +43,16 @@ class ActivityFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
type = arguments?.getSerializableCompat<ActivityType>("type") as ActivityType
userId = arguments?.getInt("userId")
activityId = arguments?.getInt("activityId")
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 {
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
)
}
arguments?.let {
type = it.getSerializableCompat<ActivityType>("type") as ActivityType
userId = it.getInt("userId")
activityId = it.getInt("activityId")
}
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.layoutManager = LinearLayoutManager(context)
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() {
val list = when (type) {
@ -114,7 +106,7 @@ class ActivityFragment : Fragment() {
ActivityType.OTHER_USER -> getActivities(userId = userId)
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(
@ -142,37 +134,33 @@ class ActivityFragment : Fragment() {
}
private fun onActivityClick(id: Int, type: String) {
when (type) {
"USER" -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), ProfileActivity::class.java)
.putExtra("userId", id), null
)
}
val intent = when (type) {
"USER" -> Intent(requireContext(), ProfileActivity::class.java).putExtra("userId", id)
"MEDIA" -> Intent(
requireContext(),
MediaDetailsActivity::class.java
).putExtra("mediaId", id)
"MEDIA" -> {
ContextCompat.startActivity(
requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java)
.putExtra("mediaId", id), null
)
}
else -> return
}
ContextCompat.startActivity(requireContext(), intent, null)
}
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
}
}
companion object {
enum class ActivityType {
GLOBAL, USER, OTHER_USER, ONE
}
enum class ActivityType { 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 {
arguments = Bundle().apply {
putSerializable("type", type)

View file

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

View file

@ -15,7 +15,7 @@ import ani.dantotsu.profile.User
import ani.dantotsu.profile.UsersDialogFragment
import ani.dantotsu.snackString
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.viewbinding.BindableItem
import kotlinx.coroutines.CoroutineScope
@ -81,7 +81,7 @@ class ActivityReplyItem(
binding.activityReply.setOnClickListener {
ContextCompat.startActivity(
context,
Intent(context, MarkdownCreatorActivity::class.java)
Intent(context, ActivityMarkdownCreator::class.java)
.putExtra("type", "replyActivity")
.putExtra("parentId", parentId)
.putExtra("other", "@${reply.user.name} "),
@ -92,7 +92,7 @@ class ActivityReplyItem(
binding.activityEdit.setOnClickListener {
ContextCompat.startActivity(
context,
Intent(context, MarkdownCreatorActivity::class.java)
Intent(context, ActivityMarkdownCreator::class.java)
.putExtra("type", "replyActivity")
.putExtra("parentId", parentId)
.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.os.Bundle
@ -14,12 +14,10 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.ActivityReply
import ani.dantotsu.databinding.BottomSheetRecyclerBinding
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.activity.ActivityReplyItem
import ani.dantotsu.snackString
import ani.dantotsu.util.MarkdownCreatorActivity
import ani.dantotsu.util.ActivityMarkdownCreator
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -50,7 +48,7 @@ class RepliesBottomDialog : BottomSheetDialogFragment() {
binding.replyButton.setOnClickListener {
ContextCompat.startActivity(
context,
Intent(context, MarkdownCreatorActivity::class.java)
Intent(context, ActivityMarkdownCreator::class.java)
.putExtra("type", "replyActivity")
.putExtra("parentId", activityId),
null
@ -73,14 +71,10 @@ class RepliesBottomDialog : BottomSheetDialogFragment() {
adapter.update(
replies.map {
ActivityReplyItem(
it,
activityId,
requireActivity(),
adapter,
clickCallback = { int, _ ->
onClick(int)
it, activityId, requireActivity(), adapter,
) { i, _ ->
onClick(i)
}
)
}
)
} else {

View file

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

View file

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

View file

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

View file

@ -50,6 +50,11 @@ class FAQActivity : AppCompatActivity() {
currContext()?.getString(R.string.question_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(
R.drawable.ic_anilist,
currContext()?.getString(R.string.question_6) ?: "",
@ -60,6 +65,11 @@ class FAQActivity : AppCompatActivity() {
currContext()?.getString(R.string.question_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(
R.drawable.ic_round_lock_open_24,
currContext()?.getString(R.string.question_9) ?: "",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -9,13 +10,21 @@ import ani.dantotsu.BottomSheetDialogFragment
import ani.dantotsu.R
import ani.dantotsu.databinding.BottomSheetRecyclerBinding
import ani.dantotsu.notifications.subscription.SubscriptionHelper
import ani.dantotsu.parsers.novel.NovelExtensionManager
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() {
private var _binding: BottomSheetRecyclerBinding? = null
private val binding get() = _binding!!
private val adapter: GroupieAdapter = GroupieAdapter()
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(
inflater: LayoutInflater,
@ -36,8 +45,33 @@ class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
val context = requireContext()
binding.title.text = context.getString(R.string.subscriptions)
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.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.customAlertDialog
class UserInterfaceSettingsActivity : AppCompatActivity() {
lateinit var binding: ActivityUserInterfaceSettingsBinding
@ -38,20 +39,23 @@ class UserInterfaceSettingsActivity : AppCompatActivity() {
binding.uiSettingsHomeLayout.setOnClickListener {
val set = PrefManager.getVal<List<Boolean>>(PrefName.HomeLayout).toMutableList()
val views = resources.getStringArray(R.array.home_layouts)
val dialog = AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.home_layout_show)).apply {
setMultiChoiceItems(
views,
PrefManager.getVal<List<Boolean>>(PrefName.HomeLayout).toBooleanArray()
) { _, i, value ->
set[i] = value
customAlertDialog().apply {
setTitle(getString(R.string.home_layout_show))
multiChoiceItems(
items = views,
checkedItems = PrefManager.getVal<List<Boolean>>(PrefName.HomeLayout).toBooleanArray()
) { selectedItems ->
for (i in selectedItems.indices) {
set[i] = selectedItems[i]
}
setPositiveButton("Done") { _, _ ->
}
setPosButton(R.string.ok) {
PrefManager.setVal(PrefName.HomeLayout, set)
restartApp()
}
}.show()
dialog.window?.setDimAmount(0.8f)
show()
}
}
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)),
VerboseLogging(Pref(Location.General, Boolean::class, false)),
DohProvider(Pref(Location.General, Int::class, 0)),
HidePrivate(Pref(Location.General, Boolean::class, false)),
DefaultUserAgent(
Pref(
Location.General,

View file

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

View file

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

View file

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

View file

@ -64,10 +64,6 @@ internal class ExtensionGithubApi {
val repos =
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 {
try {
@ -95,9 +91,10 @@ internal class ExtensionGithubApi {
// Sanity check - a small number of extensions probably means something broke
// with the repo generator
if (repoExtensions.size < 10) {
throw Exception()
}
//if (repoExtensions.size < 10) {
// 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)
} catch (e: Throwable) {
@ -157,10 +154,6 @@ internal class ExtensionGithubApi {
val repos =
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 {
try {
@ -188,9 +181,10 @@ internal class ExtensionGithubApi {
// Sanity check - a small number of extensions probably means something broke
// with the repo generator
if (repoExtensions.size < 10) {
throw Exception()
}
//if (repoExtensions.size < 10) {
// 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)
} 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: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
android:id="@+id/random"
android:layout_width="40dp"

View file

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

View file

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

View file

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

View file

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

View file

@ -65,19 +65,6 @@
</LinearLayout>
</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
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -215,7 +202,7 @@
android:id="@+id/activityRepliesContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="12dp"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
@ -243,7 +230,7 @@
android:id="@+id/activityLikeContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:padding="12dp"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">

View file

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

View file

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

View file

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

View file

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

View file

@ -25,7 +25,7 @@
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="3dp"
android:layout_marginEnd="9dp"
tools:ignore="ContentDescription" />
<LinearLayout
@ -53,6 +53,26 @@
tools:ignore="SmallSp" />
</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
android:id="@+id/closeTextView"
android:layout_width="wrap_content"

View file

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

View file

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

View file

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

View file

@ -117,6 +117,32 @@
app:drawableTint="?attr/colorPrimary" />
</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
android:id="@+id/searchChipRecycler"
android:layout_width="0dp"

View file

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

View file

@ -156,8 +156,12 @@
<string name="chap">Chapter</string>
<string name="wrong"><u>Wrong Title?</u></string>
<string name="source_not_found">
Couldn\'t find anything X( \n
Try another source.
Nothing came up from this source.\n
Let\'s look elsewhere.
</string>
<string name="download_not_found">
Your downloads are feeling a bit lonely…\n
Try downloading something.
</string>
<string name="not_supported">%1$s is not supported!</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_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="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="subscriptions_checking_time_s">Subscriptions Update Frequency : %1$s</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="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.
\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="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>
<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>
@ -664,6 +664,11 @@
<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="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>
@ -711,6 +716,7 @@
<string name="installed_manga">Installed Manga</string>
<string name="color_picker">Color Picker</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="appwidget_text">EXAMPLE</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_custom_theme_desc">Use your own color for the theme</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="enable_torrent">Enable Torrent</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="download_addon_not_found">Add-on not found</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="reading">READING</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="enable_forgot_password">Enable Forgot Password (hold clear button for 10 seconds)</string>
<string name="hide_notification_dot">Hide Notification Dot</string>
<string name="private_mode">Private</string>
</resources>

View file

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

View file

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