feat: reviews
This commit is contained in:
parent
831b99ae40
commit
a0fabd3ca6
16 changed files with 642 additions and 34 deletions
|
@ -198,6 +198,12 @@
|
|||
<activity
|
||||
android:name=".others.imagesearch.ImageSearchActivity"
|
||||
android:parentActivityName=".MainActivity" />
|
||||
<activity
|
||||
android:name=".media.ReviewActivity"
|
||||
android:parentActivityName=".media.MediaDetailsActivity" />
|
||||
<activity
|
||||
android:name=".media.ReviewViewActivity"
|
||||
android:parentActivityName=".media.ReviewActivity" />
|
||||
<activity
|
||||
android:name=".media.SearchActivity"
|
||||
android:parentActivityName=".MainActivity" />
|
||||
|
|
|
@ -2,6 +2,7 @@ package ani.dantotsu.connections.anilist
|
|||
|
||||
import ani.dantotsu.connections.anilist.Anilist.executeQuery
|
||||
import ani.dantotsu.connections.anilist.api.FuzzyDate
|
||||
import ani.dantotsu.connections.anilist.api.Query
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
|
||||
class AnilistMutations {
|
||||
|
@ -69,4 +70,10 @@ class AnilistMutations {
|
|||
val variables = """{"id":"$listId"}"""
|
||||
executeQuery<JsonObject>(query, variables)
|
||||
}
|
||||
|
||||
|
||||
suspend fun rateReview(reviewId: Int, rating: String): Query.RateReviewResponse? {
|
||||
val query = "mutation{RateReview(reviewId:$reviewId,rating:$rating){id mediaId mediaType summary body(asHtml:true)rating ratingAmount userRating score private siteUrl createdAt updatedAt user{id name bannerImage avatar{medium large}}}}"
|
||||
return executeQuery<Query.RateReviewResponse>(query)
|
||||
}
|
||||
}
|
|
@ -1504,6 +1504,13 @@ Page(page:$page,perPage:50) {
|
|||
return author
|
||||
}
|
||||
|
||||
suspend fun getReviews(mediaId: Int, page: Int = 1, sort: String = "UPDATED_AT_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
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun toggleFollow(id: Int): Query.ToggleFollow? {
|
||||
return executeQuery<Query.ToggleFollow>(
|
||||
"""mutation{ToggleFollow(userId:$id){id, isFollowing, isFollower}}"""
|
||||
|
|
|
@ -299,6 +299,70 @@ class Query {
|
|||
val following: List<ani.dantotsu.connections.anilist.api.User>?
|
||||
) : java.io.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ReviewsResponse(
|
||||
@SerialName("data")
|
||||
val data: Data
|
||||
) : java.io.Serializable {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("Page")
|
||||
val page: ReviewPage?
|
||||
) : java.io.Serializable
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ReviewPage(
|
||||
@SerialName("pageInfo")
|
||||
val pageInfo: PageInfo,
|
||||
@SerialName("reviews")
|
||||
val reviews: List<Review>?
|
||||
) : java.io.Serializable
|
||||
|
||||
@Serializable
|
||||
data class RateReviewResponse(
|
||||
@SerialName("data")
|
||||
val data: Data
|
||||
) : java.io.Serializable {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("RateReview")
|
||||
val rateReview: Review
|
||||
) : java.io.Serializable
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Review(
|
||||
@SerialName("id")
|
||||
val id: Int,
|
||||
@SerialName("mediaId")
|
||||
val mediaId: Int,
|
||||
@SerialName("mediaType")
|
||||
val mediaType: String,
|
||||
@SerialName("summary")
|
||||
val summary: String,
|
||||
@SerialName("body")
|
||||
val body: String,
|
||||
@SerialName("rating")
|
||||
var rating: Int,
|
||||
@SerialName("ratingAmount")
|
||||
var ratingAmount: Int,
|
||||
@SerialName("userRating")
|
||||
var userRating: String,
|
||||
@SerialName("score")
|
||||
val score: Int,
|
||||
@SerialName("private")
|
||||
val private: Boolean,
|
||||
@SerialName("siteUrl")
|
||||
val siteUrl: String,
|
||||
@SerialName("createdAt")
|
||||
val createdAt: Int,
|
||||
@SerialName("updatedAt")
|
||||
val updatedAt: Int?,
|
||||
@SerialName("user")
|
||||
val user: ani.dantotsu.connections.anilist.api.User?,
|
||||
) : java.io.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UserProfile(
|
||||
@SerialName("id")
|
||||
|
|
|
@ -226,7 +226,7 @@ class AnimeDownloaderService : Service() {
|
|||
task.episode
|
||||
) ?: throw Exception("Failed to create output directory")
|
||||
|
||||
outputDir.findFile("${task.getTaskName()}.mp4")?.delete()
|
||||
outputDir.findFile("${task.getTaskName()}.mkv")?.delete()
|
||||
val outputFile = outputDir.createFile("video/x-matroska", "${task.getTaskName()}.mkv")
|
||||
?: throw Exception("Failed to create output file")
|
||||
|
||||
|
@ -245,7 +245,7 @@ class AnimeDownloaderService : Service() {
|
|||
.append(defaultHeaders["User-Agent"]).append("\"\'\r\n\'")
|
||||
}
|
||||
val probeRequest =
|
||||
"-headers $headersStringBuilder -i ${task.video.file.url} -show_entries format=duration -v quiet -of csv=\"p=0\""
|
||||
"-headers $headersStringBuilder -i \"${task.video.file.url}\" -show_entries format=duration -v quiet -of csv=\"p=0\""
|
||||
ffExtension.executeFFProbe(
|
||||
probeRequest
|
||||
) {
|
||||
|
@ -256,7 +256,7 @@ class AnimeDownloaderService : Service() {
|
|||
|
||||
val headers = headersStringBuilder.toString()
|
||||
var request = "-headers $headers "
|
||||
request += "-i ${task.video.file.url} -c copy -map 0:v -map 0:a -map 0:s?" +
|
||||
request += "-i \"${task.video.file.url}\" -c copy -map 0:v -map 0:a -map 0:s?" +
|
||||
" -f matroska -timeout 600 -reconnect 1" +
|
||||
" -reconnect_streamed 1 -allowed_extensions ALL " +
|
||||
"-tls_verify 0 $path -v trace"
|
||||
|
|
|
@ -517,6 +517,7 @@ class MediaInfoFragment : Fragment() {
|
|||
}
|
||||
parent.addView(root)
|
||||
}
|
||||
}
|
||||
|
||||
ItemTitleSearchBinding.inflate(
|
||||
LayoutInflater.from(context),
|
||||
|
@ -526,18 +527,15 @@ class MediaInfoFragment : Fragment() {
|
|||
|
||||
titleSearchImage.loadImage(media.banner ?: media.cover)
|
||||
titleSearchText.text =
|
||||
getString(R.string.search_title, media.mainName())
|
||||
getString(R.string.reviews)
|
||||
titleSearchCard.setSafeOnClickListener {
|
||||
val query = Intent(requireContext(), SearchActivity::class.java)
|
||||
.putExtra("type", "ANIME")
|
||||
.putExtra("query", media.mainName())
|
||||
.putExtra("search", true)
|
||||
val query = Intent(requireContext(), ReviewActivity::class.java)
|
||||
.putExtra("mediaId", media.id)
|
||||
ContextCompat.startActivity(requireContext(), query, null)
|
||||
}
|
||||
|
||||
parent.addView(root)
|
||||
}
|
||||
}
|
||||
|
||||
ItemTitleRecyclerBinding.inflate(
|
||||
LayoutInflater.from(context),
|
||||
|
|
149
app/src/main/java/ani/dantotsu/media/ReviewActivity.kt
Normal file
149
app/src/main/java/ani/dantotsu/media/ReviewActivity.kt
Normal file
|
@ -0,0 +1,149 @@
|
|||
package ani.dantotsu.media
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableString
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.api.Query
|
||||
import ani.dantotsu.databinding.ActivityFollowBinding
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.profile.FollowerItem
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ReviewActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityFollowBinding
|
||||
val adapter = GroupieAdapter()
|
||||
private val reviews = mutableListOf<Query.Review>()
|
||||
var mediaId = 0
|
||||
private var currentPage: Int = 1
|
||||
private var hasNextPage: Boolean = true
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeManager(this).applyTheme()
|
||||
initActivity(this)
|
||||
binding = ActivityFollowBinding.inflate(layoutInflater)
|
||||
binding.listToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = statusBarHeight
|
||||
}
|
||||
binding.listFrameLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = navBarHeight
|
||||
}
|
||||
setContentView(binding.root)
|
||||
mediaId = intent.getIntExtra("mediaId", -1)
|
||||
if (mediaId == -1) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
binding.followerGrid.visibility = View.GONE
|
||||
binding.followerList.visibility = View.GONE
|
||||
binding.followFilterButton.visibility = View.GONE
|
||||
binding.listTitle.text = getString(R.string.reviews)
|
||||
binding.listRecyclerView.adapter = adapter
|
||||
binding.listRecyclerView.layoutManager = LinearLayoutManager(
|
||||
this,
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false
|
||||
)
|
||||
binding.listProgressBar.visibility = View.VISIBLE
|
||||
binding.listBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val response = Anilist.query.getReviews(mediaId)
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.listProgressBar.visibility = View.GONE
|
||||
binding.listRecyclerView.setOnTouchListener { _, event ->
|
||||
if (event?.action == MotionEvent.ACTION_UP) {
|
||||
if (hasNextPage && !binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible
|
||||
&& binding.listRecyclerView.adapter!!.itemCount != 0 &&
|
||||
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
|
||||
) {
|
||||
binding.followRefresh.visibility = ViewGroup.VISIBLE
|
||||
loadPage(++currentPage) {
|
||||
binding.followRefresh.visibility = ViewGroup.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
currentPage = response?.data?.page?.pageInfo?.currentPage ?: 1
|
||||
hasNextPage = response?.data?.page?.pageInfo?.hasNextPage ?: false
|
||||
response?.data?.page?.reviews?.let {
|
||||
reviews.addAll(it)
|
||||
fillList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadPage(page: Int, callback: () -> Unit) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val response = Anilist.query.getReviews(mediaId, page)
|
||||
currentPage = response?.data?.page?.pageInfo?.currentPage ?: 1
|
||||
hasNextPage = response?.data?.page?.pageInfo?.hasNextPage ?: false
|
||||
withContext(Dispatchers.Main) {
|
||||
response?.data?.page?.reviews?.let {
|
||||
reviews.addAll(it)
|
||||
fillList()
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
this::onUserClick
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onUserClick(userId: Int) {
|
||||
val review = reviews.find { it.id == userId }
|
||||
if (review != null) {
|
||||
startActivity(Intent(this, ReviewViewActivity::class.java).putExtra("review", review))
|
||||
}
|
||||
}
|
||||
}
|
178
app/src/main/java/ani/dantotsu/media/ReviewViewActivity.kt
Normal file
178
app/src/main/java/ani/dantotsu/media/ReviewViewActivity.kt
Normal file
|
@ -0,0 +1,178 @@
|
|||
package ani.dantotsu.media
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.api.Query
|
||||
import ani.dantotsu.databinding.ActivityReviewViewBinding
|
||||
import ani.dantotsu.getThemeColor
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.profile.activity.ActivityItemBuilder
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.toast
|
||||
import ani.dantotsu.util.AniMarkdown
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ReviewViewActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityReviewViewBinding
|
||||
private lateinit var review: Query.Review
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeManager(this).applyTheme()
|
||||
initActivity(this)
|
||||
binding = ActivityReviewViewBinding.inflate(layoutInflater)
|
||||
binding.userContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = statusBarHeight
|
||||
}
|
||||
binding.reviewContent.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin += navBarHeight
|
||||
}
|
||||
setContentView(binding.root)
|
||||
review = intent.getSerializableExtra("review") as Query.Review
|
||||
binding.userName.text = review.user?.name
|
||||
binding.userAvatar.loadImage(review.user?.avatar?.medium)
|
||||
binding.userTime.text = ActivityItemBuilder.getDateTime(review.createdAt)
|
||||
binding.profileUserBio.settings.loadWithOverviewMode = true
|
||||
binding.profileUserBio.settings.useWideViewPort = true
|
||||
binding.profileUserBio.setInitialScale(1)
|
||||
val styledHtml = AniMarkdown.getFullAniHTML(
|
||||
review.body,
|
||||
ContextCompat.getColor(this, R.color.bg_opp)
|
||||
)
|
||||
binding.profileUserBio.loadDataWithBaseURL(
|
||||
null,
|
||||
styledHtml,
|
||||
"text/html",
|
||||
"utf-8",
|
||||
null
|
||||
)
|
||||
binding.profileUserBio.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
this,
|
||||
android.R.color.transparent
|
||||
)
|
||||
)
|
||||
binding.profileUserBio.setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
||||
binding.profileUserBio.webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
binding.profileUserBio.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
this@ReviewViewActivity,
|
||||
android.R.color.transparent
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
userVote(review.userRating)
|
||||
enableVote()
|
||||
binding.voteCount.text = review.rating.toString()
|
||||
binding.voteText.text = getString(
|
||||
R.string.vote_out_of_total,
|
||||
review.rating.toString(),
|
||||
review.ratingAmount.toString()
|
||||
)
|
||||
}
|
||||
|
||||
private fun userVote(type: String) {
|
||||
val selectedColor = getThemeColor(com.google.android.material.R.attr.colorPrimary)
|
||||
val unselectedColor = getThemeColor(androidx.appcompat.R.attr.colorControlNormal)
|
||||
when (type) {
|
||||
"NO_VOTE" -> {
|
||||
binding.upvote.setColorFilter(unselectedColor)
|
||||
binding.downvote.setColorFilter(unselectedColor)
|
||||
}
|
||||
|
||||
"UP_VOTE" -> {
|
||||
binding.upvote.setColorFilter(selectedColor)
|
||||
binding.downvote.setColorFilter(unselectedColor)
|
||||
}
|
||||
|
||||
"DOWN_VOTE" -> {
|
||||
binding.upvote.setColorFilter(unselectedColor)
|
||||
binding.downvote.setColorFilter(selectedColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun rateReview(rating: String) {
|
||||
disableVote()
|
||||
lifecycleScope.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.voteCount.text = review.rating.toString()
|
||||
binding.voteText.text = getString(
|
||||
R.string.vote_out_of_total,
|
||||
review.rating.toString(),
|
||||
review.ratingAmount.toString()
|
||||
)
|
||||
userVote(review.userRating)
|
||||
enableVote()
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
toast(
|
||||
getString(R.string.error_message, "response is null")
|
||||
)
|
||||
enableVote()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun disableVote() {
|
||||
binding.upvote.setOnClickListener(null)
|
||||
binding.downvote.setOnClickListener(null)
|
||||
binding.upvote.isEnabled = false
|
||||
binding.downvote.isEnabled = false
|
||||
}
|
||||
|
||||
private fun enableVote() {
|
||||
binding.upvote.setOnClickListener {
|
||||
if (review.userRating == "UP_VOTE") {
|
||||
rateReview("NO_VOTE")
|
||||
} else {
|
||||
rateReview("UP_VOTE")
|
||||
}
|
||||
disableVote()
|
||||
}
|
||||
binding.downvote.setOnClickListener {
|
||||
if (review.userRating == "DOWN_VOTE") {
|
||||
rateReview("NO_VOTE")
|
||||
} else {
|
||||
rateReview("DOWN_VOTE")
|
||||
}
|
||||
disableVote()
|
||||
}
|
||||
binding.upvote.isEnabled = true
|
||||
binding.downvote.isEnabled = true
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package ani.dantotsu.profile
|
|||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableString
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.ImageButton
|
||||
|
@ -54,7 +55,7 @@ class FollowActivity : AppCompatActivity() {
|
|||
)
|
||||
binding.listRecyclerView.adapter = adapter
|
||||
binding.listProgressBar.visibility = View.VISIBLE
|
||||
binding.listBack.setOnClickListener { finish() }
|
||||
binding.listBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
|
||||
|
||||
val title = intent.getStringExtra("title")
|
||||
val userID = intent.getIntExtra("userId", 0)
|
||||
|
@ -97,10 +98,11 @@ class FollowActivity : AppCompatActivity() {
|
|||
}
|
||||
users?.forEach { user ->
|
||||
if (getLayoutType(selected) == 0) {
|
||||
val username = SpannableString(user.name ?: "Unknown")
|
||||
adapter.add(
|
||||
FollowerItem(
|
||||
user.id,
|
||||
user.name ?: "Unknown",
|
||||
username,
|
||||
user.avatar?.medium,
|
||||
user.bannerImage ?: user.avatar?.medium
|
||||
) { onUserClick(it) })
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ani.dantotsu.profile
|
||||
|
||||
|
||||
import android.text.SpannableString
|
||||
import android.view.View
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.blurImage
|
||||
|
@ -10,9 +11,10 @@ import com.xwray.groupie.viewbinding.BindableItem
|
|||
|
||||
class FollowerItem(
|
||||
private val id: Int,
|
||||
private val name: String,
|
||||
private val name: SpannableString,
|
||||
private val avatar: String?,
|
||||
private val banner: String?,
|
||||
private val altText: String? = null,
|
||||
val clickCallback: (Int) -> Unit
|
||||
) : BindableItem<ItemFollowerBinding>() {
|
||||
private lateinit var binding: ItemFollowerBinding
|
||||
|
@ -21,6 +23,10 @@ class FollowerItem(
|
|||
binding = viewBinding
|
||||
binding.profileUserName.text = name
|
||||
avatar?.let { binding.profileUserAvatar.loadImage(it) }
|
||||
altText?.let {
|
||||
binding.altText.visibility = View.VISIBLE
|
||||
binding.altText.text = it
|
||||
}
|
||||
blurImage(binding.profileBannerImage, banner ?: avatar)
|
||||
binding.root.setOnClickListener { clickCallback(id) }
|
||||
}
|
||||
|
|
12
app/src/main/res/drawable/ic_thumbs.xml
Normal file
12
app/src/main/res/drawable/ic_thumbs.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M7.24,11V20H5.63C4.73,20 4.01,19.28 4.01,18.39V12.62C4.01,11.73 4.74,11 5.63,11H7.24ZM18.5,9.5H13.72V6C13.72,4.9 12.82,4 11.73,4H11.64C11.24,4 10.88,4.24 10.72,4.61L7.99,11V20H17.19C17.92,20 18.54,19.48 18.67,18.76L19.99,11.26C20.15,10.34 19.45,9.5 18.51,9.5H18.5Z" />
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/surface_rounded_bg.xml
Normal file
5
app/src/main/res/drawable/surface_rounded_bg.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?attr/colorSurfaceVariant" />
|
||||
<corners android:radius="16dp" />
|
||||
</shape>
|
155
app/src/main/res/layout/activity_review_view.xml
Normal file
155
app/src/main/res/layout/activity_review_view.xml
Normal file
|
@ -0,0 +1,155 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reviewContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/userContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/userAvatarContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_margin="12dp"
|
||||
android:backgroundTint="@color/transparent"
|
||||
app:cardCornerRadius="64dp"
|
||||
app:strokeColor="@color/transparent">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/userAvatar"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
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>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/userName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
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="@color/bg_opp"
|
||||
android:textSize="18sp"
|
||||
tools:ignore="HardcodedText,RtlSymmetry" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:alpha="0.6"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="•"
|
||||
android:textSize="18sp"
|
||||
tools:ignore="HardcodedText,RtlSymmetry" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/userTime"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_weight="1"
|
||||
android:alpha="0.6"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="Time"
|
||||
android:textSize="14sp"
|
||||
tools:ignore="HardcodedText,RtlSymmetry" />
|
||||
</LinearLayout>
|
||||
|
||||
<WebView
|
||||
android:id="@+id/profileUserBio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:nestedScrollingEnabled="true"
|
||||
android:padding="16dp"
|
||||
android:textAlignment="textStart"
|
||||
tools:text="@string/slogan" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reviewContent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:background="@drawable/surface_rounded_bg"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/downvote"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:scaleX="-1"
|
||||
android:scaleY="-1"
|
||||
android:src="@drawable/ic_thumbs"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/voteCount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="0"
|
||||
android:textSize="18sp"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/upvote"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:src="@drawable/ic_thumbs"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/voteText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="8dp"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="@string/vote_out_of_total"
|
||||
android:textSize="14sp"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
|
@ -52,14 +52,34 @@
|
|||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/profileUserInfoContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="100dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/profileUserName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="120dp"
|
||||
android:fontFamily="@font/poppins_semi_bold"
|
||||
android:text="@string/username"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/altText"
|
||||
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:maxLines="2"
|
||||
android:text="@string/lorem_ipsum"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</FrameLayout>
|
|
@ -50,7 +50,7 @@
|
|||
android:gravity="center"
|
||||
android:maxLines="2"
|
||||
android:padding="4dp"
|
||||
android:text="@string/search"
|
||||
android:text="@string/reviews"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/bg_white"
|
||||
android:textSize="14sp" />
|
||||
|
|
|
@ -430,7 +430,7 @@
|
|||
<string name="error_message">Error: %1$s</string>
|
||||
<string name="install_step">Step: %1$s</string>
|
||||
<string name="review">Review</string>
|
||||
|
||||
<string name="reviews">Reviews</string>
|
||||
<string name="discord_nothing_button">Display only the first button</string>
|
||||
<string name="discord_dantotsu_button">Display dantotsu in the second button</string>
|
||||
<string name="discord_anilist_button">Display your AniList profile instead</string>
|
||||
|
@ -850,8 +850,6 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
|
|||
<string name="do_it">Do it!</string>
|
||||
<string name="password">Password</string>
|
||||
|
||||
<string name="search_title">Search %1$s</string>
|
||||
|
||||
<string name="profile_stats_widget">Track progress directly from your home screen</string>
|
||||
<string name="anime_watched">Anime\nWatched</string>
|
||||
<string name="manga_read">Manga\nRead</string>
|
||||
|
@ -981,4 +979,5 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
|
|||
<string name="download_subtitle">Download Subtitle</string>
|
||||
<string name="no_video_selected">No video selected</string>
|
||||
<string name="no_subtitles_available">No subtitles available</string>
|
||||
<string name="vote_out_of_total">(%1$s out of %2$s liked this review)</string>
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue