diff --git a/app/build.gradle b/app/build.gradle
index ed1679cd..a95c9e55 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -11,7 +11,7 @@ android {
defaultConfig {
applicationId "ani.dantotsu"
- minSdk 23
+ minSdk 21
targetSdk 34
versionCode((System.currentTimeMillis() / 60000).toInteger())
versionName "2.2.0"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5883981b..2b9f5cad 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -261,6 +261,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
true
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ val cap = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
+ return@tryWith if (cap != null) {
+ when {
+ cap.hasTransport(TRANSPORT_BLUETOOTH) ||
+ cap.hasTransport(TRANSPORT_CELLULAR) ||
+ cap.hasTransport(TRANSPORT_ETHERNET) ||
+ cap.hasTransport(TRANSPORT_LOWPAN) ||
+ cap.hasTransport(TRANSPORT_USB) ||
+ cap.hasTransport(TRANSPORT_VPN) ||
+ cap.hasTransport(TRANSPORT_WIFI) ||
+ cap.hasTransport(TRANSPORT_WIFI_AWARE) -> true
- else -> false
- }
- } else false
+ else -> false
+ }
+ } else false
+ } else {
+ @Suppress("DEPRECATION")
+ return@tryWith connectivityManager.activeNetworkInfo?.run {
+ type == ConnectivityManager.TYPE_BLUETOOTH ||
+ type == ConnectivityManager.TYPE_ETHERNET ||
+ type == ConnectivityManager.TYPE_MOBILE ||
+ type == ConnectivityManager.TYPE_MOBILE_DUN ||
+ type == ConnectivityManager.TYPE_MOBILE_HIPRI ||
+ type == ConnectivityManager.TYPE_WIFI ||
+ type == ConnectivityManager.TYPE_WIMAX ||
+ type == ConnectivityManager.TYPE_VPN
+ } ?: false
+ }
} ?: false
}
diff --git a/app/src/main/java/ani/dantotsu/MainActivity.kt b/app/src/main/java/ani/dantotsu/MainActivity.kt
index 4268aac0..d692f9b7 100644
--- a/app/src/main/java/ani/dantotsu/MainActivity.kt
+++ b/app/src/main/java/ani/dantotsu/MainActivity.kt
@@ -2,6 +2,7 @@ package ani.dantotsu
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
+import android.app.AlertDialog
import android.content.Intent
import android.content.res.Configuration
import android.content.res.Resources
@@ -14,6 +15,7 @@ import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.util.TypedValue
+import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnticipateInterpolator
@@ -28,6 +30,7 @@ import androidx.core.content.ContextCompat
import androidx.core.view.doOnAttach
import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMargins
+import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
@@ -47,17 +50,21 @@ import ani.dantotsu.home.MangaFragment
import ani.dantotsu.home.NoInternet
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.others.CustomBottomDialog
+import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.activity.FeedActivity
import ani.dantotsu.profile.activity.NotificationActivity
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefManager.asLiveBool
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.settings.saving.SharedPreferenceBooleanLiveData
+import ani.dantotsu.settings.saving.internal.PreferenceKeystore
+import ani.dantotsu.settings.saving.internal.PreferencePackager
import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.Logger
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
+import com.google.android.material.textfield.TextInputEditText
import eu.kanade.domain.source.service.SourcePreferences
import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin
@@ -91,6 +98,60 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
+ val action = intent.action
+ val type = intent.type
+ if (Intent.ACTION_VIEW == action && type != null) {
+ val uri: Uri? = intent.data
+ try {
+ if (uri == null) {
+ throw Exception("Uri is null")
+ }
+ val jsonString =
+ contentResolver.openInputStream(uri)?.readBytes()
+ ?: throw Exception("Error reading file")
+ val name =
+ DocumentFile.fromSingleUri(this, uri)?.name ?: "settings"
+ //.sani is encrypted, .ani is not
+ if (name.endsWith(".sani")) {
+ passwordAlertDialog { password ->
+ if (password != null) {
+ val salt = jsonString.copyOfRange(0, 16)
+ val encrypted = jsonString.copyOfRange(16, jsonString.size)
+ val decryptedJson = try {
+ PreferenceKeystore.decryptWithPassword(
+ password,
+ encrypted,
+ salt
+ )
+ } catch (e: Exception) {
+ toast("Incorrect password")
+ return@passwordAlertDialog
+ }
+ if (PreferencePackager.unpack(decryptedJson)) {
+ val intent = Intent(this, this.javaClass)
+ this.finish()
+ startActivity(intent)
+ }
+ } else {
+ toast("Password cannot be empty")
+ }
+ }
+ } else if (name.endsWith(".ani")) {
+ val decryptedJson = jsonString.toString(Charsets.UTF_8)
+ if (PreferencePackager.unpack(decryptedJson)) {
+ val intent = Intent(this, this.javaClass)
+ this.finish()
+ startActivity(intent)
+ }
+ } else {
+ toast("Invalid file type")
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ toast("Error importing settings")
+ }
+ }
+
val _bottomBar = findViewById(R.id.navbar)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -214,7 +275,7 @@ class MainActivity : AppCompatActivity() {
binding.root.doOnAttach {
initActivity(this)
- window.navigationBarColor = getColor(android.R.color.transparent)
+ window.navigationBarColor = ContextCompat.getColor(this, android.R.color.transparent)
selectedOption = if (fragment != null) {
when (fragment) {
AnimeFragment::class.java.name -> 0
@@ -329,6 +390,22 @@ class MainActivity : AppCompatActivity() {
snackString(this@MainActivity.getString(R.string.anilist_not_found))
}
}
+ val username = intent.extras?.getString("username")
+ if (username != null) {
+ val nameInt = username.toIntOrNull()
+ if (nameInt != null) {
+ startActivity(
+ Intent(this@MainActivity, ProfileActivity::class.java)
+ .putExtra("userId", nameInt)
+ )
+ } else {
+ startActivity(
+ Intent(this@MainActivity, ProfileActivity::class.java)
+ .putExtra("username", username)
+ )
+ }
+ }
+
delay(500)
startSubscription()
}
@@ -381,7 +458,7 @@ class MainActivity : AppCompatActivity() {
override fun onRestart() {
super.onRestart()
- window.navigationBarColor = getColor(android.R.color.transparent)
+ window.navigationBarColor = ContextCompat.getColor(this, android.R.color.transparent)
}
private val Int.toPx get() = TypedValue.applyDimension(
@@ -398,6 +475,44 @@ class MainActivity : AppCompatActivity() {
params.updateMargins(bottom = 32.toPx)
}
+ private fun passwordAlertDialog(callback: (CharArray?) -> Unit) {
+ val password = CharArray(16).apply { fill('0') }
+
+ // Inflate the dialog layout
+ val dialogView =
+ LayoutInflater.from(this).inflate(R.layout.dialog_user_agent, null)
+ dialogView.findViewById(R.id.userAgentTextBox)?.hint = "Password"
+ val subtitleTextView = dialogView.findViewById(R.id.subtitle)
+ subtitleTextView?.visibility = View.VISIBLE
+ subtitleTextView?.text = "Enter your password to decrypt the file"
+
+ val dialog = AlertDialog.Builder(this, R.style.MyPopup)
+ .setTitle("Enter Password")
+ .setView(dialogView)
+ .setPositiveButton("OK", null)
+ .setNegativeButton("Cancel") { dialog, _ ->
+ password.fill('0')
+ dialog.dismiss()
+ callback(null)
+ }
+ .create()
+
+ dialog.window?.setDimAmount(0.8f)
+ dialog.show()
+
+ // Override the positive button here
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
+ val editText = dialog.findViewById(R.id.userAgentTextBox)
+ if (editText?.text?.isNotBlank() == true) {
+ editText.text?.toString()?.trim()?.toCharArray(password)
+ dialog.dismiss()
+ callback(password)
+ } else {
+ toast("Password cannot be empty")
+ }
+ }
+ }
+
//ViewPager
private class ViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fragmentManager, lifecycle) {
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 68f18f39..6b1bc6d7 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt
@@ -54,6 +54,8 @@ class AnilistQueries {
Anilist.chapterRead = user.statistics?.manga?.chaptersRead
Anilist.adult = user.options?.displayAdultContent ?: false
Anilist.unreadNotificationCount = user.unreadNotificationCount ?: 0
+ val unread = PrefManager.getVal(PrefName.UnreadCommentNotifications)
+ Anilist.unreadNotificationCount += unread
return true
}
@@ -1345,6 +1347,18 @@ Page(page:$page,perPage:50) {
)
}
+ suspend fun getUserProfile(username: String): Query.UserProfileResponse? {
+ val id = getUserId(username) ?: return null
+ return getUserProfile(id)
+ }
+
+ suspend fun getUserId(username: String): Int? {
+ return executeQuery(
+ """{User(name:"$username"){id}}""",
+ force = true
+ )?.data?.user?.id
+ }
+
suspend fun getUserStatistics(id: Int, sort: String = "ID"): Query.StatisticsResponse? {
return executeQuery(
"""{User(id:$id){id name mediaListOptions{scoreFormat}statistics{anime{...UserStatistics}manga{...UserStatistics}}}}fragment UserStatistics on UserStatistics{count meanScore standardDeviation minutesWatched episodesWatched chaptersRead volumesRead formats(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds format}statuses(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds status}scores(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds score}lengths(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds length}releaseYears(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds releaseYear}startYears(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds startYear}genres(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds genre}tags(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds tag{id name}}countries(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds country}voiceActors(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds voiceActor{id name{first middle last full native alternative userPreferred}}characterIds}staff(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds staff{id name{first middle last full native alternative userPreferred}}}studios(sort:$sort){count meanScore minutesWatched chaptersRead mediaIds studio{id name isAnimationStudio}}}""",
@@ -1392,7 +1406,10 @@ Page(page:$page,perPage:50) {
"""{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_PAGE){notifications(resetNotificationCount:$reset){__typename...on AiringNotification{id,type,animeId,episode,contexts,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}},}...on FollowingNotification{id,userId,type,context,createdAt,user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMessageNotification{id,userId,type,activityId,context,createdAt,message{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMentionNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplySubscribedNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentMentionNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentReplyNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentSubscribedNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentLikeNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadLikeNotification{id,userId,type,threadId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on RelatedMediaAdditionNotification{id,type,context,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDataChangeNotification{id,type,mediaId,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaMergeNotification{id,type,mediaId,deletedMediaTitles,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDeletionNotification{id,type,deletedMediaTitle,context,reason,createdAt,}}}}""",
force = true
)
- if (res != null) {
+ if (res != null && resetNotification) {
+ val commentNotifications = PrefManager.getVal(PrefName.UnreadCommentNotifications, 0)
+ res.data.user.unreadNotificationCount += commentNotifications
+ PrefManager.setVal(PrefName.UnreadCommentNotifications, 0)
Anilist.unreadNotificationCount = 0
}
return res
@@ -1404,7 +1421,8 @@ Page(page:$page,perPage:50) {
else if (global) "isFollowing:false,hasRepliesOrTypeText:true,"
else "isFollowing:true,type_not:MESSAGE,"
return executeQuery(
- """{Page(page:$page,perPage:$ITEMS_PER_PAGE){activities(${filter}sort:ID_DESC){__typename ... on TextActivity{id userId type replyCount text(asHtml:true)siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on ListActivity{id userId type replyCount status progress siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}media{id title{english romaji native userPreferred}bannerImage coverImage{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on MessageActivity{id recipientId messengerId type replyCount likeCount message(asHtml:true)isLocked isSubscribed isLiked isPrivate siteUrl createdAt recipient{id name bannerImage avatar{medium large}}messenger{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}}}}"""
+ """{Page(page:$page,perPage:$ITEMS_PER_PAGE){activities(${filter}sort:ID_DESC){__typename ... on TextActivity{id userId type replyCount text(asHtml:true)siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on ListActivity{id userId type replyCount status progress siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}media{id title{english romaji native userPreferred}bannerImage coverImage{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on MessageActivity{id recipientId messengerId type replyCount likeCount message(asHtml:true)isLocked isSubscribed isLiked isPrivate siteUrl createdAt recipient{id name bannerImage avatar{medium large}}messenger{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}}}}""",
+ force = true
)
}
diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt b/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt
index 98d63d27..7df91d75 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt
@@ -11,20 +11,27 @@ import ani.dantotsu.themes.ThemeManager
class UrlMedia : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
-
ThemeManager(this).applyTheme()
- var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
- var isMAL = false
- var continueMedia = true
- if (id == 0) {
- continueMedia = false
- val data: Uri? = intent?.data
- isMAL = data?.host != "anilist.co"
- id = data?.pathSegments?.getOrNull(1)?.toIntOrNull()
- } else loadMedia = id
- startMainActivity(
- this,
- bundleOf("mediaId" to id, "mal" to isMAL, "continue" to continueMedia)
- )
+ val data: Uri? = intent?.data
+ val type = data?.pathSegments?.getOrNull(0)
+ if (type == "anime" || type == "manga") {
+ var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
+ var isMAL = false
+ var continueMedia = true
+ if (id == 0) {
+ continueMedia = false
+ isMAL = data.host != "anilist.co"
+ id = data.pathSegments?.getOrNull(1)?.toIntOrNull()
+ } else loadMedia = id
+ startMainActivity(
+ this,
+ bundleOf("mediaId" to id, "mal" to isMAL, "continue" to continueMedia)
+ )
+ } else if (type == "user") {
+ val username = data.pathSegments?.getOrNull(1)
+ startMainActivity(this, bundleOf("username" to username))
+ } else {
+ startMainActivity(this)
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Notification.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Notification.kt
index 073fadc1..f526078a 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Notification.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Notification.kt
@@ -20,7 +20,9 @@ enum class NotificationType(val value: String) {
RELATED_MEDIA_ADDITION("RELATED_MEDIA_ADDITION"),
MEDIA_DATA_CHANGE("MEDIA_DATA_CHANGE"),
MEDIA_MERGE("MEDIA_MERGE"),
- MEDIA_DELETION("MEDIA_DELETION")
+ MEDIA_DELETION("MEDIA_DELETION"),
+ //custom
+ COMMENT_REPLY("COMMENT_REPLY"),
}
@Serializable
@@ -40,7 +42,7 @@ data class NotificationResponse(
@Serializable
data class NotificationUser(
@SerialName("unreadNotificationCount")
- val unreadNotificationCount: Int,
+ var unreadNotificationCount: Int,
) : java.io.Serializable
@Serializable
@@ -56,41 +58,41 @@ data class Notification(
@SerialName("id")
val id: Int,
@SerialName("userId")
- val userId: Int?,
+ val userId: Int? = null,
@SerialName("CommentId")
val commentId: Int?,
@SerialName("type")
val notificationType: String,
@SerialName("activityId")
- val activityId: Int?,
+ val activityId: Int? = null,
@SerialName("animeId")
- val mediaId: Int?,
+ val mediaId: Int? = null,
@SerialName("episode")
- val episode: Int?,
+ val episode: Int? = null,
@SerialName("contexts")
- val contexts: List?,
+ val contexts: List? = null,
@SerialName("context")
- val context: String?,
+ val context: String? = null,
@SerialName("reason")
- val reason: String?,
+ val reason: String? = null,
@SerialName("deletedMediaTitle")
- val deletedMediaTitle: String?,
+ val deletedMediaTitle: String? = null,
@SerialName("deletedMediaTitles")
- val deletedMediaTitles: List?,
+ val deletedMediaTitles: List? = null,
@SerialName("createdAt")
val createdAt: Int,
@SerialName("media")
- val media: ani.dantotsu.connections.anilist.api.Media?,
+ val media: ani.dantotsu.connections.anilist.api.Media? = null,
@SerialName("user")
- val user: ani.dantotsu.connections.anilist.api.User?,
+ val user: ani.dantotsu.connections.anilist.api.User? = null,
@SerialName("message")
- val message: MessageActivity?,
+ val message: MessageActivity? = null,
@SerialName("activity")
- val activity: ActivityUnion?,
+ val activity: ActivityUnion? = null,
@SerialName("Thread")
- val thread: Thread?,
+ val thread: Thread? = null,
@SerialName("comment")
- val comment: ThreadComment?,
+ val comment: ThreadComment? = null,
) : java.io.Serializable
@Serializable
diff --git a/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt b/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt
index e06af9e4..dc9bf872 100644
--- a/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt
+++ b/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt
@@ -31,12 +31,15 @@ object CommentsAPI {
var isMod: Boolean = false
var totalVotes: Int = 0
- suspend fun getCommentsForId(id: Int, page: Int = 1, tag: Int?): CommentResponse? {
+ suspend fun getCommentsForId(id: Int, page: Int = 1, tag: Int?, sort: String?): CommentResponse? {
var url = "$address/comments/$id/$page"
val request = requestBuilder()
tag?.let {
url += "?tag=$it"
}
+ sort?.let {
+ url += if (tag != null) "&sort=$it" else "?sort=$it"
+ }
val json = try {
request.get(url)
} catch (e: IOException) {
diff --git a/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt
index 8b19d9ab..22b20dcb 100644
--- a/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt
@@ -54,6 +54,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
+import tachiyomi.core.util.lang.launchIO
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
@@ -355,15 +356,13 @@ class AnimeDownloaderService : Service() {
return false
}
- @OptIn(DelicateCoroutinesApi::class)
private fun saveMediaInfo(task: AnimeDownloadTask) {
- GlobalScope.launch(Dispatchers.IO) {
+ launchIO {
val directory = File(
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"${DownloadsManager.animeLocation}/${task.title}"
)
val episodeDirectory = File(directory, task.episode)
- if (!directory.exists()) directory.mkdirs()
if (!episodeDirectory.exists()) episodeDirectory.mkdirs()
val file = File(directory, "media.json")
diff --git a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
index cad2f85e..188c9dc3 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
@@ -37,6 +37,7 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SChapterImpl
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
@@ -47,6 +48,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
+import tachiyomi.core.util.lang.launchIO
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
@@ -287,8 +289,9 @@ class MangaDownloaderService : Service() {
}
}
+ @OptIn(DelicateCoroutinesApi::class)
private fun saveMediaInfo(task: DownloadTask) {
- GlobalScope.launch(Dispatchers.IO) {
+ launchIO {
val directory = File(
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Manga/${task.title}"
diff --git a/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt
index 06043976..0c3575a3 100644
--- a/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt
@@ -31,6 +31,7 @@ import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SChapterImpl
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
@@ -42,6 +43,7 @@ import kotlinx.coroutines.withContext
import okhttp3.Request
import okio.buffer
import okio.sink
+import tachiyomi.core.util.lang.launchIO
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
@@ -347,8 +349,9 @@ class NovelDownloaderService : Service() {
}
}
+ @OptIn(DelicateCoroutinesApi::class)
private fun saveMediaInfo(task: DownloadTask) {
- GlobalScope.launch(Dispatchers.IO) {
+ launchIO {
val directory = File(
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Novel/${task.title}"
diff --git a/app/src/main/java/ani/dantotsu/home/LoginFragment.kt b/app/src/main/java/ani/dantotsu/home/LoginFragment.kt
index 8efa8ac3..5f89464e 100644
--- a/app/src/main/java/ani/dantotsu/home/LoginFragment.kt
+++ b/app/src/main/java/ani/dantotsu/home/LoginFragment.kt
@@ -17,6 +17,7 @@ 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 com.google.android.material.textfield.TextInputEditText
class LoginFragment : Fragment() {
@@ -50,7 +51,7 @@ class LoginFragment : Fragment() {
DocumentFile.fromSingleUri(requireActivity(), uri)?.name ?: "settings"
//.sani is encrypted, .ani is not
if (name.endsWith(".sani")) {
- passwordAlertDialog() { password ->
+ passwordAlertDialog { password ->
if (password != null) {
val salt = jsonString.copyOfRange(0, 16)
val encrypted = jsonString.copyOfRange(16, jsonString.size)
@@ -78,7 +79,7 @@ class LoginFragment : Fragment() {
toast("Invalid file type")
}
} catch (e: Exception) {
- e.printStackTrace()
+ Logger.log(e)
toast("Error importing settings")
}
}
diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt
index a30da85f..ad8c378e 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeNameAdapter.kt
@@ -7,7 +7,7 @@ import java.util.regex.Pattern
class AnimeNameAdapter {
companion object {
const val episodeRegex =
- "(episode|ep|e)[\\s:.\\-]*([\\d]+\\.?[\\d]*)[\\s:.\\-]*\\(?\\s*(sub|subbed|dub|dubbed)*\\s*\\)?\\s*"
+ "(episode|episodio|ep|e)[\\s:.\\-]*([\\d]+\\.?[\\d]*)[\\s:.\\-]*\\(?\\s*(sub|subbed|dub|dubbed)*\\s*\\)?\\s*"
const val failedEpisodeNumberRegex =
"(? "lowest_rated"
else -> return@setOnMenuItemClickListener false
}
-
PrefManager.setVal(PrefName.CommentSortOrder, sortOrder)
- sortComments(sortOrder)
+ if (totalPages > pagesLoaded) {
+ lifecycleScope.launch {
+ loadAndDisplayComments()
+ activity.binding.commentReplyToContainer.visibility = View.GONE
+ }
+ } else {
+ sortComments(sortOrder)
+ }
binding.commentsList.scrollToPosition(0)
true
}
@@ -197,7 +202,8 @@ class CommentsFragment : Fragment() {
}
}
} else {
- snackString("No more comments")
+ //snackString("No more comments") fix spam?
+ Logger.log("No more comments")
}
}
}
@@ -219,7 +225,12 @@ class CommentsFragment : Fragment() {
private suspend fun fetchComments(): CommentResponse? {
return withContext(Dispatchers.IO) {
- CommentsAPI.getCommentsForId(mediaId, pagesLoaded + 1, filterTag)
+ CommentsAPI.getCommentsForId(
+ mediaId,
+ pagesLoaded + 1,
+ filterTag,
+ PrefManager.getVal(PrefName.CommentSortOrder, "newest")
+ )
}
}
@@ -253,7 +264,7 @@ class CommentsFragment : Fragment() {
300,
activity.binding.commentInput.text.length
)
- snackString("CommentNotificationWorker cannot be longer than 300 characters")
+ snackString("Comment cannot be longer than 300 characters")
}
}
})
@@ -377,7 +388,11 @@ class CommentsFragment : Fragment() {
section.clear()
val comments = withContext(Dispatchers.IO) {
- CommentsAPI.getCommentsForId(mediaId, tag = filterTag)
+ CommentsAPI.getCommentsForId(
+ mediaId,
+ tag = filterTag,
+ sort = PrefManager.getVal(PrefName.CommentSortOrder, "newest")
+ )
}
val sortedComments = sortComments(comments?.comments)
@@ -460,6 +475,7 @@ class CommentsFragment : Fragment() {
}
InteractionState.REPLY -> {
+ activity.binding.commentReplyToContainer.visibility = View.GONE
activity.binding.commentInput.setText("")
val imm = activity.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(activity.binding.commentInput.windowToken, 0)
@@ -587,7 +603,7 @@ class CommentsFragment : Fragment() {
private fun processComment() {
val commentText = activity.binding.commentInput.text.toString()
if (commentText.isEmpty()) {
- snackString("CommentNotificationWorker cannot be empty")
+ snackString("Comment cannot be empty")
return
}
@@ -604,6 +620,7 @@ class CommentsFragment : Fragment() {
null
)
}
+ resetOldState()
}
}
@@ -623,7 +640,7 @@ class CommentsFragment : Fragment() {
groups.forEach { item ->
if (item is CommentItem && item.comment.commentId == commentWithInteraction?.comment?.commentId) {
updateCommentItem(item, commentText)
- snackString("CommentNotificationWorker edited")
+ snackString("Comment edited")
}
}
}
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 9cbffc50..8da7c35b 100644
--- a/app/src/main/java/ani/dantotsu/media/novel/NovelResponseAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/novel/NovelResponseAdapter.kt
@@ -5,6 +5,7 @@ import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R
import ani.dantotsu.databinding.ItemNovelResponseBinding
@@ -59,11 +60,11 @@ class NovelResponseAdapter(
}
if (binding.itemEpisodeFiller.text.contains("Downloading")) {
binding.itemEpisodeFiller.setTextColor(
- fragment.requireContext().getColor(android.R.color.holo_blue_light)
+ ContextCompat.getColor(fragment.requireContext(), android.R.color.holo_blue_light)
)
} else if (binding.itemEpisodeFiller.text.contains("Downloaded")) {
binding.itemEpisodeFiller.setTextColor(
- fragment.requireContext().getColor(android.R.color.holo_green_light)
+ ContextCompat.getColor(fragment.requireContext(), android.R.color.holo_green_light)
)
} else {
binding.itemEpisodeFiller.setTextColor(color)
diff --git a/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationWorker.kt b/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationWorker.kt
index eb7d776c..50a9befb 100644
--- a/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationWorker.kt
+++ b/app/src/main/java/ani/dantotsu/notifications/anilist/AnilistNotificationWorker.kt
@@ -16,6 +16,7 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.profile.activity.ActivityItemBuilder
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
+import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.data.notification.Notifications
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -78,12 +79,13 @@ class AnilistNotificationWorker(appContext: Context, workerParams: WorkerParamet
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
putExtra("FRAGMENT_TO_LOAD", "NOTIFICATIONS")
if (notificationId != null) {
+ Logger.log("notificationId: $notificationId")
putExtra("activityId", notificationId)
}
}
val pendingIntent = PendingIntent.getActivity(
applicationContext,
- 0,
+ notificationId ?: 0,
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
diff --git a/app/src/main/java/ani/dantotsu/notifications/CommentNotificationWorker.kt b/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationWorker.kt
similarity index 82%
rename from app/src/main/java/ani/dantotsu/notifications/CommentNotificationWorker.kt
rename to app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationWorker.kt
index 7f0d6d5e..5ecc65db 100644
--- a/app/src/main/java/ani/dantotsu/notifications/CommentNotificationWorker.kt
+++ b/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationWorker.kt
@@ -1,4 +1,4 @@
-package ani.dantotsu.notifications
+package ani.dantotsu.notifications.comment
import android.Manifest
import android.app.PendingIntent
@@ -20,6 +20,7 @@ import ani.dantotsu.R
import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
+import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.data.notification.Notifications
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -58,8 +59,12 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
if (newRecentGlobal != null) {
PrefManager.setVal(PrefName.RecentGlobalNotification, newRecentGlobal)
}
+ if (notifications.isNullOrEmpty()) return@launch
+ PrefManager.setVal(PrefName.UnreadCommentNotifications,
+ PrefManager.getVal(PrefName.UnreadCommentNotifications) + (notifications?.size ?: 0)
+ )
- notifications?.forEach {
+ notifications.forEach {
val type: NotificationType = when (it.type) {
1 -> NotificationType.COMMENT_REPLY
2 -> NotificationType.COMMENT_WARNING
@@ -71,6 +76,15 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
NotificationType.COMMENT_WARNING -> {
val title = "You received a warning"
val message = it.content ?: "Be more thoughtful with your comments"
+
+ val commentStore = CommentStore(
+ title,
+ message,
+ it.mediaId,
+ it.commentId
+ )
+ addNotificationToStore(commentStore)
+
createNotification(
NotificationType.COMMENT_WARNING,
message,
@@ -83,9 +97,18 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
}
NotificationType.COMMENT_REPLY -> {
- val title = "New CommentNotificationWorker Reply"
+ val title = "New Comment Reply"
val mediaName = names[it.mediaId]?.title ?: "Unknown"
val message = "${it.username} replied to your comment in $mediaName"
+
+ val commentStore = CommentStore(
+ title,
+ message,
+ it.mediaId,
+ it.commentId
+ )
+ addNotificationToStore(commentStore)
+
createNotification(
NotificationType.COMMENT_REPLY,
message,
@@ -100,6 +123,15 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
NotificationType.APP_GLOBAL -> {
val title = "Update from Dantotsu"
val message = it.content ?: "New feature available"
+
+ val commentStore = CommentStore(
+ title,
+ message,
+ null,
+ null
+ )
+ addNotificationToStore(commentStore)
+
createNotification(
NotificationType.APP_GLOBAL,
message,
@@ -143,6 +175,22 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
return Result.success()
}
+ private fun addNotificationToStore(notification: CommentStore) {
+ val notificationStore = PrefManager.getNullableVal>(
+ PrefName.CommentNotificationStore,
+ null
+ ) ?: listOf()
+ val newStore = notificationStore.toMutableList()
+ if (newStore.size > 10) {
+ newStore.remove(newStore.minByOrNull { it.time })
+ }
+ if (newStore.any { it.content == notification.content }) {
+ return
+ }
+ newStore.add(notification)
+ PrefManager.setVal(PrefName.CommentNotificationStore, newStore)
+ }
+
private fun createNotification(
notificationType: NotificationType,
message: String,
@@ -152,6 +200,10 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
color: String,
imageUrl: String
): android.app.Notification? {
+ Logger.log(
+ "Creating notification of type $notificationType" +
+ ", message: $message, title: $title, mediaId: $mediaId, commentId: $commentId"
+ )
val notification = when (notificationType) {
NotificationType.COMMENT_WARNING -> {
val intent = Intent(applicationContext, MainActivity::class.java).apply {
@@ -162,7 +214,7 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
}
val pendingIntent = PendingIntent.getActivity(
applicationContext,
- 0,
+ commentId,
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
@@ -185,7 +237,7 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
}
val pendingIntent = PendingIntent.getActivity(
applicationContext,
- 0,
+ commentId,
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
@@ -214,7 +266,7 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
}
val pendingIntent = PendingIntent.getActivity(
applicationContext,
- 0,
+ System.currentTimeMillis().toInt(),
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
@@ -266,6 +318,6 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
companion object {
val checkIntervals = arrayOf(0L, 720, 1440)
- const val WORK_NAME = "ani.dantotsu.notifications.CommentNotificationWorker"
+ const val WORK_NAME = "ani.dantotsu.notifications.comment.CommentNotificationWorker"
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/notifications/comment/CommentStore.kt b/app/src/main/java/ani/dantotsu/notifications/comment/CommentStore.kt
new file mode 100644
index 00000000..bc6ab98b
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/notifications/comment/CommentStore.kt
@@ -0,0 +1,20 @@
+package ani.dantotsu.notifications.comment
+
+import kotlinx.serialization.Serializable
+
+
+@Suppress("INAPPROPRIATE_CONST_NAME")
+@Serializable
+data class CommentStore(
+ val title: String,
+ val content: String,
+ val mediaId: Int? = null,
+ val commentId: Int? = null,
+ val time: Long = System.currentTimeMillis(),
+) : java.io.Serializable {
+ companion object {
+
+ @Suppress("INAPPROPRIATE_CONST_NAME")
+ private const val serialVersionUID = 1L
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/notifications/MediaNameFetch.kt b/app/src/main/java/ani/dantotsu/notifications/comment/MediaNameFetch.kt
similarity index 98%
rename from app/src/main/java/ani/dantotsu/notifications/MediaNameFetch.kt
rename to app/src/main/java/ani/dantotsu/notifications/comment/MediaNameFetch.kt
index 47345104..15e0e7e6 100644
--- a/app/src/main/java/ani/dantotsu/notifications/MediaNameFetch.kt
+++ b/app/src/main/java/ani/dantotsu/notifications/comment/MediaNameFetch.kt
@@ -1,4 +1,4 @@
-package ani.dantotsu.notifications
+package ani.dantotsu.notifications.comment
import ani.dantotsu.client
import com.google.gson.Gson
diff --git a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
index 04b50c54..5ef0adf1 100644
--- a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
@@ -82,6 +82,9 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
}
private fun getDub(): Boolean {
+ if (sourceLanguage >= extension.sources.size) {
+ sourceLanguage = extension.sources.size - 1
+ }
val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource
?: return false
currContext()?.let { context ->
@@ -103,6 +106,9 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
}
fun setDub(setDub: Boolean) {
+ if (sourceLanguage >= extension.sources.size) {
+ sourceLanguage = extension.sources.size - 1
+ }
val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource
?: return
val type = when (setDub) {
diff --git a/app/src/main/java/ani/dantotsu/profile/ChartItem.kt b/app/src/main/java/ani/dantotsu/profile/ChartItem.kt
index 604f9913..98b2889e 100644
--- a/app/src/main/java/ani/dantotsu/profile/ChartItem.kt
+++ b/app/src/main/java/ani/dantotsu/profile/ChartItem.kt
@@ -7,7 +7,10 @@ import ani.dantotsu.databinding.ItemChartBinding
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
import com.github.aachartmodel.aainfographics.aachartcreator.AAMoveOverEventMessageModel
import com.github.aachartmodel.aainfographics.aachartcreator.AAOptions
+import com.xwray.groupie.OnItemClickListener
+import com.xwray.groupie.OnItemLongClickListener
import com.xwray.groupie.viewbinding.BindableItem
+import com.xwray.groupie.viewbinding.GroupieViewHolder
class ChartItem(
private val title: String,
@@ -31,6 +34,7 @@ class ChartItem(
) {
}
}
+ binding.chartView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
binding.chartView.callBack = callback
binding.chartView.reload()
binding.chartView.aa_drawChartWithChartOptions(aaOptions)
@@ -49,4 +53,32 @@ class ChartItem(
override fun initializeViewBinding(view: View): ItemChartBinding {
return ItemChartBinding.bind(view)
}
+
+ override fun bind(viewHolder: GroupieViewHolder, position: Int) {
+ viewHolder.setIsRecyclable(false)
+ super.bind(viewHolder, position)
+ }
+
+ override fun bind(
+ viewHolder: GroupieViewHolder,
+ position: Int,
+ payloads: MutableList
+ ) {
+ viewHolder.setIsRecyclable(false)
+ super.bind(viewHolder, position, payloads)
+ }
+
+ override fun bind(
+ viewHolder: GroupieViewHolder,
+ position: Int,
+ payloads: MutableList,
+ onItemClickListener: OnItemClickListener?,
+ onItemLongClickListener: OnItemLongClickListener?
+ ) {
+ viewHolder.setIsRecyclable(false)
+ super.bind(viewHolder, position, payloads, onItemClickListener, onItemLongClickListener)
+ }
+ override fun getViewType(): Int {
+ return 0
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt
index c57f07d0..c6281ca6 100644
--- a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt
+++ b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt
@@ -68,8 +68,11 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
binding.profileViewPager.isUserInputEnabled = false
lifecycleScope.launch(Dispatchers.IO) {
- val userid = intent.getIntExtra("userId", 0)
- val respond = Anilist.query.getUserProfile(userid)
+ val userid = intent.getIntExtra("userId", -1)
+ val username = intent.getStringExtra("username") ?: ""
+ val respond =
+ if (userid != -1) Anilist.query.getUserProfile(userid) else
+ Anilist.query.getUserProfile(username)
val user = respond?.data?.user
if (user == null) {
toast("User not found")
diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt b/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt
index c968b04d..cb441cba 100644
--- a/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt
+++ b/app/src/main/java/ani/dantotsu/profile/ProfileFragment.kt
@@ -65,7 +65,7 @@ class ProfileFragment : Fragment() {
binding.profileUserBio.setInitialScale(1)
val styledHtml = getFullAniHTML(
user.about ?: "",
- activity.getColor(R.color.bg_opp)
+ ContextCompat.getColor(activity, R.color.bg_opp)
)
binding.profileUserBio.loadDataWithBaseURL(
null,
@@ -76,7 +76,7 @@ class ProfileFragment : Fragment() {
)
binding.profileUserBio.setBackgroundColor(
ContextCompat.getColor(
- requireContext(),
+ activity,
android.R.color.transparent
)
)
@@ -86,7 +86,7 @@ class ProfileFragment : Fragment() {
super.onPageFinished(view, url)
binding.profileUserBio.setBackgroundColor(
ContextCompat.getColor(
- requireContext(),
+ activity,
android.R.color.transparent
)
)
@@ -146,7 +146,7 @@ class ProfileFragment : Fragment() {
}
binding.profileFavStaffRecycler.adapter = AuthorAdapter(favStaff)
binding.profileFavStaffRecycler.layoutManager = LinearLayoutManager(
- requireContext(),
+ activity,
LinearLayoutManager.HORIZONTAL,
false
)
@@ -155,7 +155,7 @@ class ProfileFragment : Fragment() {
}
binding.profileFavCharactersRecycler.adapter = CharacterAdapter(favCharacter)
binding.profileFavCharactersRecycler.layoutManager = LinearLayoutManager(
- requireContext(),
+ activity,
LinearLayoutManager.HORIZONTAL,
false
)
@@ -177,9 +177,9 @@ class ProfileFragment : Fragment() {
recyclerView.visibility = View.GONE
if (it != null) {
if (it.isNotEmpty()) {
- recyclerView.adapter = MediaAdaptor(0, it, requireActivity(), fav=true)
+ recyclerView.adapter = MediaAdaptor(0, it, activity, fav=true)
recyclerView.layoutManager = LinearLayoutManager(
- requireContext(),
+ activity,
LinearLayoutManager.HORIZONTAL,
false
)
diff --git a/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt b/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt
index 1fdeef98..b90cba33 100644
--- a/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt
+++ b/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt
@@ -51,9 +51,9 @@ class StatsFragment :
user = arguments?.getSerializable("user") as Query.UserProfile
binding.statisticList.adapter = adapter
- binding.statisticList.setHasFixedSize(true)
+ binding.statisticList.recycledViewPool.setMaxRecycledViews(0, 0)
binding.statisticList.isNestedScrollingEnabled = true
- binding.statisticList.layoutManager = LinearLayoutManager(requireContext())
+ binding.statisticList.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
binding.statisticProgressBar.visibility = View.VISIBLE
binding.compare.visibility = if (user.id == Anilist.userid) View.GONE else View.VISIBLE
binding.filterContainer.updateLayoutParams { topMargin = statusBarHeight }
@@ -104,9 +104,15 @@ class StatsFragment :
binding.filterContainer.visibility = View.GONE
}
+ override fun onPause() {
+ super.onPause()
+ binding.statisticList.visibility = View.GONE
+ }
+
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
+ binding.statisticList.visibility = View.VISIBLE
binding.root.requestLayout()
if (!loadedFirstTime) {
activity.lifecycleScope.launch {
diff --git a/app/src/main/java/ani/dantotsu/profile/activity/ActivityItemBuilder.kt b/app/src/main/java/ani/dantotsu/profile/activity/ActivityItemBuilder.kt
index d810258b..ef50027e 100644
--- a/app/src/main/java/ani/dantotsu/profile/activity/ActivityItemBuilder.kt
+++ b/app/src/main/java/ani/dantotsu/profile/activity/ActivityItemBuilder.kt
@@ -79,6 +79,10 @@ class ActivityItemBuilder {
NotificationType.MEDIA_DELETION -> {
"${notification.deletedMediaTitle} has been deleted from the site"
}
+
+ NotificationType.COMMENT_REPLY -> {
+ notification.context ?: "You should not see this"
+ }
}
}
diff --git a/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt b/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt
index f13d93e1..7ef09eb5 100644
--- a/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt
+++ b/app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt
@@ -19,6 +19,7 @@ import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.snackString
+import ani.dantotsu.util.Logger
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -80,7 +81,8 @@ class FeedFragment : Fragment() {
binding.listRecyclerView.setOnTouchListener { _, event ->
if (event?.action == MotionEvent.ACTION_UP) {
if (activityList.size % AnilistQueries.ITEMS_PER_PAGE != 0 && !global) {
- snackString("No more activities")
+ //snackString("No more activities") fix spam?
+ Logger.log("No more activities")
} else if (!scrollView.canScrollVertically(1) && !binding.feedRefresh.isVisible
&& binding.listRecyclerView.adapter!!.itemCount != 0 &&
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
diff --git a/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt b/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt
index 01dac01f..4c21bd48 100644
--- a/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt
+++ b/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt
@@ -18,6 +18,7 @@ import ani.dantotsu.databinding.ActivityFollowBinding
import ani.dantotsu.initActivity
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight
+import ani.dantotsu.notifications.comment.CommentStore
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
@@ -29,6 +30,7 @@ import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import java.util.Locale
class NotificationActivity : AppCompatActivity() {
private lateinit var binding: ActivityFollowBinding
@@ -73,7 +75,25 @@ class NotificationActivity : AppCompatActivity() {
notifications.filter { it.id == activityId }
} else {
notifications
+ }.toMutableList()
+ val commentStore = PrefManager.getNullableVal>(
+ PrefName.CommentNotificationStore,
+ null
+ ) ?: listOf()
+ commentStore.forEach {
+ val notification = Notification(
+ "COMMENT_REPLY",
+ System.currentTimeMillis().toInt(),
+ commentId = it.commentId,
+ notificationType = "COMMENT_REPLY",
+ mediaId = it.mediaId,
+ context = it.title + "\n" + it.content,
+ createdAt = (it.time / 1000L).toInt(),
+ )
+ notificationList = notificationList + notification
}
+ notificationList = notificationList.sortedByDescending { it.createdAt }
+
adapter.update(notificationList.map { NotificationItem(it, ::onNotificationClick) })
}
withContext(Dispatchers.Main) {
@@ -81,7 +101,8 @@ class NotificationActivity : AppCompatActivity() {
binding.listRecyclerView.setOnTouchListener { _, event ->
if (event?.action == MotionEvent.ACTION_UP) {
if (adapter.itemCount % AnilistQueries.ITEMS_PER_PAGE != 0) {
- snackString("No more notifications")
+ //snackString("No more notifications") fix spam?
+ Logger.log("No more notifications")
} else if (!binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible
&& binding.listRecyclerView.adapter!!.itemCount != 0 &&
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
@@ -105,7 +126,6 @@ class NotificationActivity : AppCompatActivity() {
}
}
}
-
private fun loadPage(onFinish: () -> Unit = {}) {
lifecycleScope.launch(Dispatchers.IO) {
val res = Anilist.query.getNotifications(Anilist.userid ?: 0, page)
@@ -120,7 +140,7 @@ class NotificationActivity : AppCompatActivity() {
}
}
- private fun onNotificationClick(id: Int, type: NotificationClickType) {
+ private fun onNotificationClick(id: Int, optional: Int?, type: NotificationClickType) {
when (type) {
NotificationClickType.USER -> {
ContextCompat.startActivity(
@@ -143,6 +163,16 @@ class NotificationActivity : AppCompatActivity() {
)
}
+ NotificationClickType.COMMENT -> {
+ ContextCompat.startActivity(this, Intent(this, MediaDetailsActivity::class.java)
+ .putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
+ .putExtra("mediaId", id)
+ .putExtra("commentId", optional ?: -1),
+ null
+ )
+
+ }
+
NotificationClickType.UNDEFINED -> {
// Do nothing
}
@@ -151,7 +181,7 @@ class NotificationActivity : AppCompatActivity() {
companion object {
enum class NotificationClickType {
- USER, MEDIA, ACTIVITY, UNDEFINED
+ USER, MEDIA, ACTIVITY, COMMENT, UNDEFINED
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/profile/activity/NotificationItem.kt b/app/src/main/java/ani/dantotsu/profile/activity/NotificationItem.kt
index cc9a5270..961d4c1a 100644
--- a/app/src/main/java/ani/dantotsu/profile/activity/NotificationItem.kt
+++ b/app/src/main/java/ani/dantotsu/profile/activity/NotificationItem.kt
@@ -14,7 +14,7 @@ import com.xwray.groupie.viewbinding.BindableItem
class NotificationItem(
private val notification: Notification,
- val clickCallback: (Int, NotificationClickType) -> Unit
+ val clickCallback: (Int, Int?, NotificationClickType) -> Unit
) : BindableItem() {
private lateinit var binding: ItemNotificationBinding
override fun bind(viewBinding: ItemNotificationBinding, position: Int) {
@@ -31,7 +31,7 @@ class NotificationItem(
return ItemNotificationBinding.bind(view)
}
- private fun image(user: Boolean = false) {
+ private fun image(user: Boolean = false, commentNotification: Boolean = false) {
val cover = if (user) notification.user?.bannerImage
?: notification.user?.avatar?.medium else notification.media?.bannerImage
@@ -52,7 +52,13 @@ class NotificationItem(
binding.notificationCover.visibility = View.GONE
binding.notificationCoverUser.visibility = View.VISIBLE
binding.notificationCoverUserContainer.visibility = View.VISIBLE
- binding.notificationCoverUser.loadImage(notification.user?.avatar?.large)
+ if (commentNotification) {
+ binding.notificationCoverUser.setImageResource(R.drawable.ic_dantotsu_round)
+ binding.notificationCoverUser.scaleX = 1.4f
+ binding.notificationCoverUser.scaleY = 1.4f
+ } else {
+ binding.notificationCoverUser.loadImage(notification.user?.avatar?.large)
+ }
binding.notificationBannerImage.layoutParams.height = userHeight
} else {
binding.notificationCover.visibility = View.VISIBLE
@@ -75,12 +81,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.activityId ?: 0, NotificationClickType.ACTIVITY
+ notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
)
}
}
@@ -90,12 +96,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.activityId ?: 0, NotificationClickType.ACTIVITY
+ notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
)
}
}
@@ -105,12 +111,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.userId ?: 0, NotificationClickType.USER
+ notification.userId ?: 0, null, NotificationClickType.USER
)
}
}
@@ -120,12 +126,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.activityId ?: 0, NotificationClickType.ACTIVITY
+ notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
)
}
}
@@ -135,12 +141,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
}
@@ -150,12 +156,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
}
@@ -165,12 +171,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
}
@@ -180,7 +186,7 @@ class NotificationItem(
image()
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.media?.id ?: 0, NotificationClickType.MEDIA
+ notification.media?.id ?: 0, null, NotificationClickType.MEDIA
)
}
}
@@ -190,12 +196,12 @@ class NotificationItem(
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.activityId ?: 0, NotificationClickType.ACTIVITY
+ notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
)
}
}
@@ -205,12 +211,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.activityId ?: 0, NotificationClickType.ACTIVITY
+ notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
)
}
}
@@ -220,12 +226,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
}
@@ -235,12 +241,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
}
@@ -250,12 +256,12 @@ class NotificationItem(
image(true)
binding.notificationCoverUser.setOnClickListener {
clickCallback(
- notification.user?.id ?: 0, NotificationClickType.USER
+ notification.user?.id ?: 0, null, NotificationClickType.USER
)
}
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.activityId ?: 0, NotificationClickType.ACTIVITY
+ notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
)
}
}
@@ -265,7 +271,7 @@ class NotificationItem(
image()
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.media?.id ?: 0, NotificationClickType.MEDIA
+ notification.media?.id ?: 0, null, NotificationClickType.MEDIA
)
}
}
@@ -275,7 +281,7 @@ class NotificationItem(
image()
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.media?.id ?: 0, NotificationClickType.MEDIA
+ notification.media?.id ?: 0, null, NotificationClickType.MEDIA
)
}
}
@@ -285,7 +291,7 @@ class NotificationItem(
image()
binding.notificationBannerImage.setOnClickListener {
clickCallback(
- notification.media?.id ?: 0, NotificationClickType.MEDIA
+ notification.media?.id ?: 0, null, NotificationClickType.MEDIA
)
}
}
@@ -293,6 +299,17 @@ class NotificationItem(
NotificationType.MEDIA_DELETION -> {
binding.notificationCover.visibility = View.GONE
}
+
+ NotificationType.COMMENT_REPLY -> {
+ image(user = true, commentNotification = true)
+ if (notification.commentId != null && notification.mediaId != null) {
+ binding.notificationBannerImage.setOnClickListener {
+ clickCallback(
+ notification.mediaId, notification.commentId, NotificationClickType.COMMENT
+ )
+ }
+ }
+ }
}
}
diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt
index 1df64f08..907ba068 100644
--- a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt
@@ -47,7 +47,7 @@ import ani.dantotsu.initActivity
import ani.dantotsu.loadImage
import ani.dantotsu.util.Logger
import ani.dantotsu.navBarHeight
-import ani.dantotsu.notifications.CommentNotificationWorker
+import ani.dantotsu.notifications.comment.CommentNotificationWorker
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.AppUpdater
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 a429392b..9b2beb3c 100644
--- a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt
+++ b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt
@@ -3,6 +3,7 @@ package ani.dantotsu.settings.saving
import android.graphics.Color
import ani.dantotsu.connections.comments.AuthResponse
import ani.dantotsu.connections.mal.MAL
+import ani.dantotsu.notifications.comment.CommentStore
import ani.dantotsu.settings.saving.internal.Location
import ani.dantotsu.settings.saving.internal.Pref
@@ -171,6 +172,8 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
CommentTokenExpiry(Pref(Location.Irrelevant, Long::class, 0L)),
LogToFile(Pref(Location.Irrelevant, Boolean::class, false)),
RecentGlobalNotification(Pref(Location.Irrelevant, Int::class, 0)),
+ CommentNotificationStore(Pref(Location.Irrelevant, List::class, listOf())),
+ UnreadCommentNotifications(Pref(Location.Irrelevant, Int::class, 0)),
//Protected
DiscordToken(Pref(Location.Protected, String::class, "")),
diff --git a/app/src/main/java/ani/dantotsu/util/Logger.kt b/app/src/main/java/ani/dantotsu/util/Logger.kt
index a6780748..22c0000c 100644
--- a/app/src/main/java/ani/dantotsu/util/Logger.kt
+++ b/app/src/main/java/ani/dantotsu/util/Logger.kt
@@ -2,6 +2,7 @@ package ani.dantotsu.util
import android.content.Context
import android.content.Intent
+import android.os.Build
import android.util.Log
import androidx.core.content.FileProvider
import ani.dantotsu.BuildConfig
@@ -31,8 +32,8 @@ object Logger {
}
file?.writeText("log started\n")
file?.appendText("date/time: ${Date()}\n")
- file?.appendText("device: ${android.os.Build.MODEL}\n")
- file?.appendText("os version: ${android.os.Build.VERSION.RELEASE}\n")
+ file?.appendText("device: ${Build.MODEL}\n")
+ file?.appendText("os version: ${Build.VERSION.RELEASE}\n")
file?.appendText(
"app version: ${
context.packageManager.getPackageInfo(
@@ -46,29 +47,35 @@ object Logger {
context.packageManager.getPackageInfo(
context.packageName,
0
- ).versionCode
+ ).run {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
+ longVersionCode
+ else
+ @Suppress("DEPRECATION") versionCode
+
+ }
}\n"
)
- file?.appendText("sdk version: ${android.os.Build.VERSION.SDK_INT}\n")
- file?.appendText("manufacturer: ${android.os.Build.MANUFACTURER}\n")
- file?.appendText("brand: ${android.os.Build.BRAND}\n")
- file?.appendText("product: ${android.os.Build.PRODUCT}\n")
- file?.appendText("device: ${android.os.Build.DEVICE}\n")
- file?.appendText("hardware: ${android.os.Build.HARDWARE}\n")
- file?.appendText("host: ${android.os.Build.HOST}\n")
- file?.appendText("id: ${android.os.Build.ID}\n")
- file?.appendText("type: ${android.os.Build.TYPE}\n")
- file?.appendText("user: ${android.os.Build.USER}\n")
- file?.appendText("tags: ${android.os.Build.TAGS}\n")
- file?.appendText("time: ${android.os.Build.TIME}\n")
- file?.appendText("radio: ${android.os.Build.RADIO}\n")
- file?.appendText("bootloader: ${android.os.Build.BOOTLOADER}\n")
- file?.appendText("board: ${android.os.Build.BOARD}\n")
- file?.appendText("fingerprint: ${android.os.Build.FINGERPRINT}\n")
- file?.appendText("supported_abis: ${android.os.Build.SUPPORTED_ABIS.joinToString()}\n")
- file?.appendText("supported_32_bit_abis: ${android.os.Build.SUPPORTED_32_BIT_ABIS.joinToString()}\n")
- file?.appendText("supported_64_bit_abis: ${android.os.Build.SUPPORTED_64_BIT_ABIS.joinToString()}\n")
- file?.appendText("is emulator: ${android.os.Build.FINGERPRINT.contains("generic")}\n")
+ file?.appendText("sdk version: ${Build.VERSION.SDK_INT}\n")
+ file?.appendText("manufacturer: ${Build.MANUFACTURER}\n")
+ file?.appendText("brand: ${Build.BRAND}\n")
+ file?.appendText("product: ${Build.PRODUCT}\n")
+ file?.appendText("device: ${Build.DEVICE}\n")
+ file?.appendText("hardware: ${Build.HARDWARE}\n")
+ file?.appendText("host: ${Build.HOST}\n")
+ file?.appendText("id: ${Build.ID}\n")
+ file?.appendText("type: ${Build.TYPE}\n")
+ file?.appendText("user: ${Build.USER}\n")
+ file?.appendText("tags: ${Build.TAGS}\n")
+ file?.appendText("time: ${Build.TIME}\n")
+ file?.appendText("radio: ${Build.getRadioVersion()}\n")
+ file?.appendText("bootloader: ${Build.BOOTLOADER}\n")
+ file?.appendText("board: ${Build.BOARD}\n")
+ file?.appendText("fingerprint: ${Build.FINGERPRINT}\n")
+ file?.appendText("supported_abis: ${Build.SUPPORTED_ABIS.joinToString()}\n")
+ file?.appendText("supported_32_bit_abis: ${Build.SUPPORTED_32_BIT_ABIS.joinToString()}\n")
+ file?.appendText("supported_64_bit_abis: ${Build.SUPPORTED_64_BIT_ABIS.joinToString()}\n")
+ file?.appendText("is emulator: ${Build.FINGERPRINT.contains("generic")}\n")
file?.appendText("--------------------------------\n")
} catch (e: Exception) {
Injekt.get().logException(e)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt
index 5db7ff8f..2e4ff107 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt
@@ -147,7 +147,7 @@ object Notifications {
setGroup(GROUP_COMMENTS)
},
buildNotificationChannel(CHANNEL_COMMENT_WARING, IMPORTANCE_HIGH) {
- setName("CommentNotificationWorker Warnings")
+ setName("Comment Warnings")
setGroup(GROUP_COMMENTS)
},
buildNotificationChannel(CHANNEL_ANILIST, IMPORTANCE_DEFAULT) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt
index 231443bb..280af466 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt
@@ -14,6 +14,7 @@ import android.os.PowerManager
import android.util.TypedValue
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
+import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker
import androidx.core.content.getSystemService
import androidx.core.graphics.alpha
@@ -85,7 +86,7 @@ fun Context.getThemeColor(attr: Int): Int {
val tv = TypedValue()
return if (this.theme.resolveAttribute(attr, tv, true)) {
if (tv.resourceId != 0) {
- getColor(tv.resourceId)
+ ContextCompat.getColor(this, tv.resourceId)
} else {
tv.data
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt
index 67ce5a03..53049e4b 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt
@@ -10,6 +10,7 @@ import androidx.core.app.NotificationChannelGroupCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.NotificationManagerCompat.NotificationWithIdAndTag
+import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker
import androidx.core.content.getSystemService
@@ -65,7 +66,7 @@ fun Context.notificationBuilder(
block: (NotificationCompat.Builder.() -> Unit)? = null
): NotificationCompat.Builder {
val builder = NotificationCompat.Builder(this, channelId)
- .setColor(getColor(android.R.color.holo_blue_dark))
+ .setColor(ContextCompat.getColor(this, android.R.color.holo_blue_dark))
if (block != null) {
builder.block()
}
diff --git a/app/src/main/res/layout/fragment_statistics.xml b/app/src/main/res/layout/fragment_statistics.xml
index 9f202f6d..dafdaeae 100644
--- a/app/src/main/res/layout/fragment_statistics.xml
+++ b/app/src/main/res/layout/fragment_statistics.xml
@@ -125,6 +125,7 @@
android:id="@+id/statisticList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:orientation="vertical"
android:nestedScrollingEnabled="true"
tools:listitem="@layout/item_chart" />
diff --git a/app/src/main/res/values-v23/themes.xml b/app/src/main/res/values-v23/themes.xml
new file mode 100644
index 00000000..8729e48d
--- /dev/null
+++ b/app/src/main/res/values-v23/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 504f52b4..b5fe8d03 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -486,6 +486,7 @@
Read on Dantotsu
Watch on Dantotsu
+ View Profile in Dantotsu
"Continue : Episode "
"Continue : "
"Episode "
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 87eb6f03..ececfa8f 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -29,10 +29,6 @@
-
-