diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml index 68444cd86a2..313a0be5e63 100644 --- a/.github/workflows/beta.yml +++ b/.github/workflows/beta.yml @@ -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,26 +66,40 @@ 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' java-version: 17 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 }} + - name: Build with Gradle + 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,25 +108,203 @@ 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]()" + additional_info["aayush262"]="\n Discord: <@918825160654598224>\n AniList: [aayush262]()" + additional_info["rebelonion"]="\n Discord: <@714249925248024617>\n AniList: [rebelonion]()\n PornHub: [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 }} + + # 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" - #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 + # 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>/') + message=$(echo "$message" | sed -E 's/\[#([0-9]+)\]\((https:\/\/github\.com\/[^)]+)\)/#\1<\/a>/g') + echo "$message" + done) + telegram_commit_messages="
${telegram_commit_messages}
" + # 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 }} VERSION: ${{ env.VERSION }} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1104ba865ff..4c267042acc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -201,7 +201,7 @@ android:name=".others.imagesearch.ImageSearchActivity" android:parentActivityName=".MainActivity" /> + val dialogView = DialogUserAgentBinding.inflate(layoutInflater).apply { + userAgentTextBox.hint = "Password" + subtitle.visibility = View.VISIBLE + subtitle.text = getString(R.string.enter_password_to_decrypt_file) + } + 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) + callback(password) + } else { + toast("Password cannot be empty") + } + } + setNegButton(R.string.cancel) { password.fill('0') - dialog.dismiss() callback(null) } - .create() - - dialog.window?.setDimAmount(0.8f) - dialog.show() - - // Override the positive button here - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - val editText = dialog.findViewById(R.id.userAgentTextBox) - if (editText?.text?.isNotBlank() == true) { - editText.text?.toString()?.trim()?.toCharArray(password) - dialog.dismiss() - callback(password) - } else { - toast("Password cannot be empty") - } + show() } } diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt index 1ddfbbaaaea..3d7ac85eda1 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt @@ -93,30 +93,41 @@ class AnilistMutations { ) } - suspend fun postActivity(text: String, edit : Int? = null): String { + suspend fun postActivity(text: String, edit: Int? = null): String { val encodedText = text.stringSanitizer() - val 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(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 { - val encodedText = text.replace("","").stringSanitizer() - val query = "mutation{SaveMessageActivity(${if (edit != null) "id:$edit," else ""} recipientId:$userId,message:$encodedText,private:$isPrivate){id}}" + + 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 result = executeQuery(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 { + + 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(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 diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt index 75ab4ee1eef..438dc7ad76e 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -479,6 +479,7 @@ class AnilistQueries { suspend fun initHomePage(): Map> { val removeList = PrefManager.getCustomVal("removeList", setOf()) + val hidePrivate = PrefManager.getVal(PrefName.HidePrivate) val removedMedia = ArrayList() val toShow: List = 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 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}}}}""" + private fun mediaList(media1: Page?): ArrayList { + val combinedList = arrayListOf() + media1?.media?.mapTo(combinedList) { Media(it) } + return combinedList } - - 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 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 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 recentAnimeUpdates(page: Int): String { + 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 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}}}""" + 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")} + + }""" } suspend fun loadAnimeList(): Map> { val list = mutableMapOf>() - 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(), force = true)?.data?.apply { + + fun filterRecentUpdates( + page: Page?, + ): ArrayList { val listOnly: Boolean = PrefManager.getVal(PrefName.RecentlyListOnly) val adultOnly: Boolean = PrefManager.getVal(PrefName.AdultOnly) val idArr = mutableListOf() - 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(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> { val list = mutableMapOf>() - 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(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(), 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 diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt index e6635b9a773..e909a3bc0ce 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt @@ -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?, ) } diff --git a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt index 3bcdffe1ee5..4f3876fe51b 100644 --- a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt +++ b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt @@ -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") { _, _ -> - downloadManager.removeMedia(item.title, type) - 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 + 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() + if (mediaIds.isEmpty()) { + snackString("No media found") // if this happens, terrible things have happened + } + getDownloads() } - getDownloads() - } - builder.setNegativeButton("No") { _, _ -> - // Do nothing + setNegButton(R.string.no) { + // Do nothing + } + show() } - val dialog = builder.show() - dialog.window?.setDimAmount(0.8f) true } } diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt index 6d88918e6c3..dcb462e80f1 100644 --- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt @@ -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") { _, _ -> - downloadManager.removeMedia(item.title, type) - getDownloads() - } - builder.setNegativeButton("No") { _, _ -> - // Do nothing - } - val dialog = builder.show() - dialog.window?.setDimAmount(0.8f) + 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() + } + setNegButton(R.string.no) + }.show() true } } diff --git a/app/src/main/java/ani/dantotsu/download/video/Helper.kt b/app/src/main/java/ani/dantotsu/download/video/Helper.kt index b207d634617..ab8dd73d4da 100644 --- a/app/src/main/java/ani/dantotsu/download/video/Helper.kt +++ b/app/src/main/java/ani/dantotsu/download/video/Helper.kt @@ -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() - val downloadCheck = downloadsManger + val downloadsManager = Injekt.get() + 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) { diff --git a/app/src/main/java/ani/dantotsu/home/HomeFragment.kt b/app/src/main/java/ani/dantotsu/home/HomeFragment.kt index a66506d937e..43bf8bea405 100644 --- a/app/src/main/java/ani/dantotsu/home/HomeFragment.kt +++ b/app/src/main/java/ani/dantotsu/home/HomeFragment.kt @@ -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 = PrefManager.getVal(PrefName.HomeLayout) model.initHomePage() + model.initUserStatus() (array.indices).forEach { i -> if (homeLayoutShow.elementAt(i)) { empty = false diff --git a/app/src/main/java/ani/dantotsu/home/LoginFragment.kt b/app/src/main/java/ani/dantotsu/home/LoginFragment.kt index 5f89464e3ce..13fde1e5479 100644 --- a/app/src/main/java/ani/dantotsu/home/LoginFragment.kt +++ b/app/src/main/java/ani/dantotsu/home/LoginFragment.kt @@ -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(R.id.userAgentTextBox)?.hint = "Password" - val subtitleTextView = dialogView.findViewById(R.id.subtitle) - subtitleTextView?.visibility = View.VISIBLE - subtitleTextView?.text = "Enter your password to decrypt the file" + val dialogView = DialogUserAgentBinding.inflate(layoutInflater).apply { + userAgentTextBox.hint = "Password" + subtitle.visibility = View.VISIBLE + subtitle.text = getString(R.string.enter_password_to_decrypt_file) + } - val dialog = AlertDialog.Builder(requireActivity(), R.style.MyPopup) - .setTitle("Enter Password") - .setView(dialogView) - .setPositiveButton("OK", null) - .setNegativeButton("Cancel") { dialog, _ -> + 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) + callback(password) + } else { + toast("Password cannot be empty") + } + } + setNegButton(R.string.cancel) { password.fill('0') - dialog.dismiss() callback(null) } - .create() + }.show() - dialog.window?.setDimAmount(0.8f) - dialog.show() - // Override the positive button here - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - val editText = dialog.findViewById(R.id.userAgentTextBox) - if (editText?.text?.isNotBlank() == true) { - editText.text?.toString()?.trim()?.toCharArray(password) - dialog.dismiss() - callback(password) - } else { - toast("Password cannot be empty") - } - } } private fun restartApp() { diff --git a/app/src/main/java/ani/dantotsu/home/status/Stories.kt b/app/src/main/java/ani/dantotsu/home/status/Stories.kt index d65228442aa..a017b7ab467 100644 --- a/app/src/main/java/ani/dantotsu/home/status/Stories.kt +++ b/app/src/main/java/ani/dantotsu/home/status/Stories.kt @@ -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 diff --git a/app/src/main/java/ani/dantotsu/home/status/UserStatusAdapter.kt b/app/src/main/java/ani/dantotsu/home/status/UserStatusAdapter.kt index 8aa021049e8..c8db4c99c1e 100644 --- a/app/src/main/java/ani/dantotsu/home/status/UserStatusAdapter.kt +++ b/app/src/main/java/ani/dantotsu/home/status/UserStatusAdapter.kt @@ -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) : RecyclerView.Adapter() { @@ -43,7 +42,7 @@ class UserStatusAdapter(private val user: ArrayList) : 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 ) diff --git a/app/src/main/java/ani/dantotsu/media/CalendarActivity.kt b/app/src/main/java/ani/dantotsu/media/CalendarActivity.kt index 66cec7e40fe..230f3a4e347 100644 --- a/app/src/main/java/ani/dantotsu/media/CalendarActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/CalendarActivity.kt @@ -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) } } } - } } diff --git a/app/src/main/java/ani/dantotsu/media/OtherDetailsViewModel.kt b/app/src/main/java/ani/dantotsu/media/OtherDetailsViewModel.kt index 0be0fc2224b..a086a765dda 100644 --- a/app/src/main/java/ani/dantotsu/media/OtherDetailsViewModel.kt +++ b/app/src/main/java/ani/dantotsu/media/OtherDetailsViewModel.kt @@ -26,25 +26,50 @@ class OtherDetailsViewModel : ViewModel() { if (author.value == null) author.postValue(Anilist.query.getAuthorDetails(m)) } + private var cachedAllCalendarData: Map>? = null + private var cachedLibraryCalendarData: Map>? = null private val calendar: MutableLiveData>> = MutableLiveData(null) fun getCalendar(): LiveData>> = calendar - suspend fun loadCalendar() { - val curr = System.currentTimeMillis() / 1000 - val res = Anilist.query.recentlyUpdated(curr - 86400, curr + (86400 * 6)) - val df = DateFormat.getDateInstance(DateFormat.FULL) - val map = mutableMapOf>() - val idMap = mutableMapOf>() - 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 idList = idMap.getOrPut(dateInfo) { mutableListOf() } - it.relation = "Episode ${v[0]}" - if (!idList.contains(it.id)) { - idList.add(it.id) - list.add(it) + 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 allMap = mutableMapOf>() + val libraryMap = mutableMapOf>() + val idMap = mutableMapOf>() + + 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 = 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) + } } + + cachedAllCalendarData = allMap + cachedLibraryCalendarData = libraryMap + } + + val cacheToUse: Map> = if (showOnlyLibrary) { + cachedLibraryCalendarData ?: emptyMap() + } else { + cachedAllCalendarData ?: emptyMap() } - calendar.postValue(map) + calendar.postValue(cacheToUse) } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/ReviewActivity.kt b/app/src/main/java/ani/dantotsu/media/ReviewActivity.kt index 6b08734cd6f..0f6d0e4ffc2 100644 --- a/app/src/main/java/ani/dantotsu/media/ReviewActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/ReviewActivity.kt @@ -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 ) diff --git a/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt b/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt index ecf3692c63b..21f0b683805 100644 --- a/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt @@ -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 } } - diff --git a/app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt b/app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt index f1519e2b620..4e2988e3612 100644 --- a/app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt @@ -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 diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt index a2406a21be7..e1c77312597 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt @@ -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() { 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 @@ -75,7 +73,7 @@ class AnimeWatchAdapter( null ) } - //Youtube + // Youtube if (media.anime?.youtube != null && PrefManager.getVal(PrefName.ShowYtButton)) { binding.animeSourceYT.visibility = View.VISIBLE binding.animeSourceYT.setOnClickListener { @@ -89,7 +87,7 @@ class AnimeWatchAdapter( R.string.subbed ) - //PreferDub + // PreferDub var changing = false binding.animeSourceDubbed.setOnCheckedChangeListener { _, isChecked -> binding.animeSourceDubbedText.text = @@ -99,8 +97,8 @@ class AnimeWatchAdapter( if (!changing) fragment.onDubClicked(isChecked) } - //Wrong Title - binding.animeSourceSearch.setOnClickListener { + // Wrong Title + 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 + // 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,19 +168,19 @@ class AnimeWatchAdapter( } ?: run { } } - //settings - binding.animeSourceSettings.setOnClickListener { + // Settings + binding.mediaSourceSettings.setOnClickListener { (watchSources[source] as? DynamicAnimeParser)?.let { ext -> fragment.openSettings(ext.extension) } } - //Icons + // 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,117 +188,115 @@ 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) - 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 { - reversed = !reversed - dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f - dialogBinding.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 - } - 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 - } - selected.alpha = 1f - fun selected(it: ImageButton) { - selected.alpha = 0.33f - selected = it + // Nested Button + 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) + + mediaSourceTop.rotation = if (reversed) -90f else 90f + sortText.text = if (reversed) "Down to Up" else "Up to Down" + mediaSourceTop.setOnClickListener { + reversed = !reversed + 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 -> mediaSourceList + 1 -> mediaSourceGrid + 2 -> mediaSourceCompact + else -> mediaSourceList + } + when (style) { + 0 -> layoutText.setText(R.string.list) + 1 -> layoutText.setText(R.string.grid) + 2 -> layoutText.setText(R.string.compact) + else -> mediaSourceList + } selected.alpha = 1f - } - dialogBinding.animeSourceList.setOnClickListener { - selected(it as ImageButton) - style = 0 - dialogBinding.layoutText.setText(R.string.list) - run = true - } - dialogBinding.animeSourceGrid.setOnClickListener { - selected(it as ImageButton) - style = 1 - dialogBinding.layoutText.setText(R.string.grid) - run = true - } - dialogBinding.animeSourceCompact.setOnClickListener { - selected(it as ImageButton) - style = 2 - dialogBinding.layoutText.setText(R.string.compact) - run = true - } - dialogBinding.animeWebviewContainer.setOnClickListener { - if (!WebViewUtil.supportsWebView(fragment.requireContext())) { - toast(R.string.webview_not_installed) + fun selected(it: ImageButton) { + selected.alpha = 0.33f + selected = it + selected.alpha = 1f + } + mediaSourceList.setOnClickListener { + selected(it as ImageButton) + style = 0 + layoutText.setText(R.string.list) + run = true + } + mediaSourceGrid.setOnClickListener { + selected(it as ImageButton) + style = 1 + layoutText.setText(R.string.grid) + run = true } - //start CookieCatcher activity - if (watchSources.names.isNotEmpty() && source in 0 until watchSources.names.size) { - val sourceAHH = watchSources[source] as? DynamicAnimeParser - val sourceHttp = - sourceAHH?.extension?.sources?.firstOrNull() as? AnimeHttpSource - val url = sourceHttp?.baseUrl - url?.let { - refresh = true - val headersMap = try { - sourceHttp.headers.toMultimap() - .mapValues { it.value.getOrNull(0) ?: "" } - } catch (e: Exception) { - emptyMap() + mediaSourceCompact.setOnClickListener { + selected(it as ImageButton) + style = 2 + layoutText.setText(R.string.compact) + run = true + } + mediaWebviewContainer.setOnClickListener { + if (!WebViewUtil.supportsWebView(fragment.requireContext())) { + toast(R.string.webview_not_installed) + } + // Start CookieCatcher activity + if (watchSources.names.isNotEmpty() && source in 0 until watchSources.names.size) { + val sourceAHH = watchSources[source] as? DynamicAnimeParser + val sourceHttp = + sourceAHH?.extension?.sources?.firstOrNull() as? AnimeHttpSource + val url = sourceHttp?.baseUrl + url?.let { + refresh = true + val headersMap = try { + sourceHttp.headers.toMultimap() + .mapValues { it.value.getOrNull(0) ?: "" } + } catch (e: Exception) { + emptyMap() + } + val intent = + Intent(fragment.requireContext(), CookieCatcher::class.java) + .putExtra("url", url) + .putExtra("headers", headersMap as HashMap) + startActivity(fragment.requireContext(), intent, null) } - val intent = Intent(fragment.requireContext(), CookieCatcher::class.java) - .putExtra("url", url) - .putExtra("headers", headersMap as HashMap) - startActivity(fragment.requireContext(), intent, null) } } - } - - //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") { _, _ -> - if (run) fragment.onIconPressed(style, reversed) - if (refresh) fragment.loadEpisodes(source, true) - } - .setNegativeButton("Cancel") { _, _ -> - if (refresh) fragment.loadEpisodes(source, true) - } - .setOnCancelListener { - if (refresh) fragment.loadEpisodes(source, true) + // 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) + } + setNegButton("Cancel") { + if (refresh) fragment.loadEpisodes(source, true) + } + show() } - .create() - nestedDialog?.show() + } } - //Episode Handling + // Episode Handling handleEpisodes() } @@ -308,7 +304,7 @@ class AnimeWatchAdapter( subscribe?.enabled(enabled) } - //Chips + // Chips fun updateChips(limit: Int, names: Array, arr: Array, selected: Int = 0) { val binding = _binding if (binding != null) { @@ -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( + if ((binding.itemMediaProgress.layoutParams as LinearLayout.LayoutParams).weight > PrefManager.getVal( 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(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) diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt index ecd8ee73127..847d87554ab 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt @@ -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) { @@ -266,7 +267,7 @@ class AnimeWatchFragment : Fragment() { } media.anime?.episodes = episodes - //CHIP GROUP + // CHIP GROUP val total = episodes.size val divisions = total.toDouble() / 10 start = 0 @@ -396,20 +397,18 @@ 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) { - changeUIVisibility(true) - loadEpisodes(media.selected!!.sourceIndex, true) - } + val fragment = AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) { + changeUIVisibility(true) + loadEpisodes(media.selected!!.sourceIndex, true) + } parentFragmentManager.beginTransaction() .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) .replace(R.id.fragmentExtensionsContainer, fragment) @@ -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() + } } } @@ -635,7 +635,7 @@ class AnimeWatchFragment : Fragment() { private fun reload() { val selected = model.loadSelected(media) - //Find latest episode for subscription + // Find latest episode for subscription selected.latest = media.anime?.episodes?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f selected.latest = @@ -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 { diff --git a/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt b/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt index 14be291aa1d..c0ac4352404 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt @@ -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") { _, _ -> - fragment.onAnimeEpisodeRemoveDownloadClick(episodeNumber) - } - builder.setNegativeButton("No") { _, _ -> - } - val dialog = builder.show() - dialog.window?.setDimAmount(0.8f) + 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) + } + setNegButton(R.string.no) + }.show() return@setOnClickListener } else { fragment.onAnimeEpisodeDownloadClick(episodeNumber) diff --git a/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt index e93267e2181..febd93ec41f 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt @@ -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) - if (isChecked) { - selectedAudioTracks.add(audioPair) - } else { - selectedAudioTracks.remove(audioPair) + + 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) - if (isChecked) { - selectedSubtitles.add(subtitlePair) - } else { - selectedSubtitles.remove(subtitlePair) + 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() } diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt index 6c55209065f..0a4bed33568 100644 --- a/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt +++ b/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt @@ -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, _ -> - callback() - dialog.dismiss() - } - .setNegativeButton("No") { dialog, _ -> - dialog.dismiss() - } - val dialog = alertDialog.show() - dialog?.window?.setDimAmount(0.8f) + commentsFragment.activity.customAlertDialog().apply { + setTitle(title) + setMessage(message) + setPosButton("Yes") { + callback() + } + setNegButton("No") {} + }.show() } private val usernameColors: Array = arrayOf( diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt index ff861dbe4ca..fc21805e67f 100644 --- a/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt @@ -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(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() } - val dialog = alertDialog.show() - dialog?.window?.setDimAmount(0.8f) + setNegButton("Cancel") { filterTag = null } + show() + } } 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(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() - } - val dialog = alertDialog.show() - dialog?.window?.setDimAmount(0.8f) + setNegButton(R.string.cancel) + show() + } } private fun processComment() { diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt index 177d36845c2..a98f0570ac5 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt @@ -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,31 +57,29 @@ class MangaReadAdapter( ) : RecyclerView.Adapter() { var subscribe: MediaDetailsActivity.PopImageButton? = null - private var _binding: ItemAnimeWatchBinding? = null + private var _binding: ItemMediaSourceBinding? = null val hiddenScanlators = mutableListOf() var scanlatorSelectionListener: ScanlatorSelectionListener? = null var options = listOf() 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 binding.sourceTitle.setText(R.string.chaps) - //Fuck u launch + // Fuck u launch binding.faqbutton.setOnClickListener { val intent = Intent(fragment.requireContext(), FAQActivity::class.java) startActivity(fragment.requireContext(), intent, null) } - //Wrong Title - binding.animeSourceSearch.setOnClickListener { + // Wrong Title + 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 - //Source Selection + 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,17 +143,17 @@ class MangaReadAdapter( } } - //settings - binding.animeSourceSettings.setOnClickListener { + // Settings + binding.mediaSourceSettings.setOnClickListener { (mangaReadSources[source] as? DynamicMangaParser)?.let { ext -> fragment.openSettings(ext.extension) } } - //Grids + // 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,206 +161,191 @@ 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 { - reversed = !reversed - dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f - dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down" - run = true - } + dialogBinding.apply { + mediaSourceTop.rotation = if (reversed) -90f else 90f + sortText.text = if (reversed) "Down to Up" else "Up to Down" + mediaSourceTop.setOnClickListener { + reversed = !reversed + 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 - var selected = when (style) { - 0 -> dialogBinding.animeSourceList - 1 -> dialogBinding.animeSourceCompact - else -> dialogBinding.animeSourceList - } - when (style) { - 0 -> dialogBinding.layoutText.setText(R.string.list) - 1 -> dialogBinding.layoutText.setText(R.string.compact) - else -> dialogBinding.animeSourceList - } - selected.alpha = 1f - fun selected(it: ImageButton) { - selected.alpha = 0.33f - selected = it + // Grids + mediaSourceGrid.visibility = View.GONE + var selected = when (style) { + 0 -> mediaSourceList + 1 -> mediaSourceCompact + else -> mediaSourceList + } + when (style) { + 0 -> layoutText.setText(R.string.list) + 1 -> layoutText.setText(R.string.compact) + else -> mediaSourceList + } selected.alpha = 1f - } - dialogBinding.animeSourceList.setOnClickListener { - selected(it as ImageButton) - style = 0 - dialogBinding.layoutText.setText(R.string.list) - run = true - } - dialogBinding.animeSourceCompact.setOnClickListener { - selected(it as ImageButton) - style = 1 - dialogBinding.layoutText.setText(R.string.compact) - run = true - } - dialogBinding.animeWebviewContainer.setOnClickListener { - if (!WebViewUtil.supportsWebView(fragment.requireContext())) { - toast(R.string.webview_not_installed) + fun selected(it: ImageButton) { + selected.alpha = 0.33f + selected = it + selected.alpha = 1f } - //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) - .putExtra("url", url) - startActivity(fragment.requireContext(), intent, null) - } + mediaSourceList.setOnClickListener { + selected(it as ImageButton) + style = 0 + layoutText.setText(R.string.list) + run = true } - } - - //Multi download - dialogBinding.downloadNo.text = "0" - dialogBinding.animeDownloadTop.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") - val input = NumberPicker(currContext()) - input.minValue = 1 - input.maxValue = 20 - input.value = 1 - alertDialog.setView(input) - alertDialog.setPositiveButton("OK") { _, _ -> - dialogBinding.downloadNo.text = "${input.value}" + mediaSourceCompact.setOnClickListener { + selected(it as ImageButton) + style = 1 + layoutText.setText(R.string.compact) + run = true } - 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(R.id.checkboxContainer) - val tickAllButton = dialogView2.findViewById(R.id.toggleButton) - - // Function to get the right image resource for the toggle button - fun getToggleImageResource(container: ViewGroup): Int { - var allChecked = true - var allUnchecked = true - - for (i in 0 until container.childCount) { - val checkBox = container.getChildAt(i) as CheckBox - if (!checkBox.isChecked) { - allChecked = false - } else { - allUnchecked = false - } + mediaWebviewContainer.setOnClickListener { + if (!WebViewUtil.supportsWebView(fragment.requireContext())) { + toast(R.string.webview_not_installed) } - return when { - allChecked -> R.drawable.untick_all_boxes - allUnchecked -> R.drawable.tick_all_boxes - else -> R.drawable.invert_all_boxes + // 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) + .putExtra("url", url) + startActivity(fragment.requireContext(), intent, null) + } } } - // 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)) + // Multi download + downloadNo.text = "0" + mediaDownloadTop.setOnClickListener { + // Alert dialog asking for 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 + setCustomView(input) + setPosButton(R.string.ok) { + downloadNo.text = "${input.value}" } + setNegButton(R.string.cancel) + show() } - - // Set checked if its already selected - if (media.selected!!.scanlators != null) { - checkBox.isChecked = media.selected!!.scanlators?.contains(option) != true - scanlatorSelectionListener?.onScanlatorsSelected() - } else { - checkBox.isChecked = true - } - checkboxContainer.addView(checkBox) } - // Create AlertDialog - val dialog = AlertDialog.Builder(currContext(), R.style.MyPopup) - .setView(dialogView2) - .setPositiveButton("OK") { _, _ -> - hiddenScanlators.clear() - for (i in 0 until checkboxContainer.childCount) { - val checkBox = checkboxContainer.getChildAt(i) as CheckBox + // Scanlator + 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 + + fun getToggleImageResource(container: ViewGroup): Int { + var allChecked = true + var allUnchecked = true + + for (i in 0 until container.childCount) { + val checkBox = container.getChildAt(i) as CheckBox if (!checkBox.isChecked) { - hiddenScanlators.add(checkBox.text.toString()) + allChecked = false + } else { + allUnchecked = false } } - fragment.onScanlatorChange(hiddenScanlators) - scanlatorSelectionListener?.onScanlatorsSelected() + return when { + allChecked -> R.drawable.untick_all_boxes + allUnchecked -> R.drawable.tick_all_boxes + else -> R.drawable.invert_all_boxes + } } - .setNegativeButton("Cancel", null) - .show() - dialog.window?.setDimAmount(0.8f) - - // 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 + + options.forEach { option -> + val checkBox = CheckBox(currContext()).apply { + text = option + setOnCheckedChangeListener { _, _ -> + tickAllButton.setImageResource(getToggleImageResource(checkboxContainer)) + } + } + + if (media.selected!!.scanlators != null) { + checkBox.isChecked = media.selected!!.scanlators?.contains(option) != true + scanlatorSelectionListener?.onScanlatorsSelected() + } else { + checkBox.isChecked = true + } + checkboxContainer.addView(checkBox) } - // Update image resource + 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 + if (!checkBox.isChecked) { + hiddenScanlators.add(checkBox.text.toString()) + } + } + fragment.onScanlatorChange(hiddenScanlators) + scanlatorSelectionListener?.onScanlatorsSelected() + } + setNegButton("Cancel") + }.show() + tickAllButton.setImageResource(getToggleImageResource(checkboxContainer)) - } - } - nestedDialog = AlertDialog.Builder(fragment.requireContext(), R.style.MyPopup) - .setTitle("Options") - .setView(dialogView) - .setPositiveButton("OK") { _, _ -> - if (run) fragment.onIconPressed(style, reversed) - if (dialogBinding.downloadNo.text != "0") { - fragment.multiDownload(dialogBinding.downloadNo.text.toString().toInt()) + tickAllButton.setOnClickListener { + for (i in 0 until checkboxContainer.childCount) { + val checkBox = checkboxContainer.getChildAt(i) as CheckBox + checkBox.isChecked = !checkBox.isChecked + } + tickAllButton.setImageResource(getToggleImageResource(checkboxContainer)) } - if (refresh) fragment.loadChapters(source, true) } - .setNegativeButton("Cancel") { _, _ -> - if (refresh) fragment.loadChapters(source, true) - } - .setOnCancelListener { - if (refresh) fragment.loadChapters(source, true) + + fragment.requireContext().customAlertDialog().apply { + setTitle("Options") + setCustomView(root) + setPosButton("OK") { + if (run) fragment.onIconPressed(style, reversed) + if (downloadNo.text != "0") { + fragment.multiDownload(downloadNo.text.toString().toInt()) + } + if (refresh) fragment.loadChapters(source, true) + } + setNegButton("Cancel") { + if (refresh) fragment.loadChapters(source, true) + } + show() } - .create() - nestedDialog?.show() + } } - //Chapter Handling + // Chapter Handling handleChapters() } @@ -368,7 +353,7 @@ class MangaReadAdapter( subscribe?.enabled(enabled) } - //Chips + // Chips fun updateChips(limit: Int, names: Array, arr: Array, selected: Int = 0) { val binding = _binding if (binding != null) { @@ -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 ) @@ -403,7 +388,7 @@ class MangaReadAdapter( } else { names[last - 1] } - //chip.text = "${names[limit * (position)]} - ${names[last - 1]}" + // chip.text = "${names[limit * (position)]} - ${names[last - 1]}" val chipText = "$startChapterString - $endChapterString" chip.text = chipText chip.setTextColor( @@ -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) } diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt index bce5f312830..be836ad59b9 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt @@ -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,32 +386,30 @@ 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) { - changeUIVisibility(true) - loadChapters(media.selected!!.sourceIndex, true) - } + val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id) { + changeUIVisibility(true) + loadChapters(media.selected!!.sourceIndex, true) + } parentFragmentManager.beginTransaction() .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) .replace(R.id.fragmentExtensionsContainer, fragment) .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) { @@ -584,7 +582,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener { private fun reload() { val selected = model.loadSelected(media) - //Find latest chapter for subscription + // Find latest chapter for subscription selected.latest = media.manga?.chapters?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f selected.latest = @@ -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 { diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt index 86085f58d70..bb28d7aaeee 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt @@ -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", diff --git a/app/src/main/java/ani/dantotsu/media/novel/NovelReadAdapter.kt b/app/src/main/java/ani/dantotsu/media/novel/NovelReadAdapter.kt index 7f9d34f418c..cfaf298febc 100644 --- a/app/src/main/java/ani/dantotsu/media/novel/NovelReadAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/novel/NovelReadAdapter.kt @@ -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() } diff --git a/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt b/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt index c95328f6fbe..5cd1f8add01 100644 --- a/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt @@ -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 { diff --git a/app/src/main/java/ani/dantotsu/media/novel/NovelResponseAdapter.kt b/app/src/main/java/ani/dantotsu/media/novel/NovelResponseAdapter.kt index 92ddc7399a3..7bc304cdbdb 100644 --- a/app/src/main/java/ani/dantotsu/media/novel/NovelResponseAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/novel/NovelResponseAdapter.kt @@ -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,27 +94,22 @@ 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") { _, _ -> - downloadedCheckCallback.deleteDownload(novel) - deleteDownload(novel.link) - snackString("Deleted ${novel.name}") - if (binding.itemEpisodeFiller.text.toString() - .contains("Download", ignoreCase = true) - ) { - binding.itemEpisodeFiller.text = "" + 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}") + if (binding.itemEpisodeFiller.text.toString() + .contains("Download", ignoreCase = true) + ) { + binding.itemEpisodeFiller.text = "" + } } + setNegButton(R.string.no) + show() } - builder.setNegativeButton("No") { _, _ -> - // Do nothing - } - val dialog = builder.show() - dialog.window?.setDimAmount(0.8f) true } } diff --git a/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt b/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt index 994a0c789f8..08109594c4e 100644 --- a/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt @@ -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) diff --git a/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt b/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt index 119f4d2f848..f343d70b5ba 100644 --- a/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt +++ b/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt @@ -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() }, diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt index 29ac59d44b3..856dc0b8e3c 100644 --- a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt @@ -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 diff --git a/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt b/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt index ce647f15a6e..3b9b32d05c7 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/ActivityFragment.kt @@ -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("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("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,14 +106,14 @@ 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( global: Boolean = false, userId: Int? = null, activityId: Int? = null, - filter:Boolean = false + filter: Boolean = false ): List { val res = Anilist.query.getFeed(userId, global, page, activityId)?.data?.page?.activities page += 1 @@ -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 - ) - } - - "MEDIA" -> { - ContextCompat.startActivity( - requireContext(), Intent(requireContext(), MediaDetailsActivity::class.java) - .putExtra("mediaId", id), null - ) - } + val intent = when (type) { + "USER" -> Intent(requireContext(), ProfileActivity::class.java).putExtra("userId", id) + "MEDIA" -> Intent( + requireContext(), + MediaDetailsActivity::class.java + ).putExtra("mediaId", id) + + 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) diff --git a/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt b/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt index 9dd423dab0d..bca8609818a 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/ActivityItem.kt @@ -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() { 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) diff --git a/app/src/main/java/ani/dantotsu/profile/activity/ActivityReplyItem.kt b/app/src/main/java/ani/dantotsu/profile/activity/ActivityReplyItem.kt index 1f9039d1ca0..073ada94cd2 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/ActivityReplyItem.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/ActivityReplyItem.kt @@ -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) diff --git a/app/src/main/java/ani/dantotsu/home/status/RepliesBottomDialog.kt b/app/src/main/java/ani/dantotsu/profile/activity/RepliesBottomDialog.kt similarity index 86% rename from app/src/main/java/ani/dantotsu/home/status/RepliesBottomDialog.kt rename to app/src/main/java/ani/dantotsu/profile/activity/RepliesBottomDialog.kt index 218bf7fe584..184c7af9b4c 100644 --- a/app/src/main/java/ani/dantotsu/home/status/RepliesBottomDialog.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/RepliesBottomDialog.kt @@ -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 { diff --git a/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt b/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt index e1e77cf39bb..7508903182e 100644 --- a/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/notification/NotificationActivity.kt @@ -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) diff --git a/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt b/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt index 2e6f2d2886c..6360763ddda 100644 --- a/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt +++ b/app/src/main/java/ani/dantotsu/profile/notification/NotificationFragment.kt @@ -46,8 +46,10 @@ class NotificationFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - type = arguments?.getSerializableCompat("type") as NotificationType - getID = arguments?.getInt("id") ?: -1 + arguments?.let { + getID = it.getInt("id") + type = it.getSerializableCompat("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 { diff --git a/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt b/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt index 748e476e5f7..46d42eb9f0d 100644 --- a/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt @@ -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}") - if (currentFragment is SearchQueryHandler) { - currentFragment.notifyDataChanged() + 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() + show() } - val dialog = builder.show() - dialog.window?.setDimAmount(0.8f) } binding.settingsContainer.updateLayoutParams { 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>(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() } - .create() - .show() + setNegButton(R.string.cancel) + 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() + } } } } diff --git a/app/src/main/java/ani/dantotsu/settings/FAQActivity.kt b/app/src/main/java/ani/dantotsu/settings/FAQActivity.kt index 49656f5593a..98a248e26c9 100644 --- a/app/src/main/java/ani/dantotsu/settings/FAQActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/FAQActivity.kt @@ -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) ?: "", diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt index 2e6d650cacd..4d3350e3aa5 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt @@ -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 diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt index 0a66c64886c..c11ec2233c6 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt @@ -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 = diff --git a/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt index 9006228a449..e3999509ba2 100644 --- a/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt @@ -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( - resizeModes, - PrefManager.getVal(PrefName.Resize) - ) { dialog, count -> - PrefManager.setVal(PrefName.Resize, count) - dialog.dismiss() - }.show() - dialog.window?.setDimAmount(0.8f) + customAlertDialog().apply { + setTitle(getString(R.string.default_resize_mode)) + singleChoiceItems( + resizeModes, + PrefManager.getVal(PrefName.Resize) + ) { count -> + PrefManager.setVal(PrefName.Resize, count) + } + 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( - colorsPrimary, - PrefManager.getVal(PrefName.PrimaryColor) - ) { dialog, count -> - PrefManager.setVal(PrefName.PrimaryColor, count) - dialog.dismiss() - updateSubPreview() - }.show() - dialog.window?.setDimAmount(0.8f) + customAlertDialog().apply { + setTitle(getString(R.string.primary_sub_color)) + singleChoiceItems( + colorsPrimary, + PrefManager.getVal(PrefName.PrimaryColor) + ) { count -> + PrefManager.setVal(PrefName.PrimaryColor, count) + updateSubPreview() + } + 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( - colorsSecondary, - PrefManager.getVal(PrefName.SecondaryColor) - ) { dialog, count -> - PrefManager.setVal(PrefName.SecondaryColor, count) - dialog.dismiss() - updateSubPreview() - }.show() - dialog.window?.setDimAmount(0.8f) + customAlertDialog().apply { + setTitle(getString(R.string.outline_sub_color)) + singleChoiceItems( + colorsSecondary, + PrefManager.getVal(PrefName.SecondaryColor) + ) { count -> + PrefManager.setVal(PrefName.SecondaryColor, count) + updateSubPreview() + } + 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( - typesOutline, - PrefManager.getVal(PrefName.Outline) - ) { dialog, count -> - PrefManager.setVal(PrefName.Outline, count) - dialog.dismiss() - updateSubPreview() - }.show() - dialog.window?.setDimAmount(0.8f) + customAlertDialog().apply { + setTitle(getString(R.string.outline_type)) + singleChoiceItems( + typesOutline, + PrefManager.getVal(PrefName.Outline) + ) { count -> + PrefManager.setVal(PrefName.Outline, count) + updateSubPreview() + } + 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( - colorsSubBackground, - PrefManager.getVal(PrefName.SubBackground) - ) { dialog, count -> - PrefManager.setVal(PrefName.SubBackground, count) - dialog.dismiss() - updateSubPreview() - }.show() - dialog.window?.setDimAmount(0.8f) + customAlertDialog().apply { + setTitle(getString(R.string.sub_background_color_select)) + singleChoiceItems( + colorsSubBackground, + PrefManager.getVal(PrefName.SubBackground) + ) { count -> + PrefManager.setVal(PrefName.SubBackground, count) + updateSubPreview() + } + 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( - colorsSubWindow, - PrefManager.getVal(PrefName.SubWindow) - ) { dialog, count -> - PrefManager.setVal(PrefName.SubWindow, count) - dialog.dismiss() - updateSubPreview() - }.show() - dialog.window?.setDimAmount(0.8f) + customAlertDialog().apply { + setTitle(getString(R.string.sub_window_color_select)) + singleChoiceItems( + colorsSubWindow, + PrefManager.getVal(PrefName.SubWindow) + ) { count -> + PrefManager.setVal(PrefName.SubWindow, count) + updateSubPreview() + } + 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( - fonts, - PrefManager.getVal(PrefName.Font) - ) { dialog, count -> - PrefManager.setVal(PrefName.Font, count) - dialog.dismiss() - updateSubPreview() - }.show() - dialog.window?.setDimAmount(0.8f) + customAlertDialog().apply { + setTitle(getString(R.string.subtitle_font)) + singleChoiceItems( + fonts, + PrefManager.getVal(PrefName.Font) + ) { count -> + PrefManager.setVal(PrefName.Font, count) + updateSubPreview() + } + show() + } } binding.subtitleFontSize.setText(PrefManager.getVal(PrefName.FontSize).toString()) binding.subtitleFontSize.setOnEditorActionListener { _, actionId, _ -> diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt index c8dc29d5518..dea8666c14e 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt @@ -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(R.id.passwordInput) - val confirmPasswordInput = - dialog.findViewById(R.id.confirmPasswordInput) - val forgotPasswordCheckbox = - dialog.findViewById(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(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(R.id.passwordInput) - ?.requestFocus() - val biometricCheckbox = - passwordDialog.findViewById(R.id.biometricCheckbox) - val forgotPasswordCheckbox = - passwordDialog.findViewById(R.id.forgotPasswordCheckbox) - val canAuthenticate = - BiometricManager.from(applicationContext).canAuthenticate( - BiometricManager.Authenticators.BIOMETRIC_WEAK - ) == BiometricManager.BIOMETRIC_SUCCESS - biometricCheckbox?.isVisible = canAuthenticate - biometricCheckbox?.isChecked = - PrefManager.getVal(PrefName.BiometricToken, "").isNotEmpty() - forgotPasswordCheckbox?.isChecked = - PrefManager.getVal(PrefName.OverridePassword) + setOnShowListener { + view.passwordInput.requestFocus() + val canAuthenticate = + BiometricManager.from(applicationContext).canAuthenticate( + BiometricManager.Authenticators.BIOMETRIC_WEAK + ) == BiometricManager.BIOMETRIC_SUCCESS + view.biometricCheckbox.isVisible = canAuthenticate + view.biometricCheckbox.isChecked = + PrefManager.getVal(PrefName.BiometricToken, "").isNotEmpty() + view.forgotPasswordCheckbox.isChecked = + PrefManager.getVal(PrefName.OverridePassword) + } + show() } - passwordDialog.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(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), diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt index abf742ba391..a054b89aeea 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt @@ -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>(repoList).minus(item) + context.customAlertDialog().apply { + setTitle(R.string.rem_repository) + setMessage(item) + setPosButton(R.string.ok) { + val repos = PrefManager.getVal>(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( + dialog, + editText, + MediaType.MANGA, + it.attachView + ) + } + }.show() - processEditorAction( - alertDialog, - editText, - MediaType.MANGA, - it.attachView - ) - alertDialog.show() - alertDialog.window?.setDimAmount(0.8f) }, 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(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( diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt index b97dfa4cd4e..c7a9b1e63c6 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt @@ -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( diff --git a/app/src/main/java/ani/dantotsu/settings/SubscriptionItem.kt b/app/src/main/java/ani/dantotsu/settings/SubscriptionItem.kt index bb8c0396292..bb60cd4e30c 100644 --- a/app/src/main/java/ani/dantotsu/settings/SubscriptionItem.kt +++ b/app/src/main/java/ani/dantotsu/settings/SubscriptionItem.kt @@ -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() { 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) } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/settings/SubscriptionSource.kt b/app/src/main/java/ani/dantotsu/settings/SubscriptionSource.kt new file mode 100644 index 00000000000..80975159d95 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/settings/SubscriptionSource.kt @@ -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, + private val adapter: GroupieAdapter, + private var parserIcon: Drawable? = null, + private val onGroupRemoved: (SubscriptionSource) -> Unit +) : BindableItem() { + 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) +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt b/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt index 2b8331149a8..93fd58dbb13 100644 --- a/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt +++ b/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt @@ -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 = 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 } } diff --git a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt index 8d1a958261e..08b6345e5e6 100644 --- a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt @@ -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>(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>(PrefName.HomeLayout).toBooleanArray() - ) { _, i, value -> - set[i] = value + customAlertDialog().apply { + setTitle(getString(R.string.home_layout_show)) + multiChoiceItems( + items = views, + checkedItems = PrefManager.getVal>(PrefName.HomeLayout).toBooleanArray() + ) { selectedItems -> + for (i in selectedItems.indices) { + set[i] = selectedItems[i] } - setPositiveButton("Done") { _, _ -> - PrefManager.setVal(PrefName.HomeLayout, set) - restartApp() - } - }.show() - dialog.window?.setDimAmount(0.8f) + } + setPosButton(R.string.ok) { + PrefManager.setVal(PrefName.HomeLayout, set) + restartApp() + } + show() + } + } binding.uiSettingsSmallView.isChecked = PrefManager.getVal(PrefName.SmallView) diff --git a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt index b4e43ac872f..6f188b854c3 100644 --- a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt +++ b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt @@ -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, diff --git a/app/src/main/java/ani/dantotsu/util/MarkdownCreatorActivity.kt b/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt similarity index 83% rename from app/src/main/java/ani/dantotsu/util/MarkdownCreatorActivity.kt rename to app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt index a66dba939bb..ccddfbab335 100644 --- a/app/src/main/java/ani/dantotsu/util/MarkdownCreatorActivity.kt +++ b/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt @@ -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" -> { - parentId = intent.getIntExtra("parentId", -1) - if (parentId == -1) { - toast("Error: No parent ID") - finish() - return - } - getString(R.string.create_new_reply) + val editId = intent.getIntExtra("edit", -1) + val userId = intent.getIntExtra("userId", -1) + parentId = intent.getIntExtra("parentId", -1) + when (type) { + "replyActivity" -> if (parentId == -1) { + toast("Error: No parent ID") + finish() + return } - else -> "" + "message" -> { + if (editId == -1) { + binding.privateCheckbox.visibility = ViewGroup.VISIBLE + } + } } + var private = false + binding.privateCheckbox.setOnCheckedChangeListener { _, isChecked -> + private = isChecked + } + 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 - Anilist.mutation.postMessage(userId , text, editId) + "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() } diff --git a/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt b/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt index 14e71432fd8..374eb3d142f 100644 --- a/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt +++ b/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt @@ -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, - selectedItemIndex: Int = -1, - onItemSelected: (Int) -> Unit - ): AlertDialogBuilder { + fun onDismiss(onDismiss: (() -> Unit)? = null): AlertDialogBuilder { + this.onDismiss = onDismiss + return this + } + + fun singleChoiceItems(items: Array, selectedItemIndex: Int = -1, onItemSelected: (Int) -> Unit): AlertDialogBuilder { this.items = items this.selectedItemIndex = selectedItemIndex this.onItemSelected = onItemSelected return this } - fun multiChoiceItems( - items: Array, - checkedItems: BooleanArray? = null, - onItemsSelected: (BooleanArray) -> Unit - ): AlertDialogBuilder { + fun multiChoiceItems(items: Array, 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,15 +167,25 @@ 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 { return AlertDialogBuilder(this) -} \ No newline at end of file +} diff --git a/app/src/main/java/ani/dantotsu/util/StoragePermissions.kt b/app/src/main/java/ani/dantotsu/util/StoragePermissions.kt index 8291c567a83..ee2a9aa85a7 100644 --- a/app/src/main/java/ani/dantotsu/util/StoragePermissions.kt +++ b/app/src/main/java/ani/dantotsu/util/StoragePermissions.kt @@ -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, _ -> - launcher.registerForCallback(complete) - launcher.launch() - dialog.dismiss() - } - builder.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - complete(false) - } - val dialog = builder.show() - dialog.window?.setDimAmount(0.8f) + 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() + } + setNegButton(getString(R.string.cancel)) { + complete(false) + } + }.show() } private fun pathToUri(path: String): Uri { diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt index c67eefeaacf..d42ece546d1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt @@ -64,10 +64,6 @@ internal class ExtensionGithubApi { val repos = PrefManager.getVal>(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>(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) { diff --git a/app/src/main/res/drawable/ic_round_history_24.xml b/app/src/main/res/drawable/ic_round_history_24.xml new file mode 100644 index 00000000000..1d5164d3409 --- /dev/null +++ b/app/src/main/res/drawable/ic_round_history_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_round_library_books_24.xml b/app/src/main/res/drawable/ic_round_library_books_24.xml new file mode 100644 index 00000000000..ee9f6511453 --- /dev/null +++ b/app/src/main/res/drawable/ic_round_library_books_24.xml @@ -0,0 +1,11 @@ + + + + diff --git a/app/src/main/res/layout/activity_list.xml b/app/src/main/res/layout/activity_list.xml index 63aa5d81521..d70668fcd5a 100644 --- a/app/src/main/res/layout/activity_list.xml +++ b/app/src/main/res/layout/activity_list.xml @@ -55,6 +55,15 @@ app:srcCompat="@drawable/ic_round_search_24" app:tint="?attr/colorOnBackground" /> + + - + android:fitsSystemWindows="false" + android:orientation="vertical" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + android:layout_height="48dp" + android:orientation="horizontal" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> - + - + - + -