Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
aayush262 2024-03-18 10:39:42 +05:30
commit 22dccaa24b
40 changed files with 561 additions and 165 deletions

View file

@ -11,7 +11,7 @@ android {
defaultConfig { defaultConfig {
applicationId "ani.dantotsu" applicationId "ani.dantotsu"
minSdk 23 minSdk 21
targetSdk 34 targetSdk 34
versionCode((System.currentTimeMillis() / 60000).toInteger()) versionCode((System.currentTimeMillis() / 60000).toInteger())
versionName "2.2.0" versionName "2.2.0"

View file

@ -261,6 +261,17 @@
<data android:host="myanimelist.net" /> <data android:host="myanimelist.net" />
<data android:pathPrefix="/anime" /> <data android:pathPrefix="/anime" />
</intent-filter> </intent-filter>
<intent-filter android:label="@string/view_profile_in_dantotsu">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="anilist.co" />
<data android:pathPrefix="/user" />
</intent-filter>
</activity> </activity>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
@ -276,6 +287,15 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER" /> <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:pathPattern=".*\\.ani" />
<data android:pathPattern=".*\\.sani" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
</intent-filter>
</activity> </activity>
<activity <activity
android:name="eu.kanade.tachiyomi.extension.manga.util.MangaExtensionInstallActivity" android:name="eu.kanade.tachiyomi.extension.manga.util.MangaExtensionInstallActivity"

View file

@ -13,7 +13,7 @@ import ani.dantotsu.aniyomi.anime.custom.AppModule
import ani.dantotsu.aniyomi.anime.custom.PreferenceModule import ani.dantotsu.aniyomi.anime.custom.PreferenceModule
import ani.dantotsu.connections.comments.CommentsAPI import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.notifications.CommentNotificationWorker import ani.dantotsu.notifications.comment.CommentNotificationWorker
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
import ani.dantotsu.others.DisabledReports import ani.dantotsu.others.DisabledReports
import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.AnimeSources
@ -134,7 +134,8 @@ class App : MultiDexApplication() {
// CommentNotificationWorker // CommentNotificationWorker
val commentInterval = CommentNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.CommentNotificationInterval)] val commentInterval = CommentNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.CommentNotificationInterval)]
if (commentInterval.toInt() != 0) { if (commentInterval.toInt() != 0) {
val recurringWork = PeriodicWorkRequest.Builder(CommentNotificationWorker::class.java, val recurringWork = PeriodicWorkRequest.Builder(
CommentNotificationWorker::class.java,
commentInterval, java.util.concurrent.TimeUnit.MINUTES) commentInterval, java.util.concurrent.TimeUnit.MINUTES)
.setConstraints(constraints) .setConstraints(constraints)
.build() .build()
@ -146,7 +147,8 @@ class App : MultiDexApplication() {
} else { } else {
androidx.work.WorkManager.getInstance(this).cancelUniqueWork(CommentNotificationWorker.WORK_NAME) androidx.work.WorkManager.getInstance(this).cancelUniqueWork(CommentNotificationWorker.WORK_NAME)
//run once //run once
androidx.work.WorkManager.getInstance(this).enqueue(OneTimeWorkRequest.Companion.from(CommentNotificationWorker::class.java)) androidx.work.WorkManager.getInstance(this).enqueue(OneTimeWorkRequest.Companion.from(
CommentNotificationWorker::class.java))
} }
// AnilistNotificationWorker // AnilistNotificationWorker

View file

@ -244,21 +244,35 @@ fun isOnline(context: Context): Boolean {
val connectivityManager = val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return tryWith { return tryWith {
val cap = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return@tryWith if (cap != null) { val cap = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
when { return@tryWith if (cap != null) {
cap.hasTransport(TRANSPORT_BLUETOOTH) || when {
cap.hasTransport(TRANSPORT_CELLULAR) || cap.hasTransport(TRANSPORT_BLUETOOTH) ||
cap.hasTransport(TRANSPORT_ETHERNET) || cap.hasTransport(TRANSPORT_CELLULAR) ||
cap.hasTransport(TRANSPORT_LOWPAN) || cap.hasTransport(TRANSPORT_ETHERNET) ||
cap.hasTransport(TRANSPORT_USB) || cap.hasTransport(TRANSPORT_LOWPAN) ||
cap.hasTransport(TRANSPORT_VPN) || cap.hasTransport(TRANSPORT_USB) ||
cap.hasTransport(TRANSPORT_WIFI) || cap.hasTransport(TRANSPORT_VPN) ||
cap.hasTransport(TRANSPORT_WIFI_AWARE) -> true 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 } ?: false
} }

View file

@ -2,6 +2,7 @@ package ani.dantotsu
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
@ -14,6 +15,7 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.provider.Settings import android.provider.Settings
import android.util.TypedValue import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.AnticipateInterpolator import android.view.animation.AnticipateInterpolator
@ -28,6 +30,7 @@ import androidx.core.content.ContextCompat
import androidx.core.view.doOnAttach import androidx.core.view.doOnAttach
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMargins import androidx.core.view.updateMargins
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
@ -47,17 +50,21 @@ import ani.dantotsu.home.MangaFragment
import ani.dantotsu.home.NoInternet import ani.dantotsu.home.NoInternet
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.others.CustomBottomDialog import ani.dantotsu.others.CustomBottomDialog
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.activity.FeedActivity import ani.dantotsu.profile.activity.FeedActivity
import ani.dantotsu.profile.activity.NotificationActivity import ani.dantotsu.profile.activity.NotificationActivity
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefManager.asLiveBool import ani.dantotsu.settings.saving.PrefManager.asLiveBool
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.settings.saving.SharedPreferenceBooleanLiveData 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.subcriptions.Subscription.Companion.startSubscription
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputEditText
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin import io.noties.markwon.SoftBreakAddsNewLinePlugin
@ -91,6 +98,60 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) 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<AnimatedBottomBar>(R.id.navbar) val _bottomBar = findViewById<AnimatedBottomBar>(R.id.navbar)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@ -214,7 +275,7 @@ class MainActivity : AppCompatActivity() {
binding.root.doOnAttach { binding.root.doOnAttach {
initActivity(this) initActivity(this)
window.navigationBarColor = getColor(android.R.color.transparent) window.navigationBarColor = ContextCompat.getColor(this, android.R.color.transparent)
selectedOption = if (fragment != null) { selectedOption = if (fragment != null) {
when (fragment) { when (fragment) {
AnimeFragment::class.java.name -> 0 AnimeFragment::class.java.name -> 0
@ -329,6 +390,22 @@ class MainActivity : AppCompatActivity() {
snackString(this@MainActivity.getString(R.string.anilist_not_found)) 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) delay(500)
startSubscription() startSubscription()
} }
@ -381,7 +458,7 @@ class MainActivity : AppCompatActivity() {
override fun onRestart() { override fun onRestart() {
super.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( private val Int.toPx get() = TypedValue.applyDimension(
@ -398,6 +475,44 @@ class MainActivity : AppCompatActivity() {
params.updateMargins(bottom = 32.toPx) 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<TextInputEditText>(R.id.userAgentTextBox)?.hint = "Password"
val subtitleTextView = dialogView.findViewById<TextView>(R.id.subtitle)
subtitleTextView?.visibility = View.VISIBLE
subtitleTextView?.text = "Enter your password to decrypt the file"
val dialog = AlertDialog.Builder(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<TextInputEditText>(R.id.userAgentTextBox)
if (editText?.text?.isNotBlank() == true) {
editText.text?.toString()?.trim()?.toCharArray(password)
dialog.dismiss()
callback(password)
} else {
toast("Password cannot be empty")
}
}
}
//ViewPager //ViewPager
private class ViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : private class ViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fragmentManager, lifecycle) { FragmentStateAdapter(fragmentManager, lifecycle) {

View file

@ -54,6 +54,8 @@ class AnilistQueries {
Anilist.chapterRead = user.statistics?.manga?.chaptersRead Anilist.chapterRead = user.statistics?.manga?.chaptersRead
Anilist.adult = user.options?.displayAdultContent ?: false Anilist.adult = user.options?.displayAdultContent ?: false
Anilist.unreadNotificationCount = user.unreadNotificationCount ?: 0 Anilist.unreadNotificationCount = user.unreadNotificationCount ?: 0
val unread = PrefManager.getVal<Int>(PrefName.UnreadCommentNotifications)
Anilist.unreadNotificationCount += unread
return true 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<Query.User>(
"""{User(name:"$username"){id}}""",
force = true
)?.data?.user?.id
}
suspend fun getUserStatistics(id: Int, sort: String = "ID"): Query.StatisticsResponse? { suspend fun getUserStatistics(id: Int, sort: String = "ID"): Query.StatisticsResponse? {
return executeQuery<Query.StatisticsResponse>( return executeQuery<Query.StatisticsResponse>(
"""{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}}}""", """{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,}}}}""", """{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 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 Anilist.unreadNotificationCount = 0
} }
return res return res
@ -1404,7 +1421,8 @@ Page(page:$page,perPage:50) {
else if (global) "isFollowing:false,hasRepliesOrTypeText:true," else if (global) "isFollowing:false,hasRepliesOrTypeText:true,"
else "isFollowing:true,type_not:MESSAGE," else "isFollowing:true,type_not:MESSAGE,"
return executeQuery<FeedResponse>( return executeQuery<FeedResponse>(
"""{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
) )
} }

View file

@ -11,20 +11,27 @@ import ani.dantotsu.themes.ThemeManager
class UrlMedia : Activity() { class UrlMedia : Activity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
ThemeManager(this).applyTheme() ThemeManager(this).applyTheme()
var id: Int? = intent?.extras?.getInt("media", 0) ?: 0 val data: Uri? = intent?.data
var isMAL = false val type = data?.pathSegments?.getOrNull(0)
var continueMedia = true if (type == "anime" || type == "manga") {
if (id == 0) { var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
continueMedia = false var isMAL = false
val data: Uri? = intent?.data var continueMedia = true
isMAL = data?.host != "anilist.co" if (id == 0) {
id = data?.pathSegments?.getOrNull(1)?.toIntOrNull() continueMedia = false
} else loadMedia = id isMAL = data.host != "anilist.co"
startMainActivity( id = data.pathSegments?.getOrNull(1)?.toIntOrNull()
this, } else loadMedia = id
bundleOf("mediaId" to id, "mal" to isMAL, "continue" to continueMedia) 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)
}
} }
} }

View file

@ -20,7 +20,9 @@ enum class NotificationType(val value: String) {
RELATED_MEDIA_ADDITION("RELATED_MEDIA_ADDITION"), RELATED_MEDIA_ADDITION("RELATED_MEDIA_ADDITION"),
MEDIA_DATA_CHANGE("MEDIA_DATA_CHANGE"), MEDIA_DATA_CHANGE("MEDIA_DATA_CHANGE"),
MEDIA_MERGE("MEDIA_MERGE"), MEDIA_MERGE("MEDIA_MERGE"),
MEDIA_DELETION("MEDIA_DELETION") MEDIA_DELETION("MEDIA_DELETION"),
//custom
COMMENT_REPLY("COMMENT_REPLY"),
} }
@Serializable @Serializable
@ -40,7 +42,7 @@ data class NotificationResponse(
@Serializable @Serializable
data class NotificationUser( data class NotificationUser(
@SerialName("unreadNotificationCount") @SerialName("unreadNotificationCount")
val unreadNotificationCount: Int, var unreadNotificationCount: Int,
) : java.io.Serializable ) : java.io.Serializable
@Serializable @Serializable
@ -56,41 +58,41 @@ data class Notification(
@SerialName("id") @SerialName("id")
val id: Int, val id: Int,
@SerialName("userId") @SerialName("userId")
val userId: Int?, val userId: Int? = null,
@SerialName("CommentId") @SerialName("CommentId")
val commentId: Int?, val commentId: Int?,
@SerialName("type") @SerialName("type")
val notificationType: String, val notificationType: String,
@SerialName("activityId") @SerialName("activityId")
val activityId: Int?, val activityId: Int? = null,
@SerialName("animeId") @SerialName("animeId")
val mediaId: Int?, val mediaId: Int? = null,
@SerialName("episode") @SerialName("episode")
val episode: Int?, val episode: Int? = null,
@SerialName("contexts") @SerialName("contexts")
val contexts: List<String>?, val contexts: List<String>? = null,
@SerialName("context") @SerialName("context")
val context: String?, val context: String? = null,
@SerialName("reason") @SerialName("reason")
val reason: String?, val reason: String? = null,
@SerialName("deletedMediaTitle") @SerialName("deletedMediaTitle")
val deletedMediaTitle: String?, val deletedMediaTitle: String? = null,
@SerialName("deletedMediaTitles") @SerialName("deletedMediaTitles")
val deletedMediaTitles: List<String>?, val deletedMediaTitles: List<String>? = null,
@SerialName("createdAt") @SerialName("createdAt")
val createdAt: Int, val createdAt: Int,
@SerialName("media") @SerialName("media")
val media: ani.dantotsu.connections.anilist.api.Media?, val media: ani.dantotsu.connections.anilist.api.Media? = null,
@SerialName("user") @SerialName("user")
val user: ani.dantotsu.connections.anilist.api.User?, val user: ani.dantotsu.connections.anilist.api.User? = null,
@SerialName("message") @SerialName("message")
val message: MessageActivity?, val message: MessageActivity? = null,
@SerialName("activity") @SerialName("activity")
val activity: ActivityUnion?, val activity: ActivityUnion? = null,
@SerialName("Thread") @SerialName("Thread")
val thread: Thread?, val thread: Thread? = null,
@SerialName("comment") @SerialName("comment")
val comment: ThreadComment?, val comment: ThreadComment? = null,
) : java.io.Serializable ) : java.io.Serializable
@Serializable @Serializable

View file

@ -31,12 +31,15 @@ object CommentsAPI {
var isMod: Boolean = false var isMod: Boolean = false
var totalVotes: Int = 0 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" var url = "$address/comments/$id/$page"
val request = requestBuilder() val request = requestBuilder()
tag?.let { tag?.let {
url += "?tag=$it" url += "?tag=$it"
} }
sort?.let {
url += if (tag != null) "&sort=$it" else "?sort=$it"
}
val json = try { val json = try {
request.get(url) request.get(url)
} catch (e: IOException) { } catch (e: IOException) {

View file

@ -54,6 +54,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import tachiyomi.core.util.lang.launchIO
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
@ -355,15 +356,13 @@ class AnimeDownloaderService : Service() {
return false return false
} }
@OptIn(DelicateCoroutinesApi::class)
private fun saveMediaInfo(task: AnimeDownloadTask) { private fun saveMediaInfo(task: AnimeDownloadTask) {
GlobalScope.launch(Dispatchers.IO) { launchIO {
val directory = File( val directory = File(
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"${DownloadsManager.animeLocation}/${task.title}" "${DownloadsManager.animeLocation}/${task.title}"
) )
val episodeDirectory = File(directory, task.episode) val episodeDirectory = File(directory, task.episode)
if (!directory.exists()) directory.mkdirs()
if (!episodeDirectory.exists()) episodeDirectory.mkdirs() if (!episodeDirectory.exists()) episodeDirectory.mkdirs()
val file = File(directory, "media.json") val file = File(directory, "media.json")

View file

@ -37,6 +37,7 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SChapterImpl import eu.kanade.tachiyomi.source.model.SChapterImpl
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -47,6 +48,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import tachiyomi.core.util.lang.launchIO
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
@ -287,8 +289,9 @@ class MangaDownloaderService : Service() {
} }
} }
@OptIn(DelicateCoroutinesApi::class)
private fun saveMediaInfo(task: DownloadTask) { private fun saveMediaInfo(task: DownloadTask) {
GlobalScope.launch(Dispatchers.IO) { launchIO {
val directory = File( val directory = File(
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Manga/${task.title}" "Dantotsu/Manga/${task.title}"

View file

@ -31,6 +31,7 @@ import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SChapterImpl import eu.kanade.tachiyomi.source.model.SChapterImpl
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -42,6 +43,7 @@ import kotlinx.coroutines.withContext
import okhttp3.Request import okhttp3.Request
import okio.buffer import okio.buffer
import okio.sink import okio.sink
import tachiyomi.core.util.lang.launchIO
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
@ -347,8 +349,9 @@ class NovelDownloaderService : Service() {
} }
} }
@OptIn(DelicateCoroutinesApi::class)
private fun saveMediaInfo(task: DownloadTask) { private fun saveMediaInfo(task: DownloadTask) {
GlobalScope.launch(Dispatchers.IO) { launchIO {
val directory = File( val directory = File(
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Novel/${task.title}" "Dantotsu/Novel/${task.title}"

View file

@ -17,6 +17,7 @@ import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.settings.saving.internal.PreferenceKeystore import ani.dantotsu.settings.saving.internal.PreferenceKeystore
import ani.dantotsu.settings.saving.internal.PreferencePackager import ani.dantotsu.settings.saving.internal.PreferencePackager
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.util.Logger
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
class LoginFragment : Fragment() { class LoginFragment : Fragment() {
@ -50,7 +51,7 @@ class LoginFragment : Fragment() {
DocumentFile.fromSingleUri(requireActivity(), uri)?.name ?: "settings" DocumentFile.fromSingleUri(requireActivity(), uri)?.name ?: "settings"
//.sani is encrypted, .ani is not //.sani is encrypted, .ani is not
if (name.endsWith(".sani")) { if (name.endsWith(".sani")) {
passwordAlertDialog() { password -> passwordAlertDialog { password ->
if (password != null) { if (password != null) {
val salt = jsonString.copyOfRange(0, 16) val salt = jsonString.copyOfRange(0, 16)
val encrypted = jsonString.copyOfRange(16, jsonString.size) val encrypted = jsonString.copyOfRange(16, jsonString.size)
@ -78,7 +79,7 @@ class LoginFragment : Fragment() {
toast("Invalid file type") toast("Invalid file type")
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() Logger.log(e)
toast("Error importing settings") toast("Error importing settings")
} }
} }

View file

@ -7,7 +7,7 @@ import java.util.regex.Pattern
class AnimeNameAdapter { class AnimeNameAdapter {
companion object { companion object {
const val episodeRegex = 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 = const val failedEpisodeNumberRegex =
"(?<!part\\s)\\b(\\d+)\\b" "(?<!part\\s)\\b(\\d+)\\b"
const val seasonRegex = "(season|s)[\\s:.\\-]*(\\d+)[\\s:.\\-]*" const val seasonRegex = "(season|s)[\\s:.\\-]*(\\d+)[\\s:.\\-]*"

View file

@ -130,12 +130,12 @@ class CommentItem(val comment: Comment,
viewBinding.modBadge.visibility = if (comment.isMod == true) View.VISIBLE else View.GONE viewBinding.modBadge.visibility = if (comment.isMod == true) View.VISIBLE else View.GONE
viewBinding.adminBadge.visibility = if (comment.isAdmin == true) View.VISIBLE else View.GONE viewBinding.adminBadge.visibility = if (comment.isAdmin == true) View.VISIBLE else View.GONE
viewBinding.commentDelete.setOnClickListener { viewBinding.commentDelete.setOnClickListener {
dialogBuilder("Delete CommentNotificationWorker", "Are you sure you want to delete this comment?") { dialogBuilder("Delete Comment", "Are you sure you want to delete this comment?") {
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
scope.launch { scope.launch {
val success = CommentsAPI.deleteComment(comment.commentId) val success = CommentsAPI.deleteComment(comment.commentId)
if (success) { if (success) {
snackString("CommentNotificationWorker Deleted") snackString("Comment Deleted")
parentSection.remove(this@CommentItem) parentSection.remove(this@CommentItem)
} }
} }
@ -153,12 +153,12 @@ class CommentItem(val comment: Comment,
} }
} }
viewBinding.commentReport.setOnClickListener { viewBinding.commentReport.setOnClickListener {
dialogBuilder("Report CommentNotificationWorker", "Only report comments that violate the rules. Are you sure you want to report this comment?") { dialogBuilder("Report Comment", "Only report comments that violate the rules. Are you sure you want to report this comment?") {
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
scope.launch { scope.launch {
val success = CommentsAPI.reportComment(comment.commentId, comment.username, commentsFragment.mediaName, comment.userId) val success = CommentsAPI.reportComment(comment.commentId, comment.username, commentsFragment.mediaName, comment.userId)
if (success) { if (success) {
snackString("CommentNotificationWorker Reported") snackString("Comment Reported")
} }
} }
} }

View file

@ -13,7 +13,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.EditText import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.animation.doOnEnd import androidx.core.animation.doOnEnd
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
@ -33,7 +32,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.toast import ani.dantotsu.toast
import com.bumptech.glide.Glide import ani.dantotsu.util.Logger
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.Section import com.xwray.groupie.Section
import io.noties.markwon.editor.MarkwonEditor import io.noties.markwon.editor.MarkwonEditor
@ -139,9 +138,15 @@ class CommentsFragment : Fragment() {
R.id.comment_sort_lowest_rated -> "lowest_rated" R.id.comment_sort_lowest_rated -> "lowest_rated"
else -> return@setOnMenuItemClickListener false else -> return@setOnMenuItemClickListener false
} }
PrefManager.setVal(PrefName.CommentSortOrder, sortOrder) 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) binding.commentsList.scrollToPosition(0)
true true
} }
@ -197,7 +202,8 @@ class CommentsFragment : Fragment() {
} }
} }
} else { } 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? { private suspend fun fetchComments(): CommentResponse? {
return withContext(Dispatchers.IO) { 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, 300,
activity.binding.commentInput.text.length 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() section.clear()
val comments = withContext(Dispatchers.IO) { 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) val sortedComments = sortComments(comments?.comments)
@ -460,6 +475,7 @@ class CommentsFragment : Fragment() {
} }
InteractionState.REPLY -> { InteractionState.REPLY -> {
activity.binding.commentReplyToContainer.visibility = View.GONE
activity.binding.commentInput.setText("") activity.binding.commentInput.setText("")
val imm = activity.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager val imm = activity.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(activity.binding.commentInput.windowToken, 0) imm.hideSoftInputFromWindow(activity.binding.commentInput.windowToken, 0)
@ -587,7 +603,7 @@ class CommentsFragment : Fragment() {
private fun processComment() { private fun processComment() {
val commentText = activity.binding.commentInput.text.toString() val commentText = activity.binding.commentInput.text.toString()
if (commentText.isEmpty()) { if (commentText.isEmpty()) {
snackString("CommentNotificationWorker cannot be empty") snackString("Comment cannot be empty")
return return
} }
@ -604,6 +620,7 @@ class CommentsFragment : Fragment() {
null null
) )
} }
resetOldState()
} }
} }
@ -623,7 +640,7 @@ class CommentsFragment : Fragment() {
groups.forEach { item -> groups.forEach { item ->
if (item is CommentItem && item.comment.commentId == commentWithInteraction?.comment?.commentId) { if (item is CommentItem && item.comment.commentId == commentWithInteraction?.comment?.commentId) {
updateCommentItem(item, commentText) updateCommentItem(item, commentText)
snackString("CommentNotificationWorker edited") snackString("Comment edited")
} }
} }
} }

View file

@ -5,6 +5,7 @@ import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.databinding.ItemNovelResponseBinding import ani.dantotsu.databinding.ItemNovelResponseBinding
@ -59,11 +60,11 @@ class NovelResponseAdapter(
} }
if (binding.itemEpisodeFiller.text.contains("Downloading")) { if (binding.itemEpisodeFiller.text.contains("Downloading")) {
binding.itemEpisodeFiller.setTextColor( 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")) { } else if (binding.itemEpisodeFiller.text.contains("Downloaded")) {
binding.itemEpisodeFiller.setTextColor( binding.itemEpisodeFiller.setTextColor(
fragment.requireContext().getColor(android.R.color.holo_green_light) ContextCompat.getColor(fragment.requireContext(), android.R.color.holo_green_light)
) )
} else { } else {
binding.itemEpisodeFiller.setTextColor(color) binding.itemEpisodeFiller.setTextColor(color)

View file

@ -16,6 +16,7 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.profile.activity.ActivityItemBuilder import ani.dantotsu.profile.activity.ActivityItemBuilder
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers 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 flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
putExtra("FRAGMENT_TO_LOAD", "NOTIFICATIONS") putExtra("FRAGMENT_TO_LOAD", "NOTIFICATIONS")
if (notificationId != null) { if (notificationId != null) {
Logger.log("notificationId: $notificationId")
putExtra("activityId", notificationId) putExtra("activityId", notificationId)
} }
} }
val pendingIntent = PendingIntent.getActivity( val pendingIntent = PendingIntent.getActivity(
applicationContext, applicationContext,
0, notificationId ?: 0,
intent, intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
) )

View file

@ -1,4 +1,4 @@
package ani.dantotsu.notifications package ani.dantotsu.notifications.comment
import android.Manifest import android.Manifest
import android.app.PendingIntent import android.app.PendingIntent
@ -20,6 +20,7 @@ import ani.dantotsu.R
import ani.dantotsu.connections.comments.CommentsAPI import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -58,8 +59,12 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
if (newRecentGlobal != null) { if (newRecentGlobal != null) {
PrefManager.setVal(PrefName.RecentGlobalNotification, newRecentGlobal) PrefManager.setVal(PrefName.RecentGlobalNotification, newRecentGlobal)
} }
if (notifications.isNullOrEmpty()) return@launch
PrefManager.setVal(PrefName.UnreadCommentNotifications,
PrefManager.getVal<Int>(PrefName.UnreadCommentNotifications) + (notifications?.size ?: 0)
)
notifications?.forEach { notifications.forEach {
val type: NotificationType = when (it.type) { val type: NotificationType = when (it.type) {
1 -> NotificationType.COMMENT_REPLY 1 -> NotificationType.COMMENT_REPLY
2 -> NotificationType.COMMENT_WARNING 2 -> NotificationType.COMMENT_WARNING
@ -71,6 +76,15 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
NotificationType.COMMENT_WARNING -> { NotificationType.COMMENT_WARNING -> {
val title = "You received a warning" val title = "You received a warning"
val message = it.content ?: "Be more thoughtful with your comments" val message = it.content ?: "Be more thoughtful with your comments"
val commentStore = CommentStore(
title,
message,
it.mediaId,
it.commentId
)
addNotificationToStore(commentStore)
createNotification( createNotification(
NotificationType.COMMENT_WARNING, NotificationType.COMMENT_WARNING,
message, message,
@ -83,9 +97,18 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
} }
NotificationType.COMMENT_REPLY -> { NotificationType.COMMENT_REPLY -> {
val title = "New CommentNotificationWorker Reply" val title = "New Comment Reply"
val mediaName = names[it.mediaId]?.title ?: "Unknown" val mediaName = names[it.mediaId]?.title ?: "Unknown"
val message = "${it.username} replied to your comment in $mediaName" val message = "${it.username} replied to your comment in $mediaName"
val commentStore = CommentStore(
title,
message,
it.mediaId,
it.commentId
)
addNotificationToStore(commentStore)
createNotification( createNotification(
NotificationType.COMMENT_REPLY, NotificationType.COMMENT_REPLY,
message, message,
@ -100,6 +123,15 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
NotificationType.APP_GLOBAL -> { NotificationType.APP_GLOBAL -> {
val title = "Update from Dantotsu" val title = "Update from Dantotsu"
val message = it.content ?: "New feature available" val message = it.content ?: "New feature available"
val commentStore = CommentStore(
title,
message,
null,
null
)
addNotificationToStore(commentStore)
createNotification( createNotification(
NotificationType.APP_GLOBAL, NotificationType.APP_GLOBAL,
message, message,
@ -143,6 +175,22 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
return Result.success() return Result.success()
} }
private fun addNotificationToStore(notification: CommentStore) {
val notificationStore = PrefManager.getNullableVal<List<CommentStore>>(
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( private fun createNotification(
notificationType: NotificationType, notificationType: NotificationType,
message: String, message: String,
@ -152,6 +200,10 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
color: String, color: String,
imageUrl: String imageUrl: String
): android.app.Notification? { ): android.app.Notification? {
Logger.log(
"Creating notification of type $notificationType" +
", message: $message, title: $title, mediaId: $mediaId, commentId: $commentId"
)
val notification = when (notificationType) { val notification = when (notificationType) {
NotificationType.COMMENT_WARNING -> { NotificationType.COMMENT_WARNING -> {
val intent = Intent(applicationContext, MainActivity::class.java).apply { val intent = Intent(applicationContext, MainActivity::class.java).apply {
@ -162,7 +214,7 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
} }
val pendingIntent = PendingIntent.getActivity( val pendingIntent = PendingIntent.getActivity(
applicationContext, applicationContext,
0, commentId,
intent, intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
) )
@ -185,7 +237,7 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
} }
val pendingIntent = PendingIntent.getActivity( val pendingIntent = PendingIntent.getActivity(
applicationContext, applicationContext,
0, commentId,
intent, intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
) )
@ -214,7 +266,7 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
} }
val pendingIntent = PendingIntent.getActivity( val pendingIntent = PendingIntent.getActivity(
applicationContext, applicationContext,
0, System.currentTimeMillis().toInt(),
intent, intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
) )
@ -266,6 +318,6 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
companion object { companion object {
val checkIntervals = arrayOf(0L, 720, 1440) val checkIntervals = arrayOf(0L, 720, 1440)
const val WORK_NAME = "ani.dantotsu.notifications.CommentNotificationWorker" const val WORK_NAME = "ani.dantotsu.notifications.comment.CommentNotificationWorker"
} }
} }

View file

@ -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
}
}

View file

@ -1,4 +1,4 @@
package ani.dantotsu.notifications package ani.dantotsu.notifications.comment
import ani.dantotsu.client import ani.dantotsu.client
import com.google.gson.Gson import com.google.gson.Gson

View file

@ -82,6 +82,9 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
} }
private fun getDub(): Boolean { private fun getDub(): Boolean {
if (sourceLanguage >= extension.sources.size) {
sourceLanguage = extension.sources.size - 1
}
val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource
?: return false ?: return false
currContext()?.let { context -> currContext()?.let { context ->
@ -103,6 +106,9 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
} }
fun setDub(setDub: Boolean) { fun setDub(setDub: Boolean) {
if (sourceLanguage >= extension.sources.size) {
sourceLanguage = extension.sources.size - 1
}
val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource val configurableSource = extension.sources[sourceLanguage] as? ConfigurableAnimeSource
?: return ?: return
val type = when (setDub) { val type = when (setDub) {

View file

@ -7,7 +7,10 @@ import ani.dantotsu.databinding.ItemChartBinding
import com.github.aachartmodel.aainfographics.aachartcreator.AAChartView import com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
import com.github.aachartmodel.aainfographics.aachartcreator.AAMoveOverEventMessageModel import com.github.aachartmodel.aainfographics.aachartcreator.AAMoveOverEventMessageModel
import com.github.aachartmodel.aainfographics.aachartcreator.AAOptions 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.BindableItem
import com.xwray.groupie.viewbinding.GroupieViewHolder
class ChartItem( class ChartItem(
private val title: String, private val title: String,
@ -31,6 +34,7 @@ class ChartItem(
) { ) {
} }
} }
binding.chartView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
binding.chartView.callBack = callback binding.chartView.callBack = callback
binding.chartView.reload() binding.chartView.reload()
binding.chartView.aa_drawChartWithChartOptions(aaOptions) binding.chartView.aa_drawChartWithChartOptions(aaOptions)
@ -49,4 +53,32 @@ class ChartItem(
override fun initializeViewBinding(view: View): ItemChartBinding { override fun initializeViewBinding(view: View): ItemChartBinding {
return ItemChartBinding.bind(view) return ItemChartBinding.bind(view)
} }
override fun bind(viewHolder: GroupieViewHolder<ItemChartBinding>, position: Int) {
viewHolder.setIsRecyclable(false)
super.bind(viewHolder, position)
}
override fun bind(
viewHolder: GroupieViewHolder<ItemChartBinding>,
position: Int,
payloads: MutableList<Any>
) {
viewHolder.setIsRecyclable(false)
super.bind(viewHolder, position, payloads)
}
override fun bind(
viewHolder: GroupieViewHolder<ItemChartBinding>,
position: Int,
payloads: MutableList<Any>,
onItemClickListener: OnItemClickListener?,
onItemLongClickListener: OnItemLongClickListener?
) {
viewHolder.setIsRecyclable(false)
super.bind(viewHolder, position, payloads, onItemClickListener, onItemLongClickListener)
}
override fun getViewType(): Int {
return 0
}
} }

View file

@ -68,8 +68,11 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
binding.profileViewPager.isUserInputEnabled = false binding.profileViewPager.isUserInputEnabled = false
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val userid = intent.getIntExtra("userId", 0) val userid = intent.getIntExtra("userId", -1)
val respond = Anilist.query.getUserProfile(userid) val username = intent.getStringExtra("username") ?: ""
val respond =
if (userid != -1) Anilist.query.getUserProfile(userid) else
Anilist.query.getUserProfile(username)
val user = respond?.data?.user val user = respond?.data?.user
if (user == null) { if (user == null) {
toast("User not found") toast("User not found")

View file

@ -65,7 +65,7 @@ class ProfileFragment : Fragment() {
binding.profileUserBio.setInitialScale(1) binding.profileUserBio.setInitialScale(1)
val styledHtml = getFullAniHTML( val styledHtml = getFullAniHTML(
user.about ?: "", user.about ?: "",
activity.getColor(R.color.bg_opp) ContextCompat.getColor(activity, R.color.bg_opp)
) )
binding.profileUserBio.loadDataWithBaseURL( binding.profileUserBio.loadDataWithBaseURL(
null, null,
@ -76,7 +76,7 @@ class ProfileFragment : Fragment() {
) )
binding.profileUserBio.setBackgroundColor( binding.profileUserBio.setBackgroundColor(
ContextCompat.getColor( ContextCompat.getColor(
requireContext(), activity,
android.R.color.transparent android.R.color.transparent
) )
) )
@ -86,7 +86,7 @@ class ProfileFragment : Fragment() {
super.onPageFinished(view, url) super.onPageFinished(view, url)
binding.profileUserBio.setBackgroundColor( binding.profileUserBio.setBackgroundColor(
ContextCompat.getColor( ContextCompat.getColor(
requireContext(), activity,
android.R.color.transparent android.R.color.transparent
) )
) )
@ -146,7 +146,7 @@ class ProfileFragment : Fragment() {
} }
binding.profileFavStaffRecycler.adapter = AuthorAdapter(favStaff) binding.profileFavStaffRecycler.adapter = AuthorAdapter(favStaff)
binding.profileFavStaffRecycler.layoutManager = LinearLayoutManager( binding.profileFavStaffRecycler.layoutManager = LinearLayoutManager(
requireContext(), activity,
LinearLayoutManager.HORIZONTAL, LinearLayoutManager.HORIZONTAL,
false false
) )
@ -155,7 +155,7 @@ class ProfileFragment : Fragment() {
} }
binding.profileFavCharactersRecycler.adapter = CharacterAdapter(favCharacter) binding.profileFavCharactersRecycler.adapter = CharacterAdapter(favCharacter)
binding.profileFavCharactersRecycler.layoutManager = LinearLayoutManager( binding.profileFavCharactersRecycler.layoutManager = LinearLayoutManager(
requireContext(), activity,
LinearLayoutManager.HORIZONTAL, LinearLayoutManager.HORIZONTAL,
false false
) )
@ -177,9 +177,9 @@ class ProfileFragment : Fragment() {
recyclerView.visibility = View.GONE recyclerView.visibility = View.GONE
if (it != null) { if (it != null) {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
recyclerView.adapter = MediaAdaptor(0, it, requireActivity(), fav=true) recyclerView.adapter = MediaAdaptor(0, it, activity, fav=true)
recyclerView.layoutManager = LinearLayoutManager( recyclerView.layoutManager = LinearLayoutManager(
requireContext(), activity,
LinearLayoutManager.HORIZONTAL, LinearLayoutManager.HORIZONTAL,
false false
) )

View file

@ -51,9 +51,9 @@ class StatsFragment :
user = arguments?.getSerializable("user") as Query.UserProfile user = arguments?.getSerializable("user") as Query.UserProfile
binding.statisticList.adapter = adapter binding.statisticList.adapter = adapter
binding.statisticList.setHasFixedSize(true) binding.statisticList.recycledViewPool.setMaxRecycledViews(0, 0)
binding.statisticList.isNestedScrollingEnabled = true binding.statisticList.isNestedScrollingEnabled = true
binding.statisticList.layoutManager = LinearLayoutManager(requireContext()) binding.statisticList.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
binding.statisticProgressBar.visibility = View.VISIBLE binding.statisticProgressBar.visibility = View.VISIBLE
binding.compare.visibility = if (user.id == Anilist.userid) View.GONE else View.VISIBLE binding.compare.visibility = if (user.id == Anilist.userid) View.GONE else View.VISIBLE
binding.filterContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin = statusBarHeight } binding.filterContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin = statusBarHeight }
@ -104,9 +104,15 @@ class StatsFragment :
binding.filterContainer.visibility = View.GONE binding.filterContainer.visibility = View.GONE
} }
override fun onPause() {
super.onPause()
binding.statisticList.visibility = View.GONE
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (this::binding.isInitialized) { if (this::binding.isInitialized) {
binding.statisticList.visibility = View.VISIBLE
binding.root.requestLayout() binding.root.requestLayout()
if (!loadedFirstTime) { if (!loadedFirstTime) {
activity.lifecycleScope.launch { activity.lifecycleScope.launch {

View file

@ -79,6 +79,10 @@ class ActivityItemBuilder {
NotificationType.MEDIA_DELETION -> { NotificationType.MEDIA_DELETION -> {
"${notification.deletedMediaTitle} has been deleted from the site" "${notification.deletedMediaTitle} has been deleted from the site"
} }
NotificationType.COMMENT_REPLY -> {
notification.context ?: "You should not see this"
}
} }
} }

View file

@ -19,6 +19,7 @@ import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -80,7 +81,8 @@ class FeedFragment : Fragment() {
binding.listRecyclerView.setOnTouchListener { _, event -> binding.listRecyclerView.setOnTouchListener { _, event ->
if (event?.action == MotionEvent.ACTION_UP) { if (event?.action == MotionEvent.ACTION_UP) {
if (activityList.size % AnilistQueries.ITEMS_PER_PAGE != 0 && !global) { 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 } else if (!scrollView.canScrollVertically(1) && !binding.feedRefresh.isVisible
&& binding.listRecyclerView.adapter!!.itemCount != 0 && && binding.listRecyclerView.adapter!!.itemCount != 0 &&
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1) (binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)

View file

@ -18,6 +18,7 @@ import ani.dantotsu.databinding.ActivityFollowBinding
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.notifications.comment.CommentStore
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
@ -29,6 +30,7 @@ import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.Locale
class NotificationActivity : AppCompatActivity() { class NotificationActivity : AppCompatActivity() {
private lateinit var binding: ActivityFollowBinding private lateinit var binding: ActivityFollowBinding
@ -73,7 +75,25 @@ class NotificationActivity : AppCompatActivity() {
notifications.filter { it.id == activityId } notifications.filter { it.id == activityId }
} else { } else {
notifications notifications
}.toMutableList()
val commentStore = PrefManager.getNullableVal<List<CommentStore>>(
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) }) adapter.update(notificationList.map { NotificationItem(it, ::onNotificationClick) })
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -81,7 +101,8 @@ class NotificationActivity : AppCompatActivity() {
binding.listRecyclerView.setOnTouchListener { _, event -> binding.listRecyclerView.setOnTouchListener { _, event ->
if (event?.action == MotionEvent.ACTION_UP) { if (event?.action == MotionEvent.ACTION_UP) {
if (adapter.itemCount % AnilistQueries.ITEMS_PER_PAGE != 0) { 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 } else if (!binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible
&& binding.listRecyclerView.adapter!!.itemCount != 0 && && binding.listRecyclerView.adapter!!.itemCount != 0 &&
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1) (binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
@ -105,7 +126,6 @@ class NotificationActivity : AppCompatActivity() {
} }
} }
} }
private fun loadPage(onFinish: () -> Unit = {}) { private fun loadPage(onFinish: () -> Unit = {}) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val res = Anilist.query.getNotifications(Anilist.userid ?: 0, page) 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) { when (type) {
NotificationClickType.USER -> { NotificationClickType.USER -> {
ContextCompat.startActivity( 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 -> { NotificationClickType.UNDEFINED -> {
// Do nothing // Do nothing
} }
@ -151,7 +181,7 @@ class NotificationActivity : AppCompatActivity() {
companion object { companion object {
enum class NotificationClickType { enum class NotificationClickType {
USER, MEDIA, ACTIVITY, UNDEFINED USER, MEDIA, ACTIVITY, COMMENT, UNDEFINED
} }
} }
} }

View file

@ -14,7 +14,7 @@ import com.xwray.groupie.viewbinding.BindableItem
class NotificationItem( class NotificationItem(
private val notification: Notification, private val notification: Notification,
val clickCallback: (Int, NotificationClickType) -> Unit val clickCallback: (Int, Int?, NotificationClickType) -> Unit
) : BindableItem<ItemNotificationBinding>() { ) : BindableItem<ItemNotificationBinding>() {
private lateinit var binding: ItemNotificationBinding private lateinit var binding: ItemNotificationBinding
override fun bind(viewBinding: ItemNotificationBinding, position: Int) { override fun bind(viewBinding: ItemNotificationBinding, position: Int) {
@ -31,7 +31,7 @@ class NotificationItem(
return ItemNotificationBinding.bind(view) 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 val cover = if (user) notification.user?.bannerImage
?: notification.user?.avatar?.medium else notification.media?.bannerImage ?: notification.user?.avatar?.medium else notification.media?.bannerImage
@ -52,7 +52,13 @@ class NotificationItem(
binding.notificationCover.visibility = View.GONE binding.notificationCover.visibility = View.GONE
binding.notificationCoverUser.visibility = View.VISIBLE binding.notificationCoverUser.visibility = View.VISIBLE
binding.notificationCoverUserContainer.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 binding.notificationBannerImage.layoutParams.height = userHeight
} else { } else {
binding.notificationCover.visibility = View.VISIBLE binding.notificationCover.visibility = View.VISIBLE
@ -75,12 +81,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -90,12 +96,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -105,12 +111,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.userId ?: 0, NotificationClickType.USER notification.userId ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -120,12 +126,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -135,12 +141,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -150,12 +156,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -165,12 +171,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -180,7 +186,7 @@ class NotificationItem(
image() image()
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( 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.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -205,12 +211,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -220,12 +226,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -235,12 +241,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -250,12 +256,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -265,7 +271,7 @@ class NotificationItem(
image() image()
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.media?.id ?: 0, NotificationClickType.MEDIA notification.media?.id ?: 0, null, NotificationClickType.MEDIA
) )
} }
} }
@ -275,7 +281,7 @@ class NotificationItem(
image() image()
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.media?.id ?: 0, NotificationClickType.MEDIA notification.media?.id ?: 0, null, NotificationClickType.MEDIA
) )
} }
} }
@ -285,7 +291,7 @@ class NotificationItem(
image() image()
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.media?.id ?: 0, NotificationClickType.MEDIA notification.media?.id ?: 0, null, NotificationClickType.MEDIA
) )
} }
} }
@ -293,6 +299,17 @@ class NotificationItem(
NotificationType.MEDIA_DELETION -> { NotificationType.MEDIA_DELETION -> {
binding.notificationCover.visibility = View.GONE 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
)
}
}
}
} }
} }

View file

@ -47,7 +47,7 @@ import ani.dantotsu.initActivity
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.notifications.CommentNotificationWorker import ani.dantotsu.notifications.comment.CommentNotificationWorker
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
import ani.dantotsu.openLinkInBrowser import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.AppUpdater import ani.dantotsu.others.AppUpdater

View file

@ -3,6 +3,7 @@ package ani.dantotsu.settings.saving
import android.graphics.Color import android.graphics.Color
import ani.dantotsu.connections.comments.AuthResponse import ani.dantotsu.connections.comments.AuthResponse
import ani.dantotsu.connections.mal.MAL 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.Location
import ani.dantotsu.settings.saving.internal.Pref 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)), CommentTokenExpiry(Pref(Location.Irrelevant, Long::class, 0L)),
LogToFile(Pref(Location.Irrelevant, Boolean::class, false)), LogToFile(Pref(Location.Irrelevant, Boolean::class, false)),
RecentGlobalNotification(Pref(Location.Irrelevant, Int::class, 0)), RecentGlobalNotification(Pref(Location.Irrelevant, Int::class, 0)),
CommentNotificationStore(Pref(Location.Irrelevant, List::class, listOf<CommentStore>())),
UnreadCommentNotifications(Pref(Location.Irrelevant, Int::class, 0)),
//Protected //Protected
DiscordToken(Pref(Location.Protected, String::class, "")), DiscordToken(Pref(Location.Protected, String::class, "")),

View file

@ -2,6 +2,7 @@ package ani.dantotsu.util
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build
import android.util.Log import android.util.Log
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import ani.dantotsu.BuildConfig import ani.dantotsu.BuildConfig
@ -31,8 +32,8 @@ object Logger {
} }
file?.writeText("log started\n") file?.writeText("log started\n")
file?.appendText("date/time: ${Date()}\n") file?.appendText("date/time: ${Date()}\n")
file?.appendText("device: ${android.os.Build.MODEL}\n") file?.appendText("device: ${Build.MODEL}\n")
file?.appendText("os version: ${android.os.Build.VERSION.RELEASE}\n") file?.appendText("os version: ${Build.VERSION.RELEASE}\n")
file?.appendText( file?.appendText(
"app version: ${ "app version: ${
context.packageManager.getPackageInfo( context.packageManager.getPackageInfo(
@ -46,29 +47,35 @@ object Logger {
context.packageManager.getPackageInfo( context.packageManager.getPackageInfo(
context.packageName, context.packageName,
0 0
).versionCode ).run {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
longVersionCode
else
@Suppress("DEPRECATION") versionCode
}
}\n" }\n"
) )
file?.appendText("sdk version: ${android.os.Build.VERSION.SDK_INT}\n") file?.appendText("sdk version: ${Build.VERSION.SDK_INT}\n")
file?.appendText("manufacturer: ${android.os.Build.MANUFACTURER}\n") file?.appendText("manufacturer: ${Build.MANUFACTURER}\n")
file?.appendText("brand: ${android.os.Build.BRAND}\n") file?.appendText("brand: ${Build.BRAND}\n")
file?.appendText("product: ${android.os.Build.PRODUCT}\n") file?.appendText("product: ${Build.PRODUCT}\n")
file?.appendText("device: ${android.os.Build.DEVICE}\n") file?.appendText("device: ${Build.DEVICE}\n")
file?.appendText("hardware: ${android.os.Build.HARDWARE}\n") file?.appendText("hardware: ${Build.HARDWARE}\n")
file?.appendText("host: ${android.os.Build.HOST}\n") file?.appendText("host: ${Build.HOST}\n")
file?.appendText("id: ${android.os.Build.ID}\n") file?.appendText("id: ${Build.ID}\n")
file?.appendText("type: ${android.os.Build.TYPE}\n") file?.appendText("type: ${Build.TYPE}\n")
file?.appendText("user: ${android.os.Build.USER}\n") file?.appendText("user: ${Build.USER}\n")
file?.appendText("tags: ${android.os.Build.TAGS}\n") file?.appendText("tags: ${Build.TAGS}\n")
file?.appendText("time: ${android.os.Build.TIME}\n") file?.appendText("time: ${Build.TIME}\n")
file?.appendText("radio: ${android.os.Build.RADIO}\n") file?.appendText("radio: ${Build.getRadioVersion()}\n")
file?.appendText("bootloader: ${android.os.Build.BOOTLOADER}\n") file?.appendText("bootloader: ${Build.BOOTLOADER}\n")
file?.appendText("board: ${android.os.Build.BOARD}\n") file?.appendText("board: ${Build.BOARD}\n")
file?.appendText("fingerprint: ${android.os.Build.FINGERPRINT}\n") file?.appendText("fingerprint: ${Build.FINGERPRINT}\n")
file?.appendText("supported_abis: ${android.os.Build.SUPPORTED_ABIS.joinToString()}\n") file?.appendText("supported_abis: ${Build.SUPPORTED_ABIS.joinToString()}\n")
file?.appendText("supported_32_bit_abis: ${android.os.Build.SUPPORTED_32_BIT_ABIS.joinToString()}\n") file?.appendText("supported_32_bit_abis: ${Build.SUPPORTED_32_BIT_ABIS.joinToString()}\n")
file?.appendText("supported_64_bit_abis: ${android.os.Build.SUPPORTED_64_BIT_ABIS.joinToString()}\n") file?.appendText("supported_64_bit_abis: ${Build.SUPPORTED_64_BIT_ABIS.joinToString()}\n")
file?.appendText("is emulator: ${android.os.Build.FINGERPRINT.contains("generic")}\n") file?.appendText("is emulator: ${Build.FINGERPRINT.contains("generic")}\n")
file?.appendText("--------------------------------\n") file?.appendText("--------------------------------\n")
} catch (e: Exception) { } catch (e: Exception) {
Injekt.get<CrashlyticsInterface>().logException(e) Injekt.get<CrashlyticsInterface>().logException(e)

View file

@ -147,7 +147,7 @@ object Notifications {
setGroup(GROUP_COMMENTS) setGroup(GROUP_COMMENTS)
}, },
buildNotificationChannel(CHANNEL_COMMENT_WARING, IMPORTANCE_HIGH) { buildNotificationChannel(CHANNEL_COMMENT_WARING, IMPORTANCE_HIGH) {
setName("CommentNotificationWorker Warnings") setName("Comment Warnings")
setGroup(GROUP_COMMENTS) setGroup(GROUP_COMMENTS)
}, },
buildNotificationChannel(CHANNEL_ANILIST, IMPORTANCE_DEFAULT) { buildNotificationChannel(CHANNEL_ANILIST, IMPORTANCE_DEFAULT) {

View file

@ -14,6 +14,7 @@ import android.os.PowerManager
import android.util.TypedValue import android.util.TypedValue
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker import androidx.core.content.PermissionChecker
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.graphics.alpha import androidx.core.graphics.alpha
@ -85,7 +86,7 @@ fun Context.getThemeColor(attr: Int): Int {
val tv = TypedValue() val tv = TypedValue()
return if (this.theme.resolveAttribute(attr, tv, true)) { return if (this.theme.resolveAttribute(attr, tv, true)) {
if (tv.resourceId != 0) { if (tv.resourceId != 0) {
getColor(tv.resourceId) ContextCompat.getColor(this, tv.resourceId)
} else { } else {
tv.data tv.data
} }

View file

@ -10,6 +10,7 @@ import androidx.core.app.NotificationChannelGroupCompat
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.app.NotificationManagerCompat.NotificationWithIdAndTag import androidx.core.app.NotificationManagerCompat.NotificationWithIdAndTag
import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker import androidx.core.content.PermissionChecker
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
@ -65,7 +66,7 @@ fun Context.notificationBuilder(
block: (NotificationCompat.Builder.() -> Unit)? = null block: (NotificationCompat.Builder.() -> Unit)? = null
): NotificationCompat.Builder { ): NotificationCompat.Builder {
val builder = NotificationCompat.Builder(this, channelId) 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) { if (block != null) {
builder.block() builder.block()
} }

View file

@ -125,6 +125,7 @@
android:id="@+id/statisticList" android:id="@+id/statisticList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"
android:nestedScrollingEnabled="true" android:nestedScrollingEnabled="true"
tools:listitem="@layout/item_chart" /> tools:listitem="@layout/item_chart" />
</LinearLayout> </LinearLayout>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Dantotsu" parent="Theme.Base">
<item name="android:windowLightStatusBar">false</item>
</style>
</resources>

View file

@ -486,6 +486,7 @@
<string name="read_on_dantotsu">Read on Dantotsu</string> <string name="read_on_dantotsu">Read on Dantotsu</string>
<string name="watch_on_dantotsu">Watch on Dantotsu</string> <string name="watch_on_dantotsu">Watch on Dantotsu</string>
<string name="view_profile_in_dantotsu">View Profile in Dantotsu</string>
<string name="continue_episode">"Continue : Episode "</string> <string name="continue_episode">"Continue : Episode "</string>
<string name="continue_chapter">"Continue : "</string> <string name="continue_chapter">"Continue : "</string>
<string name="episode">"Episode "</string> <string name="episode">"Episode "</string>

View file

@ -29,10 +29,6 @@
</item> </item>
</style> </style>
<style name="Theme.Dantotsu" parent="Theme.Base">
<item name="android:windowLightStatusBar">false</item>
</style>
<style name="Theme.Dantotsu.NoActionBar"> <style name="Theme.Dantotsu.NoActionBar">
<item name="windowActionBar">false</item> <item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>