mirror of
https://github.com/rebelonion/Dantotsu.git
synced 2026-01-25 02:01:01 +00:00
Merge branch 'rebelonion:dev' into dev
This commit is contained in:
@@ -200,6 +200,7 @@
|
||||
android:parentActivityName=".MainActivity" />
|
||||
<activity
|
||||
android:name=".util.MarkdownCreatorActivity"/>
|
||||
<activity android:name=".parsers.ParserTestActivity" />
|
||||
<activity
|
||||
android:name=".media.ReviewActivity"
|
||||
android:parentActivityName=".media.MediaDetailsActivity" />
|
||||
|
||||
@@ -21,6 +21,20 @@ import eu.kanade.tachiyomi.util.system.getApplicationIcon
|
||||
|
||||
class AddonLoader {
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Load an extension from a package name with a specific class name
|
||||
* @param context the context
|
||||
* @param packageName the package name of the extension
|
||||
* @param type the type of extension
|
||||
* @return the loaded extension
|
||||
* @throws IllegalStateException if the extension is not of the correct type
|
||||
* @throws ClassNotFoundException if the extension class is not found
|
||||
* @throws NoClassDefFoundError if the extension class is not found
|
||||
* @throws Exception if any other error occurs
|
||||
* @throws PackageManager.NameNotFoundException if the package is not found
|
||||
* @throws IllegalStateException if the extension is not found
|
||||
*/
|
||||
fun loadExtension(
|
||||
context: Context,
|
||||
packageName: String,
|
||||
@@ -70,11 +84,11 @@ class AddonLoader {
|
||||
val loadedClass = try {
|
||||
Class.forName(className, false, classLoader)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
Logger.log("Extension load error: $extName ($className)")
|
||||
Logger.log("ClassNotFoundException load error: $extName ($className)")
|
||||
Logger.log(e)
|
||||
throw e
|
||||
} catch (e: NoClassDefFoundError) {
|
||||
Logger.log("Extension load error: $extName ($className)")
|
||||
Logger.log("NoClassDefFoundError load error: $extName ($className)")
|
||||
Logger.log(e)
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
@@ -117,24 +131,43 @@ class AddonLoader {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an extension from a package name (class is determined by type)
|
||||
* @param context the context
|
||||
* @param packageName the package name of the extension
|
||||
* @param type the type of extension
|
||||
* @return the loaded extension
|
||||
*/
|
||||
fun loadFromPkgName(context: Context, packageName: String, type: AddonType): LoadResult? {
|
||||
return when (type) {
|
||||
AddonType.TORRENT -> loadExtension(
|
||||
context,
|
||||
packageName,
|
||||
TorrentAddonManager.TORRENT_CLASS,
|
||||
type
|
||||
)
|
||||
return try {
|
||||
when (type) {
|
||||
AddonType.TORRENT -> loadExtension(
|
||||
context,
|
||||
packageName,
|
||||
TorrentAddonManager.TORRENT_CLASS,
|
||||
type
|
||||
)
|
||||
|
||||
AddonType.DOWNLOAD -> loadExtension(
|
||||
context,
|
||||
packageName,
|
||||
DownloadAddonManager.DOWNLOAD_CLASS,
|
||||
type
|
||||
)
|
||||
AddonType.DOWNLOAD -> loadExtension(
|
||||
context,
|
||||
packageName,
|
||||
DownloadAddonManager.DOWNLOAD_CLASS,
|
||||
type
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Logger.log("Error loading extension from package name: $packageName")
|
||||
Logger.log(e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a package is an extension by comparing the package name
|
||||
* @param type the type of extension
|
||||
* @param pkgInfo the package info
|
||||
* @return true if the package is an extension
|
||||
*/
|
||||
private fun isPackageAnExtension(type: String, pkgInfo: PackageInfo): Boolean {
|
||||
return pkgInfo.packageName.equals(type)
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class AnilistQueries {
|
||||
media.cameFromContinue = false
|
||||
|
||||
val query =
|
||||
"""{Media(id:${media.id}){id favourites popularity episodes chapters mediaListEntry{id status score(format:POINT_100)progress private notes repeat customLists updatedAt startedAt{year month day}completedAt{year month day}}isFavourite siteUrl idMal nextAiringEpisode{episode airingAt}source countryOfOrigin format duration season seasonYear startDate{year month day}endDate{year month day}genres studios(isMain:true){nodes{id name siteUrl}}description trailer{site id}synonyms tags{name rank isMediaSpoiler}characters(sort:[ROLE,FAVOURITES_DESC],perPage:25,page:1){edges{role voiceActors { id name { first middle last full native userPreferred } image { large medium } languageV2 } node{id image{medium}name{userPreferred}isFavourite}}}relations{edges{relationType(version:2)node{id idMal mediaListEntry{progress private score(format:POINT_100)status}episodes chapters nextAiringEpisode{episode}popularity meanScore isAdult isFavourite format title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}staffPreview:staff(perPage:8,sort:[RELEVANCE,ID]){edges{role node{id image{large medium}name{userPreferred}}}}recommendations(sort:RATING_DESC){nodes{mediaRecommendation{id idMal mediaListEntry{progress private score(format:POINT_100)status}episodes chapters nextAiringEpisode{episode}meanScore isAdult isFavourite format title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}externalLinks{url site}}Page(page:1){pageInfo{total perPage currentPage lastPage hasNextPage}mediaList(isFollowing:true,sort:[STATUS],mediaId:${media.id}){id status score(format: POINT_100) progress progressVolumes user{id name avatar{large medium}}}}}"""
|
||||
"""{Media(id:${media.id}){id favourites popularity episodes chapters mediaListEntry{id status score(format:POINT_100)progress private notes repeat customLists updatedAt startedAt{year month day}completedAt{year month day}}reviews(perPage:3, sort:SCORE_DESC){nodes{id mediaId mediaType summary body(asHtml:true) rating ratingAmount userRating score private siteUrl createdAt updatedAt user{id name bannerImage avatar{medium large}}}}isFavourite siteUrl idMal nextAiringEpisode{episode airingAt}source countryOfOrigin format duration season seasonYear startDate{year month day}endDate{year month day}genres studios(isMain:true){nodes{id name siteUrl}}description trailer{site id}synonyms tags{name rank isMediaSpoiler}characters(sort:[ROLE,FAVOURITES_DESC],perPage:25,page:1){edges{role voiceActors { id name { first middle last full native userPreferred } image { large medium } languageV2 } node{id image{medium}name{userPreferred}isFavourite}}}relations{edges{relationType(version:2)node{id idMal mediaListEntry{progress private score(format:POINT_100)status}episodes chapters nextAiringEpisode{episode}popularity meanScore isAdult isFavourite format title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}staffPreview:staff(perPage:8,sort:[RELEVANCE,ID]){edges{role node{id image{large medium}name{userPreferred}}}}recommendations(sort:RATING_DESC){nodes{mediaRecommendation{id idMal mediaListEntry{progress private score(format:POINT_100)status}episodes chapters nextAiringEpisode{episode}meanScore isAdult isFavourite format title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}externalLinks{url site}}Page(page:1){pageInfo{total perPage currentPage lastPage hasNextPage}mediaList(isFollowing:true,sort:[STATUS],mediaId:${media.id}){id status score(format: POINT_100) progress progressVolumes user{id name avatar{large medium}}}}}"""
|
||||
runBlocking {
|
||||
val anilist = async {
|
||||
var response = executeQuery<Query.Media>(query, force = true)
|
||||
@@ -211,6 +211,9 @@ class AnilistQueries {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fetchedMedia.reviews?.nodes != null){
|
||||
media.review = fetchedMedia.reviews!!.nodes as ArrayList<Query.Review>
|
||||
}
|
||||
if (user?.mediaList?.isNotEmpty() == true) {
|
||||
media.users = user.mediaList?.mapNotNull {
|
||||
it.user?.let { user ->
|
||||
@@ -1505,7 +1508,7 @@ Page(page:$page,perPage:50) {
|
||||
return author
|
||||
}
|
||||
|
||||
suspend fun getReviews(mediaId: Int, page: Int = 1, sort: String = "UPDATED_AT_DESC"): Query.ReviewsResponse? {
|
||||
suspend fun getReviews(mediaId: Int, page: Int = 1, sort: String = "SCORE_DESC"): Query.ReviewsResponse? {
|
||||
return executeQuery<Query.ReviewsResponse>(
|
||||
"""{Page(page:$page,perPage:10){pageInfo{currentPage,hasNextPage,total}reviews(mediaId:$mediaId,sort:$sort){id,mediaId,mediaType,summary,body(asHtml:true)rating,ratingAmount,userRating,score,private,siteUrl,createdAt,updatedAt,user{id,name,bannerImage avatar{medium,large}}}}}""",
|
||||
force = true
|
||||
|
||||
@@ -152,7 +152,7 @@ data class Media(
|
||||
@SerialName("mediaListEntry") var mediaListEntry: MediaList?,
|
||||
|
||||
// User reviews of the media
|
||||
// @SerialName("reviews") var reviews: ReviewConnection?,
|
||||
@SerialName("reviews") var reviews: ReviewConnection?,
|
||||
|
||||
// User recommendations for similar media
|
||||
@SerialName("recommendations") var recommendations: RecommendationConnection?,
|
||||
@@ -537,4 +537,9 @@ data class MediaListGroup(
|
||||
@SerialName("isSplitCompletedList") var isSplitCompletedList: Boolean?,
|
||||
|
||||
@SerialName("status") var status: MediaListStatus?,
|
||||
) : java.io.Serializable
|
||||
) : java.io.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ReviewConnection(
|
||||
@SerialName("nodes") var nodes: List<Query.Review>?,
|
||||
)
|
||||
@@ -26,7 +26,8 @@ enum class NotificationType(val value: String) {
|
||||
//custom
|
||||
COMMENT_REPLY("COMMENT_REPLY"),
|
||||
COMMENT_WARNING("COMMENT_WARNING"),
|
||||
DANTOTSU_UPDATE("DANTOTSU_UPDATE");
|
||||
DANTOTSU_UPDATE("DANTOTSU_UPDATE"),
|
||||
SUBSCRIPTION("SUBSCRIPTION");
|
||||
|
||||
fun toFormattedString(): String {
|
||||
return this.value.replace("_", " ").lowercase(Locale.ROOT)
|
||||
|
||||
@@ -84,15 +84,15 @@ class Contributors {
|
||||
"https://anilist.co/user/6244220"
|
||||
),
|
||||
Developer(
|
||||
"Zaidsenior",
|
||||
"Ziadsenior",
|
||||
"https://s4.anilist.co/file/anilistcdn/user/avatar/large/b6049773-8cjYeUOFUguv.jpg",
|
||||
"Comment Moderator",
|
||||
"Comment Moderator and Arabic Translator",
|
||||
"https://anilist.co/user/6049773"
|
||||
),
|
||||
Developer(
|
||||
"hastsu",
|
||||
"https://cdn.discordapp.com/avatars/602422545077108749/20b4a6efa4314550e4ed51cdbe4fef3d.webp?size=160",
|
||||
"Comment Moderator",
|
||||
"Comment Moderator and Arabic Translator",
|
||||
"https://anilist.co/user/6183359"
|
||||
),
|
||||
)
|
||||
|
||||
@@ -12,13 +12,14 @@ import ani.dantotsu.getThemeColor
|
||||
|
||||
class CircleView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
|
||||
private var parts: Int = 3
|
||||
private var gapAngle: Float = 9f
|
||||
private var gapAngle: Float = 12f
|
||||
private val path = Path()
|
||||
private var isUser = false
|
||||
private var booleanList = listOf<Boolean>()
|
||||
private val paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = 6f
|
||||
strokeCap = Paint.Cap.ROUND
|
||||
}
|
||||
|
||||
@SuppressLint("DrawAllocation")
|
||||
|
||||
@@ -42,6 +42,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
class Stories @JvmOverloads constructor(
|
||||
@@ -264,29 +265,44 @@ class Stories @JvmOverloads constructor(
|
||||
|
||||
|
||||
private var startClickTime = 0L
|
||||
private var startX = 0f
|
||||
private var startY = 0f
|
||||
private var isLongPress = false
|
||||
private val swipeThreshold = 100
|
||||
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
|
||||
val maxClickDuration = 200
|
||||
when (event?.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
startX = event.x
|
||||
startY = event.y
|
||||
startClickTime = Calendar.getInstance().timeInMillis
|
||||
pause()
|
||||
isLongPress = false
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val deltaX = event.x - startX
|
||||
val deltaY = event.y - startY
|
||||
if (!isLongPress && (abs(deltaX) > swipeThreshold || abs(deltaY) > swipeThreshold)) {
|
||||
isLongPress = true
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
val clickDuration = Calendar.getInstance().timeInMillis - startClickTime
|
||||
if (clickDuration < maxClickDuration) {
|
||||
//click occurred
|
||||
view?.let {
|
||||
if (it.id == R.id.leftTouchPanel) {
|
||||
leftPanelTouch()
|
||||
} else if (it.id == R.id.rightTouchPanel) {
|
||||
rightPanelTouch()
|
||||
}
|
||||
if (clickDuration < maxClickDuration && !isLongPress) {
|
||||
when (view?.id) {
|
||||
R.id.leftTouchPanel -> leftPanelTouch()
|
||||
R.id.rightTouchPanel -> rightPanelTouch()
|
||||
}
|
||||
} else {
|
||||
//hold click occurred
|
||||
resume()
|
||||
}
|
||||
val deltaX = event.x - startX
|
||||
if (abs(deltaX) > swipeThreshold) {
|
||||
if (deltaX > 0) onStoriesPrevious()
|
||||
else onStoriesCompleted()
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -359,16 +375,14 @@ class Stories @JvmOverloads constructor(
|
||||
)
|
||||
}
|
||||
fun visible(isList: Boolean) {
|
||||
val visible = if (isList) View.VISIBLE else View.GONE
|
||||
val gone = if (isList) View.GONE else View.VISIBLE
|
||||
binding.textActivity.visibility = gone
|
||||
binding.textActivityContainer.visibility = gone
|
||||
binding.infoText.visibility = visible
|
||||
binding.coverImage.visibility = visible
|
||||
binding.textActivity.isVisible = !isList
|
||||
binding.textActivityContainer.isVisible = !isList
|
||||
binding.infoText.isVisible = isList
|
||||
binding.coverImage.isVisible = isList
|
||||
binding.infoText.visibility = if (isList) View.VISIBLE else View.INVISIBLE
|
||||
binding.infoText.text = ""
|
||||
binding.contentImageViewKen.visibility = visible
|
||||
binding.contentImageView.visibility = visible
|
||||
binding.contentImageViewKen.isVisible = isList
|
||||
binding.contentImageView.isVisible = isList
|
||||
}
|
||||
|
||||
when (story.typename) {
|
||||
@@ -383,16 +397,15 @@ class Stories @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
} ${story.progress ?: story.media?.title?.userPreferred} " +
|
||||
if (
|
||||
story.status?.contains("completed") == false &&
|
||||
!story.status.contains("plans") &&
|
||||
!story.status.contains("repeating")
|
||||
)
|
||||
{
|
||||
"of ${story.media?.title?.userPreferred}"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
if (
|
||||
story.status?.contains("completed") == false &&
|
||||
!story.status.contains("plans") &&
|
||||
!story.status.contains("repeating")
|
||||
) {
|
||||
"of ${story.media?.title?.userPreferred}"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
binding.infoText.text = text
|
||||
val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
|
||||
blurImage(
|
||||
@@ -404,18 +417,16 @@ class Stories @JvmOverloads constructor(
|
||||
ContextCompat.startActivity(
|
||||
context,
|
||||
Intent(context, MediaDetailsActivity::class.java).putExtra(
|
||||
"mediaId",
|
||||
story.media?.id
|
||||
),
|
||||
"mediaId",
|
||||
story.media?.id
|
||||
),
|
||||
ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||
activity,
|
||||
binding.coverImage,
|
||||
ViewCompat.getTransitionName(binding.coverImage)!!
|
||||
).toBundle()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"TextActivity" -> {
|
||||
@@ -445,11 +456,13 @@ class Stories @JvmOverloads constructor(
|
||||
val likeColor = ContextCompat.getColor(context, R.color.yt_red)
|
||||
val notLikeColor = ContextCompat.getColor(context, R.color.bg_opp)
|
||||
binding.activityRepliesContainer.setOnClickListener {
|
||||
RepliesBottomDialog.newInstance(story.id).show(activity.supportFragmentManager, "replies")
|
||||
RepliesBottomDialog.newInstance(story.id)
|
||||
.show(activity.supportFragmentManager, "replies")
|
||||
}
|
||||
binding.activityLike.setColorFilter(if (story.isLiked == true) likeColor else notLikeColor)
|
||||
binding.replyCount.text = story.replyCount.toString()
|
||||
binding.activityLikeCount.text = story.likeCount.toString()
|
||||
binding.activityLike.setColorFilter(if (story.isLiked == true) likeColor else notLikeColor)
|
||||
binding.activityReplies.setColorFilter(ContextCompat.getColor(context, R.color.bg_opp))
|
||||
binding.activityLikeContainer.setOnClickListener {
|
||||
like()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import ani.dantotsu.connections.anilist.api.FuzzyDate
|
||||
import ani.dantotsu.connections.anilist.api.MediaEdge
|
||||
import ani.dantotsu.connections.anilist.api.MediaList
|
||||
import ani.dantotsu.connections.anilist.api.MediaType
|
||||
import ani.dantotsu.connections.anilist.api.Query
|
||||
import ani.dantotsu.media.anime.Anime
|
||||
import ani.dantotsu.media.manga.Manga
|
||||
import ani.dantotsu.profile.User
|
||||
@@ -62,6 +63,7 @@ data class Media(
|
||||
var timeUntilAiring: Long? = null,
|
||||
|
||||
var characters: ArrayList<Character>? = null,
|
||||
var review: ArrayList<Query.Review>? = null,
|
||||
var staff: ArrayList<Author>? = null,
|
||||
var prequel: Media? = null,
|
||||
var sequel: Media? = null,
|
||||
|
||||
@@ -34,7 +34,6 @@ import ani.dantotsu.databinding.ItemChipBinding
|
||||
import ani.dantotsu.databinding.ItemQuelsBinding
|
||||
import ani.dantotsu.databinding.ItemTitleChipgroupBinding
|
||||
import ani.dantotsu.databinding.ItemTitleRecyclerBinding
|
||||
import ani.dantotsu.databinding.ItemTitleSearchBinding
|
||||
import ani.dantotsu.databinding.ItemTitleTextBinding
|
||||
import ani.dantotsu.databinding.ItemTitleTrailerBinding
|
||||
import ani.dantotsu.displayTimer
|
||||
@@ -46,6 +45,7 @@ import ani.dantotsu.px
|
||||
import ani.dantotsu.setSafeOnClickListener
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -81,7 +81,8 @@ class MediaInfoFragment : Fragment() {
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val model: MediaDetailsViewModel by activityViewModels()
|
||||
val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode) || !isOnline(requireContext())
|
||||
val offline: Boolean =
|
||||
PrefManager.getVal(PrefName.OfflineMode) || !isOnline(requireContext())
|
||||
binding.mediaInfoProgressBar.isGone = loaded
|
||||
binding.mediaInfoContainer.isVisible = loaded
|
||||
binding.mediaInfoContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin += 128f.px + navBarHeight }
|
||||
@@ -254,7 +255,8 @@ class MediaInfoFragment : Fragment() {
|
||||
if (!media.users.isNullOrEmpty() && !offline) {
|
||||
val users: ArrayList<User> = media.users ?: arrayListOf()
|
||||
if (Anilist.token != null && media.userStatus != null) {
|
||||
users.add(0,
|
||||
users.add(
|
||||
0,
|
||||
User(
|
||||
id = Anilist.userid!!,
|
||||
name = getString(R.string.you),
|
||||
@@ -263,7 +265,8 @@ class MediaInfoFragment : Fragment() {
|
||||
status = media.userStatus,
|
||||
score = media.userScore.toFloat(),
|
||||
progress = media.userProgress,
|
||||
totalEpisodes = media.anime?.totalEpisodes ?: media.manga?.totalChapters,
|
||||
totalEpisodes = media.anime?.totalEpisodes
|
||||
?: media.manga?.totalChapters,
|
||||
nextAiringEpisode = media.anime?.nextAiringEpisode
|
||||
)
|
||||
)
|
||||
@@ -519,22 +522,41 @@ class MediaInfoFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
ItemTitleSearchBinding.inflate(
|
||||
LayoutInflater.from(context),
|
||||
parent,
|
||||
false
|
||||
).apply {
|
||||
|
||||
titleSearchImage.loadImage(media.banner ?: media.cover)
|
||||
titleSearchText.text =
|
||||
getString(R.string.reviews)
|
||||
titleSearchCard.setSafeOnClickListener {
|
||||
val query = Intent(requireContext(), ReviewActivity::class.java)
|
||||
.putExtra("mediaId", media.id)
|
||||
ContextCompat.startActivity(requireContext(), query, null)
|
||||
if (!media.review.isNullOrEmpty()) {
|
||||
ItemTitleRecyclerBinding.inflate(
|
||||
LayoutInflater.from(context),
|
||||
parent,
|
||||
false
|
||||
).apply {
|
||||
fun onUserClick(userId: Int) {
|
||||
val review = media.review!!.find { i -> i.id == userId }
|
||||
if (review != null) {
|
||||
startActivity(
|
||||
Intent(requireContext(), ReviewViewActivity::class.java)
|
||||
.putExtra("review", review)
|
||||
)
|
||||
}
|
||||
}
|
||||
val adapter = GroupieAdapter()
|
||||
media.review!!.forEach {
|
||||
adapter.add(ReviewAdapter(it, ::onUserClick))
|
||||
}
|
||||
itemTitle.setText(R.string.reviews)
|
||||
itemRecycler.adapter = adapter
|
||||
itemRecycler.layoutManager = LinearLayoutManager(
|
||||
requireContext(),
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false
|
||||
)
|
||||
itemMore.visibility = View.VISIBLE
|
||||
itemMore.setSafeOnClickListener {
|
||||
startActivity(
|
||||
Intent(requireContext(), ReviewActivity::class.java)
|
||||
.putExtra("mediaId", media.id)
|
||||
)
|
||||
}
|
||||
parent.addView(root)
|
||||
}
|
||||
|
||||
parent.addView(root)
|
||||
}
|
||||
|
||||
ItemTitleRecyclerBinding.inflate(
|
||||
|
||||
@@ -77,7 +77,7 @@ class ReviewActivity : AppCompatActivity() {
|
||||
binding.listBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val response = Anilist.query.getReviews(mediaId)
|
||||
val response = Anilist.query.getReviews(mediaId)?.data?.page
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.listProgressBar.visibility = View.GONE
|
||||
binding.listRecyclerView.setOnTouchListener { _, event ->
|
||||
@@ -94,9 +94,9 @@ class ReviewActivity : AppCompatActivity() {
|
||||
}
|
||||
false
|
||||
}
|
||||
currentPage = response?.data?.page?.pageInfo?.currentPage ?: 1
|
||||
hasNextPage = response?.data?.page?.pageInfo?.hasNextPage ?: false
|
||||
response?.data?.page?.reviews?.let {
|
||||
currentPage = response?.pageInfo?.currentPage ?: 1
|
||||
hasNextPage = response?.pageInfo?.hasNextPage ?: false
|
||||
response?.reviews?.let {
|
||||
reviews.addAll(it)
|
||||
fillList()
|
||||
}
|
||||
@@ -122,29 +122,9 @@ class ReviewActivity : AppCompatActivity() {
|
||||
private fun fillList() {
|
||||
adapter.clear()
|
||||
reviews.forEach {
|
||||
val username = it.user?.name ?: "Unknown"
|
||||
val name = SpannableString(username + " - " + it.score)
|
||||
//change the size of the score
|
||||
name.setSpan(
|
||||
android.text.style.RelativeSizeSpan(0.9f),
|
||||
0,
|
||||
name.length,
|
||||
android.text.Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
//give the text an underline
|
||||
name.setSpan(
|
||||
android.text.style.UnderlineSpan(),
|
||||
username.length + 3,
|
||||
name.length,
|
||||
android.text.Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
adapter.add(
|
||||
FollowerItem(
|
||||
it.id,
|
||||
name,
|
||||
it.user?.avatar?.medium,
|
||||
it.user?.bannerImage,
|
||||
it.summary,
|
||||
ReviewAdapter(
|
||||
it,
|
||||
this::onUserClick
|
||||
)
|
||||
)
|
||||
|
||||
125
app/src/main/java/ani/dantotsu/media/ReviewAdapter.kt
Normal file
125
app/src/main/java/ani/dantotsu/media/ReviewAdapter.kt
Normal file
@@ -0,0 +1,125 @@
|
||||
package ani.dantotsu.media
|
||||
|
||||
|
||||
import android.view.View
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.api.Query
|
||||
import ani.dantotsu.databinding.ItemReviewsBinding
|
||||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.profile.activity.ActivityItemBuilder
|
||||
import ani.dantotsu.toast
|
||||
import com.xwray.groupie.viewbinding.BindableItem
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ReviewAdapter(
|
||||
private var review: Query.Review,
|
||||
val clickCallback: (Int) -> Unit
|
||||
) : BindableItem<ItemReviewsBinding>() {
|
||||
private lateinit var binding: ItemReviewsBinding
|
||||
|
||||
override fun bind(viewBinding: ItemReviewsBinding, position: Int) {
|
||||
binding = viewBinding
|
||||
binding.reviewUserName.text = review.user?.name
|
||||
binding.reviewUserAvatar.loadImage(review.user?.avatar?.medium)
|
||||
binding.reviewText.text = review.summary
|
||||
binding.reviewPostTime.text = ActivityItemBuilder.getDateTime(review.createdAt)
|
||||
val text = "[${review.score/ 10.0f}]"
|
||||
binding.reviewTag.text = text
|
||||
binding.root.setOnClickListener { clickCallback(review.id) }
|
||||
userVote(review.userRating)
|
||||
enableVote()
|
||||
binding.reviewTotalVotes.text = review.rating.toString()
|
||||
}
|
||||
|
||||
override fun getLayout(): Int {
|
||||
return R.layout.item_reviews
|
||||
}
|
||||
|
||||
override fun initializeViewBinding(view: View): ItemReviewsBinding {
|
||||
return ItemReviewsBinding.bind(view)
|
||||
}
|
||||
private fun userVote(type: String) {
|
||||
when (type) {
|
||||
"NO_VOTE" -> {
|
||||
binding.reviewUpVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
|
||||
binding.reviewDownVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
|
||||
binding.reviewUpVote.alpha = 0.6f
|
||||
binding.reviewDownVote.alpha = 0.6f
|
||||
}
|
||||
|
||||
"UP_VOTE" -> {
|
||||
binding.reviewUpVote.setImageResource(R.drawable.ic_round_upvote_active_24)
|
||||
binding.reviewDownVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
|
||||
binding.reviewUpVote.alpha = 1f
|
||||
binding.reviewDownVote.alpha = 0.6f
|
||||
}
|
||||
|
||||
"DOWN_VOTE" -> {
|
||||
binding.reviewUpVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
|
||||
binding.reviewDownVote.setImageResource(R.drawable.ic_round_upvote_active_24)
|
||||
binding.reviewDownVote.alpha = 1f
|
||||
binding.reviewUpVote.alpha = 0.6f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun rateReview(rating: String) {
|
||||
disableVote()
|
||||
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
scope.launch {
|
||||
val result = Anilist.mutation.rateReview(review.id, rating)
|
||||
if (result != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
val res = result.data.rateReview
|
||||
review.rating = res.rating
|
||||
review.ratingAmount = res.ratingAmount
|
||||
review.userRating = res.userRating
|
||||
userVote(review.userRating)
|
||||
binding.reviewTotalVotes.text = review.rating.toString()
|
||||
userVote(review.userRating)
|
||||
enableVote()
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
toast(
|
||||
binding.root.context.getString(R.string.error_message, "response is null")
|
||||
)
|
||||
enableVote()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun disableVote() {
|
||||
binding.reviewUpVote.setOnClickListener(null)
|
||||
binding.reviewDownVote.setOnClickListener(null)
|
||||
binding.reviewUpVote.isEnabled = false
|
||||
binding.reviewDownVote.isEnabled = false
|
||||
}
|
||||
|
||||
private fun enableVote() {
|
||||
binding.reviewUpVote.setOnClickListener {
|
||||
if (review.userRating == "UP_VOTE") {
|
||||
rateReview("NO_VOTE")
|
||||
} else {
|
||||
rateReview("UP_VOTE")
|
||||
}
|
||||
disableVote()
|
||||
}
|
||||
binding.reviewDownVote.setOnClickListener {
|
||||
if (review.userRating == "DOWN_VOTE") {
|
||||
rateReview("NO_VOTE")
|
||||
} else {
|
||||
rateReview("DOWN_VOTE")
|
||||
}
|
||||
disableVote()
|
||||
}
|
||||
binding.reviewUpVote.isEnabled = true
|
||||
binding.reviewDownVote.isEnabled = true
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ class CommentNotificationTask : Task {
|
||||
null
|
||||
) ?: listOf()
|
||||
val newStore = notificationStore.toMutableList()
|
||||
if (newStore.size > 10) {
|
||||
if (newStore.size > 30) {
|
||||
newStore.remove(newStore.minByOrNull { it.time })
|
||||
}
|
||||
if (newStore.any { it.content == notification.content }) {
|
||||
|
||||
@@ -13,8 +13,6 @@ data class CommentStore(
|
||||
val time: Long = System.currentTimeMillis(),
|
||||
) : java.io.Serializable {
|
||||
companion object {
|
||||
|
||||
@Suppress("INAPPROPRIATE_CONST_NAME")
|
||||
private const val serialVersionUID = 2L
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,15 @@ class SubscriptionNotificationTask : Task {
|
||||
if (ep != null) ep.number + " " + context.getString(R.string.just_released) to null
|
||||
else null
|
||||
} ?: return@map
|
||||
addSubscriptionToStore(
|
||||
SubscriptionStore(
|
||||
media.name,
|
||||
text.first,
|
||||
media.id
|
||||
)
|
||||
)
|
||||
PrefManager.setVal(PrefName.UnreadCommentNotifications,
|
||||
PrefManager.getVal<Int>(PrefName.UnreadCommentNotifications) + 1)
|
||||
val notification = createNotification(
|
||||
context.applicationContext,
|
||||
media,
|
||||
@@ -219,4 +228,17 @@ class SubscriptionNotificationTask : Task {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun addSubscriptionToStore(notification: SubscriptionStore) {
|
||||
val notificationStore = PrefManager.getNullableVal<List<SubscriptionStore>>(
|
||||
PrefName.SubscriptionNotificationStore,
|
||||
null
|
||||
) ?: listOf()
|
||||
val newStore = notificationStore.toMutableList()
|
||||
if (newStore.size >= 100) {
|
||||
newStore.remove(newStore.minByOrNull { it.time })
|
||||
}
|
||||
newStore.add(notification)
|
||||
PrefManager.setVal(PrefName.SubscriptionNotificationStore, newStore)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ani.dantotsu.notifications.subscription
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SubscriptionStore(
|
||||
val title: String,
|
||||
val content: String,
|
||||
val mediaId: Int,
|
||||
val type: String = "SUBSCRIPTION",
|
||||
val time: Long = System.currentTimeMillis(),
|
||||
) : java.io.Serializable {
|
||||
companion object {
|
||||
private const val serialVersionUID = 1L
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,11 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
|
||||
|
||||
override val name = extension.name
|
||||
override val saveName = extension.name
|
||||
override val hostUrl = extension.sources.first().name
|
||||
override val hostUrl =
|
||||
(extension.sources.first() as? AnimeHttpSource)?.baseUrl ?: extension.sources.first().name
|
||||
override val isNSFW = extension.isNsfw
|
||||
override val icon = extension.icon
|
||||
|
||||
override var selectDub: Boolean
|
||||
get() = getDub()
|
||||
set(value) {
|
||||
@@ -324,8 +327,10 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
|
||||
|
||||
override val name = extension.name
|
||||
override val saveName = extension.name
|
||||
override val hostUrl = extension.sources.first().name
|
||||
override val hostUrl =
|
||||
(extension.sources.first() as? HttpSource)?.baseUrl ?: extension.sources.first().name
|
||||
override val isNSFW = extension.isNsfw
|
||||
override val icon = extension.icon
|
||||
|
||||
override suspend fun loadChapters(
|
||||
mangaLink: String,
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
package ani.dantotsu.parsers
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import ani.dantotsu.FileUrl
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.media.Media
|
||||
import ani.dantotsu.okHttpClient
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.util.Logger
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import me.xdrop.fuzzywuzzy.FuzzySearch
|
||||
import okhttp3.Request
|
||||
import java.io.Serializable
|
||||
import java.net.URLDecoder
|
||||
import java.net.URLEncoder
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
|
||||
abstract class BaseParser {
|
||||
@@ -41,6 +45,11 @@ abstract class BaseParser {
|
||||
* **/
|
||||
open val language = "English"
|
||||
|
||||
/**
|
||||
* Icon of the site, can be null
|
||||
*/
|
||||
open val icon: Drawable? = null
|
||||
|
||||
/**
|
||||
* Search for Anime/Manga/Novel, returns a List of Responses
|
||||
*
|
||||
@@ -133,10 +142,39 @@ abstract class BaseParser {
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* ping the site to check if it's working or not.
|
||||
* @return Triple<Int, Int?, String> : First Int is the status code, Second Int is the response time in milliseconds, Third String is the response message.
|
||||
*/
|
||||
fun ping(): Triple<Int, Int?, String> {
|
||||
val client = okHttpClient
|
||||
var statusCode = 0
|
||||
var responseTime: Int? = null
|
||||
var responseMessage = ""
|
||||
try {
|
||||
val request = Request.Builder()
|
||||
.url(hostUrl)
|
||||
.build()
|
||||
responseTime = measureTimeMillis {
|
||||
client.newCall(request).execute().use { response ->
|
||||
statusCode = response.code
|
||||
responseMessage = response.message
|
||||
}
|
||||
}.toInt()
|
||||
} catch (e: Exception) {
|
||||
Logger.log("Failed to ping $name")
|
||||
statusCode = -1
|
||||
responseMessage = if (e.message.isNullOrEmpty()) "None" else e.message!!
|
||||
Logger.log(e)
|
||||
}
|
||||
return Triple(statusCode, responseTime, responseMessage)
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get an existing Search Response which was selected by the user.
|
||||
* **/
|
||||
* @param mediaId : The mediaId of the Media object.
|
||||
* @return ShowResponse? : The ShowResponse object if found, else null.
|
||||
*/
|
||||
open suspend fun loadSavedShowResponse(mediaId: Int): ShowResponse? {
|
||||
checkIfVariablesAreEmpty()
|
||||
return PrefManager.getNullableCustomVal(
|
||||
@@ -148,7 +186,10 @@ abstract class BaseParser {
|
||||
|
||||
/**
|
||||
* Used to save Shows Response using `saveName`.
|
||||
* **/
|
||||
* @param mediaId : The mediaId of the Media object.
|
||||
* @param response : The ShowResponse object to save.
|
||||
* @param selected : Boolean : If the ShowResponse was selected by the user or not.
|
||||
*/
|
||||
open fun saveShowResponse(mediaId: Int, response: ShowResponse?, selected: Boolean = false) {
|
||||
if (response != null) {
|
||||
checkIfVariablesAreEmpty()
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package ani.dantotsu.parsers
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.databinding.ItemExtensionSelectBinding
|
||||
import com.xwray.groupie.viewbinding.BindableItem
|
||||
|
||||
class ExtensionSelectItem(
|
||||
private val name: String,
|
||||
private val image: Drawable?,
|
||||
private var isSelected: Boolean,
|
||||
val selectCallback: (String, Boolean) -> Unit
|
||||
) : BindableItem<ItemExtensionSelectBinding>() {
|
||||
private lateinit var binding: ItemExtensionSelectBinding
|
||||
|
||||
override fun bind(viewBinding: ItemExtensionSelectBinding, position: Int) {
|
||||
binding = viewBinding
|
||||
binding.extensionNameTextView.text = name
|
||||
image?.let {
|
||||
binding.extensionIconImageView.setImageDrawable(it)
|
||||
}
|
||||
binding.extensionCheckBox.setOnCheckedChangeListener(null)
|
||||
binding.extensionCheckBox.isChecked = isSelected
|
||||
binding.extensionCheckBox.setOnCheckedChangeListener { _, isChecked ->
|
||||
isSelected = isChecked
|
||||
selectCallback(name, isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLayout(): Int {
|
||||
return R.layout.item_extension_select
|
||||
}
|
||||
|
||||
override fun initializeViewBinding(view: View): ItemExtensionSelectBinding {
|
||||
return ItemExtensionSelectBinding.bind(view)
|
||||
}
|
||||
}
|
||||
367
app/src/main/java/ani/dantotsu/parsers/ExtensionTestItem.kt
Normal file
367
app/src/main/java/ani/dantotsu/parsers/ExtensionTestItem.kt
Normal file
@@ -0,0 +1,367 @@
|
||||
package ani.dantotsu.parsers
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.databinding.ItemExtensionTestBinding
|
||||
import ani.dantotsu.getThemeColor
|
||||
import com.xwray.groupie.viewbinding.BindableItem
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ExtensionTestItem(
|
||||
private var extensionType: String,
|
||||
private var testType: String,
|
||||
private var extension: BaseParser,
|
||||
private var searchString: String = "Chainsaw Man"
|
||||
) : BindableItem<ItemExtensionTestBinding>() {
|
||||
private lateinit var binding: ItemExtensionTestBinding
|
||||
private lateinit var context: Context
|
||||
private var job: Job? = null
|
||||
private var isRunning = false
|
||||
private var pingResult: Triple<Int, Int?, String>? = null
|
||||
private var searchResultSize: Int? = null
|
||||
private var episodeResultSize: Int? = null
|
||||
private var serverResultSize: Int? = null
|
||||
|
||||
override fun bind(viewBinding: ItemExtensionTestBinding, position: Int) {
|
||||
binding = viewBinding
|
||||
context = binding.root.context
|
||||
binding.extensionIconImageView.setImageDrawable(extension.icon)
|
||||
binding.extensionNameTextView.text = extension.name
|
||||
binding.extensionLoading.isVisible = isRunning
|
||||
hideAllResults()
|
||||
|
||||
pingResult()
|
||||
searchResult()
|
||||
episodeResult()
|
||||
serverResult()
|
||||
}
|
||||
|
||||
override fun getLayout(): Int {
|
||||
return R.layout.item_extension_test
|
||||
}
|
||||
|
||||
override fun initializeViewBinding(view: View): ItemExtensionTestBinding {
|
||||
return ItemExtensionTestBinding.bind(view)
|
||||
}
|
||||
|
||||
private fun hideAllResults() {
|
||||
if (::binding.isInitialized.not()) return
|
||||
binding.searchResultText.isVisible = false
|
||||
binding.episodeResultText.isVisible = false
|
||||
binding.serverResultText.isVisible = false
|
||||
}
|
||||
|
||||
fun cancelJob() {
|
||||
job?.cancel()
|
||||
job = null
|
||||
binding.extensionLoading.isVisible = false
|
||||
}
|
||||
|
||||
fun startTest() {
|
||||
pingResult = null
|
||||
searchResultSize = null
|
||||
episodeResultSize = null
|
||||
serverResultSize = null
|
||||
isRunning = true
|
||||
hideAllResults()
|
||||
job?.cancel()
|
||||
job = Job()
|
||||
CoroutineScope(Dispatchers.IO + job!!).launch {
|
||||
when (extensionType) {
|
||||
"anime" -> {
|
||||
val extension = extension as AnimeParser
|
||||
runAnimeTest(extension)
|
||||
}
|
||||
|
||||
"manga" -> {
|
||||
val extension = extension as MangaParser
|
||||
runMangaTest(extension)
|
||||
}
|
||||
|
||||
"novel" -> {
|
||||
val extension = extension as NovelParser
|
||||
runNovelTest(extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun runAnimeTest(extension: AnimeParser) {
|
||||
pingResult = extension.ping()
|
||||
withContext(Dispatchers.Main) {
|
||||
pingResult()
|
||||
}
|
||||
if (testType == "ping") {
|
||||
done()
|
||||
return
|
||||
}
|
||||
val searchResult = extension.search(searchString)
|
||||
searchResultSize = searchResult.size
|
||||
withContext(Dispatchers.Main) {
|
||||
searchResult()
|
||||
}
|
||||
if (searchResultSize == 0 || testType == "basic") {
|
||||
done()
|
||||
return
|
||||
}
|
||||
val episodeResult = extension.loadEpisodes("", null, searchResult.first().sAnime!!)
|
||||
episodeResultSize = episodeResult.size
|
||||
withContext(Dispatchers.Main) {
|
||||
episodeResult()
|
||||
}
|
||||
if (episodeResultSize == 0) {
|
||||
done()
|
||||
return
|
||||
}
|
||||
val serverResult = extension.loadVideoServers("", null, episodeResult.first().sEpisode!!)
|
||||
serverResultSize = serverResult.size
|
||||
withContext(Dispatchers.Main) {
|
||||
serverResult()
|
||||
}
|
||||
|
||||
done()
|
||||
}
|
||||
|
||||
private suspend fun runMangaTest(extension: MangaParser) {
|
||||
pingResult = extension.ping()
|
||||
withContext(Dispatchers.Main) {
|
||||
pingResult()
|
||||
}
|
||||
if (testType == "ping") {
|
||||
done()
|
||||
return
|
||||
}
|
||||
val searchResult = extension.search(searchString)
|
||||
searchResultSize = searchResult.size
|
||||
withContext(Dispatchers.Main) {
|
||||
searchResult()
|
||||
}
|
||||
if (searchResultSize == 0 || testType == "basic") {
|
||||
done()
|
||||
return
|
||||
}
|
||||
val chapterResult = extension.loadChapters("", null, searchResult.first().sManga!!)
|
||||
episodeResultSize = chapterResult.size
|
||||
withContext(Dispatchers.Main) {
|
||||
episodeResult()
|
||||
}
|
||||
if (episodeResultSize == 0) {
|
||||
done()
|
||||
return
|
||||
}
|
||||
val serverResult = extension.loadImages("", chapterResult.first().sChapter)
|
||||
serverResultSize = serverResult.size
|
||||
withContext(Dispatchers.Main) {
|
||||
serverResult()
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
if (::binding.isInitialized )
|
||||
binding.extensionLoading.isVisible = false
|
||||
isRunning = false
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun runNovelTest(extension: NovelParser) {
|
||||
withContext(Dispatchers.Main) {
|
||||
pingResult()
|
||||
}
|
||||
if (testType == "ping") {
|
||||
done()
|
||||
return
|
||||
}
|
||||
val searchResult = extension.search(searchString)
|
||||
searchResultSize = searchResult.size
|
||||
withContext(Dispatchers.Main) {
|
||||
searchResult()
|
||||
}
|
||||
if (searchResultSize == 0 || testType == "basic") {
|
||||
done()
|
||||
return
|
||||
}
|
||||
val chapterResult = extension.loadBook(searchResult.first().link, null)
|
||||
episodeResultSize = chapterResult.links.size
|
||||
withContext(Dispatchers.Main) {
|
||||
episodeResult()
|
||||
serverResult()
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
if (::binding.isInitialized )
|
||||
binding.extensionLoading.isVisible = false
|
||||
isRunning = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun done() {
|
||||
if (::binding.isInitialized.not()) return
|
||||
binding.extensionLoading.isVisible = false
|
||||
isRunning = false
|
||||
}
|
||||
|
||||
private fun pingResult() {
|
||||
if (::binding.isInitialized.not()) return
|
||||
if (extensionType == "novel") {
|
||||
binding.pingResultText.isVisible = true
|
||||
binding.pingResultText.text = context.getString(R.string.test_not_supported)
|
||||
binding.pingResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_round_info_24, 0, 0, 0
|
||||
)
|
||||
return
|
||||
}
|
||||
if (pingResult == null) {
|
||||
binding.pingResultText.isVisible = false
|
||||
return
|
||||
} else {
|
||||
binding.pingResultText.isVisible = true
|
||||
}
|
||||
binding.pingResultText.setTextColor(
|
||||
context.getThemeColor(com.google.android.material.R.attr.colorPrimary)
|
||||
)
|
||||
val (code, time, message) = pingResult!!
|
||||
if (code == 200) {
|
||||
binding.pingResultText.text = context.getString(R.string.ping_success, time.toString())
|
||||
binding.pingResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_circle_check, 0, 0, 0
|
||||
)
|
||||
return
|
||||
}
|
||||
binding.pingResultText.text =
|
||||
context.getString(R.string.ping_error, code.toString(), message)
|
||||
binding.pingResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_circle_cancel, 0, 0, 0
|
||||
)
|
||||
binding.pingResultText.setTextColor(
|
||||
context.getThemeColor(com.google.android.material.R.attr.colorError)
|
||||
)
|
||||
}
|
||||
|
||||
private fun searchResult() {
|
||||
if (::binding.isInitialized.not()) return
|
||||
if (searchResultSize == null) {
|
||||
binding.searchResultText.isVisible = false
|
||||
return
|
||||
}
|
||||
binding.searchResultText.setTextColor(
|
||||
context.getThemeColor(com.google.android.material.R.attr.colorPrimary)
|
||||
)
|
||||
binding.searchResultText.isVisible = true
|
||||
if (searchResultSize == 0) {
|
||||
val text = context.getString(R.string.title_search_test,
|
||||
context.getString(R.string.no_results_found))
|
||||
binding.searchResultText.text = text
|
||||
binding.searchResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_circle_cancel, 0, 0, 0
|
||||
)
|
||||
binding.searchResultText.setTextColor(
|
||||
context.getThemeColor(com.google.android.material.R.attr.colorError)
|
||||
)
|
||||
return
|
||||
}
|
||||
val text = context.getString(R.string.title_search_test,
|
||||
context.getString(R.string.results_found, searchResultSize.toString()))
|
||||
binding.searchResultText.text = text
|
||||
binding.searchResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_circle_check, 0, 0, 0
|
||||
)
|
||||
}
|
||||
|
||||
private fun episodeResult() {
|
||||
if (::binding.isInitialized.not()) return
|
||||
if (episodeResultSize == null) {
|
||||
binding.episodeResultText.isVisible = false
|
||||
return
|
||||
}
|
||||
binding.episodeResultText.setTextColor(
|
||||
context.getThemeColor(com.google.android.material.R.attr.colorPrimary)
|
||||
)
|
||||
binding.episodeResultText.isVisible = true
|
||||
if (episodeResultSize == 0) {
|
||||
val text = when(extensionType) {
|
||||
"anime" -> context.getString(R.string.episode_search_test,
|
||||
context.getString(R.string.no_results_found))
|
||||
"manga" -> context.getString(R.string.chapter_search_test,
|
||||
context.getString(R.string.no_results_found))
|
||||
else -> context.getString(R.string.book_search_test,
|
||||
context.getString(R.string.no_results_found))
|
||||
}
|
||||
binding.episodeResultText.text = text
|
||||
binding.episodeResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_circle_cancel, 0, 0, 0
|
||||
)
|
||||
binding.episodeResultText.setTextColor(
|
||||
context.getThemeColor(com.google.android.material.R.attr.colorError)
|
||||
)
|
||||
return
|
||||
}
|
||||
val text = when(extensionType) {
|
||||
"anime" -> context.getString(R.string.episode_search_test,
|
||||
context.getString(R.string.results_found, episodeResultSize.toString()))
|
||||
"manga" -> context.getString(R.string.chapter_search_test,
|
||||
context.getString(R.string.results_found, episodeResultSize.toString()))
|
||||
else -> context.getString(R.string.book_search_test,
|
||||
context.getString(R.string.results_found, episodeResultSize.toString()))
|
||||
}
|
||||
binding.episodeResultText.text = text
|
||||
binding.episodeResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_circle_check, 0, 0, 0
|
||||
)
|
||||
}
|
||||
|
||||
private fun serverResult() {
|
||||
if (::binding.isInitialized.not()) return
|
||||
if (extensionType == "novel") {
|
||||
binding.pingResultText.isVisible = true
|
||||
binding.pingResultText.text = context.getString(R.string.test_not_supported)
|
||||
binding.pingResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_round_info_24, 0, 0, 0
|
||||
)
|
||||
return
|
||||
}
|
||||
if (serverResultSize == null) {
|
||||
binding.serverResultText.isVisible = false
|
||||
return
|
||||
}
|
||||
binding.serverResultText.setTextColor(
|
||||
context.getThemeColor(com.google.android.material.R.attr.colorPrimary)
|
||||
)
|
||||
binding.serverResultText.isVisible = true
|
||||
if (serverResultSize == 0) {
|
||||
val text = when(extensionType) {
|
||||
"anime" -> context.getString(R.string.video_search_test,
|
||||
context.getString(R.string.no_results_found))
|
||||
"manga" -> context.getString(R.string.image_search_test,
|
||||
context.getString(R.string.no_results_found))
|
||||
else -> context.getString(R.string.book_search_test,
|
||||
context.getString(R.string.no_results_found))
|
||||
}
|
||||
binding.serverResultText.text = text
|
||||
binding.serverResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_circle_cancel, 0, 0, 0
|
||||
)
|
||||
binding.serverResultText.setTextColor(
|
||||
context.getThemeColor(com.google.android.material.R.attr.colorError)
|
||||
)
|
||||
return
|
||||
}
|
||||
val text = when(extensionType) {
|
||||
"anime" -> context.getString(R.string.video_search_test,
|
||||
context.getString(R.string.results_found, serverResultSize.toString()))
|
||||
"manga" -> context.getString(R.string.image_search_test,
|
||||
context.getString(R.string.results_found, serverResultSize.toString()))
|
||||
else -> context.getString(R.string.book_search_test,
|
||||
context.getString(R.string.results_found, serverResultSize.toString()))
|
||||
}
|
||||
binding.serverResultText.text = text
|
||||
binding.serverResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_circle_check, 0, 0, 0
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package ani.dantotsu.parsers
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import ani.dantotsu.BottomSheetDialogFragment
|
||||
import ani.dantotsu.databinding.BottomSheetExtensionTestSettingsBinding
|
||||
import ani.dantotsu.parsers.novel.NovelExtensionManager
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
|
||||
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class ExtensionTestSettingsBottomDialog : BottomSheetDialogFragment() {
|
||||
private var _binding: BottomSheetExtensionTestSettingsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val adapter: GroupieAdapter = GroupieAdapter()
|
||||
private val animeExtension: AnimeExtensionManager = Injekt.get()
|
||||
private val mangaExtensions: MangaExtensionManager = Injekt.get()
|
||||
private val novelExtensions: NovelExtensionManager = Injekt.get()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
_binding = BottomSheetExtensionTestSettingsBinding.inflate(inflater, container, false)
|
||||
return _binding?.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
binding.extensionSelectionRecyclerView.adapter = adapter
|
||||
binding.extensionSelectionRecyclerView.layoutManager = LinearLayoutManager(
|
||||
context,
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false
|
||||
)
|
||||
binding.animeRadioButton.setOnCheckedChangeListener { _, b ->
|
||||
if (b) {
|
||||
extensionType = "anime"
|
||||
extensionsToTest.clear()
|
||||
setupAdapter()
|
||||
}
|
||||
}
|
||||
binding.mangaRadioButton.setOnCheckedChangeListener { _, b ->
|
||||
if (b) {
|
||||
extensionType = "manga"
|
||||
extensionsToTest.clear()
|
||||
setupAdapter()
|
||||
}
|
||||
}
|
||||
binding.novelsRadioButton.setOnCheckedChangeListener { _, b ->
|
||||
if (b) {
|
||||
extensionType = "novel"
|
||||
extensionsToTest.clear()
|
||||
setupAdapter()
|
||||
}
|
||||
}
|
||||
binding.pingRadioButton.setOnCheckedChangeListener { _, b ->
|
||||
if (b) {
|
||||
testType = "ping"
|
||||
}
|
||||
}
|
||||
binding.basicRadioButton.setOnCheckedChangeListener { _, b ->
|
||||
if (b) {
|
||||
testType = "basic"
|
||||
}
|
||||
}
|
||||
binding.fullRadioButton.setOnCheckedChangeListener { _, b ->
|
||||
if (b) {
|
||||
testType = "full"
|
||||
}
|
||||
}
|
||||
setupAdapter()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
_binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupAdapter() {
|
||||
val namesAndUrls: Map<String,Drawable?> = when (extensionType) {
|
||||
"anime" -> animeExtension.installedExtensionsFlow.value.associate { it.name to it.icon }
|
||||
"manga" -> mangaExtensions.installedExtensionsFlow.value.associate { it.name to it.icon }
|
||||
"novel" -> novelExtensions.installedExtensionsFlow.value.associate { it.name to it.icon }
|
||||
else -> emptyMap()
|
||||
}
|
||||
adapter.clear()
|
||||
namesAndUrls.forEach { (name, icon) ->
|
||||
val isSelected = extensionsToTest.contains(name)
|
||||
adapter.add(ExtensionSelectItem(name, icon, isSelected, ::selectedCallback))
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectedCallback(name: String, isSelected: Boolean) {
|
||||
if (isSelected) {
|
||||
extensionsToTest.add(name)
|
||||
} else {
|
||||
extensionsToTest.remove(name)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(): ExtensionTestSettingsBottomDialog {
|
||||
return ExtensionTestSettingsBottomDialog()
|
||||
}
|
||||
|
||||
var extensionType = "anime"
|
||||
var testType = "ping"
|
||||
var extensionsToTest: MutableList<String> = mutableListOf()
|
||||
}
|
||||
}
|
||||
112
app/src/main/java/ani/dantotsu/parsers/ParserTestActivity.kt
Normal file
112
app/src/main/java/ani/dantotsu/parsers/ParserTestActivity.kt
Normal file
@@ -0,0 +1,112 @@
|
||||
package ani.dantotsu.parsers
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.databinding.ActivityParserTestBinding
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.toast
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
|
||||
class ParserTestActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityParserTestBinding
|
||||
val adapter = GroupieAdapter()
|
||||
val extensionsToTest: MutableList<ExtensionTestItem> = mutableListOf()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeManager(this).applyTheme()
|
||||
initActivity(this)
|
||||
binding = ActivityParserTestBinding.inflate(layoutInflater)
|
||||
binding.toolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = statusBarHeight
|
||||
}
|
||||
binding.extensionResultsRecyclerView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = navBarHeight
|
||||
}
|
||||
setContentView(binding.root)
|
||||
|
||||
binding.extensionResultsRecyclerView.adapter = adapter
|
||||
binding.extensionResultsRecyclerView.layoutManager = LinearLayoutManager(
|
||||
this,
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false
|
||||
)
|
||||
binding.backButton.setOnClickListener {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
binding.optionsLayout.setOnClickListener {
|
||||
ExtensionTestSettingsBottomDialog.newInstance()
|
||||
.show(supportFragmentManager, "extension_test_settings")
|
||||
}
|
||||
|
||||
binding.startButton.setOnClickListener {
|
||||
if (ExtensionTestSettingsBottomDialog.extensionsToTest.isEmpty()) {
|
||||
toast(R.string.no_extensions_selected)
|
||||
return@setOnClickListener
|
||||
}
|
||||
extensionsToTest.forEach {
|
||||
it.cancelJob()
|
||||
}
|
||||
extensionsToTest.clear()
|
||||
adapter.clear()
|
||||
when (ExtensionTestSettingsBottomDialog.extensionType) {
|
||||
"anime" -> {
|
||||
ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name ->
|
||||
val extension =
|
||||
AnimeSources.list.find { source -> source.name == name }?.get?.value
|
||||
extension?.let {
|
||||
extensionsToTest.add(
|
||||
ExtensionTestItem(
|
||||
"anime",
|
||||
ExtensionTestSettingsBottomDialog.testType,
|
||||
it
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"manga" -> {
|
||||
ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name ->
|
||||
val extension =
|
||||
MangaSources.list.find { source -> source.name == name }?.get?.value
|
||||
extension?.let {
|
||||
extensionsToTest.add(
|
||||
ExtensionTestItem(
|
||||
"manga",
|
||||
ExtensionTestSettingsBottomDialog.testType,
|
||||
it
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"novel" -> {
|
||||
ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name ->
|
||||
val extension =
|
||||
NovelSources.list.find { source -> source.name == name }?.get?.value
|
||||
extension?.let {
|
||||
extensionsToTest.add(
|
||||
ExtensionTestItem(
|
||||
"novel",
|
||||
ExtensionTestSettingsBottomDialog.testType,
|
||||
it
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
extensionsToTest.forEach {
|
||||
adapter.add(it)
|
||||
it.startTest()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,10 @@ class ActivityItemBuilder {
|
||||
NotificationType.DANTOTSU_UPDATE -> {
|
||||
notification.context ?: "You should not see this"
|
||||
}
|
||||
|
||||
NotificationType.SUBSCRIPTION -> {
|
||||
notification.context ?: "You should not see this"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.media.MediaDetailsActivity
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.notifications.comment.CommentStore
|
||||
import ani.dantotsu.notifications.subscription.SubscriptionStore
|
||||
import ani.dantotsu.profile.ProfileActivity
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
@@ -41,6 +42,8 @@ import kotlinx.coroutines.withContext
|
||||
|
||||
class NotificationActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityFollowBinding
|
||||
private lateinit var commentStore: List<CommentStore>
|
||||
private lateinit var subscriptionStore: List<SubscriptionStore>
|
||||
private var adapter: GroupieAdapter = GroupieAdapter()
|
||||
private var notificationList: List<Notification> = emptyList()
|
||||
val filters = ArrayList<String>()
|
||||
@@ -70,6 +73,15 @@ class NotificationActivity : AppCompatActivity() {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
binding.listProgressBar.visibility = ViewGroup.VISIBLE
|
||||
commentStore = PrefManager.getNullableVal<List<CommentStore>>(
|
||||
PrefName.CommentNotificationStore,
|
||||
null
|
||||
) ?: listOf()
|
||||
subscriptionStore = PrefManager.getNullableVal<List<SubscriptionStore>>(
|
||||
PrefName.SubscriptionNotificationStore,
|
||||
null
|
||||
) ?: listOf()
|
||||
|
||||
binding.followFilterButton.setOnClickListener {
|
||||
val dialogView = LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null)
|
||||
val checkboxContainer = dialogView.findViewById<LinearLayout>(R.id.checkboxContainer)
|
||||
@@ -193,22 +205,39 @@ class NotificationActivity : AppCompatActivity() {
|
||||
notifications
|
||||
}.toMutableList()
|
||||
}
|
||||
if (activityId == -1 && currentPage == 1) {
|
||||
val commentStore = PrefManager.getNullableVal<List<CommentStore>>(
|
||||
PrefName.CommentNotificationStore,
|
||||
null
|
||||
) ?: listOf()
|
||||
if (activityId == -1) {
|
||||
val furthestTime = newNotifications.minOfOrNull { it.createdAt } ?: 0
|
||||
commentStore.forEach {
|
||||
val notification = Notification(
|
||||
it.type.toString(),
|
||||
System.currentTimeMillis().toInt(),
|
||||
commentId = it.commentId,
|
||||
notificationType = it.type.toString(),
|
||||
mediaId = it.mediaId,
|
||||
context = it.title + "\n" + it.content,
|
||||
createdAt = (it.time / 1000L).toInt(),
|
||||
)
|
||||
newNotifications += notification
|
||||
if ((it.time > furthestTime * 1000L || !hasNextPage) && notificationList.none { notification ->
|
||||
notification.commentId == it.commentId && notification.createdAt == (it.time / 1000L).toInt()
|
||||
}) {
|
||||
val notification = Notification(
|
||||
it.type.toString(),
|
||||
System.currentTimeMillis().toInt(),
|
||||
commentId = it.commentId,
|
||||
notificationType = it.type.toString(),
|
||||
mediaId = it.mediaId,
|
||||
context = it.title + "\n" + it.content,
|
||||
createdAt = (it.time / 1000L).toInt(),
|
||||
)
|
||||
newNotifications += notification
|
||||
}
|
||||
}
|
||||
subscriptionStore.forEach {
|
||||
if ((it.time > furthestTime * 1000L || !hasNextPage) && notificationList.none { notification ->
|
||||
notification.mediaId == it.mediaId && notification.createdAt == (it.time / 1000L).toInt()
|
||||
}) {
|
||||
val notification = Notification(
|
||||
it.type,
|
||||
System.currentTimeMillis().toInt(),
|
||||
commentId = it.mediaId,
|
||||
mediaId = it.mediaId,
|
||||
notificationType = it.type,
|
||||
context = it.content,
|
||||
createdAt = (it.time / 1000L).toInt(),
|
||||
)
|
||||
newNotifications += notification
|
||||
}
|
||||
}
|
||||
newNotifications.sortByDescending { it.createdAt }
|
||||
}
|
||||
|
||||
@@ -332,6 +332,20 @@ class NotificationItem(
|
||||
NotificationType.DANTOTSU_UPDATE -> {
|
||||
image(user = true)
|
||||
}
|
||||
|
||||
NotificationType.SUBSCRIPTION -> {
|
||||
image(user = true, commentNotification = true)
|
||||
binding.notificationCoverUser.setOnClickListener {
|
||||
clickCallback(
|
||||
notification.mediaId ?: 0, null, NotificationClickType.MEDIA
|
||||
)
|
||||
}
|
||||
binding.notificationBannerImage.setOnClickListener {
|
||||
clickCallback(
|
||||
notification.mediaId ?: 0, null, NotificationClickType.MEDIA
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ani.dantotsu.settings
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
@@ -13,6 +14,7 @@ import android.view.inputmethod.EditorInfo
|
||||
import android.widget.AutoCompleteTextView
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
@@ -28,6 +30,7 @@ import ani.dantotsu.media.MediaType
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.others.AndroidBug5497Workaround
|
||||
import ani.dantotsu.others.LanguageMapper
|
||||
import ani.dantotsu.parsers.ParserTestActivity
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import ani.dantotsu.statusBarHeight
|
||||
@@ -70,6 +73,14 @@ class ExtensionsActivity : AppCompatActivity() {
|
||||
bottomMargin = statusBarHeight + navBarHeight
|
||||
}
|
||||
|
||||
binding.testButton.setOnClickListener {
|
||||
ContextCompat.startActivity(
|
||||
this,
|
||||
Intent(this, ParserTestActivity::class.java),
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
val tabLayout = findViewById<TabLayout>(R.id.tabLayout)
|
||||
val viewPager = findViewById<ViewPager2>(R.id.viewPager)
|
||||
viewPager.offscreenPageLimit = 1
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ani.dantotsu.settings
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.KeyEvent
|
||||
@@ -9,6 +10,7 @@ import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@@ -20,6 +22,7 @@ import ani.dantotsu.databinding.ItemRepositoryBinding
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.media.MediaType
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.parsers.ParserTestActivity
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import ani.dantotsu.statusBarHeight
|
||||
@@ -232,6 +235,20 @@ class SettingsExtensionsActivity : AppCompatActivity() {
|
||||
setExtensionOutput(it.attachView, MediaType.MANGA)
|
||||
}
|
||||
),
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.extension_test),
|
||||
desc = getString(R.string.extension_test_desc),
|
||||
icon = R.drawable.ic_round_search_sources_24,
|
||||
isActivity = true,
|
||||
onClick = {
|
||||
ContextCompat.startActivity(
|
||||
context,
|
||||
Intent(context, ParserTestActivity::class.java),
|
||||
null
|
||||
)
|
||||
}
|
||||
),
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.user_agent),
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.databinding.ItemSubscriptionBinding
|
||||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.media.MediaDetailsActivity
|
||||
import ani.dantotsu.notifications.subscription.SubscriptionHelper
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
@@ -25,7 +26,7 @@ class SubscriptionItem(
|
||||
else
|
||||
SubscriptionHelper.getMangaParser(media.id).name
|
||||
val mediaName = media.name
|
||||
val showName = "$mediaName - $parserName"
|
||||
val showName = "$mediaName ($parserName)"
|
||||
binding.subscriptionName.text = showName
|
||||
binding.root.setOnClickListener {
|
||||
ContextCompat.startActivity(
|
||||
@@ -36,6 +37,7 @@ class SubscriptionItem(
|
||||
null
|
||||
)
|
||||
}
|
||||
binding.subscriptionCover.loadImage(media.image)
|
||||
binding.deleteSubscription.setOnClickListener {
|
||||
SubscriptionHelper.deleteSubscription(id, true)
|
||||
adapter.remove(this)
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.graphics.Color
|
||||
import ani.dantotsu.connections.comments.AuthResponse
|
||||
import ani.dantotsu.connections.mal.MAL
|
||||
import ani.dantotsu.notifications.comment.CommentStore
|
||||
import ani.dantotsu.notifications.subscription.SubscriptionStore
|
||||
import ani.dantotsu.settings.saving.internal.Location
|
||||
import ani.dantotsu.settings.saving.internal.Pref
|
||||
|
||||
@@ -186,6 +187,7 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
|
||||
LogToFile(Pref(Location.Irrelevant, Boolean::class, false)),
|
||||
RecentGlobalNotification(Pref(Location.Irrelevant, Int::class, 0)),
|
||||
CommentNotificationStore(Pref(Location.Irrelevant, List::class, listOf<CommentStore>())),
|
||||
SubscriptionNotificationStore(Pref(Location.Irrelevant, List::class, listOf<SubscriptionStore>())),
|
||||
UnreadCommentNotifications(Pref(Location.Irrelevant, Int::class, 0)),
|
||||
DownloadsDir(Pref(Location.Irrelevant, String::class, "")),
|
||||
RefreshStatus(Pref(Location.Irrelevant, Boolean::class, false)),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.extension.util
|
||||
|
||||
import android.app.DownloadManager
|
||||
import android.app.ForegroundServiceStartNotAllowedException
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -11,9 +12,11 @@ import android.os.Environment
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.net.toUri
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.media.AddonType
|
||||
import ani.dantotsu.media.MediaType
|
||||
import ani.dantotsu.media.Type
|
||||
import ani.dantotsu.toast
|
||||
import ani.dantotsu.util.Logger
|
||||
import com.jakewharton.rxrelay.PublishRelay
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
@@ -160,7 +163,16 @@ class ExtensionInstaller(private val context: Context) {
|
||||
else -> {
|
||||
val intent =
|
||||
ExtensionInstallService.getIntent(context, type, downloadId, uri, installer)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
try {
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
} catch (e: RuntimeException) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && e is ForegroundServiceStartNotAllowedException) {
|
||||
toast(context.getString(R.string.error_msg, context.getString(R.string.foreground_service_not_allowed)))
|
||||
} else {
|
||||
toast(context.getString(R.string.error_msg, e.message))
|
||||
}
|
||||
Logger.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,15 @@
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/testButton"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:contentDescription="@string/sort_by"
|
||||
app:srcCompat="@drawable/ic_round_search_sources_24"
|
||||
app:tint="?attr/colorOnBackground" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/openSettingsButton"
|
||||
android:layout_width="48dp"
|
||||
|
||||
109
app/src/main/res/layout/activity_parser_test.xml
Normal file
109
app/src/main/res/layout/activity_parser_test.xml
Normal file
@@ -0,0 +1,109 @@
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".settings.SettingsNotificationActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="12dp"
|
||||
android:src="@drawable/ic_round_arrow_back_ios_new_24"
|
||||
app:tint="?attr/colorOnBackground"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="44dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:gravity="center|start"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
|
||||
android:textColor="?attr/colorOnBackground"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/extension_test" />
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/optionsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
app:srcCompat="@drawable/ic_round_filter_24"
|
||||
app:tint="?attr/colorPrimary"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:text="@string/view_options"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:rotation="180"
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/ic_round_arrow_back_ios_new_24"
|
||||
app:tint="?attr/colorPrimary"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:visibility="visible" />
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/startButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:backgroundTint="?attr/colorPrimaryContainer"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:text="@string/start_test"
|
||||
android:textColor="?attr/colorOnPrimaryContainer"
|
||||
app:cornerRadius="12dp"
|
||||
app:iconTint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/extensionResultsRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
@@ -2,6 +2,7 @@
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:scrollbars="none"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
100
app/src/main/res/layout/bottom_sheet_extension_test_settings.xml
Normal file
100
app/src/main/res/layout/bottom_sheet_extension_test_settings.xml
Normal file
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:text="@string/extension_type"
|
||||
android:textAlignment="center"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<RadioGroup
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/animeRadioButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:checked="true"
|
||||
android:text="@string/anime"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/mangaRadioButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/manga"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/novelsRadioButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/novels"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:text="@string/test_type"
|
||||
android:textAlignment="center"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<RadioGroup
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/pingRadioButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:checked="true"
|
||||
android:text="@string/ping"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/basicRadioButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/basic"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/fullRadioButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/full"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/extensionSelectionRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
tools:listitem="@layout/item_extension_select"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -27,7 +27,6 @@
|
||||
android:id="@+id/activityUserAvatar"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_gravity="center"
|
||||
app:srcCompat="@drawable/ic_round_add_circle_24"
|
||||
tools:ignore="ContentDescription,ImageContrastCheck"
|
||||
tools:tint="@color/transparent" />
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:gravity="center_vertical"
|
||||
@@ -45,7 +45,9 @@
|
||||
android:id="@+id/devRole"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/poppins_bold" />
|
||||
android:text="@string/lorem_ipsum"
|
||||
android:maxLines="2"
|
||||
android:fontFamily="@font/poppins_semi_bold" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
40
app/src/main/res/layout/item_extension_select.xml
Normal file
40
app/src/main/res/layout/item_extension_select.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/extensionCardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSurface"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/extensionIconImageView"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="3dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/extensionNameTextView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="@string/extension_name"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/extensionCheckBox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp" />
|
||||
|
||||
</LinearLayout>
|
||||
111
app/src/main/res/layout/item_extension_test.xml
Normal file
111
app/src/main/res/layout/item_extension_test.xml
Normal file
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/extensionCardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSurface"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/extensionIconImageView"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="3dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/extensionNameTextView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="@string/extension_name"
|
||||
android:textSize="15sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/resultLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pingResultText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="10dp"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="@string/placeholder"
|
||||
android:textSize="13sp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
app:drawableStartCompat="@drawable/ic_round_help_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/searchResultText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="10dp"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="@string/placeholder"
|
||||
android:textSize="13sp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
app:drawableStartCompat="@drawable/ic_round_help_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/episodeResultText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="10dp"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="@string/placeholder"
|
||||
android:textSize="13sp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
app:drawableStartCompat="@drawable/ic_round_help_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/serverResultText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="10dp"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="@string/placeholder"
|
||||
android:textSize="13sp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
app:drawableStartCompat="@drawable/ic_round_help_24" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/extensionLoading"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginVertical="10dp"
|
||||
android:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -74,11 +74,12 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:ellipsize="end"
|
||||
android:alpha="0.75"
|
||||
android:maxLines="2"
|
||||
android:text="@string/lorem_ipsum"
|
||||
android:textSize="14sp"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
@@ -12,14 +12,16 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:backgroundTint="@color/bg_white"
|
||||
android:backgroundTint="@color/transparent"
|
||||
app:strokeColor="@color/transparent"
|
||||
app:cardCornerRadius="124dp">
|
||||
|
||||
<ImageView
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/profileUserAvatar"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
tools:ignore="ContentDescription,ImageContrastCheck"
|
||||
android:layout_width="92dp"
|
||||
android:layout_height="92dp"
|
||||
app:srcCompat="@drawable/ic_round_add_circle_24"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:tint="@color/transparent" />
|
||||
|
||||
<LinearLayout
|
||||
|
||||
182
app/src/main/res/layout/item_reviews.xml
Normal file
182
app/src/main/res/layout/item_reviews.xml
Normal file
@@ -0,0 +1,182 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/commentsCardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout5"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="8dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/reviewUserAvatarContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
|
||||
android:backgroundTint="@color/transparent"
|
||||
app:cardCornerRadius="64dp"
|
||||
app:strokeColor="@color/transparent">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/reviewUserAvatar"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_gravity="center"
|
||||
app:srcCompat="@drawable/ic_round_add_circle_24"
|
||||
tools:ignore="ContentDescription,ImageContrastCheck"
|
||||
tools:tint="@color/bg_black_50" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reviewUserDetailsLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintStart_toStartOf="@+id/reviewText"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reviewUserName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:paddingTop="1dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:singleLine="true"
|
||||
android:text="Username"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textSize="15sp"
|
||||
tools:ignore="HardcodedText,RtlSymmetry" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reviewTag"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:alpha="0.8"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:text="[1]"
|
||||
android:textSize="12sp"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:alpha="0.6"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="•"
|
||||
android:textSize="16sp"
|
||||
tools:ignore="HardcodedText,RtlSymmetry" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reviewPostTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:alpha="0.6"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="Time"
|
||||
android:textSize="12sp"
|
||||
tools:ignore="HardcodedText,RtlSymmetry" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reviewText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:maxLines="2"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="@string/slogan"
|
||||
android:textSize="12sp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@+id/linearLayout7"
|
||||
app:layout_constraintHeight_max="200dp"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/linearLayout5"
|
||||
app:layout_constraintTop_toBottomOf="@+id/reviewUserDetailsLayout" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout6"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="52dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintStart_toEndOf="@+id/linearLayout5"
|
||||
app:layout_constraintTop_toBottomOf="@+id/reviewText">
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
<ImageView
|
||||
android:id="@+id/reviewUpVote"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:alpha="0.6"
|
||||
android:padding="2dp"
|
||||
app:srcCompat="@drawable/ic_round_upvote_inactive_24"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reviewTotalVotes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="4dp"
|
||||
android:alpha="0.6"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:gravity="center"
|
||||
android:text="100"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@+id/commentUpVote"
|
||||
app:layout_constraintTop_toBottomOf="@+id/reviewText"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/reviewDownVote"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:alpha="0.6"
|
||||
android:padding="2dp"
|
||||
android:rotation="180"
|
||||
app:layout_constraintStart_toEndOf="@+id/commentTotalVotes"
|
||||
app:layout_constraintTop_toBottomOf="@+id/reviewText"
|
||||
app:srcCompat="@drawable/ic_round_upvote_inactive_24"
|
||||
tools:ignore="ContentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout7"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -30,7 +30,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_circle_cancel"
|
||||
android:src="@drawable/ic_round_close_24"
|
||||
android:textSize="14sp"
|
||||
app:tint="?attr/colorOnBackground"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
@@ -8,14 +8,35 @@
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/subscriptionCoverContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:backgroundTint="@color/bg_white"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:strokeColor="@color/transparent">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/subscriptionCover"
|
||||
android:layout_width="108dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="centerCrop"
|
||||
tools:ignore="ContentDescription,ImageContrastCheck"
|
||||
tools:srcCompat="@tools:sample/backgrounds/scenic"
|
||||
tools:tint="@color/transparent" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
<TextView
|
||||
android:id="@+id/subscriptionName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="@string/placeholder"
|
||||
android:textSize="16sp" />
|
||||
|
||||
@@ -26,7 +47,7 @@
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:contentDescription="@string/delete"
|
||||
android:src="@drawable/ic_circle_cancel"
|
||||
android:src="@drawable/ic_round_close_24"
|
||||
app:tint="?attr/colorOnBackground" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -5,16 +5,35 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemTitle"
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:text="@string/relations"
|
||||
android:textSize="16sp" />
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:padding="8dp"
|
||||
android:text="@string/relations"
|
||||
android:textSize="16sp" />
|
||||
<ImageView
|
||||
android:id="@+id/itemMore"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:padding="8dp"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/arrow_mark"
|
||||
android:textSize="16sp"
|
||||
tools:ignore="ContentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
<ani.dantotsu.FadingEdgeRecyclerView
|
||||
android:id="@+id/itemRecycler"
|
||||
|
||||
Reference in New Issue
Block a user